Compare commits

...

93 Commits

Author SHA1 Message Date
pierre dd13073037 docs(connect-android): add build note 2023-03-15 12:29:23 +01:00
Pierre Dommerc 1010df1077 refactor(wallet): ui adjustments (#3182) 2023-03-15 12:22:00 +01:00
Bogdan-Ștefan Neacşu 9eaf9cf491 Feature/fix resharing (#3139)
* Compare verified vks against current group instead of initial dealers

* Fix various dkg logs

* API auto-advance epoch even on corrupt states

* Use verified vks as ultimate truth for dealers

* Set initial dealers based of verified vk

* Extend register period even more

* Fix test

* Use shares from current epoch

* Save initial dealers only when triggering resharing

* Fix tests

* Backup the last InProgress state too

* Reset previous signers that are not initial dealers

* Add unit test for bug reproduction

* More verbose debug logging

* Handle edge case for coconut keypair removal

* Update dkg api test

* Remove dealings directly for each key

* Replacement data is saved only on the first reshare start

* More debug logging

* On failed DKG, just reset

* Clippy fix
2023-03-15 11:16:43 +00:00
pierre 8e96318478 build(connect-android): try to fix fdroid build 2023-03-15 10:25:53 +01:00
Jędrzej Stuczyński 54287666e8 chore: simplify mnemonic zeroize story (#3165)
* updated bip39 dependency to simplify our zeroize story

* Replaced UserPassword wrapper with Zeroizing type alias

* fixed wallet-types cosmwasm-std dependency version
2023-03-13 11:29:56 +00:00
Tommy Verrall 6de829163d Merge pull request #3173 from nymtech/feature/nym-cli-tweak
Feature/nym cli tweak
2023-03-13 13:24:13 +02:00
benedettadavico adb5ed7c30 typo fix 2023-03-13 12:14:14 +01:00
benedettadavico 2b019e57df merge develop 2023-03-13 12:12:00 +01:00
benedettadavico 30c07712e3 format 2023-03-13 12:03:38 +01:00
benedettadavico 82c92501d9 vesting stuff 2023-03-13 12:01:22 +01:00
benedettadavico c2a871a1a7 typo 2023-03-10 17:10:27 +01:00
benedettadavico dfd7bd5889 adding pledge more 2023-03-10 17:10:01 +01:00
Pierre Dommerc 7ff043d8df Feature/bonding signature UI (#3157)
* wip

* updated gateways 'sign' command

* in-wallet verification of mix bonding signature

* changed signature of vesting contract trait method

* updated wallet bonding endpoints

* tauri commands for generating signing payloads

* renamed signer to sender

* verifying new signatures in the contract

* fixed existing mixnet unit tests

* unit tests for invalid signatures

* fixed other usages of MessageSignature + FromStr

* using base58-encoded serialization

* removed owner-signature from details response

* added ability to construct bonding payloads via nym-cli

* removed signature from bonding payload args

* moved 'message_type' from 'ContractMessageContent' to 'SignableMessage'

* refactor(wallet-rust): rename owner_signature args

* feat(wallet-bonding): handle user signature

* feat(wallet-bonding): fix bonding

* feat(wallet-bonding): fix lint issue

* feat(wallet-bonding): ui adjustment

* make the location field mandatory for payload signing

* feat(wallet-bonding): remove ownersignature field, remove dead code

---------

Co-authored-by: Jędrzej Stuczyński <jedrzej.stuczynski@gmail.com>
Co-authored-by: Tommy Verrall <tommyvez@protonmail.com>
2023-03-10 15:53:21 +01:00
Jon Häggblad c904d245d2 Remove old erc20 bandwidth-claim-contract (#3162) 2023-03-08 13:57:11 +01:00
Jon Häggblad d315a2a91b Add nym prefix to coconut contracts and crates (#3160)
* Add nym prefix to coconut-dkg

* Add nym prefix to coconut-bandwidth

* Add nym prefix to bandwidth-claim-contract

* Add nym prefix to coconut-bandwidth-contract-common

* Add nym prefix to coconut-dkg-common

* Add nym prefix to group-contract-common

* Add nym prefix to multisig-contract-common

* Add nym prefix to coconut-interface

* Add nym prefix to credential-storage

* rustfmt

* Mark coconut-test crate as private

* Fix build errors

* rustfmt
2023-03-08 13:17:09 +01:00
Tommy Verrall 0741d05ab3 Merge pull request #3140 from nymtech/feature/mock-nym-api
Add mock Nym API Typescript package
2023-03-08 14:06:54 +02:00
Jon Häggblad 61aa920362 Fix unnecessary parentheses around index expression (#3159) 2023-03-08 12:21:24 +01:00
Jon Häggblad 24b9b17e64 Add a few more nym- crate prefixes (#3158)
* Add nym- to socks5-prefixes crate

* Update imports

* rustfmt

* Add nym-socks5 prefix to proxy-helpers crate

* rustfmt

* Add nym prefix to ordered-buffer crate

* rustfmt

* Add nym prefix to service-providers-common crate

* rustfmt

* Add nym prefix to dkg crate

* Add nym prefix to credentials crate

* rustfmt

* fix build fail in tests
2023-03-08 11:56:29 +01:00
Jon Häggblad 5e3e633c4b Merge branch 'jon/fix/docs-rs-build' into develop 2023-03-08 09:37:54 +01:00
Jon Häggblad f3cff902ba Bump nym-vesting-contract to 1.2.0-pre.1 2023-03-08 09:21:27 +01:00
Jon Häggblad 4fe1b4c26f Fix docs.rs build for nym-vesting-contract and nym-bin-common 2023-03-08 09:14:07 +01:00
farbanas cb24a08b06 Merge branch 'master' into develop 2023-03-07 15:22:49 +01:00
farbanas 597a53d11a chore: bump mixnet and vesting contracts to prerelease version, update cargo locks 2023-03-07 15:17:07 +01:00
farbanas 639deeb502 Merge branch 'release/v1.1.12' 2023-03-07 14:32:34 +01:00
farbanas 03e082725e chore: update changelogs for release 2023-03-07 14:30:38 +01:00
farbanas 3e3307887e chore: bumped versions for release 2023-03-07 14:19:45 +01:00
farbanas d85683aaa8 chore: bumped version of contracts 2023-03-07 13:49:03 +01:00
farbanas e89ed985fc chore: bumped version of common crates 2023-03-07 13:27:03 +01:00
Jon Häggblad 45ebd7c37a Add list of published common crates 2023-03-07 09:58:07 +01:00
Jon Häggblad 3506020e55 Update Cargo.lock (#3152) 2023-03-07 09:57:09 +01:00
Pierre Dommerc eca77d684b feat(wallet): send - add user fees settings and memo field (#3146)
* feat(wallet): send - add user fees settings, memo field

* fix(wallet): send, custom fees validation
2023-03-06 18:26:07 +01:00
Jon Häggblad e263a4a21f wallet_storage: fix clippy useless-conversion (#3148) 2023-03-06 17:24:20 +01:00
Jon Häggblad dc4353c682 Fix docs.rs build (#3145)
* contracts: fix docs.rs build

* Cargo.lock

* rustfmt

* vesting: option_env for git info

* Change to vergen =7.4.3
2023-03-06 16:38:14 +01:00
Fouad e1d8069967 Explorer Service Provider List (#3142)
* create service providers route

* make request for well known service providers

* fetch and display service providers

* service provider overview

handle undefined data

fix linting

fix type

* use full width column
2023-03-06 16:36:55 +01:00
farbanas 060726b7a3 fix: add step to install wasm-opt to our CI for building and uploading binaries 2023-03-06 16:35:31 +01:00
Fouad f72ccb0f0d Update tooltips for routing and average score (#3133)
* update tooltips for routing and average score

* fix up table alignment

fix lint errors

* add node_performance to explorer api response for mixnodes

* use mixnode node_performance for avg and lastest values

* move stake sat to top table

fix lint errors

* update stake saturation text color
2023-03-06 15:58:33 +01:00
Jędrzej Stuczyński 2ff6bfbdd8 feat: ability to inject custom topology into clients (#3055)
* ability to specify custom TopologyProvider in TopologyRefresher

* topology provider builder method for base client

* ability to take manual control over topology

* wasm fixes

* added topology injection to nym-sdk API

* added examples to nym-sdk and exposed additional helper methods
2023-03-06 13:50:06 +00:00
Bogdan-Ștefan Neacșu d4f0b4772b Fix Service stats address 2023-03-06 14:00:14 +02:00
Jędrzej Stuczyński 17d258d094 renamed serialized GetCW2ContractVersion to 'get_cw2_contract_version' instead of 'get_c_w2_contract_version' 2023-03-03 15:29:10 +00:00
Drazen Urch 8288d38257 Audit fixes (#2922)
* oak-2

* oak-8

* oak-13

* oak-15

* oak-18

* Minor clippy nit

* 2023-01-13-OAK-6

* 2023-01-13-OAK-3

* 2023-01-13-OAK-13

Implemented via direct dependency on cw2 and calling the appropriate code on migration

* Removed few instances of password being unecessarily copied

* 2023-01-13-OAK-10

* 2023-01-13-OAK-12

* 2021-09-13-JP-S-NYM-02

* 2021-09-13-JP-S-NYM-03

* Removed further instances of needlessly copying the mnemonic

* 2021-09-13-JP-O-PROT-03

* 2021-09-13-JP-S-NYM-01*

*: we still have one vulnerability on 'time' pulled from chrono via sqlx. However, apparently its usage is fine... Having said that, I'd still recommend removing all dependencies on chrono, but this will require some database migrations...

* 2023-01-13-OAK-11 (#3009)

* wip

* Introducing the concept of starting epoch transition in `nym-api`

* split epoch operations into multiple files

* epoch operation failure recovery

* sending rewarding transactions in correct order

* tests and fixes due to epoch state progression

* lint

* missed rebasing import changes

* Setting cw2 contract version during first migration run

* calling 'reconcile_epoch_events' at least once

* Made message to BeginEpochTransition more consistent with other variants

* Merge layer assignment updates

---------

Co-authored-by: Jędrzej Stuczyński <jedrzej.stuczynski@gmail.com>
2023-03-03 14:15:38 +00:00
Jędrzej Stuczyński 1cd560c85e fixed standalone build of 'client-core' 2023-03-03 11:39:59 +00:00
Mark Sinclair 9018642992 Add mock Nym API Typescript package 2023-03-03 11:09:35 +00:00
Pierre Dommerc 04e652441e fix(explorer): fix layout responsiveness in mixnode details view (#3130) 2023-03-01 16:29:47 +01:00
Fouad ccf5990bc7 update selected service provider description style (#3128) 2023-03-01 16:22:52 +01:00
Fouad 3d7c9ee2b8 NE - fix gateways' version number sorting on the gateway list page (#3131)
* fix filter input sizes

* reorder gateway columns

* update tooltips

* fix column sorting in explorer lists

fix lint errors
2023-03-01 16:21:33 +01:00
Jędrzej Stuczyński ad35c4006e Fixed logged hours value when determining staleness of persisted surb data (#2690) 2023-03-01 14:30:49 +00:00
Pierre Dommerc 2ec790e851 fix(explorer): fix layout responsiveness in mixnode details view (#3130) 2023-03-01 15:16:40 +01:00
Jędrzej Stuczyński 8f8cec0785 Fixed the build badge 2023-03-01 10:37:44 +00:00
farbanas 4a80cd301b fix: add step to install wasm-opt to our CI for building and uploading binaries 2023-03-01 11:06:40 +01:00
farbanas 63524eceff fix: update versions of the latest release in changelogs from 1.0.11 to 1.1.11 2023-03-01 11:06:40 +01:00
Fouad a477b007e1 TS Validator Client updates (#3085)
* Squashed clients/validator content from old branch

* fix up tests

fix up linting

* add bundle script

* add build prod script to package json

update tests

* update readme + copy to dist output

update global types

update types and tests!

* update package build

* move types and tests into src

* Squashed clients/validator content from old branch

fix up tests

fix up linting

* add bundle script

* add build prod script to package json

update tests

* update readme + copy to dist output

update global types

update types and tests!

update package build

* move types and tests into src

* build to sub-dir

* Fixing the few broken tests

---------

Co-authored-by: Jon Häggblad <jon.haggblad@gmail.com>
Co-authored-by: benedettadavico <benedetta.davico@gmail.com>
2023-03-01 10:22:35 +01:00
Jędrzej Stuczyński 5b81510325 added additional makefile targets that skip mobile-related steps (#3127) 2023-03-01 09:12:47 +00:00
farbanas 3bef973326 fix: update versions of the latest release in changelogs from 1.0.11 to 1.1.11 2023-02-28 17:27:58 +01:00
farbanas bc5bb271d8 feat: remove unused workflows, update a couple of if conditions in github workflows and add the command for optimizing contract sizes to Makefile 2023-02-28 17:26:39 +01:00
farbanas 158e3cb073 Merge branch 'master' into develop 2023-02-28 17:09:58 +01:00
farbanas 36fb0eba29 Update versions as part of release/v1.1.11 2023-02-28 13:53:14 +01:00
farbanas 1b37e85418 Update changelogs as part of release/v1.1.11 2023-02-28 13:43:32 +01:00
Bogdan-Ștefan Neacşu 6a93497c8f Extend public key submission in case no dealer registered (#3106) 2023-02-28 12:58:01 +01:00
Tommy Verrall b8ee3465f8 Update build_and_run.sh
remove unnecessary environment variable
2023-02-28 10:33:45 +01:00
Mark Sinclair a7dfb36a84 Merge pull request #3111 from nymtech/feature/sdk-1.1.7
Typescript SDK 1.1.7
2023-02-27 18:07:08 +00:00
Mark Sinclair 14a7b5bdc8 Typescript SDK 1.1.7 2023-02-27 17:55:41 +00:00
Tommy Verrall ffbd76539a Merge pull request #3108 from nymtech/feature/nym-connect-select-sp
Feature/nym connect select sp
2023-02-27 17:48:31 +02:00
fmtabbara bdcc19e86a PR update 2023-02-27 15:33:54 +00:00
fmtabbara 7929bac685 turn off user defined SP address is input it empty 2023-02-27 14:20:16 +00:00
Jon Häggblad f590aad42c nym-api: uptime rework (#3053)
* nym-api: cache updates as node performance

* nym-api: update get mixnode avg_uptime endpoint

* nym-api: mixnode report to use cached data

* nym-api: annotate gateway bond with node performance

* nym-api: gateway report to use cached data

* wip

* Add get_gateway_avg_uptime

* Add comment

* update NR gateways to include node_performance on frontend

* use node_performance values on frontend

* fixup select gateway from list

* fix up lint errors

---------

Co-authored-by: fmtabbara <fmtabbara@hotmail.co.uk>
2023-02-27 12:40:00 +01:00
fmtabbara ec23f3dcb7 disable input when connected
fix lint errors
2023-02-27 11:28:38 +00:00
fmtabbara 9644eb4329 pick service provider 2023-02-27 11:28:38 +00:00
fmtabbara 7a4c6e4ed4 storage type
update types
2023-02-27 11:28:36 +00:00
fmtabbara d5ad504104 reuseable storage functions 2023-02-27 11:28:36 +00:00
Tommy Verrall d684f6d7ae Merge pull request #3099 from nymtech/feature/nym-connect-select-sp
NymConnect Select a service provider
2023-02-27 12:26:00 +02:00
farbanas 1ba6444e72 delete gitlab agent 2023-02-27 10:10:42 +01:00
farbanas dc08b1170a add gitlab agent 2023-02-27 10:03:28 +01:00
Bogdan-Ștefan Neacşu f0d9703587 Feature/fix db name collision (#3103)
* Fix db naming collision

* Increase gateway timeout to accommodate bandwidth spending time
2023-02-24 16:52:56 +02:00
Tommy Verrall aa78bf702c Merge pull request #3104 from nymtech/feature/nym-api-tests
some small api test fixes
2023-02-24 15:09:32 +02:00
benedettadavico cd9cdfa5bb some small api test fixes 2023-02-24 14:06:56 +01:00
Pierre Dommerc 7e1e86bc77 build(nc-mobile): prepare for fdroid inclusion (#3102)
* ci(nc-android): remove uneeded deps

* chore(nc-mobile): improve android build

* ci(nc-mobile): remove useless system deps

* ci(nc-mobile): remove useless system deps

* Revert "ci(nc-mobile): remove useless system deps"

This reverts commit 011db2c58e.

* ci(nc-mobile): restore uneeded sys deps (<3 debian based distro)
2023-02-24 14:06:41 +01:00
Raphaël Walther 2178f2b509 Github Actions: fix notification display issue 2023-02-24 11:12:53 +01:00
fmtabbara c08efef8ed add autofocus to IdentityKeyComponent 2023-02-23 22:58:59 +00:00
fmtabbara 6d29774744 style tweaks 2023-02-23 22:57:21 +00:00
fmtabbara af6bab7703 fix lint error 2023-02-23 17:40:39 +00:00
fmtabbara 7033f92d82 add UI for picking service provider 2023-02-23 17:33:03 +00:00
Tommy Verrall 8ed808124e Update build-and-upload-binaries-ci.yml
publish credential binary
2023-02-23 17:15:32 +01:00
Tommy Verrall 0deef37778 Update ci-binary-checker.yml
correct positioning on install
2023-02-23 16:56:25 +01:00
Tommy Verrall d759462e4e Update ci-binary-checker.yml
install jq
2023-02-23 16:53:23 +01:00
Tommy Verrall f36cb3a00b Update ci-binary-checker.yml
use custom runner
2023-02-23 16:50:02 +01:00
Tommy Verrall 6428f90b5a Update README.md 2023-02-23 15:38:20 +01:00
Tommy Verrall e2d00fb002 Merge pull request #3098 from nymtech/feature/update-checker-to-use-master
Feature/update checker to use master
2023-02-23 16:37:37 +02:00
Tommy Verrall dc556706c1 Update ci-binary-checker.yml
update the to run from the branch
2023-02-23 13:25:46 +01:00
Tommy Verrall 18a3366cf3 Update ci-binary-checker.yml 2023-02-23 12:37:41 +01:00
Tommy Verrall ea161329dc Update ci-binary-checker.yml
testing manually the changes for the timebeing
2023-02-23 12:34:26 +01:00
Tommy Verrall 02a621ed8b Update ci-binary-checker.yml 2023-02-23 12:16:50 +01:00
Tommy Verrall 5784e7519f Merge pull request #3096 from nymtech/binary-checker-action
Create ci-binary-checker.yml
2023-02-23 13:12:25 +02:00
Tommy Verrall 323d5fbb3c Create ci-binary-checker.yml 2023-02-23 12:10:39 +01:00
507 changed files with 14686 additions and 6676 deletions
@@ -69,7 +69,7 @@ jobs:
uses: actions-rs/cargo@v1
with:
command: build
args: --workspace --release
args: --workspace --release --all
- name: Install Rust stable
uses: actions-rs/toolchain@v1
@@ -79,6 +79,9 @@ jobs:
override: true
components: rustfmt, clippy
- name: Install wasm-opt
run: cargo install wasm-opt
- name: Build release contracts
run: make wasm
@@ -95,7 +98,8 @@ jobs:
cp target/release/nym-network-requester $OUTPUT_DIR
cp target/release/nym-network-statistics $OUTPUT_DIR
cp target/release/nym-cli $OUTPUT_DIR
cp target/release/credential $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/mixnet_contract.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/vesting_contract.wasm $OUTPUT_DIR
+56
View File
@@ -0,0 +1,56 @@
name: Run config checks on all binaries
on:
workflow_dispatch:
release:
types: [created]
push:
paths:
- 'clients/**'
- 'common/**'
- 'contracts/**'
- 'integrations/**'
- 'mixnode/**'
- 'sdk/rust/nym-sdk/**'
- 'service-providers/**'
pull_request:
paths:
- 'clients/**'
- 'common/**'
- 'gateway/**'
- 'integrations/**'
- 'mixnode/**'
- 'sdk/rust/nym-sdk/**'
- 'service-providers/**'
env:
NETWORK: mainnet
jobs:
publish-nym:
strategy:
fail-fast: false
matrix:
platform: [custom-runner-linux]
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v3
- name: Install Dependencies (Linux)
run: sudo apt-get update && sudo apt-get -y install jq vim libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools
continue-on-error: true
- name: Install Rust stable
uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: Branch name
run: echo running on branch ${GITHUB_REF##*/}
- name: Run tests against binaries
run: ./build_and_run.sh ${{ github.head_ref || github.ref_name }}
working-directory: tests/
+7 -3
View File
@@ -23,15 +23,12 @@ jobs:
run: |
sudo apt-get update
sudo apt-get -y install \
libwebkit2gtk-4.0-dev \
build-essential \
unzip \
curl \
wget \
libssl-dev \
libgtk-3-dev \
squashfs-tools \
libayatana-appindicator3-dev \
librsvg2-dev
- name: Checkout
@@ -64,6 +61,11 @@ jobs:
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
# TODO this step takes a considerable amount of time
# We could avoid to compile from source tauri-cli and use instead
# pre-compiled binary provided by the node package `@tauri-apps/cli`
# But when using the later the build fails for some reason
# so keep installing and using tauri-cli
- name: Install tauri cli
run: cargo install tauri-cli --version "^2.0.0-alpha.2"
@@ -93,11 +95,13 @@ jobs:
- name: Build APK
working-directory: nym-connect/mobile
env:
# NODE_TAURI_CLI=${{ github.workspace }}/nym-connect/mobile/node_modules/.bin/tauri
ANDROID_SDK_ROOT: ${{ env.ANDROID_HOME }}
WRY_ANDROID_PACKAGE: net.nymtech.nym_connect
WRY_ANDROID_LIBRARY: nym_connect
# TODO build with release profile (--release), it will requires
# to sign the APK. For now build with debug profile to avoid that
# TODO build using `yarn tauri`, provide NODE_TAURI_CLI, see TODO notes above
run: cargo tauri android build --debug --apk --split-per-abi -t aarch64
# TODO add the version number to APK name
-1
View File
@@ -49,7 +49,6 @@ jobs:
librsvg2-dev \
libsoup-3.0-dev \
libjavascriptcoregtk-4.1-dev
#continue-on-error: true
- name: Checkout
uses: actions/checkout@v3
+4 -1
View File
@@ -6,7 +6,7 @@ on:
jobs:
build:
if: ${{ startsWith(github.ref, 'refs/tags/nym-contracts-') && github.event_name == 'release' }}
if: ${{ (startsWith(github.ref, 'refs/tags/nym-contracts-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
runs-on: [self-hosted, custom-runner-linux]
steps:
- uses: actions/checkout@v2
@@ -19,6 +19,9 @@ jobs:
override: true
components: rustfmt, clippy
- name: Install wasm-opt
run: cargo install wasm-opt
- name: Build release contracts
run: make wasm
@@ -1,57 +0,0 @@
name: CI for Network Explorer API
on:
workflow_dispatch:
release:
types: [created]
env:
NETWORK: mainnet
jobs:
publish-nym:
if: ${{ startsWith(github.ref, 'refs/tags/nym-explorer-api-') && (github.event_name == 'release' || github.event_name == 'workflow_dispatch') }}
strategy:
fail-fast: false
matrix:
platform: [ubuntu-20.04]
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v3
- 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
continue-on-error: true
- name: Check the release tag starts with `nym-explorer-api-`
uses: actions/github-script@v3
with:
script: |
core.setFailed('Release tag did not start with nym-explorer-api-...')
- name: Install Rust stable
uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: Build all explorer-api
uses: actions-rs/cargo@v1
with:
command: build
args: --manifest-path explorer-api/Cargo.toml --workspace --release
- name: Upload Artifact
uses: actions/upload-artifact@v3
with:
name: my-artifact
path: |
target/release/explorer-api
retention-days: 30
- name: Upload to release based on tag name
uses: softprops/action-gh-release@v1
if: github.event_name == 'release'
with:
files: |
target/release/explorer-api
-50
View File
@@ -1,50 +0,0 @@
name: Publish Nym CLI binaries
on:
workflow_dispatch:
release:
types: [created]
env:
NETWORK: mainnet
jobs:
publish-nym-cli:
if: ${{ startsWith(github.ref, 'refs/tags/nym-cli-') && (github.event_name == 'release' || github.event_name == 'workflow_dispatch') }}
strategy:
fail-fast: false
matrix:
platform: [ubuntu-20.04, windows-latest, macos-latest]
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v3
- name: Check the release tag starts with `nym-cli-`
uses: actions/github-script@v3
with:
script: |
core.setFailed('Release tag did not start with nym-cli-...')
- name: Install Rust stable
uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: Build binary
run: make build-nym-cli
- name: Upload Artifact
uses: actions/upload-artifact@v3
with:
name: nym-cli-${{ matrix.platform }}
path: |
target/release/nym-cli*
retention-days: 30
- name: Upload to release based on tag name
uses: softprops/action-gh-release@v1
if: github.event_name == 'release'
with:
files: |
target/release/nym-cli
@@ -10,7 +10,7 @@ defaults:
jobs:
publish-tauri:
if: ${{ startsWith(github.ref, 'refs/tags/nym-connect-') && github.event_name == 'release' }}
if: ${{ (startsWith(github.ref, 'refs/tags/nym-connect-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
strategy:
fail-fast: false
matrix:
@@ -10,7 +10,7 @@ defaults:
jobs:
publish-tauri:
if: ${{ startsWith(github.ref, 'refs/tags/nym-connect-') && github.event_name == 'release' }}
if: ${{ (startsWith(github.ref, 'refs/tags/nym-connect-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
strategy:
fail-fast: false
matrix:
+1 -1
View File
@@ -16,7 +16,7 @@ env:
jobs:
publish-nym:
if: ${{ startsWith(github.ref, 'refs/tags/nym-binaries-') && github.event_name == 'release' }}
if: ${{ (startsWith(github.ref, 'refs/tags/nym-binaries-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
strategy:
fail-fast: false
matrix:
@@ -10,7 +10,7 @@ defaults:
jobs:
publish-tauri:
if: ${{ startsWith(github.ref, 'refs/tags/nym-wallet-') && github.event_name == 'release' }}
if: ${{ (startsWith(github.ref, 'refs/tags/nym-wallet-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
strategy:
fail-fast: false
matrix:
@@ -9,7 +9,7 @@ defaults:
jobs:
publish-tauri:
if: ${{ startsWith(github.ref, 'refs/tags/nym-wallet-') && github.event_name == 'release' }}
if: ${{ (startsWith(github.ref, 'refs/tags/nym-wallet-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
strategy:
fail-fast: false
matrix:
@@ -1,9 +1,14 @@
🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥
> :rocket: {{ env.NYM_PROJECT_NAME }}
>
> 🔴 **FAILURE** :cry:
>
> `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }}
>
> `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }}
>
> `build ` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }}
>
Commit message:
```
@@ -1,10 +1,16 @@
🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩
> :rocket: {{ env.NYM_PROJECT_NAME }} ➡️➡️➡️➡️➡️ **View output:** https://{{ env.NYM_CI_WWW_LOCATION }}.{{ env.NYM_CI_WWW_BASE }}/
>
> `storybook`: https://{{ env.NYM_CI_WWW_LOCATION_STORYBOOK }}.{{ env.NYM_CI_WWW_BASE }}
>
> ✅ **SUCCESS**
>
> `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }}
>
> `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }}
>
> `build ` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }}
>
Commit message by `{{ env.GITHUB_ACTOR }}` at {{ timestamp }}:
```
@@ -151,7 +151,7 @@ async function getMessageBody(context) {
return `${icon} ${job.conclusion}: ${job.name} - ${job.html_url}`;
})
// and join with newlines for display in the template
.join('\n');
.join('\n\n');
return template({ ...context, jobResults });
}
@@ -1,9 +1,15 @@
🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥
> :rocket: {{ env.NYM_PROJECT_NAME }}
>
> 🔴 **FAILURE** :cry:
>
> `when` {{ timestamp }}
>
> `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }}
>
> `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }}
>
> `build ` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }}
>
{{ jobResults }}
@@ -1,9 +1,15 @@
🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩
> :rocket: {{ env.NYM_PROJECT_NAME }}
>
> ✅ **SUCCESS**
>
> `when` {{ timestamp }}
>
> `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }}
>
> `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }}
>
> `build ` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }}
>
{{ jobResults }}
{{ jobResults }}
@@ -5,6 +5,7 @@ const localStorage = new LocalStorage('./scratch');
const {
LocalStorageCryptoStore,
} = require('matrix-js-sdk/lib/crypto/store/localStorage-crypto-store');
var showdown = require('showdown');
// hide all matrix client output
console.error = (error) => console.log('❌ error: ', error);
@@ -54,7 +55,9 @@ function createClient(context, room, message) {
}
async function sendMatrixMessage(contextArg, messageAsMarkdown, roomId) {
const client = createClient(contextArg, roomId, messageAsMarkdown);
const converter = new showdown.Converter();
const messageAsHtml = converter.makeHtml(messageAsMarkdown);
const client = createClient(contextArg, roomId, messageAsHtml);
await client.initCrypto();
await client.startClient({ initialSyncLimit: 1 });
}
@@ -1,9 +1,14 @@
🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥
> :rocket: {{ env.NYM_PROJECT_NAME }}
>
> 🔴 **FAILURE** :cry:
>
> `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }}
>
> `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }}
>
> `build ` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }}
>
Commit message:
```
@@ -1,9 +1,14 @@
🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩
> :rocket: {{ env.NYM_PROJECT_NAME }} ➡️➡️➡️➡️➡️ **View storybook:** https://{{ env.NYM_CI_WWW_LOCATION }}.{{ env.NYM_CI_WWW_BASE }}/
>
> ✅ **SUCCESS**
>
> `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }}
>
> `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }}
>
> `build ` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }}
>
Commit message by `{{ env.GITHUB_ACTOR }}` at {{ timestamp }}:
```
@@ -1,9 +1,14 @@
🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥
> :rocket: {{ env.NYM_PROJECT_NAME }}
>
> 🔴 **FAILURE** :cry:
>
> `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }}
>
> `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }}
>
> `build ` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }}
>
Commit message:
```
@@ -1,13 +1,18 @@
🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩
> :rocket: {{ env.NYM_PROJECT_NAME }}
>
> ✅ **SUCCESS**
>
> ➡️➡️➡️➡️➡️ **View output:**
>
> `storybook`: https://{{ env.NYM_CI_WWW_LOCATION }}.{{ env.NYM_CI_WWW_BASE }}
>
> `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }}
>
> `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }}
>
> `build ` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }}
>
Commit message by `{{ env.GITHUB_ACTOR }}` at {{ timestamp }}:
```
@@ -17,6 +17,7 @@
"remark-emoji": "^2.2.0",
"remark-html": "^13.0.2",
"remark-parse": "^9.0.0",
"showdown": "^2.1.0",
"to-vfile": "^6.1.0",
"unified": "^9.2.2"
},
@@ -1,9 +1,14 @@
🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥
> :rocket: {{ env.NYM_PROJECT_NAME }}
>
> 🔴 **FAILURE** :cry:
>
> `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }}
>
> `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }}
>
> `build ` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }}
>
Commit message:
```
@@ -1,14 +1,20 @@
🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩
> :rocket: {{ env.NYM_PROJECT_NAME }}
>
> ✅ **SUCCESS**
>
> ➡️➡️➡️➡️➡️ **View output:**
>
> `storybook`: https://{{ env.NYM_CI_WWW_LOCATION }}.{{ env.NYM_CI_WWW_BASE }}
>
> `example`: https://{{ env.NYM_CI_WWW_LOCATION }}-example.{{ env.NYM_CI_WWW_BASE }}
>
> `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }}
>
> `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }}
>
> `build ` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }}
>
Commit message by `{{ env.GITHUB_ACTOR }}` at {{ timestamp }}:
```
+34
View File
@@ -4,6 +4,40 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
## [Unreleased]
## [v1.1.12] (2023-03-07)
- Fix generated docs for mixnet and vesting contract on docs.rs ([#3093])
- Introduce a way of injecting topology into the client ([#3044])
- Update mixnet TypeScript client methods #1 ([#2783])
- Update tooltips for routing and average score ([#3133])
- update selected service provider description style ([#3128])
[#3093]: https://github.com/nymtech/nym/issues/3093
[#3044]: https://github.com/nymtech/nym/issues/3044
[#2783]: https://github.com/nymtech/nym/issues/2783
[#3133]: https://github.com/nymtech/nym/pull/3133
[#3128]: https://github.com/nymtech/nym/pull/3128
## [v1.1.11] (2023-02-28)
- Fix empty dealer set loop ([#3105])
- The nym-api db.sqlite is broken when trying to run against it it in `enabled-credentials-mode true` there is an ordering issue with migrations when using the credential binary to purchase bandwidth ([#3100])
- Feature/latency based gateway selection ([#3081])
- Fix the credential binary to handle transactions to sleep when in non-inProgress epochs ([#3057])
- Publish mixnet contract to crates.io ([#1919])
- Publish vesting contract to crates.io ([#1920])
- Feature/update checker to use master ([#3097])
- Feature/improve binary checks ([#3094])
[#3105]: https://github.com/nymtech/nym/issues/3105
[#3100]: https://github.com/nymtech/nym/issues/3100
[#3081]: https://github.com/nymtech/nym/pull/3081
[#3057]: https://github.com/nymtech/nym/issues/3057
[#1919]: https://github.com/nymtech/nym/issues/1919
[#1920]: https://github.com/nymtech/nym/issues/1920
[#3097]: https://github.com/nymtech/nym/pull/3097
[#3094]: https://github.com/nymtech/nym/pull/3094
## [v1.1.10] (2023-02-21)
- Verloc listener causing mixnode unexpected shutdown ([#3038])
Generated
+280 -425
View File
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -22,7 +22,6 @@ members = [
"clients/native",
"clients/native/websocket-requests",
"clients/socks5",
"common/bandwidth-claim-contract",
"common/bin-common",
"common/client-libs/gateway-client",
"common/client-libs/mixnet-client",
@@ -105,9 +104,10 @@ edition = "2021"
license = "Apache-2.0"
[workspace.dependencies]
async-trait = "0.1.63"
async-trait = "0.1.64"
bip39 = { version = "2.0.0", features = ["zeroize"] }
cfg-if = "1.0.0"
dotenv = "0.15.0"
dotenvy = "0.15.6"
lazy_static = "1.4.0"
log = "0.4"
once_cell = "1.7.2"
+16 -5
View File
@@ -1,13 +1,22 @@
test: clippy-all cargo-test wasm fmt
test-no-mobile: clippy-all-no-mobile cargo-test-no-mobile wasm fmt-no-mobile
test-all: test cargo-test-expensive
test-all-no-mobile: test-no-mobile cargo-test-expensive
no-clippy: build cargo-test wasm fmt
no-clippy-no-mobile: build-no-mobile cargo-test-no-mobile wasm fmt-no-mobile
happy: fmt clippy-happy test
clippy-all: clippy-main clippy-main-examples clippy-all-contracts clippy-all-wallet clippy-all-connect clippy-all-connect-mobile clippy-all-wasm-client
clippy-happy: clippy-happy-main clippy-happy-contracts clippy-happy-wallet clippy-happy-connect clippy-happy-connect-mobile
cargo-test: test-main test-contracts test-wallet test-connect test-connect-mobile
happy-no-mobile: fmt-no-mobile clippy-happy-no-mobile test-no-mobile
clippy-all: clippy-all-no-mobile clippy-all-connect-mobile
clippy-all-no-mobile: clippy-main clippy-main-examples clippy-all-contracts clippy-all-wallet clippy-all-connect clippy-all-wasm-client
clippy-happy: clippy-happy-no-mobile clippy-happy-connect-mobile
clippy-happy-no-mobile: clippy-happy-main clippy-happy-contracts clippy-happy-wallet clippy-happy-connect
cargo-test: cargo-test-no-mobile test-connect-mobile
cargo-test-no-mobile: test-main test-contracts test-wallet test-connect
cargo-test-expensive: test-main-expensive test-contracts-expensive test-wallet-expensive test-connect-expensive
build: build-contracts build-wallet build-main build-main-examples build-connect build-connect-mobile build-wasm-client
fmt: fmt-main fmt-contracts fmt-wallet fmt-connect fmt-connect-mobile fmt-wasm-client
build: build-no-mobile build-connect-mobile
build-no-mobile: build-contracts build-wallet build-main build-main-examples build-connect build-wasm-client
fmt: fmt-no-mobile fmt-connect-mobile
fmt-no-mobile: fmt-main fmt-contracts fmt-wallet fmt-connect fmt-wasm-client
clippy-happy-main:
cargo clippy
@@ -126,6 +135,8 @@ fmt-wasm-client:
wasm:
RUSTFLAGS='-C link-arg=-s' cargo build --manifest-path contracts/Cargo.toml --release --target wasm32-unknown-unknown
wasm-opt -Os contracts/target/wasm32-unknown-unknown/release/vesting_contract.wasm -o contracts/target/wasm32-unknown-unknown/release/vesting_contract.wasm
wasm-opt -Os contracts/target/wasm32-unknown-unknown/release/mixnet_contract.wasm -o contracts/target/wasm32-unknown-unknown/release/mixnet_contract.wasm
mixnet-opt: wasm
cd contracts/mixnet && make opt
+1 -1
View File
@@ -16,7 +16,7 @@ The platform is composed of multiple Rust crates. Top-level executable binary cr
* nym-wallet - a desktop wallet implemented using the [Tauri](https://tauri.studio/en/docs/about/intro) framework.
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg?style=for-the-badge)](https://opensource.org/licenses/Apache-2.0)
[![Build Status](https://img.shields.io/github/workflow/status/nymtech/nym/Continuous%20integration/develop?style=for-the-badge&logo=github-actions)](https://github.com/nymtech/nym/actions?query=branch%3Adevelop)
[![Build Status](https://img.shields.io/github/actions/workflow/status/nymtech/nym/build.yml?branch=develop&style=for-the-badge&logo=github-actions)](https://github.com/nymtech/nym/actions?query=branch%3Adevelop)
### Building
+6 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "client-core"
version = "1.1.10"
version = "1.1.12"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
edition = "2021"
rust-version = "1.66"
@@ -8,7 +8,7 @@ rust-version = "1.66"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
async-trait = { version = "0.1.58" }
async-trait = { workspace = true }
dirs = "4.0"
dashmap = "5.4.0"
futures = "0.3"
@@ -37,6 +37,10 @@ nym-topology = { path = "../../common/topology" }
validator-client = { path = "../../common/client-libs/validator-client", default-features = false }
nym-task = { path = "../../common/task" }
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.validator-client]
path = "../../common/client-libs/validator-client"
features = ["nyxd-client"]
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-stream]
version = "0.1.11"
features = ["time"]
@@ -15,6 +15,7 @@ use crate::client::replies::reply_controller::{ReplyControllerReceiver, ReplyCon
use crate::client::replies::reply_storage::{
CombinedReplyStorage, PersistentReplyStorage, ReplyStorageBackend, SentReplyKeys,
};
use crate::client::topology_control::nym_api_provider::NymApiTopologyProvider;
use crate::client::topology_control::{
TopologyAccessor, TopologyRefresher, TopologyRefresherConfig,
};
@@ -37,10 +38,12 @@ use nym_sphinx::addressing::nodes::NodeIdentity;
use nym_sphinx::receiver::ReconstructedMessage;
use nym_task::connections::{ConnectionCommandReceiver, ConnectionCommandSender, LaneQueueLengths};
use nym_task::{TaskClient, TaskManager};
use nym_topology::provider_trait::TopologyProvider;
use std::sync::Arc;
use std::time::Duration;
use tap::TapFallible;
use url::Url;
#[cfg(not(target_arch = "wasm32"))]
use validator_client::nyxd::CosmWasmClient;
@@ -90,6 +93,7 @@ impl ClientOutput {
pub struct ClientState {
pub shared_lane_queue_lengths: LaneQueueLengths,
pub reply_controller_sender: ReplyControllerSender,
pub topology_accessor: TopologyAccessor,
}
pub enum ClientInputStatus {
@@ -154,6 +158,7 @@ pub struct BaseClientBuilder<'a, B, C: Clone> {
nym_api_endpoints: Vec<Url>,
reply_storage_backend: B,
custom_topology_provider: Option<Box<dyn TopologyProvider>>,
bandwidth_controller: Option<BandwidthController<C>>,
key_manager: KeyManager,
}
@@ -177,6 +182,7 @@ where
bandwidth_controller,
reply_storage_backend,
key_manager,
custom_topology_provider: None,
}
}
@@ -195,11 +201,17 @@ where
disabled_credentials: credentials_toggle.is_disabled(),
nym_api_endpoints,
reply_storage_backend,
custom_topology_provider: None,
bandwidth_controller,
key_manager,
}
}
pub fn with_topology_provider(mut self, provider: Box<dyn TopologyProvider>) -> Self {
self.custom_topology_provider = Some(provider);
self
}
pub fn as_mix_recipient(&self) -> Recipient {
Recipient::new(
*self.key_manager.identity_keypair().public_key(),
@@ -341,25 +353,38 @@ where
Ok(gateway_client)
}
fn setup_topology_provider(
custom_provider: Option<Box<dyn TopologyProvider>>,
nym_api_urls: Vec<Url>,
) -> Box<dyn TopologyProvider> {
// if no custom provider was ... provided ..., create one using nym-api
custom_provider.unwrap_or_else(|| {
Box::new(NymApiTopologyProvider::new(
nym_api_urls,
env!("CARGO_PKG_VERSION").to_string(),
))
})
}
// future responsible for periodically polling directory server and updating
// the current global view of topology
async fn start_topology_refresher(
nym_api_urls: Vec<Url>,
topology_provider: Box<dyn TopologyProvider>,
refresh_rate: Duration,
topology_accessor: TopologyAccessor,
shutdown: TaskClient,
) -> Result<(), ClientCoreError> {
let topology_refresher_config = TopologyRefresherConfig::new(
nym_api_urls,
refresh_rate,
env!("CARGO_PKG_VERSION").to_string(),
let topology_refresher_config = TopologyRefresherConfig::new(refresh_rate);
let mut topology_refresher = TopologyRefresher::new(
topology_refresher_config,
topology_accessor,
topology_provider,
);
let mut topology_refresher =
TopologyRefresher::new(topology_refresher_config, topology_accessor);
// before returning, block entire runtime to refresh the current network view so that any
// components depending on topology would see a non-empty view
info!("Obtaining initial network topology");
topology_refresher.refresh().await;
topology_refresher.try_refresh().await;
if let Err(err) = topology_refresher.ensure_topology_is_routable().await {
log::error!(
@@ -468,8 +493,12 @@ where
)
.await?;
let topology_provider = Self::setup_topology_provider(
self.custom_topology_provider.take(),
self.nym_api_endpoints,
);
Self::start_topology_refresher(
self.nym_api_endpoints.clone(),
topology_provider,
self.debug_config.topology_refresh_rate,
shared_topology_accessor.clone(),
task_manager.subscribe(),
@@ -530,7 +559,7 @@ where
self.debug_config,
self.key_manager.ack_key(),
self_address,
shared_topology_accessor,
shared_topology_accessor.clone(),
sphinx_message_sender,
task_manager.subscribe(),
);
@@ -554,6 +583,7 @@ where
client_state: ClientState {
shared_lane_queue_lengths,
reply_controller_sender,
topology_accessor: shared_topology_accessor,
},
task_manager,
})
@@ -155,18 +155,21 @@ impl Backend {
// (assuming no key rotation has happened)
// but the way it's currently coded, everyone will purge old data
let since_last_flush = OffsetDateTime::now_utc() - last_flush;
if since_last_flush.whole_days() > 0 {
info!("it's been over {} days and {} hours since we last used our data store. our reply surbs are already outdated - we're going to purge them now.", since_last_flush.whole_days(), since_last_flush.whole_hours());
let days = since_last_flush.whole_days();
let hours = since_last_flush.whole_hours() % 24;
if days > 0 {
info!("it's been over {days} days and {hours} hours since we last used our data store. our reply surbs are already outdated - we're going to purge them now.");
manager.delete_all_reply_surb_data().await?;
}
if since_last_flush.whole_days() > 1 {
info!("it's been over {} days and {} hours since we last used our data store. our reply keys are already outdated - we're going to purge them now.", since_last_flush.whole_days(), since_last_flush.whole_hours());
if days > 1 {
info!("it's been over {days} days and {hours} hours since we last used our data store. our reply keys are already outdated - we're going to purge them now.");
manager.delete_all_reply_keys().await?;
}
if since_last_flush.whole_days() > 2 {
info!("it's been over {} days and {} hours since we last used our data store. our used sender tags are already outdated - we're going to purge them now.", since_last_flush.whole_days(), since_last_flush.whole_hours());
if days > 2 {
info!("it's been over {days} days and {hours} hours since we last used our data store. our used sender tags are already outdated - we're going to purge them now.");
manager.delete_all_tags().await?;
}
@@ -1,336 +0,0 @@
// Copyright 2021-2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::spawn_future;
use futures::StreamExt;
use log::*;
use nym_sphinx::addressing::clients::Recipient;
use nym_sphinx::params::DEFAULT_NUM_MIX_HOPS;
use nym_topology::{nym_topology_from_detailed, NymTopology, NymTopologyError};
use rand::seq::SliceRandom;
use rand::thread_rng;
use std::ops::Deref;
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::{RwLock, RwLockReadGuard};
use url::Url;
// I'm extremely curious why compiler NEVER complained about lack of Debug here before
#[derive(Debug)]
pub struct TopologyAccessorInner(Option<NymTopology>);
impl AsRef<Option<NymTopology>> for TopologyAccessorInner {
fn as_ref(&self) -> &Option<NymTopology> {
&self.0
}
}
impl TopologyAccessorInner {
fn new() -> Self {
TopologyAccessorInner(None)
}
fn update(&mut self, new: Option<NymTopology>) {
self.0 = new;
}
}
pub struct TopologyReadPermit<'a> {
permit: RwLockReadGuard<'a, TopologyAccessorInner>,
}
impl<'a> Deref for TopologyReadPermit<'a> {
type Target = TopologyAccessorInner;
fn deref(&self) -> &Self::Target {
&self.permit
}
}
impl<'a> TopologyReadPermit<'a> {
/// Using provided topology read permit, tries to get an immutable reference to the underlying
/// topology. For obvious reasons the lifetime of the topology reference is bound to the permit.
pub(super) fn try_get_valid_topology_ref(
&'a self,
ack_recipient: &Recipient,
packet_recipient: Option<&Recipient>,
) -> Result<&'a NymTopology, NymTopologyError> {
// 1. Have we managed to get anything from the refresher, i.e. have the nym-api queries gone through?
let topology = self
.permit
.as_ref()
.as_ref()
.ok_or(NymTopologyError::EmptyNetworkTopology)?;
// 2. does it have any mixnode at all?
// 3. does it have any gateways at all?
// 4. does it have a mixnode on each layer?
topology.ensure_can_construct_path_through(DEFAULT_NUM_MIX_HOPS)?;
// 5. does it contain OUR gateway (so that we could create an ack packet)?
if !topology.gateway_exists(ack_recipient.gateway()) {
return Err(NymTopologyError::NonExistentGatewayError {
identity_key: ack_recipient.gateway().to_base58_string(),
});
}
// 6. for our target recipient, does it contain THEIR gateway (so that we could create
if let Some(recipient) = packet_recipient {
if !topology.gateway_exists(recipient.gateway()) {
return Err(NymTopologyError::NonExistentGatewayError {
identity_key: recipient.gateway().to_base58_string(),
});
}
}
Ok(topology)
}
}
impl<'a> From<RwLockReadGuard<'a, TopologyAccessorInner>> for TopologyReadPermit<'a> {
fn from(read_permit: RwLockReadGuard<'a, TopologyAccessorInner>) -> Self {
TopologyReadPermit {
permit: read_permit,
}
}
}
#[derive(Clone, Debug)]
pub struct TopologyAccessor {
// `RwLock` *seems to* be the better approach for this as write access is only requested every
// few seconds, while reads are needed every single packet generated.
// However, proper benchmarks will be needed to determine if `RwLock` is indeed a better
// approach than a `Mutex`
inner: Arc<RwLock<TopologyAccessorInner>>,
}
impl TopologyAccessor {
pub fn new() -> Self {
TopologyAccessor {
inner: Arc::new(RwLock::new(TopologyAccessorInner::new())),
}
}
pub async fn get_read_permit(&self) -> TopologyReadPermit<'_> {
self.inner.read().await.into()
}
async fn update_global_topology(&self, new_topology: Option<NymTopology>) {
self.inner.write().await.update(new_topology);
}
// only used by the client at startup to get a slightly more reasonable error message
// (currently displays as unused because health checker is disabled due to required changes)
pub async fn ensure_is_routable(&self) -> Result<(), NymTopologyError> {
match &self.inner.read().await.0 {
None => Err(NymTopologyError::EmptyNetworkTopology),
Some(ref topology) => topology.ensure_can_construct_path_through(DEFAULT_NUM_MIX_HOPS),
}
}
}
impl Default for TopologyAccessor {
fn default() -> Self {
TopologyAccessor::new()
}
}
pub struct TopologyRefresherConfig {
nym_api_urls: Vec<Url>,
refresh_rate: Duration,
client_version: String,
}
impl TopologyRefresherConfig {
pub fn new(nym_api_urls: Vec<Url>, refresh_rate: Duration, client_version: String) -> Self {
TopologyRefresherConfig {
nym_api_urls,
refresh_rate,
client_version,
}
}
}
pub struct TopologyRefresher {
validator_client: validator_client::client::NymApiClient,
client_version: String,
nym_api_urls: Vec<Url>,
topology_accessor: TopologyAccessor,
refresh_rate: Duration,
currently_used_api: usize,
was_latest_valid: bool,
}
impl TopologyRefresher {
pub fn new(mut cfg: TopologyRefresherConfig, topology_accessor: TopologyAccessor) -> Self {
cfg.nym_api_urls.shuffle(&mut thread_rng());
TopologyRefresher {
validator_client: validator_client::client::NymApiClient::new(
cfg.nym_api_urls[0].clone(),
),
client_version: cfg.client_version,
nym_api_urls: cfg.nym_api_urls,
topology_accessor,
refresh_rate: cfg.refresh_rate,
currently_used_api: 0,
was_latest_valid: true,
}
}
fn use_next_nym_api(&mut self) {
if self.nym_api_urls.len() == 1 {
warn!("There's only a single nym API available - it won't be possible to use a different one");
return;
}
self.currently_used_api = (self.currently_used_api + 1) % self.nym_api_urls.len();
self.validator_client
.change_nym_api(self.nym_api_urls[self.currently_used_api].clone())
}
/// Verifies whether nodes a reasonably distributed among all mix layers.
///
/// In ideal world we would have 33% nodes on layer 1, 33% on layer 2 and 33% on layer 3.
/// However, this is a rather unrealistic expectation, instead we check whether there exists
/// a layer with more than 66% of nodes or with fewer than 15% and if so, we trigger a failure.
///
/// # Arguments
///
/// * `topology`: active topology constructed from validator api data
fn check_layer_distribution(&self, active_topology: &NymTopology) -> bool {
let mixes = active_topology.mixes();
let mixnodes_count = active_topology.num_mixnodes();
if active_topology.gateways().is_empty() {
return false;
}
// trivial check to see if have at least a single node on each layer (regardless of active set size)
if mixes.get(&1).is_none() || mixes.get(&2).is_none() || mixes.get(&3).is_none() {
return false;
}
let upper_bound = (mixnodes_count as f32 * 0.66) as usize;
let lower_bound = (mixnodes_count as f32 * 0.15) as usize;
let layer1 = mixes.get(&1).unwrap().len();
let layer2 = mixes.get(&2).unwrap().len();
let layer3 = mixes.get(&3).unwrap().len();
if layer1 < lower_bound || layer1 > upper_bound {
warn!(
"nodes: {}, layer1: {}, layer2: {}, layer3: {}",
mixnodes_count, layer1, layer2, layer3
);
return false;
}
if layer2 < lower_bound || layer2 > upper_bound {
warn!(
"nodes: {}, layer1: {}, layer2: {}, layer3: {}",
mixnodes_count, layer1, layer2, layer3
);
return false;
}
if layer3 < lower_bound || layer3 > upper_bound {
warn!(
"nodes: {}, layer1: {}, layer2: {}, layer3: {}",
mixnodes_count, layer1, layer2, layer3
);
return false;
}
true
}
async fn get_current_compatible_topology(&self) -> Option<NymTopology> {
// TODO: optimization for the future:
// only refresh mixnodes on timer and refresh gateways only when
// we have to send to a new, unknown, gateway
let mixnodes = match self.validator_client.get_cached_active_mixnodes().await {
Err(err) => {
error!("failed to get network mixnodes - {err}");
return None;
}
Ok(mixes) => mixes,
};
let gateways = match self.validator_client.get_cached_gateways().await {
Err(err) => {
error!("failed to get network gateways - {err}");
return None;
}
Ok(gateways) => gateways,
};
let topology = nym_topology_from_detailed(mixnodes, gateways)
.filter_system_version(&self.client_version);
if !self.check_layer_distribution(&topology) {
warn!("The current filtered active topology has extremely skewed layer distribution. It cannot be used.");
None
} else {
Some(topology)
}
}
pub async fn refresh(&mut self) {
trace!("Refreshing the topology");
let new_topology = self.get_current_compatible_topology().await;
if new_topology.is_none() {
self.use_next_nym_api();
}
if new_topology.is_none() && self.was_latest_valid {
// if we failed to grab this topology, but the one before it was alright, let's assume
// validator had a tiny hiccup and use the old data
warn!("we're going to keep on using the old topology for this iteration");
self.was_latest_valid = false;
return;
} else if new_topology.is_some() {
self.was_latest_valid = true;
}
self.topology_accessor
.update_global_topology(new_topology)
.await;
}
pub async fn ensure_topology_is_routable(&self) -> Result<(), NymTopologyError> {
self.topology_accessor.ensure_is_routable().await
}
pub fn start_with_shutdown(mut self, mut shutdown: nym_task::TaskClient) {
spawn_future(async move {
debug!("Started TopologyRefresher with graceful shutdown support");
#[cfg(not(target_arch = "wasm32"))]
let mut interval = tokio_stream::wrappers::IntervalStream::new(tokio::time::interval(
self.refresh_rate,
));
#[cfg(target_arch = "wasm32")]
let mut interval =
gloo_timers::future::IntervalStream::new(self.refresh_rate.as_millis() as u32);
while !shutdown.is_shutdown() {
tokio::select! {
_ = interval.next() => {
self.refresh().await;
},
_ = shutdown.recv() => {
log::trace!("TopologyRefresher: Received shutdown");
},
}
}
shutdown.recv_timeout().await;
log::debug!("TopologyRefresher: Exiting");
})
}
}
@@ -0,0 +1,154 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use nym_sphinx::addressing::clients::Recipient;
use nym_sphinx::params::DEFAULT_NUM_MIX_HOPS;
use nym_topology::{NymTopology, NymTopologyError};
use std::ops::Deref;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use tokio::sync::{Notify, RwLock, RwLockReadGuard};
#[derive(Debug)]
pub struct TopologyAccessorInner {
controlled_manually: AtomicBool,
released_manual_control: Notify,
// `RwLock` *seems to* be the better approach for this as write access is only requested every
// few seconds, while reads are needed every single packet generated.
// However, proper benchmarks will be needed to determine if `RwLock` is indeed a better
// approach than a `Mutex`
topology: RwLock<Option<NymTopology>>,
}
impl TopologyAccessorInner {
fn new() -> Self {
TopologyAccessorInner {
controlled_manually: AtomicBool::new(false),
released_manual_control: Notify::new(),
topology: RwLock::new(None),
}
}
async fn update(&self, new: Option<NymTopology>) {
*self.topology.write().await = new;
}
}
pub struct TopologyReadPermit<'a> {
permit: RwLockReadGuard<'a, Option<NymTopology>>,
}
impl<'a> Deref for TopologyReadPermit<'a> {
type Target = Option<NymTopology>;
fn deref(&self) -> &Self::Target {
&self.permit
}
}
impl<'a> TopologyReadPermit<'a> {
/// Using provided topology read permit, tries to get an immutable reference to the underlying
/// topology. For obvious reasons the lifetime of the topology reference is bound to the permit.
pub(crate) fn try_get_valid_topology_ref(
&'a self,
ack_recipient: &Recipient,
packet_recipient: Option<&Recipient>,
) -> Result<&'a NymTopology, NymTopologyError> {
// 1. Have we managed to get anything from the refresher, i.e. have the nym-api queries gone through?
let topology = self
.permit
.as_ref()
.ok_or(NymTopologyError::EmptyNetworkTopology)?;
// 2. does it have any mixnode at all?
// 3. does it have any gateways at all?
// 4. does it have a mixnode on each layer?
topology.ensure_can_construct_path_through(DEFAULT_NUM_MIX_HOPS)?;
// 5. does it contain OUR gateway (so that we could create an ack packet)?
if !topology.gateway_exists(ack_recipient.gateway()) {
return Err(NymTopologyError::NonExistentGatewayError {
identity_key: ack_recipient.gateway().to_base58_string(),
});
}
// 6. for our target recipient, does it contain THEIR gateway (so that we could create
if let Some(recipient) = packet_recipient {
if !topology.gateway_exists(recipient.gateway()) {
return Err(NymTopologyError::NonExistentGatewayError {
identity_key: recipient.gateway().to_base58_string(),
});
}
}
Ok(topology)
}
}
impl<'a> From<RwLockReadGuard<'a, Option<NymTopology>>> for TopologyReadPermit<'a> {
fn from(read_permit: RwLockReadGuard<'a, Option<NymTopology>>) -> Self {
TopologyReadPermit {
permit: read_permit,
}
}
}
#[derive(Clone, Debug)]
pub struct TopologyAccessor {
inner: Arc<TopologyAccessorInner>,
}
impl TopologyAccessor {
pub fn new() -> Self {
TopologyAccessor {
inner: Arc::new(TopologyAccessorInner::new()),
}
}
pub fn controlled_manually(&self) -> bool {
self.inner.controlled_manually.load(Ordering::SeqCst)
}
pub async fn get_read_permit(&self) -> TopologyReadPermit<'_> {
self.inner.topology.read().await.into()
}
pub(crate) async fn update_global_topology(&self, new_topology: Option<NymTopology>) {
self.inner.update(new_topology).await;
}
pub(crate) async fn wait_for_released_manual_control(&self) {
self.inner.released_manual_control.notified().await
}
pub async fn current_topology(&self) -> Option<NymTopology> {
self.inner.topology.read().await.clone()
}
pub async fn manually_change_topology(&self, new_topology: NymTopology) {
self.inner.controlled_manually.store(true, Ordering::SeqCst);
self.inner.update(Some(new_topology)).await;
}
pub fn release_manual_control(&self) {
self.inner
.controlled_manually
.store(false, Ordering::SeqCst);
self.inner.released_manual_control.notify_waiters();
}
// only used by the client at startup to get a slightly more reasonable error message
// (currently displays as unused because health checker is disabled due to required changes)
pub async fn ensure_is_routable(&self) -> Result<(), NymTopologyError> {
match self.inner.topology.read().await.deref() {
None => Err(NymTopologyError::EmptyNetworkTopology),
Some(ref topology) => topology.ensure_can_construct_path_through(DEFAULT_NUM_MIX_HOPS),
}
}
}
impl Default for TopologyAccessor {
fn default() -> Self {
TopologyAccessor::new()
}
}
@@ -0,0 +1,115 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::spawn_future;
pub(crate) use accessor::{TopologyAccessor, TopologyReadPermit};
use futures::StreamExt;
use log::*;
use nym_topology::provider_trait::TopologyProvider;
use nym_topology::NymTopologyError;
use std::time::Duration;
mod accessor;
pub(crate) mod nym_api_provider;
// TODO: move it to config later
const MAX_FAILURE_COUNT: usize = 10;
pub struct TopologyRefresherConfig {
refresh_rate: Duration,
}
impl TopologyRefresherConfig {
pub fn new(refresh_rate: Duration) -> Self {
TopologyRefresherConfig { refresh_rate }
}
}
pub struct TopologyRefresher {
topology_provider: Box<dyn TopologyProvider>,
topology_accessor: TopologyAccessor,
refresh_rate: Duration,
consecutive_failure_count: usize,
}
impl TopologyRefresher {
pub fn new(
cfg: TopologyRefresherConfig,
topology_accessor: TopologyAccessor,
topology_provider: Box<dyn TopologyProvider>,
) -> Self {
TopologyRefresher {
topology_provider,
topology_accessor,
refresh_rate: cfg.refresh_rate,
consecutive_failure_count: 0,
}
}
pub fn change_topology_provider(&mut self, provider: Box<dyn TopologyProvider>) {
self.topology_provider = provider;
}
pub async fn try_refresh(&mut self) {
trace!("Refreshing the topology");
if self.topology_accessor.controlled_manually() {
info!("topology is being controlled manually - we're going to wait until the control is released...");
self.topology_accessor
.wait_for_released_manual_control()
.await;
}
let new_topology = self.topology_provider.get_new_topology().await;
if new_topology.is_none() {
warn!("failed to obtain new network topology");
}
if new_topology.is_none() && self.consecutive_failure_count < MAX_FAILURE_COUNT {
// if we failed to grab this topology, but the one before it was alright, let's assume
// validator had a tiny hiccup and use the old data
warn!("we're going to keep on using the old topology for this iteration");
self.consecutive_failure_count += 1;
return;
} else if new_topology.is_some() {
self.consecutive_failure_count = 0;
}
self.topology_accessor
.update_global_topology(new_topology)
.await;
}
pub async fn ensure_topology_is_routable(&self) -> Result<(), NymTopologyError> {
self.topology_accessor.ensure_is_routable().await
}
pub fn start_with_shutdown(mut self, mut shutdown: nym_task::TaskClient) {
spawn_future(async move {
debug!("Started TopologyRefresher with graceful shutdown support");
#[cfg(not(target_arch = "wasm32"))]
let mut interval = tokio_stream::wrappers::IntervalStream::new(tokio::time::interval(
self.refresh_rate,
));
#[cfg(target_arch = "wasm32")]
let mut interval =
gloo_timers::future::IntervalStream::new(self.refresh_rate.as_millis() as u32);
while !shutdown.is_shutdown() {
tokio::select! {
_ = interval.next() => {
self.try_refresh().await;
},
_ = shutdown.recv() => {
log::trace!("TopologyRefresher: Received shutdown");
},
}
}
shutdown.recv_timeout().await;
log::debug!("TopologyRefresher: Exiting");
})
}
}
@@ -0,0 +1,106 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use async_trait::async_trait;
use log::{error, warn};
use nym_topology::provider_trait::TopologyProvider;
use nym_topology::{nym_topology_from_detailed, NymTopology, NymTopologyError};
use rand::prelude::SliceRandom;
use rand::thread_rng;
use url::Url;
pub(crate) struct NymApiTopologyProvider {
validator_client: validator_client::client::NymApiClient,
nym_api_urls: Vec<Url>,
client_version: String,
currently_used_api: usize,
}
impl NymApiTopologyProvider {
pub(crate) fn new(mut nym_api_urls: Vec<Url>, client_version: String) -> Self {
nym_api_urls.shuffle(&mut thread_rng());
NymApiTopologyProvider {
validator_client: validator_client::client::NymApiClient::new(nym_api_urls[0].clone()),
nym_api_urls,
client_version,
currently_used_api: 0,
}
}
fn use_next_nym_api(&mut self) {
if self.nym_api_urls.len() == 1 {
warn!("There's only a single nym API available - it won't be possible to use a different one");
return;
}
self.currently_used_api = (self.currently_used_api + 1) % self.nym_api_urls.len();
self.validator_client
.change_nym_api(self.nym_api_urls[self.currently_used_api].clone())
}
/// Verifies whether nodes a reasonably distributed among all mix layers.
///
/// In ideal world we would have 33% nodes on layer 1, 33% on layer 2 and 33% on layer 3.
/// However, this is a rather unrealistic expectation, instead we check whether there exists
/// a layer with more than 66% of nodes or with fewer than 15% and if so, we trigger a failure.
///
/// # Arguments
///
/// * `topology`: active topology constructed from validator api data
fn check_layer_distribution(
&self,
active_topology: &NymTopology,
) -> Result<(), NymTopologyError> {
let lower_threshold = 0.15;
let upper_threshold = 0.66;
active_topology.ensure_even_layer_distribution(lower_threshold, upper_threshold)
}
async fn get_current_compatible_topology(&mut self) -> Option<NymTopology> {
let mixnodes = match self.validator_client.get_cached_active_mixnodes().await {
Err(err) => {
error!("failed to get network mixnodes - {err}");
return None;
}
Ok(mixes) => mixes,
};
let gateways = match self.validator_client.get_cached_gateways().await {
Err(err) => {
error!("failed to get network gateways - {err}");
return None;
}
Ok(gateways) => gateways,
};
let topology = nym_topology_from_detailed(mixnodes, gateways)
.filter_system_version(&self.client_version);
if let Err(err) = self.check_layer_distribution(&topology) {
warn!("The current filtered active topology has extremely skewed layer distribution. It cannot be used: {err}");
self.use_next_nym_api();
None
} else {
Some(topology)
}
}
}
// hehe, wasm
#[cfg(not(target_arch = "wasm32"))]
#[async_trait]
impl TopologyProvider for NymApiTopologyProvider {
async fn get_new_topology(&mut self) -> Option<NymTopology> {
self.get_current_compatible_topology().await
}
}
#[cfg(target_arch = "wasm32")]
#[async_trait(?Send)]
impl TopologyProvider for NymApiTopologyProvider {
async fn get_new_topology(&mut self) -> Option<NymTopology> {
self.get_current_compatible_topology().await
}
}
+2 -2
View File
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
use nym_config::defaults::NymNetworkDetails;
use nym_config::{NymConfig, OptionalSet, DB_FILE_NAME};
use nym_config::{NymConfig, OptionalSet, CRED_DB_FILE_NAME};
use nym_sphinx::params::PacketSize;
use serde::{Deserialize, Serialize};
use std::marker::PhantomData;
@@ -579,7 +579,7 @@ impl<T: NymConfig> Client<T> {
}
fn default_database_path(id: &str) -> PathBuf {
T::default_data_directory(id).join(DB_FILE_NAME)
T::default_data_directory(id).join(CRED_DB_FILE_NAME)
}
}
+4 -4
View File
@@ -6,7 +6,7 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
bip39 = "1.0.1"
bip39 = { workspace = true }
clap = { version = "4.0", features = ["cargo", "derive"] }
log = "0.4"
rand = "0.7.3"
@@ -15,10 +15,10 @@ thiserror = "1.0"
url = "2.2"
tokio = { version = "1.24.1", features = ["rt-multi-thread", "net", "signal", "macros"] } # async runtime
coconut-interface = { path = "../../common/coconut-interface" }
nym-coconut-interface = { path = "../../common/coconut-interface" }
nym-config = { path = "../../common/config" }
credentials = { path = "../../common/credentials" }
credential-storage = { path = "../../common/credential-storage" }
nym-credentials = { path = "../../common/credentials" }
nym-credential-storage = { path = "../../common/credential-storage" }
nym-crypto = { path = "../../common/crypto", features = ["rand", "asymmetric", "symmetric", "aes", "hashing"] }
nym-bin-common = { path = "../../common/bin-common"}
nym-network-defaults = { path = "../../common/network-defaults" }
+5 -5
View File
@@ -7,11 +7,11 @@ use nym_bin_common::completions::ArgShell;
use rand::rngs::OsRng;
use std::str::FromStr;
use coconut_interface::{Base58, Parameters};
use credential_storage::storage::Storage;
use credential_storage::PersistentStorage;
use credentials::coconut::bandwidth::{BandwidthVoucher, TOTAL_ATTRIBUTES};
use credentials::coconut::utils::obtain_aggregate_signature;
use nym_coconut_interface::{Base58, Parameters};
use nym_credential_storage::storage::Storage;
use nym_credential_storage::PersistentStorage;
use nym_credentials::coconut::bandwidth::{BandwidthVoucher, TOTAL_ATTRIBUTES};
use nym_credentials::coconut::utils::obtain_aggregate_signature;
use nym_crypto::asymmetric::{encryption, identity};
use nym_network_defaults::VOUCHER_INFO;
use validator_client::nyxd::traits::DkgQueryClient;
+2 -2
View File
@@ -4,8 +4,8 @@
use std::time::SystemTimeError;
use thiserror::Error;
use credential_storage::error::StorageError;
use credentials::error::Error as CredentialError;
use nym_credential_storage::error::StorageError;
use nym_credentials::error::Error as CredentialError;
use nym_crypto::asymmetric::encryption::KeyRecoveryError;
use nym_crypto::asymmetric::identity::Ed25519RecoveryError;
use validator_client::nyxd::error::NyxdError;
+6 -3
View File
@@ -11,7 +11,7 @@ use commands::*;
use error::Result;
use log::*;
use nym_bin_common::completions::fig_generate;
use nym_config::{DATA_DIR, DB_FILE_NAME};
use nym_config::{CRED_DB_FILE_NAME, DATA_DIR};
use nym_network_defaults::{setup_env, NymNetworkDetails};
use std::process::exit;
use std::time::{Duration, SystemTime};
@@ -73,8 +73,11 @@ async fn main() -> Result<()> {
match args.command {
Command::Run(r) => {
let db_path = r.client_home_directory.join(DATA_DIR).join(DB_FILE_NAME);
let shared_storage = credential_storage::initialise_storage(db_path).await;
let db_path = r
.client_home_directory
.join(DATA_DIR)
.join(CRED_DB_FILE_NAME);
let shared_storage = nym_credential_storage::initialise_storage(db_path).await;
let recovery_storage = recovery_storage::RecoveryStorage::new(r.recovery_dir)?;
let network_details = NymNetworkDetails::new_from_env();
+1 -1
View File
@@ -1,7 +1,7 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use credentials::coconut::bandwidth::BandwidthVoucher;
use nym_credentials::coconut::bandwidth::BandwidthVoucher;
use std::fs::{create_dir_all, read_dir, File};
use std::io::{Read, Write};
use std::path::PathBuf;
+2 -2
View File
@@ -1,8 +1,8 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use coconut_interface::Parameters;
use credentials::coconut::bandwidth::BandwidthVoucher;
use nym_coconut_interface::Parameters;
use nym_credentials::coconut::bandwidth::BandwidthVoucher;
use nym_crypto::asymmetric::{encryption, identity};
+4 -4
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-client"
version = "1.1.10"
version = "1.1.12"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
description = "Implementation of the Nym Client"
edition = "2021"
@@ -36,10 +36,10 @@ tokio-tungstenite = "0.14" # websocket
## internal
nym-bin-common = { path = "../../common/bin-common" }
client-core = { path = "../client-core", features = ["fs-surb-storage"] }
coconut-interface = { path = "../../common/coconut-interface" }
nym-coconut-interface = { path = "../../common/coconut-interface" }
nym-config = { path = "../../common/config" }
credential-storage = { path = "../../common/credential-storage" }
credentials = { path = "../../common/credentials" }
nym-credential-storage = { path = "../../common/credential-storage" }
nym-credentials = { path = "../../common/credentials" }
nym-crypto = { path = "../../common/crypto" }
gateway-client = { path = "../../common/client-libs/gateway-client" }
gateway-requests = { path = "../../gateway/gateway-requests" }
+2 -1
View File
@@ -74,7 +74,7 @@ impl SocketClient {
let client = validator_client::Client::new_query(client_config)
.expect("Could not construct query client");
BandwidthController::new(
credential_storage::initialise_storage(config.get_base().get_database_path()).await,
nym_credential_storage::initialise_storage(config.get_base().get_database_path()).await,
client,
)
}
@@ -101,6 +101,7 @@ impl SocketClient {
let ClientState {
shared_lane_queue_lengths,
reply_controller_sender,
..
} = client_state;
let websocket_handler = websocket::HandlerBuilder::new(
+9 -9
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-socks5-client"
version = "1.1.10"
version = "1.1.12"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
edition = "2021"
@@ -29,26 +29,26 @@ url = "2.2"
# internal
nym-bin-common = { path = "../../common/bin-common" }
client-core = { path = "../client-core", features = ["fs-surb-storage"] }
coconut-interface = { path = "../../common/coconut-interface" }
nym-coconut-interface = { path = "../../common/coconut-interface" }
nym-config = { path = "../../common/config" }
credential-storage = { path = "../../common/credential-storage", optional = true }
nym-credential-storage = { path = "../../common/credential-storage", optional = true }
mobile-storage = { path = "../../common/mobile-storage", optional = true }
credentials = { path = "../../common/credentials" }
nym-credentials = { path = "../../common/credentials" }
nym-crypto = { path = "../../common/crypto" }
gateway-client = { path = "../../common/client-libs/gateway-client" }
gateway-requests = { path = "../../gateway/gateway-requests" }
nym-network-defaults = { path = "../../common/network-defaults" }
nym-sphinx = { path = "../../common/nymsphinx" }
ordered-buffer = { path = "../../common/socks5/ordered-buffer" }
nym-ordered-buffer = { path = "../../common/socks5/ordered-buffer" }
nym-pemstore = { path = "../../common/pemstore" }
proxy-helpers = { path = "../../common/socks5/proxy-helpers" }
service-providers-common = { path = "../../service-providers/common" }
socks5-requests = { path = "../../common/socks5/requests" }
nym-socks5-proxy-helpers = { path = "../../common/socks5/proxy-helpers" }
nym-service-providers-common = { path = "../../service-providers/common" }
nym-socks5-requests = { path = "../../common/socks5/requests" }
nym-task = { path = "../../common/task" }
nym-topology = { path = "../../common/topology" }
validator-client = { path = "../../common/client-libs/validator-client", features = ["nyxd-client"] }
[features]
default = ["credential-storage"]
default = ["nym-credential-storage"]
eth = []
mobile = ["mobile-storage", "gateway-client/mobile"]
+2 -2
View File
@@ -7,10 +7,10 @@ pub use client_core::config::MISSING_VALUE;
use client_core::config::{ClientCoreConfigTrait, DebugConfig};
use nym_config::defaults::DEFAULT_SOCKS5_LISTENING_PORT;
use nym_config::{NymConfig, OptionalSet};
use nym_service_providers_common::interface::ProviderInterfaceVersion;
use nym_socks5_requests::Socks5ProtocolVersion;
use nym_sphinx::addressing::clients::Recipient;
use serde::{Deserialize, Serialize};
use service_providers_common::interface::ProviderInterfaceVersion;
use socks5_requests::Socks5ProtocolVersion;
use std::fmt::Debug;
use std::path::PathBuf;
use std::str::FromStr;
+2 -2
View File
@@ -92,7 +92,7 @@ impl NymClient {
#[cfg(not(feature = "mobile"))]
let storage =
credential_storage::initialise_storage(config.get_base().get_database_path()).await;
nym_credential_storage::initialise_storage(config.get_base().get_database_path()).await;
#[cfg(feature = "mobile")]
let storage = mobile_storage::PersistentStorage {};
@@ -123,7 +123,7 @@ impl NymClient {
let ClientState {
shared_lane_queue_lengths,
reply_controller_sender: _,
..
} = client_status;
let authenticator = Authenticator::new(auth_methods, allowed_users);
+1 -1
View File
@@ -1,6 +1,6 @@
use crate::socks::types::SocksProxyError;
use client_core::error::ClientCoreError;
use socks5_requests::{ConnectionError, ConnectionId};
use nym_socks5_requests::{ConnectionError, ConnectionId};
#[derive(thiserror::Error, Debug)]
pub enum Socks5ClientError {
+8 -8
View File
@@ -8,19 +8,19 @@ use client_core::client::inbound_messages::{InputMessage, InputMessageSender};
use futures::channel::mpsc;
use futures::task::{Context, Poll};
use log::*;
use nym_service_providers_common::interface::{ProviderInterfaceVersion, RequestVersion};
use nym_socks5_proxy_helpers::connection_controller::{
ConnectionReceiver, ControllerCommand, ControllerSender,
};
use nym_socks5_proxy_helpers::proxy_runner::ProxyRunner;
use nym_socks5_requests::{
ConnectionId, RemoteAddress, Socks5ProtocolVersion, Socks5ProviderRequest, Socks5Request,
};
use nym_sphinx::addressing::clients::Recipient;
use nym_task::connections::{LaneQueueLengths, TransmissionLane};
use nym_task::TaskClient;
use pin_project::pin_project;
use proxy_helpers::connection_controller::{
ConnectionReceiver, ControllerCommand, ControllerSender,
};
use proxy_helpers::proxy_runner::ProxyRunner;
use rand::RngCore;
use service_providers_common::interface::{ProviderInterfaceVersion, RequestVersion};
use socks5_requests::{
ConnectionId, RemoteAddress, Socks5ProtocolVersion, Socks5ProviderRequest, Socks5Request,
};
use std::io;
use std::net::SocketAddr;
use std::pin::Pin;
+3 -3
View File
@@ -4,11 +4,11 @@ use log::*;
use client_core::client::received_buffer::ReconstructedMessagesReceiver;
use client_core::client::received_buffer::{ReceivedBufferMessage, ReceivedBufferRequestSender};
use nym_service_providers_common::interface::{ControlResponse, ResponseContent};
use nym_socks5_proxy_helpers::connection_controller::ControllerSender;
use nym_socks5_requests::{Socks5ProviderResponse, Socks5Response, Socks5ResponseContent};
use nym_sphinx::receiver::ReconstructedMessage;
use nym_task::TaskClient;
use proxy_helpers::connection_controller::ControllerSender;
use service_providers_common::interface::{ControlResponse, ResponseContent};
use socks5_requests::{Socks5ProviderResponse, Socks5Response, Socks5ResponseContent};
use crate::error::Socks5ClientError;
+1 -1
View File
@@ -8,10 +8,10 @@ use client_core::client::{
inbound_messages::InputMessageSender, received_buffer::ReceivedBufferRequestSender,
};
use log::*;
use nym_socks5_proxy_helpers::connection_controller::Controller;
use nym_sphinx::addressing::clients::Recipient;
use nym_task::connections::{ConnectionCommandSender, LaneQueueLengths};
use nym_task::TaskClient;
use proxy_helpers::connection_controller::Controller;
use std::net::SocketAddr;
use tap::TapFallible;
use tokio::net::TcpListener;
+1 -1
View File
@@ -1,4 +1,4 @@
use socks5_requests::Socks5RequestError;
use nym_socks5_requests::Socks5RequestError;
use std::string::FromUtf8Error;
use thiserror::Error;
+68 -80
View File
@@ -1,83 +1,71 @@
{
"root": true,
"env": {
"browser": true,
"es6": true,
"node": true
},
"parserOptions": {
"ecmaVersion": 2019,
"sourceType": "module"
},
"globals": {
"Atomics": "readonly",
"SharedArrayBuffer": "readonly"
},
"plugins": ["prettier", "mocha"],
"extends": [
"airbnb-base",
"airbnb-typescript/base",
"prettier"],
"rules": {
"prettier/prettier": "error",
"import/prefer-default-export": "off",
"import/no-extraneous-dependencies": [
"error",
{
"devDependencies": [
"**/*.test.[jt]s",
"**/*.spec.[jt]s"
]
}
],
"import/extensions": [
"error",
"ignorePackages",
{
"ts": "never",
"js": "never"
}
]
},
"overrides": [
{
"files": "**/*.ts",
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": "./tsconfig.json"
},
"plugins": ["@typescript-eslint/eslint-plugin"],
"extends": [
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"prettier"
],
"rules": {
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-var-requires": "off",
"no-use-before-define": [0],
"@typescript-eslint/no-use-before-define": [1],
"import/no-unresolved": 0,
"import/no-extraneous-dependencies": [
"error",
{
"devDependencies": [
"**/*.test.ts",
"**/*.spec.ts"
]
}
],
"quotes": "off",
"@typescript-eslint/quotes": [
2,
"single",
{
"avoidEscape": true
}
],
"@typescript-eslint/no-unused-vars": [2, { "argsIgnorePattern": "^_" }]
}
}
"root": true,
"env": {
"browser": true,
"es6": true,
"node": true
},
"parserOptions": {
"ecmaVersion": 2019,
"sourceType": "module"
},
"globals": {
"Atomics": "readonly",
"SharedArrayBuffer": "readonly"
},
"plugins": ["prettier", "mocha"],
"extends": ["airbnb-base", "airbnb-typescript/base", "prettier"],
"rules": {
"prettier/prettier": "error",
"import/prefer-default-export": "off",
"import/no-extraneous-dependencies": [
"error",
{
"devDependencies": ["**/*.test.[jt]s", "**/*.spec.[jt]s"]
}
],
"import/extensions": [
"error",
"ignorePackages",
{
"ts": "never",
"js": "never"
}
]
},
"overrides": [
{
"files": "**/*.ts",
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": "./tsconfig.json"
},
"plugins": ["@typescript-eslint/eslint-plugin"],
"extends": ["plugin:@typescript-eslint/eslint-recommended", "plugin:@typescript-eslint/recommended", "prettier"],
"rules": {
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-var-requires": "off",
"no-use-before-define": [0],
"@typescript-eslint/no-use-before-define": [1],
"import/no-unresolved": 0,
"import/no-extraneous-dependencies": [
"error",
{
"devDependencies": ["**/*.test.ts", "**/*.spec.ts"]
}
],
"quotes": "off",
"@typescript-eslint/quotes": [
2,
"single",
{
"avoidEscape": true
}
],
"@typescript-eslint/no-unused-vars": [2, { "argsIgnorePattern": "^_" }]
}
}
],
"ignorePatterns": ["tsconfig.json", "*.d.ts", "dist/**/*", "dist", "node_modules"]
}
+2 -1
View File
@@ -4,4 +4,5 @@ coverage
dist
docs
examples/accounts
node_modules
node_modules
.env
+3 -1
View File
@@ -1,3 +1,5 @@
coverage
node_modules
tests
tests
src
type
+24 -25
View File
@@ -1,40 +1,39 @@
Nym Validator Client
====================
# Nym Validator Client (Typescript)
A TypeScript client for interacting with CosmWasm smart contracts in Nym validators.
A TypeScript client for interacting with CosmWasm smart contracts in Nym validators.
Running examples
-----------------
With the code checked out, `cd examples`. This folder contains runnable example code that will set up a blockchain and allow you to interact with it through the client.
Running tests
-------------
Include the Nym Validator in your project:
```
npm test
yarn add @nymproject/nym-validator-client
```
You can also trigger test execution with a test watcher. I don't have the centuries of life left to me that are needed to fight through the arcana of wiring up a working TypeScript mocha triggered execution setup, so for now my Cargo-based hack is:
Connect to validator and make queries
```
cargo watch -s "cd clients/validator && npm test"
import Validator from '@nymproject/nym-validator-client'
const main = async () => {
const client = await Validator.connectForQuery(rpcAddress, validatorAddress, prefix, mixnetContractAddress, vestingContractAddress, denom)
client.getBalance(address)
}
```
It's ugly but works fine if you have Cargo installed. TypeScript setup help happily accepted here.
Connect to validator for performing actions
Generating Documentation
------------------------
```
import Validator from '@nymproject/nym-validator-client'
You can generate docs by running `npm run docs`. Generated output will appear in the `docs` directory.
const main = async () => {
Packaging
------------------------
const client = await Validator.connect(mnemonic, rpcAddress, validatorAddress, prefix, mixnetContractAddress, vestingContractAddress, denom)
If you're a Nym platform developer who's made changes to the client and wants to re-publish the package to NPM, here's how you do it:
const res = await client.send(address, [{ amount: '10000000', denom: 'unym' }]);
1. Bump the version number (use SemVer)
1. `npm run build`
1. `npm login` (if you haven't already)
1. `npm publish`
}
```
+34 -18
View File
@@ -4,15 +4,23 @@
"description": "A TypeScript client for interacting with smart contracts in Nym validators",
"repository": "https://github.com/nymtech/nym",
"main": "./dist/index.js",
"types": "dist/index.d.ts",
"types": "./dist/index.d.ts",
"scripts": {
"build": "tsc",
"test": "ts-mocha tests/**/*.test.ts",
"build": "rollup -c ./rollup.config.mjs",
"build:types": "rollup-type-bundler --dist ./dist/nym-validator-client",
"build:prod": "sh ./scripts/build-prod.sh",
"test": "ts-mocha -p ./tsconfig.test.json ./src/tests/**/*.test.ts",
"testmock": "ts-mocha -p ./tsconfig.test.json ./src/tests/mock/*.test.ts",
"coverage": "nyc npm test",
"lint": "eslint src",
"lint:fix": "eslint src --fix",
"clean": "rm -rf ./dist",
"lint": "eslint",
"lint:fix": "eslint --fix",
"lint:tsc": "tsc --noEmit",
"docs": "typedoc --out docs src/index.ts"
},
"files": [
"./dist/*"
],
"keywords": [],
"author": "Nym Technologies SA (https://nymtech.net)",
"contributors": [
@@ -21,6 +29,14 @@
],
"license": "Apache-2.0",
"devDependencies": {
"@favware/rollup-type-bundler": "^2.0.0",
"@nymproject/types": "^1.0.0",
"@rollup/plugin-commonjs": "^24.0.1",
"@rollup/plugin-json": "^6.0.0",
"@rollup/plugin-typescript": "^11.0.0",
"@rollup/plugin-node-resolve": "^15.0.1",
"rollup": "^3.17.2",
"rollup-plugin-dts": "^5.2.0",
"@typescript-eslint/eslint-plugin": "^5.7.0",
"@typescript-eslint/parser": "^5.7.0",
"eslint": "^7.18.0",
@@ -31,21 +47,21 @@
"eslint-plugin-import": "^2.25.4",
"eslint-plugin-mocha": "^10.0.3",
"eslint-plugin-prettier": "^4.0.0",
"expect": "^28.1.3",
"mocha": "^10.0.0",
"prettier": "^2.5.1",
"typedoc": "^0.22.13",
"ts-mocha": "^10.0.0",
"typescript": "^4.6.2"
},
"dependencies": {
"@cosmjs/cosmwasm-stargate": "^0.28.0",
"@cosmjs/crypto": "^0.28.0",
"@cosmjs/math": "^0.28.0",
"@cosmjs/proto-signing": "^0.28.0",
"@cosmjs/stargate": "^0.28.0",
"@cosmjs/tendermint-rpc": "^0.28.0",
"axios": "^0.26.1",
"cosmjs-types": "^0.4.1"
"typedoc": "^0.22.13",
"typescript": "^4.6.2",
"cosmjs-types": "^0.4.1",
"dotenv": "^16.0.3",
"expect": "^28.1.3",
"moq.ts": "^7.3.4",
"@cosmjs/cosmwasm-stargate": "^0.29.5",
"@cosmjs/crypto": "^0.29.5",
"@cosmjs/math": "^0.29.5",
"@cosmjs/proto-signing": "^0.29.5",
"@cosmjs/stargate": "^0.29.5",
"@cosmjs/tendermint-rpc": "^0.29.5",
"axios": "^1.3.3"
}
}
+15
View File
@@ -0,0 +1,15 @@
import typescript from '@rollup/plugin-typescript';
import resolve from '@rollup/plugin-node-resolve';
import json from '@rollup/plugin-json';
import commonjs from '@rollup/plugin-commonjs';
export default [
{
input: './src/index.ts',
output: {
dir: 'dist/nym-validator-client',
format: 'cjs',
},
plugins: [resolve(), typescript(), commonjs(), json()],
},
];
+31
View File
@@ -0,0 +1,31 @@
#!/usr/bin/env bash
set -o errexit
set -o nounset
rm -rf ./dist || true
rm -rf ../../dist || true
# Bundle application
yarn build
# Bundle types
yarn build:types
# Build package.json for bundle
node ./scripts/buildPackageJson.mjs
# Copy README
cp README.md dist/nym-validator-client
# move the output outside of the yarn/npm workspaces
mv ./dist ../../
echo "Output can be found in:"
realpath ../../dist
@@ -0,0 +1,20 @@
import * as fs from 'fs';
// parse the package.json from the SDK, so we can keep fields like the name and version
const json = JSON.parse(fs.readFileSync('./package.json').toString());
// defaults (NB: these are in the output file locations)
const main = 'index.js';
const types = 'index.d.ts';
// make a package.json for the bundle
const packageJson = {
name: json.name,
version: json.version,
license: json.license,
author: json.author,
main,
types,
};
fs.writeFileSync('./dist/nym-validator-client/package.json', JSON.stringify(packageJson, null, 2));
+1 -9
View File
@@ -1,5 +1,6 @@
import { Decimal } from '@cosmjs/math';
import { Coin } from '@cosmjs/stargate';
import { CoinMap } from './types/shared';
// NARROW NO-BREAK SPACE (U+202F)
const thinSpace = '\u202F';
@@ -34,15 +35,6 @@ export function nativeToPrintable(nativeValue: string): string {
return Decimal.fromAtomics(nativeValue, 6).toString();
}
export interface MappedCoin {
readonly denom: string;
readonly fractionalDigits: number;
}
export interface CoinMap {
readonly [key: string]: MappedCoin;
}
export function nativeCoinToDisplay(coin: Coin, coinMap: CoinMap): Coin {
if (!coinMap) return coin;
+133 -75
View File
@@ -1,6 +1,3 @@
import { Bip39, Random } from '@cosmjs/crypto';
import { DirectSecp256k1HdWallet, EncodeObject } from '@cosmjs/proto-signing';
import { coin as cosmosCoin, Coin, DeliverTxResponse, isDeliverTxFailure, StdFee } from '@cosmjs/stargate';
import {
ExecuteResult,
InstantiateOptions,
@@ -8,45 +5,37 @@ import {
MigrateResult,
UploadResult,
} from '@cosmjs/cosmwasm-stargate';
import SigningClient, { ISigningClient } from './signing-client';
import { Bip39, Random } from '@cosmjs/crypto';
import { DirectSecp256k1HdWallet, EncodeObject } from '@cosmjs/proto-signing';
import { Coin, coin as cosmosCoin, DeliverTxResponse, isDeliverTxFailure, StdFee } from '@cosmjs/stargate';
import {
ContractStateParams,
Delegation,
Gateway,
GatewayBond,
GatewayOwnershipResponse,
LayerDistribution,
MixnetContractVersion,
MixNode,
MixNodeBond,
MixNodeCostParams,
MixNodeDetails,
MixNodeRewarding,
MixOwnershipResponse,
PagedAllDelegationsResponse,
PagedDelegatorDelegationsResponse,
PagedGatewayResponse,
PagedMixDelegationsResponse,
PagedMixnodeResponse,
} from './types';
import {
CoinMap,
displayAmountToNative,
MappedCoin,
nativeCoinToDisplay,
nativeToPrintable,
printableBalance,
printableCoin,
} from './currency';
PagedMixNodeBondResponse,
PagedMixNodeDetailsResponse,
PagedUnbondedMixnodesResponse,
RewardingParams,
StakeSaturationResponse,
UnbondedMixnodeResponse,
} from '@nymproject/types';
import QueryClient from './query-client';
import { nymGasPrice } from './stargate-helper';
export { coins, coin } from '@cosmjs/stargate';
export { Coin };
export {
displayAmountToNative,
nativeCoinToDisplay,
printableCoin,
printableBalance,
nativeToPrintable,
MappedCoin,
CoinMap,
};
export { nymGasPrice };
import SigningClient, { ISigningClient } from './signing-client';
import { ContractState } from './types/shared';
export interface INymClient {
readonly mixnetContract: string;
@@ -147,7 +136,7 @@ export default class ValidatorClient implements INymClient {
return DirectSecp256k1HdWallet.fromMnemonic(mnemonic, signerOptions);
}
getBalance(address: string): Promise<Coin> {
async getBalance(address: string): Promise<Coin> {
return this.client.getBalance(address, this.denom);
}
@@ -159,15 +148,39 @@ export default class ValidatorClient implements INymClient {
return this.client.getCachedMixnodes();
}
async getActiveMixnodes(): Promise<MixNodeBond[]> {
async getStakeSaturation(mixId: number): Promise<StakeSaturationResponse> {
return this.client.getStakeSaturation(this.mixnetContract, mixId);
}
async getActiveMixnodes(): Promise<MixNodeDetails[]> {
return this.client.getActiveMixnodes();
}
async getUnbondedMixNodeInformation(mixId: number): Promise<UnbondedMixnodeResponse> {
return this.client.getUnbondedMixNodeInformation(this.mixnetContract, mixId);
}
async getRewardedMixnodes(): Promise<MixNodeBond[]> {
return this.client.getRewardedMixnodes();
}
public async getMixnetContractSettings(): Promise<ContractStateParams> {
async getMixnodeRewardingDetails(mixId: number): Promise<MixNodeRewarding> {
return this.client.getMixnodeRewardingDetails(this.mixnetContract, mixId);
}
async getOwnedMixnode(address: string): Promise<MixOwnershipResponse> {
return this.client.getOwnedMixnode(this.mixnetContract, address);
}
async ownsGateway(address: string): Promise<GatewayOwnershipResponse> {
return this.client.ownsGateway(this.mixnetContract, address);
}
async getLayerDistribution(): Promise<LayerDistribution> {
return this.client.getLayerDistribution(this.mixnetContract);
}
public async getMixnetContractSettings(): Promise<ContractState> {
return this.client.getStateParams(this.mixnetContract);
}
@@ -175,29 +188,74 @@ export default class ValidatorClient implements INymClient {
return this.client.getContractVersion(this.mixnetContract);
}
public async getRewardPool(): Promise<string> {
return this.client.getRewardPool(this.mixnetContract);
public async getVestingContractVersion(): Promise<MixnetContractVersion> {
return this.client.getContractVersion(this.vestingContract);
}
public async getCirculatingSupply(): Promise<string> {
return this.client.getCirculatingSupply(this.mixnetContract);
public async getSpendableCoins(vestingAccountAddress: string): Promise<MixnetContractVersion> {
return this.client.getSpendableCoins(this.vestingContract, vestingAccountAddress);
}
public async getSybilResistancePercent(): Promise<number> {
return this.client.getSybilResistancePercent(this.mixnetContract);
public async getRewardParams(): Promise<RewardingParams> {
return this.client.getRewardParams(this.mixnetContract);
}
public async getIntervalRewardPercent(): Promise<number> {
return this.client.getIntervalRewardPercent(this.mixnetContract);
async getUnbondedMixNodes(): Promise<UnbondedMixnodeResponse[]> {
let mixNodes: UnbondedMixnodeResponse[] = [];
const limit = 50;
let startAfter;
for (;;) {
// eslint-disable-next-line no-await-in-loop
const pagedResponse: PagedUnbondedMixnodesResponse = await this.client.getUnbondedMixNodes(
this.mixnetContract,
limit,
startAfter,
);
mixNodes = mixNodes.concat(pagedResponse.nodes);
startAfter = pagedResponse.start_next_after;
// if `start_next_after` is not set, we're done
if (!startAfter) {
break;
}
}
return mixNodes;
}
public async getAllNyxdMixnodes(): Promise<MixNodeBond[]> {
public async getMixNodeBonds(): Promise<MixNodeBond[]> {
let mixNodes: MixNodeBond[] = [];
const limit = 50;
let startAfter;
for (; ;) {
for (;;) {
// eslint-disable-next-line no-await-in-loop
const pagedResponse: PagedMixnodeResponse = await this.client.getMixNodesPaged(this.mixnetContract, limit);
const pagedResponse: PagedMixNodeBondResponse = await this.client.getMixNodeBonds(
this.mixnetContract,
limit,
startAfter,
);
mixNodes = mixNodes.concat(pagedResponse.nodes);
startAfter = pagedResponse.start_next_after;
// if `start_next_after` is not set, we're done
if (!startAfter) {
break;
}
}
return mixNodes;
}
public async getMixNodesDetailed(): Promise<MixNodeDetails[]> {
let mixNodes: MixNodeDetails[] = [];
const limit = 50;
let startAfter;
for (;;) {
// eslint-disable-next-line no-await-in-loop
const pagedResponse: PagedMixNodeDetailsResponse = await this.client.getMixNodesDetailed(
this.mixnetContract,
limit,
startAfter,
);
mixNodes = mixNodes.concat(pagedResponse.nodes);
startAfter = pagedResponse.start_next_after;
// if `start_next_after` is not set, we're done
@@ -210,37 +268,24 @@ export default class ValidatorClient implements INymClient {
}
public async getAllNyxdGateways(): Promise<GatewayBond[]> {
let gateways: GatewayBond[] = [];
const limit = 50;
let startAfter;
for (; ;) {
// eslint-disable-next-line no-await-in-loop
const pagedResponse: PagedGatewayResponse = await this.client.getGatewaysPaged(this.mixnetContract, limit);
gateways = gateways.concat(pagedResponse.nodes);
startAfter = pagedResponse.start_next_after;
// if `start_next_after` is not set, we're done
if (!startAfter) {
break;
}
}
return gateways;
const pagedResponse: PagedGatewayResponse = await this.client.getGatewaysPaged(this.mixnetContract);
return pagedResponse.nodes;
}
/**
* Gets list of all delegations towards particular mixnode.
*
* @param mixIdentity identity of the node to which the delegation was sent
* @param mix_id identity of the node to which the delegation was sent
*/
public async getAllNyxdSingleMixnodeDelegations(mixIdentity: string): Promise<Delegation[]> {
public async getAllNyxdSingleMixnodeDelegations(mix_id: number): Promise<Delegation[]> {
let delegations: Delegation[] = [];
const limit = 250;
let startAfter;
for (; ;) {
for (;;) {
// eslint-disable-next-line no-await-in-loop
const pagedResponse: PagedMixDelegationsResponse = await this.client.getMixNodeDelegationsPaged(
this.mixnetContract,
mixIdentity,
mix_id,
limit,
startAfter,
);
@@ -259,7 +304,7 @@ export default class ValidatorClient implements INymClient {
let delegations: Delegation[] = [];
const limit = 250;
let startAfter;
for (; ;) {
for (;;) {
// eslint-disable-next-line no-await-in-loop
const pagedResponse: PagedDelegatorDelegationsResponse = await this.client.getDelegatorDelegationsPaged(
this.mixnetContract,
@@ -278,13 +323,13 @@ export default class ValidatorClient implements INymClient {
return delegations;
}
public async getAllNyxdNetworkDelegations(): Promise<Delegation[]> {
public async getAllNyxdDelegations(): Promise<Delegation[]> {
let delegations: Delegation[] = [];
const limit = 250;
let startAfter;
for (; ;) {
for (;;) {
// eslint-disable-next-line no-await-in-loop
const pagedResponse: PagedAllDelegationsResponse = await this.client.getAllNetworkDelegationsPaged(
const pagedResponse: PagedAllDelegationsResponse = await this.client.getAllDelegationsPaged(
this.mixnetContract,
limit,
startAfter,
@@ -300,6 +345,10 @@ export default class ValidatorClient implements INymClient {
return delegations;
}
public async getDelegationDetails(mix_id: number, delegator: string): Promise<Delegation> {
return this.client.getDelegationDetails(this.mixnetContract, mix_id, delegator);
}
/**
* Generate a minimum gateway bond required to create a fresh mixnode.
*
@@ -308,7 +357,7 @@ export default class ValidatorClient implements INymClient {
public async minimumMixnodePledge(): Promise<Coin> {
const stateParams = await this.getMixnetContractSettings();
// we trust the contract to return a valid number
return cosmosCoin(stateParams.minimum_mixnode_pledge, this.prefix);
return cosmosCoin(stateParams.params.minimum_mixnode_pledge, this.prefix);
}
/**
@@ -319,7 +368,7 @@ export default class ValidatorClient implements INymClient {
public async minimumGatewayPledge(): Promise<Coin> {
const stateParams = await this.getMixnetContractSettings();
// we trust the contract to return a valid number
return cosmosCoin(stateParams.minimum_gateway_pledge, this.prefix);
return cosmosCoin(stateParams.params.minimum_gateway_pledge, this.prefix);
}
public async send(
@@ -393,12 +442,21 @@ export default class ValidatorClient implements INymClient {
public async bondMixNode(
mixNode: MixNode,
ownerSignature: string,
costParams: MixNodeCostParams,
pledge: Coin,
fee?: StdFee | 'auto' | number,
memo?: string,
): Promise<ExecuteResult> {
this.assertSigning();
return (this.client as ISigningClient).bondMixNode(this.mixnetContract, mixNode, ownerSignature, pledge, fee, memo);
return (this.client as ISigningClient).bondMixNode(
this.mixnetContract,
mixNode,
costParams,
ownerSignature,
pledge,
fee,
memo,
);
}
public async unbondMixNode(fee?: StdFee | 'auto' | number, memo?: string): Promise<ExecuteResult> {
@@ -423,29 +481,29 @@ export default class ValidatorClient implements INymClient {
}
public async delegateToMixNode(
mixIdentity: string,
mixId: number,
amount: Coin,
fee?: StdFee | 'auto' | number,
memo?: string,
): Promise<ExecuteResult> {
this.assertSigning();
return (this.client as ISigningClient).delegateToMixNode(this.mixnetContract, mixIdentity, amount, fee, memo);
return (this.client as ISigningClient).delegateToMixNode(this.mixnetContract, mixId, amount, fee, memo);
}
public async undelegateFromMixNode(
mixIdentity: string,
mixId: number,
fee?: StdFee | 'auto' | number,
memo?: string,
): Promise<ExecuteResult> {
return (this.client as ISigningClient).undelegateFromMixNode(this.mixnetContract, mixIdentity, fee, memo);
return (this.client as ISigningClient).undelegateFromMixNode(this.mixnetContract, mixId, fee, memo);
}
public async updateMixnodeConfig(
mixIdentity: string,
mixId: number,
fee: StdFee | 'auto' | number,
profitPercentage: number,
): Promise<ExecuteResult> {
return (this.client as ISigningClient).updateMixnodeConfig(this.mixnetContract, mixIdentity, profitPercentage, fee);
return (this.client as ISigningClient).updateMixnodeConfig(this.mixnetContract, mixId, profitPercentage, fee);
}
public async updateContractStateParams(
+3 -4
View File
@@ -2,9 +2,8 @@
* Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
* SPDX-License-Identifier: Apache-2.0
*/
import axios from 'axios';
import { GatewayBond, MixNodeBond } from './types';
import { GatewayBond, MixNodeBond, MixNodeDetails } from '@nymproject/types';
export const NYM_API_VERSION = '/v1';
export const NYM_API_GATEWAYS_PATH = `${NYM_API_VERSION}/gateways`;
@@ -17,7 +16,7 @@ export interface INymApiQuery {
getCachedGateways(): Promise<GatewayBond[]>;
getActiveMixnodes(): Promise<MixNodeBond[]>;
getActiveMixnodes(): Promise<MixNodeDetails[]>;
getRewardedMixnodes(): Promise<MixNodeBond[]>;
}
@@ -51,7 +50,7 @@ export default class NymApiQuerier implements INymApiQuery {
throw new Error('None of the provided validator APIs seem to be alive');
}
async getActiveMixnodes(): Promise<MixNodeBond[]> {
async getActiveMixnodes(): Promise<MixNodeDetails[]> {
const url = new URL(this.nymApiUrl);
url.pathname += NYM_API_ACTIVE_MIXNODES_PATH;
+72 -56
View File
@@ -2,28 +2,24 @@
* Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
* SPDX-License-Identifier: Apache-2.0
*/
import { JsonObject } from '@cosmjs/cosmwasm-stargate';
// eslint-disable-next-line import/no-cycle
import { INyxdQuery } from './query-client';
import { Delegation, RewardingParams, StakeSaturationResponse } from '@nymproject/types';
import {
ContractStateParams,
Delegation,
UnbondedMixnodeResponse,
GatewayOwnershipResponse,
LayerDistribution,
MixnetContractVersion,
MixOwnershipResponse,
PagedAllDelegationsResponse,
PagedDelegatorDelegationsResponse,
PagedGatewayResponse,
PagedMixDelegationsResponse,
PagedMixnodeResponse,
RewardingStatus,
} from './types';
interface SmartContractQuery {
queryContractSmart(address: string, queryMsg: Record<string, unknown>): Promise<JsonObject>;
}
PagedMixNodeBondResponse,
PagedMixNodeDetailsResponse,
PagedUnbondedMixnodesResponse,
LayerDistribution,
} from '@nymproject/types';
import { ContractState, SmartContractQuery } from './types/shared';
export default class NyxdQuerier implements INyxdQuery {
client: SmartContractQuery;
@@ -38,15 +34,44 @@ export default class NyxdQuerier implements INyxdQuery {
});
}
getMixNodesPaged(mixnetContractAddress: string, limit?: number, startAfter?: string): Promise<PagedMixnodeResponse> {
getMixNodeBonds(
mixnetContractAddress: string,
limit?: number,
startAfter?: string,
): Promise<PagedMixNodeBondResponse> {
return this.client.queryContractSmart(mixnetContractAddress, {
get_mix_nodes: {
get_mix_node_bonds: {
limit,
start_after: startAfter,
},
});
}
getMixNodesDetailed(
mixnetContractAddress: string,
limit?: number,
startAfter?: string,
): Promise<PagedMixNodeDetailsResponse> {
return this.client.queryContractSmart(mixnetContractAddress, {
get_mix_nodes_detailed: {
limit,
start_after: startAfter,
},
});
}
getStakeSaturation(mixnetContractAddress: string, mixId: number): Promise<StakeSaturationResponse> {
return this.client.queryContractSmart(mixnetContractAddress, {
get_stake_saturation: { mix_id: mixId },
});
}
getMixnodeRewardingDetails(mixnetContractAddress: string, mixId: number): Promise<any> {
return this.client.queryContractSmart(mixnetContractAddress, {
get_mixnode_rewarding_details: { mix_id: mixId },
});
}
getGatewaysPaged(mixnetContractAddress: string, limit?: number, startAfter?: string): Promise<PagedGatewayResponse> {
return this.client.queryContractSmart(mixnetContractAddress, {
get_gateways: {
@@ -56,35 +81,51 @@ export default class NyxdQuerier implements INyxdQuery {
});
}
ownsMixNode(mixnetContractAddress: string, address: string): Promise<MixOwnershipResponse> {
getOwnedMixnode(mixnetContractAddress: string, address: string): Promise<MixOwnershipResponse> {
return this.client.queryContractSmart(mixnetContractAddress, {
owns_mixnode: {
get_owned_mixnode: {
address,
},
});
}
getUnbondedMixNodes(
mixnetContractAddress: string,
limit?: number,
startAfter?: string,
): Promise<PagedUnbondedMixnodesResponse> {
return this.client.queryContractSmart(mixnetContractAddress, {
get_unbonded_mix_nodes: { limit, start_after: startAfter },
});
}
getUnbondedMixNodeInformation(mixnetContractAddress: string, mixId: number): Promise<UnbondedMixnodeResponse> {
return this.client.queryContractSmart(mixnetContractAddress, {
get_unbonded_mix_node_information: { mix_id: mixId },
});
}
ownsGateway(mixnetContractAddress: string, address: string): Promise<GatewayOwnershipResponse> {
return this.client.queryContractSmart(mixnetContractAddress, {
owns_gateway: {
get_owned_gateway: {
address,
},
});
}
getStateParams(mixnetContractAddress: string): Promise<ContractStateParams> {
getStateParams(mixnetContractAddress: string): Promise<ContractState> {
return this.client.queryContractSmart(mixnetContractAddress, {
state_params: {},
get_state: {},
});
}
getAllNetworkDelegationsPaged(
getAllDelegationsPaged(
mixnetContractAddress: string,
limit?: number,
startAfter?: [string, string],
): Promise<PagedAllDelegationsResponse> {
return this.client.queryContractSmart(mixnetContractAddress, {
get_all_network_delegations: {
get_all_delegations: {
start_after: startAfter,
limit,
},
@@ -93,13 +134,13 @@ export default class NyxdQuerier implements INyxdQuery {
getMixNodeDelegationsPaged(
mixnetContractAddress: string,
mixIdentity: string,
mix_id: number,
limit?: number,
startAfter?: string,
): Promise<PagedMixDelegationsResponse> {
return this.client.queryContractSmart(mixnetContractAddress, {
get_mixnode_delegations: {
mix_identity: mixIdentity,
mix_id: mix_id,
start_after: startAfter,
limit,
},
@@ -121,10 +162,10 @@ export default class NyxdQuerier implements INyxdQuery {
});
}
getDelegationDetails(mixnetContractAddress: string, mixIdentity: string, delegator: string): Promise<Delegation> {
getDelegationDetails(mixnetContractAddress: string, mix_id: number, delegator: string): Promise<Delegation> {
return this.client.queryContractSmart(mixnetContractAddress, {
get_delegation_details: {
mix_identity: mixIdentity,
mix_id: mix_id,
delegator,
},
});
@@ -132,44 +173,19 @@ export default class NyxdQuerier implements INyxdQuery {
getLayerDistribution(mixnetContractAddress: string): Promise<LayerDistribution> {
return this.client.queryContractSmart(mixnetContractAddress, {
layer_distribution: {},
get_layer_distribution: {},
});
}
getRewardPool(mixnetContractAddress: string): Promise<string> {
getRewardParams(mixnetContractAddress: string): Promise<RewardingParams> {
return this.client.queryContractSmart(mixnetContractAddress, {
get_reward_pool: {},
get_rewarding_params: {},
});
}
getCirculatingSupply(mixnetContractAddress: string): Promise<string> {
return this.client.queryContractSmart(mixnetContractAddress, {
get_circulating_supply: {},
});
}
getIntervalRewardPercent(mixnetContractAddress: string): Promise<number> {
return this.client.queryContractSmart(mixnetContractAddress, {
get_interval_reward_percent: {},
});
}
getSybilResistancePercent(mixnetContractAddress: string): Promise<number> {
return this.client.queryContractSmart(mixnetContractAddress, {
get_sybil_resistance_percent: {},
});
}
getRewardingStatus(
mixnetContractAddress: string,
mixIdentity: string,
rewardingIntervalNonce: number,
): Promise<RewardingStatus> {
return this.client.queryContractSmart(mixnetContractAddress, {
get_rewarding_status: {
mix_identity: mixIdentity,
rewarding_interval_nonce: rewardingIntervalNonce,
},
getSpendableCoins(vestingContractAddress: string, vestingAccountAddress: string): Promise<any> {
return this.client.queryContractSmart(vestingContractAddress, {
vesting_account_address: vestingAccountAddress,
});
}
}
+84 -93
View File
@@ -1,75 +1,56 @@
import { CosmWasmClient, JsonObject } from '@cosmjs/cosmwasm-stargate';
import { Tendermint34Client } from '@cosmjs/tendermint-rpc';
import {
Account,
Block,
Coin,
DeliverTxResponse,
IndexedTx,
SearchTxFilter,
SearchTxQuery,
SequenceResponse,
} from '@cosmjs/stargate';
import { Code, CodeDetails, Contract, ContractCodeHistoryEntry } from '@cosmjs/cosmwasm-stargate/build/cosmwasmclient';
import { CosmWasmClient } from '@cosmjs/cosmwasm-stargate';
// eslint-disable-next-line import/no-cycle
import NyxdQuerier from './nyxd-querier';
import {
ContractStateParams,
Delegation,
GatewayBond,
GatewayOwnershipResponse,
LayerDistribution,
MixnetContractVersion,
MixNodeBond,
MixNodeDetails,
MixOwnershipResponse,
PagedAllDelegationsResponse,
PagedDelegatorDelegationsResponse,
PagedGatewayResponse,
PagedMixDelegationsResponse,
PagedMixnodeResponse,
RewardingStatus,
} from './types';
import NymApiQuerier, { INymApiQuery as INymApiQuery } from './nym-api-querier';
export interface ICosmWasmQuery {
// methods exposed by `CosmWasmClient`
getChainId(): Promise<string>;
getHeight(): Promise<number>;
getAccount(searchAddress: string): Promise<Account | null>;
getSequence(address: string): Promise<SequenceResponse>;
getBlock(height?: number): Promise<Block>;
getBalance(address: string, searchDenom: string): Promise<Coin>;
getTx(id: string): Promise<IndexedTx | null>;
searchTx(query: SearchTxQuery, filter?: SearchTxFilter): Promise<readonly IndexedTx[]>;
disconnect(): void;
broadcastTx(tx: Uint8Array, timeoutMs?: number, pollIntervalMs?: number): Promise<DeliverTxResponse>;
getCodes(): Promise<readonly Code[]>;
getCodeDetails(codeId: number): Promise<CodeDetails>;
getContracts(codeId: number): Promise<readonly string[]>;
getContract(address: string): Promise<Contract>;
getContractCodeHistory(address: string): Promise<readonly ContractCodeHistoryEntry[]>;
queryContractRaw(address: string, key: Uint8Array): Promise<Uint8Array | null>;
queryContractSmart(address: string, queryMsg: Record<string, unknown>): Promise<JsonObject>;
}
PagedMixNodeBondResponse,
PagedMixNodeDetailsResponse,
PagedUnbondedMixnodesResponse,
StakeSaturationResponse,
UnbondedMixnodeResponse,
MixNodeBond,
MixNodeRewarding,
} from '@nymproject/types';
import NymApiQuerier, { INymApiQuery } from './nym-api-querier';
import { ContractState, ICosmWasmQuery } from './types/shared';
import { RewardingParams } from '@nymproject/types';
import { Tendermint34Client } from '@cosmjs/tendermint-rpc';
export interface INyxdQuery {
// nym-specific implemented inside NymQuerier
getContractVersion(mixnetContractAddress: string): Promise<MixnetContractVersion>;
getMixNodesPaged(mixnetContractAddress: string, limit?: number, startAfter?: string): Promise<PagedMixnodeResponse>;
getMixNodeBonds(
mixnetContractAddress: string,
limit?: number,
startAfter?: string,
): Promise<PagedMixNodeBondResponse>;
getMixNodesDetailed(
mixnetContractAddress: string,
limit?: number,
startAfter?: string,
): Promise<PagedMixNodeDetailsResponse>;
getGatewaysPaged(mixnetContractAddress: string, limit?: number, startAfter?: string): Promise<PagedGatewayResponse>;
ownsMixNode(mixnetContractAddress: string, address: string): Promise<MixOwnershipResponse>;
getOwnedMixnode(mixnetContractAddress: string, address: string): Promise<MixOwnershipResponse>;
ownsGateway(mixnetContractAddress: string, address: string): Promise<GatewayOwnershipResponse>;
getStateParams(mixnetContractAddress: string): Promise<ContractStateParams>;
getAllNetworkDelegationsPaged(
getStateParams(mixnetContractAddress: string): Promise<ContractState>;
getAllDelegationsPaged(
mixnetContractAddress: string,
limit?: number,
startAfter?: [string, string],
): Promise<PagedAllDelegationsResponse>;
getMixNodeDelegationsPaged(
mixnetContractAddress: string,
mixIdentity: string,
mix_id: number,
limit?: number,
startAfter?: string,
): Promise<PagedMixDelegationsResponse>;
@@ -79,21 +60,15 @@ export interface INyxdQuery {
limit?: number,
startAfter?: string,
): Promise<PagedDelegatorDelegationsResponse>;
getDelegationDetails(mixnetContractAddress: string, mixIdentity: string, delegator: string): Promise<Delegation>;
getDelegationDetails(mixnetContractAddress: string, mix_id: number, delegator: string): Promise<Delegation>;
getLayerDistribution(mixnetContractAddress: string): Promise<LayerDistribution>;
getRewardPool(mixnetContractAddress: string): Promise<string>;
getCirculatingSupply(mixnetContractAddress: string): Promise<string>;
getIntervalRewardPercent(mixnetContractAddress: string): Promise<number>;
getSybilResistancePercent(mixnetContractAddress: string): Promise<number>;
getRewardingStatus(
mixnetContractAddress: string,
mixIdentity: string,
rewardingIntervalNonce: number,
): Promise<RewardingStatus>;
getRewardParams(mixnetContractAddress: string): Promise<RewardingParams>;
getStakeSaturation(mixnetContractAddress: string, mixId: number): Promise<StakeSaturationResponse>;
getUnbondedMixNodeInformation(mixnetContractAddress: string, mixId: number): Promise<UnbondedMixnodeResponse>;
getMixnodeRewardingDetails(mixnetContractAddress: string, mixId: number): Promise<MixNodeRewarding>;
}
export interface IQueryClient extends ICosmWasmQuery, INyxdQuery, INymApiQuery { }
export interface IQueryClient extends ICosmWasmQuery, INyxdQuery, INymApiQuery {}
export default class QueryClient extends CosmWasmClient implements IQueryClient {
private nyxdQuerier: NyxdQuerier;
@@ -115,41 +90,73 @@ export default class QueryClient extends CosmWasmClient implements IQueryClient
return this.nyxdQuerier.getContractVersion(mixnetContractAddress);
}
getMixNodesPaged(mixnetContractAddress: string, limit?: number, startAfter?: string): Promise<PagedMixnodeResponse> {
return this.nyxdQuerier.getMixNodesPaged(mixnetContractAddress, limit, startAfter);
getMixNodeBonds(
mixnetContractAddress: string,
limit?: number,
startAfter?: string,
): Promise<PagedMixNodeBondResponse> {
return this.nyxdQuerier.getMixNodeBonds(mixnetContractAddress, limit, startAfter);
}
getMixNodesDetailed(
mixnetContractAddress: string,
limit?: number,
startAfter?: string,
): Promise<PagedMixNodeDetailsResponse> {
return this.nyxdQuerier.getMixNodesDetailed(mixnetContractAddress, limit, startAfter);
}
getStakeSaturation(mixnetContractAddress: string, mixId: number): Promise<StakeSaturationResponse> {
return this.nyxdQuerier.getStakeSaturation(mixnetContractAddress, mixId);
}
getMixnodeRewardingDetails(mixnetContractAddress: string, mixId: number): Promise<MixNodeRewarding> {
return this.nyxdQuerier.getMixnodeRewardingDetails(mixnetContractAddress, mixId);
}
getGatewaysPaged(mixnetContractAddress: string, limit?: number, startAfter?: string): Promise<PagedGatewayResponse> {
return this.nyxdQuerier.getGatewaysPaged(mixnetContractAddress, limit, startAfter);
}
ownsMixNode(mixnetContractAddress: string, address: string): Promise<MixOwnershipResponse> {
return this.nyxdQuerier.ownsMixNode(mixnetContractAddress, address);
getOwnedMixnode(mixnetContractAddress: string, address: string): Promise<MixOwnershipResponse> {
return this.nyxdQuerier.getOwnedMixnode(mixnetContractAddress, address);
}
ownsGateway(mixnetContractAddress: string, address: string): Promise<GatewayOwnershipResponse> {
return this.nyxdQuerier.ownsGateway(mixnetContractAddress, address);
}
getStateParams(mixnetContractAddress: string): Promise<ContractStateParams> {
getUnbondedMixNodes(
mixnetContractAddress: string,
limit?: number,
startAfter?: string,
): Promise<PagedUnbondedMixnodesResponse> {
return this.nyxdQuerier.getUnbondedMixNodes(mixnetContractAddress, limit, startAfter);
}
getUnbondedMixNodeInformation(mixnetContractAddress: string, mixId: number): Promise<UnbondedMixnodeResponse> {
return this.nyxdQuerier.getUnbondedMixNodeInformation(mixnetContractAddress, mixId);
}
getStateParams(mixnetContractAddress: string): Promise<ContractState> {
return this.nyxdQuerier.getStateParams(mixnetContractAddress);
}
getAllNetworkDelegationsPaged(
getAllDelegationsPaged(
mixnetContractAddress: string,
limit?: number,
startAfter?: [string, string],
): Promise<PagedAllDelegationsResponse> {
return this.nyxdQuerier.getAllNetworkDelegationsPaged(mixnetContractAddress, limit, startAfter);
return this.nyxdQuerier.getAllDelegationsPaged(mixnetContractAddress, limit, startAfter);
}
getMixNodeDelegationsPaged(
mixnetContractAddress: string,
mixIdentity: string,
mix_id: number,
limit?: number,
startAfter?: string,
): Promise<PagedMixDelegationsResponse> {
return this.nyxdQuerier.getMixNodeDelegationsPaged(mixnetContractAddress, mixIdentity, limit, startAfter);
return this.nyxdQuerier.getMixNodeDelegationsPaged(mixnetContractAddress, mix_id, limit, startAfter);
}
getDelegatorDelegationsPaged(
@@ -161,36 +168,16 @@ export default class QueryClient extends CosmWasmClient implements IQueryClient
return this.nyxdQuerier.getDelegatorDelegationsPaged(mixnetContractAddress, delegator, limit, startAfter);
}
getDelegationDetails(mixnetContractAddress: string, mixIdentity: string, delegator: string): Promise<Delegation> {
return this.nyxdQuerier.getDelegationDetails(mixnetContractAddress, mixIdentity, delegator);
getDelegationDetails(mixnetContractAddress: string, mix_id: number, delegator: string): Promise<Delegation> {
return this.nyxdQuerier.getDelegationDetails(mixnetContractAddress, mix_id, delegator);
}
getLayerDistribution(mixnetContractAddress: string): Promise<LayerDistribution> {
return this.nyxdQuerier.getLayerDistribution(mixnetContractAddress);
}
getRewardPool(mixnetContractAddress: string): Promise<string> {
return this.nyxdQuerier.getRewardPool(mixnetContractAddress);
}
getCirculatingSupply(mixnetContractAddress: string): Promise<string> {
return this.nyxdQuerier.getCirculatingSupply(mixnetContractAddress);
}
getIntervalRewardPercent(mixnetContractAddress: string): Promise<number> {
return this.nyxdQuerier.getIntervalRewardPercent(mixnetContractAddress);
}
getSybilResistancePercent(mixnetContractAddress: string): Promise<number> {
return this.nyxdQuerier.getSybilResistancePercent(mixnetContractAddress);
}
getRewardingStatus(
mixnetContractAddress: string,
mixIdentity: string,
rewardingIntervalNonce: number,
): Promise<RewardingStatus> {
return this.nyxdQuerier.getRewardingStatus(mixnetContractAddress, mixIdentity, rewardingIntervalNonce);
getRewardParams(mixnetContractAddress: string): Promise<RewardingParams> {
return this.nyxdQuerier.getRewardParams(mixnetContractAddress);
}
getCachedGateways(): Promise<GatewayBond[]> {
@@ -201,11 +188,15 @@ export default class QueryClient extends CosmWasmClient implements IQueryClient
return this.nymApiQuerier.getCachedMixnodes();
}
getActiveMixnodes(): Promise<MixNodeBond[]> {
getActiveMixnodes(): Promise<MixNodeDetails[]> {
return this.nymApiQuerier.getActiveMixnodes();
}
getRewardedMixnodes(): Promise<MixNodeBond[]> {
return this.nymApiQuerier.getRewardedMixnodes();
}
getSpendableCoins(vestingContractAddress: string, vestingAccountAddress: string): Promise<any> {
return this.nyxdQuerier.getSpendableCoins(vestingContractAddress, vestingAccountAddress);
}
}
+73 -47
View File
@@ -25,15 +25,22 @@ import {
MixnetContractVersion,
MixNode,
MixNodeBond,
MixNodeCostParams,
MixNodeDetails,
MixNodeRewarding,
MixOwnershipResponse,
PagedAllDelegationsResponse,
PagedDelegatorDelegationsResponse,
PagedGatewayResponse,
PagedMixDelegationsResponse,
PagedMixnodeResponse,
RewardingStatus,
} from './types';
PagedMixNodeBondResponse,
PagedMixNodeDetailsResponse,
PagedUnbondedMixnodesResponse,
RewardingParams,
UnbondedMixnodeResponse,
} from '@nymproject/types';
import NymApiQuerier from './nym-api-querier';
import { ContractState } from './types/shared';
// methods exposed by `SigningCosmWasmClient`
export interface ICosmWasmSigning {
@@ -143,6 +150,7 @@ export interface ISigningClient extends IQueryClient, ICosmWasmSigning, INymSign
bondMixNode(
mixnetContractAddress: string,
mixNode: MixNode,
costParams: MixNodeCostParams,
ownerSignature: string,
pledge: Coin,
fee?: StdFee | 'auto' | number,
@@ -164,7 +172,7 @@ export interface ISigningClient extends IQueryClient, ICosmWasmSigning, INymSign
delegateToMixNode(
mixnetContractAddress: string,
mixIdentity: string,
mixId: number,
amount: Coin,
fee?: StdFee | 'auto' | number,
memo?: string,
@@ -172,14 +180,14 @@ export interface ISigningClient extends IQueryClient, ICosmWasmSigning, INymSign
undelegateFromMixNode(
mixnetContractAddress: string,
mixIdentity: string,
mixId: number,
fee?: StdFee | 'auto' | number,
memo?: string,
): Promise<ExecuteResult>;
updateMixnodeConfig(
mixnetContractAddress: string,
mixIdentity: string,
mixId: number,
profitMarginPercent: number,
fee: StdFee | 'auto' | number,
): Promise<ExecuteResult>;
@@ -235,44 +243,76 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
// query related:
getContractVersion(mixnetContractAddress: string): Promise<MixnetContractVersion> {
return this.nyxdQuerier.getContractVersion(mixnetContractAddress);
return this.getContractVersion(mixnetContractAddress);
}
getMixNodesPaged(mixnetContractAddress: string, limit?: number, startAfter?: string): Promise<PagedMixnodeResponse> {
return this.nyxdQuerier.getMixNodesPaged(mixnetContractAddress, limit, startAfter);
getMixNodeBonds(
mixnetContractAddress: string,
limit?: number,
startAfter?: string,
): Promise<PagedMixNodeBondResponse> {
return this.nyxdQuerier.getMixNodeBonds(mixnetContractAddress, limit, startAfter);
}
getMixNodesDetailed(
mixnetContractAddress: string,
limit?: number,
startAfter?: string,
): Promise<PagedMixNodeDetailsResponse> {
return this.nyxdQuerier.getMixNodesDetailed(mixnetContractAddress, limit, startAfter);
}
getStakeSaturation(mixnetContractAddress: string, mixId: number) {
return this.nyxdQuerier.getStakeSaturation(mixnetContractAddress, mixId);
}
getUnbondedMixNodeInformation(mixnetContractAddress: string, mixId: number): Promise<UnbondedMixnodeResponse> {
return this.nyxdQuerier.getUnbondedMixNodeInformation(mixnetContractAddress, mixId);
}
getMixnodeRewardingDetails(mixnetContractAddress: string, mixId: number): Promise<MixNodeRewarding> {
return this.nyxdQuerier.getMixnodeRewardingDetails(mixnetContractAddress, mixId);
}
getGatewaysPaged(mixnetContractAddress: string, limit?: number, startAfter?: string): Promise<PagedGatewayResponse> {
return this.nyxdQuerier.getGatewaysPaged(mixnetContractAddress, limit, startAfter);
}
ownsMixNode(mixnetContractAddress: string, address: string): Promise<MixOwnershipResponse> {
return this.nyxdQuerier.ownsMixNode(mixnetContractAddress, address);
getOwnedMixnode(mixnetContractAddress: string, address: string): Promise<MixOwnershipResponse> {
return this.nyxdQuerier.getOwnedMixnode(mixnetContractAddress, address);
}
ownsGateway(mixnetContractAddress: string, address: string): Promise<GatewayOwnershipResponse> {
return this.nyxdQuerier.ownsGateway(mixnetContractAddress, address);
}
getStateParams(mixnetContractAddress: string): Promise<ContractStateParams> {
getStateParams(mixnetContractAddress: string): Promise<ContractState> {
return this.nyxdQuerier.getStateParams(mixnetContractAddress);
}
getAllNetworkDelegationsPaged(
getAllDelegationsPaged(
mixnetContractAddress: string,
limit?: number,
startAfter?: [string, string],
): Promise<PagedAllDelegationsResponse> {
return this.nyxdQuerier.getAllNetworkDelegationsPaged(mixnetContractAddress, limit, startAfter);
return this.getAllDelegationsPaged(mixnetContractAddress, limit, startAfter);
}
getUnbondedMixNodes(
mixnetContractAddress: string,
limit?: number,
startAfter?: string,
): Promise<PagedUnbondedMixnodesResponse> {
return this.nyxdQuerier.getUnbondedMixNodes(mixnetContractAddress, limit, startAfter);
}
getMixNodeDelegationsPaged(
mixnetContractAddress: string,
mixIdentity: string,
mix_id: number,
limit?: number,
startAfter?: string,
): Promise<PagedMixDelegationsResponse> {
return this.nyxdQuerier.getMixNodeDelegationsPaged(mixnetContractAddress, mixIdentity, limit, startAfter);
return this.nyxdQuerier.getMixNodeDelegationsPaged(mixnetContractAddress, mix_id, limit, startAfter);
}
getDelegatorDelegationsPaged(
@@ -284,36 +324,16 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
return this.nyxdQuerier.getDelegatorDelegationsPaged(mixnetContractAddress, delegator, limit, startAfter);
}
getDelegationDetails(mixnetContractAddress: string, mixIdentity: string, delegator: string): Promise<Delegation> {
return this.nyxdQuerier.getDelegationDetails(mixnetContractAddress, mixIdentity, delegator);
getDelegationDetails(mixnetContractAddress: string, mix_id: number, delegator: string): Promise<Delegation> {
return this.nyxdQuerier.getDelegationDetails(mixnetContractAddress, mix_id, delegator);
}
getLayerDistribution(mixnetContractAddress: string): Promise<LayerDistribution> {
return this.nyxdQuerier.getLayerDistribution(mixnetContractAddress);
}
getRewardPool(mixnetContractAddress: string): Promise<string> {
return this.nyxdQuerier.getRewardPool(mixnetContractAddress);
}
getCirculatingSupply(mixnetContractAddress: string): Promise<string> {
return this.nyxdQuerier.getCirculatingSupply(mixnetContractAddress);
}
getIntervalRewardPercent(mixnetContractAddress: string): Promise<number> {
return this.nyxdQuerier.getIntervalRewardPercent(mixnetContractAddress);
}
getSybilResistancePercent(mixnetContractAddress: string): Promise<number> {
return this.nyxdQuerier.getSybilResistancePercent(mixnetContractAddress);
}
getRewardingStatus(
mixnetContractAddress: string,
mixIdentity: string,
rewardingIntervalNonce: number,
): Promise<RewardingStatus> {
return this.nyxdQuerier.getRewardingStatus(mixnetContractAddress, mixIdentity, rewardingIntervalNonce);
getRewardParams(mixnetContractAddress: string): Promise<RewardingParams> {
return this.nyxdQuerier.getRewardParams(mixnetContractAddress);
}
getCachedGateways(): Promise<GatewayBond[]> {
@@ -324,7 +344,7 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
return this.nymApiQuerier.getCachedMixnodes();
}
getActiveMixnodes(): Promise<MixNodeBond[]> {
getActiveMixnodes(): Promise<MixNodeDetails[]> {
return this.nymApiQuerier.getActiveMixnodes();
}
@@ -332,11 +352,16 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
return this.nymApiQuerier.getRewardedMixnodes();
}
getSpendableCoins(vestingContractAddress: string, vestingAccountAddress: string): Promise<any> {
return this.nyxdQuerier.getSpendableCoins(vestingContractAddress, vestingAccountAddress);
}
// signing related:
bondMixNode(
mixnetContractAddress: string,
mixNode: MixNode,
costParams: MixNodeCostParams,
ownerSignature: string,
pledge: Coin,
fee: StdFee | 'auto' | number = 'auto',
@@ -348,6 +373,7 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
{
bond_mixnode: {
mix_node: mixNode,
cost_params: costParams,
owner_signature: ownerSignature,
},
},
@@ -414,7 +440,7 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
delegateToMixNode(
mixnetContractAddress: string,
mixIdentity: string,
mixId: number,
amount: Coin,
fee: StdFee | 'auto' | number = 'auto',
memo = 'Default MixNode Delegation from Typescript',
@@ -424,7 +450,7 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
mixnetContractAddress,
{
delegate_to_mixnode: {
mix_identity: mixIdentity,
mix_id: mixId,
},
},
fee,
@@ -435,7 +461,7 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
undelegateFromMixNode(
mixnetContractAddress: string,
mixIdentity: string,
mixId: number,
fee: StdFee | 'auto' | number = 'auto',
memo = 'Default MixNode Undelegation from Typescript',
): Promise<ExecuteResult> {
@@ -444,7 +470,7 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
mixnetContractAddress,
{
undelegate_from_mixnode: {
mix_identity: mixIdentity,
mix_id: mixId,
},
},
fee,
@@ -454,14 +480,14 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
updateMixnodeConfig(
mixnetContractAddress: string,
mixIdentity: string,
mixId: number,
profitMarginPercent: number,
fee: StdFee | 'auto' | number,
): Promise<ExecuteResult> {
return this.execute(
this.clientAddress,
mixnetContractAddress,
{ update_mixnode_config: { profit_margin_percent: profitMarginPercent, mix_identity: mixIdentity } },
{ update_mixnode_config: { profit_margin_percent: profitMarginPercent, mix_id: mixId } },
fee,
);
}
@@ -0,0 +1,178 @@
import expect from 'expect';
export const amountDemon = {
amount: expect.any(String),
denom: expect.any(String)
}
export const delegation = {
owner: expect.any(String),
mix_id: expect.any(Number),
cumulative_reward_ratio: expect.any(String),
amount: amountDemon,
height: expect.any(Number || BigInt),
proxy: expect.any(String || null)
}
export const detailedDelegation = {
delegation: delegation,
mixnode_still_bonded: expect.any(Boolean)
}
export const gateway = {
pledge_amount: amountDemon,
owner: expect.any(String),
block_height: expect.any(Number || BigInt),
gateway: {
host: expect.any(String),
mix_port: expect.any(Number),
clients_port: expect.any(Number),
location: expect.any(String),
sphinx_key: expect.any(String),
identity_key: expect.any(String),
version: expect.any(String),
},
proxy: expect.any(String || null)
}
export const pagedGateway = {
nodes: gateway,
per_page: expect.any(Number),
start_next_after: expect.any(Number)
}
export const ownGateway = {
address: expect.any(String),
gateway: gateway
}
export const rewardingdetails = {
cost_params: {
profit_margin_percent: expect.any(String),
interval_operating_cost: {
denom: expect.any(String),
amount: expect.any(String)
}
},
operator: expect.any(String),
delegates: expect.any(String),
total_unit_reward: expect.any(String),
unit_delegation: expect.any(String),
last_rewarded_epoch: expect.any(Number),
unique_delegations: expect.any(Number)
}
export const mix_node = {
host: expect.any(String),
mix_port: expect.any(Number),
verloc_port: expect.any(Number),
http_api_port: expect.any(Number),
sphinx_key: expect.any(String),
identity_key: expect.any(String),
version: expect.any(String)
}
export const mixnodebond = {
mix_id: expect.any(Number),
owner: expect.any(String),
original_pledge: {
denom: expect.any(String),
amount: expect.any(String)
},
layer: expect.any(String),
mix_node: mix_node,
proxy: expect.any(String) || null,
bonding_height: expect.any(Number || BigInt),
is_unbonding: expect.any(Boolean)
}
export const mixnode = {
bond_information: mixnodebond,
rewarding_details: rewardingdetails
}
export const ownedNode = {
address: expect.any(String),
mixnode_details: {
bond_information: mixnodebond,
rewarding_details: rewardingdetails
}
}
export const saturation = {
mix_id: expect.any(Number),
current_saturation: expect.any(String),
uncapped_saturation: expect.any(String)
}
export const contractVersion = {
build_timestamp: expect.any(String),
build_version: expect.any(String),
commit_sha: expect.any(String),
commit_timestamp: expect.any(String),
commit_branch: expect.any(String),
rustc_version: expect.any(String)
};
export const stateParams = {
minimum_gateway_pledge: amountDemon,
minimum_mixnode_pledge: expect.any(String),
mixnode_rewarded_set_size: expect.any(Number),
mixnode_active_set_size: expect.any(Number)
}
export const contract = {
owner: expect.any(Number),
rewarding_validator_address: expect.any(Number),
vesting_contract_address: expect.any(Number),
rewarding_denom: expect.any(String),
params: stateParams
}
export const rewardingnode = {
mix_id: expect.any(Number),
rewarding_details: rewardingdetails
}
export const unbondednode = {
mix_id: expect.any(Number),
unbonded_info: {
identity_key: expect.any(String),
owner: expect.any(String),
proxy: expect.any(String) || null,
unbonding_height: expect.any(Number)
}
}
export const allunbondednodes = [
expect.any(Number), {
identity_key: expect.any(String),
owner: expect.any(String),
proxy: expect.any(String) || null,
unbonding_height: expect.any(Number)
}
]
export const layerDistribution = {
layer1: expect.any(Number),
layer2: expect.any(Number),
layer3: expect.any(Number)
}
export const intervalRewardParams = {
reward_pool: expect.any(Number),
staking_supply: expect.any(Number),
staking_supply_scale_factor: expect.any(Number),
epoch_reward_budget: expect.any(Number),
stake_saturation_point: expect.any(Number),
sybil_resistance: expect.any(Number),
active_set_work_factor: expect.any(Number),
interval_pool_emission: expect.any(Number)
}
export const rewardingParams = {
interval: intervalRewardParams,
rewarded_set_size: expect.any(Number),
active_set_size: expect.any(Number)
}
@@ -0,0 +1,19 @@
import { Mock, Times } from 'moq.ts';
import expect from 'expect';
import { INyxdQuery } from '../../query-client';
export class TestHelper {
buildMethod = async <T>(methodName: string, args: any[], expectedResult: any): Promise<T> => {
const client = new Mock<INyxdQuery>()
.setup((nym) => nym[methodName](...args))
.returns(Promise.resolve(expectedResult));
const obj = client.object();
const actualDetails = await obj[methodName](...args);
client.verify((nym) => nym[methodName](...args), Times.Exactly(1));
expect(Object.keys([actualDetails])).toEqual(Object.keys(expectedResult));
expect(actualDetails).toBeDefined();
return actualDetails;
};
}
@@ -0,0 +1,40 @@
import expect from 'expect';
import { Delegation } from '@nymproject/types';
import { PagedAllDelegationsResponse, PagedDelegatorDelegationsResponse } from '../../types/shared-types';
import { TestHelper } from './client';
import { mixnet, mixnodeowneraddress, mix_id } from './testData';
describe('Delegation mock tests', () => {
const testHelper = new TestHelper();
it('get Delegation Details', () => {
const execute = testHelper.buildMethod('getDelegationDetails', [mixnet, mix_id, mixnodeowneraddress], <Delegation>{
owner: mixnodeowneraddress,
mix_id: 0,
cumulative_reward_ratio: '',
amount: {
denom: 'nym',
amount: '10',
},
height: 1314134144132n,
proxy: 'null',
});
expect(execute).toBeTruthy();
});
it('get All Delegations Paged', () => {
const execute = testHelper.buildMethod('getAllDelegationsPaged', [mixnet], <PagedAllDelegationsResponse>{
delegations: [],
});
expect(execute).toBeTruthy();
});
it('get Delegator Delegations Paged', () => {
const execute = testHelper.buildMethod('getDelegatorDelegationsPaged', [mixnet, mixnodeowneraddress], <
PagedDelegatorDelegationsResponse
>{
delegations: [],
});
expect(execute).toBeTruthy();
});
});
@@ -0,0 +1,24 @@
import expect from 'expect';
import { GatewayOwnershipResponse, PagedGatewayResponse } from '../../types/shared-types';
import { TestHelper } from './client';
import { gatewayowneraddress, mixnet } from './testData';
describe('Gateway mock tests', () => {
const testHelper = new TestHelper();
it('get Gateways Paged', () => {
const execute = testHelper.buildMethod('getGatewaysPaged', [mixnet], <PagedGatewayResponse>{
nodes: [],
per_page: 25,
});
expect(execute).toBeTruthy();
});
it('owns Gateway', () => {
const execute = testHelper.buildMethod('ownsGateway', [mixnet, gatewayowneraddress], <GatewayOwnershipResponse>{
address: gatewayowneraddress,
gateway: {},
});
expect(execute).toBeTruthy();
});
});
@@ -0,0 +1,65 @@
import expect from 'expect';
import { LayerDistribution, MixnetContractVersion, StakeSaturationResponse } from '@nymproject/types';
import { TestHelper } from './client';
import { mixnet, mix_id } from './testData';
import { RewardingParams } from '@nymproject/types';
import { ContractState } from '../../types/shared';
describe('Mixnet mock tests', () => {
const testHelper = new TestHelper();
it('get Layer Distribution', () => {
const execute = testHelper.buildMethod('getLayerDistribution', [mixnet], <LayerDistribution>{
layer1: 2,
layer2: 2,
layer3: 5,
});
expect(execute).toBeTruthy();
});
it('get Reward Params', () => {
const execute = testHelper.buildMethod('getRewardParams', [mixnet], <RewardingParams>{
interval: {},
rewarded_set_size: 0,
active_set_size: 0,
});
expect(execute).toBeTruthy();
});
it('get Stake Saturation', () => {
const execute = testHelper.buildMethod('getStakeSaturation', [mixnet, mix_id], <StakeSaturationResponse>{
mix_id: 0,
current_saturation: '',
uncapped_saturation: '',
});
expect(execute).toBeTruthy();
});
it('get State Params', () => {
const execute = testHelper.buildMethod('getStateParams', [mixnet], <ContractState>{
owner: '',
rewarding_validator_address: '',
vesting_contract_address: '',
rewarding_denom: 'unym',
params: {
minimum_mixnode_pledge: '',
minimum_gateway_pledge: '',
mixnode_rewarded_set_size: 240,
mixnode_active_set_size: 240,
},
});
expect(execute).toBeTruthy();
});
it('get Contract Version', () => {
const execute = testHelper.buildMethod('getContractVersion', [mixnet], <MixnetContractVersion>{
build_timestamp: 'test',
commit_branch: 'test',
build_version: 'test',
rustc_version: 'test',
commit_sha: 'test',
commit_timestamp: 'test',
});
expect(execute).toBeTruthy();
});
});
@@ -0,0 +1,71 @@
import expect from 'expect';
import {
MixNodeRewarding,
MixOwnershipResponse,
PagedMixDelegationsResponse,
PagedMixNodeBondResponse,
PagedMixNodeDetailsResponse,
UnbondedMixnodeResponse,
} from '@nymproject/types';
import { TestHelper } from './client';
import { mixnet, mixnodeowneraddress, mix_id } from './testData';
describe('Mixnode mock tests', () => {
const testHelper = new TestHelper();
it('get Mixnode Bonds', () => {
const execute = testHelper.buildMethod('getMixNodeBonds', [mixnet], <PagedMixNodeBondResponse>{
nodes: [],
per_page: 25,
});
expect(execute).toBeTruthy();
});
it('get Mixnode Delegations Paged', () => {
const execute = testHelper.buildMethod('getMixNodeDelegationsPaged', [mixnet, mix_id], <
PagedMixDelegationsResponse
>{
delegations: [],
per_page: 25,
});
expect(execute).toBeTruthy();
});
it('get Mixnodes Detailed', () => {
const execute = testHelper.buildMethod('getMixNodesDetailed', [mixnet], <PagedMixNodeDetailsResponse>{
nodes: [],
per_page: 25,
});
expect(execute).toBeTruthy();
});
it('get Mixnode Rewarding Details', () => {
const execute = testHelper.buildMethod('getMixnodeRewardingDetails', [mixnet, mix_id], <MixNodeRewarding>{
cost_params: {},
operator: '',
delegates: '',
total_unit_reward: '',
unit_delegation: '',
last_rewarded_epoch: 1,
unique_delegations: 1,
});
expect(execute).toBeTruthy();
});
it('get Owned Mixnode', () => {
const execute = testHelper.buildMethod('getOwnedMixnode', [mixnet, mixnodeowneraddress], <MixOwnershipResponse>{
address: '',
mixnode: {},
});
expect(execute).toBeTruthy();
});
it('get Unbonded Mixnode Information', () => {
const execute = testHelper.buildMethod(
'getUnbondedMixNodeInformation',
[mixnet, mix_id],
<UnbondedMixnodeResponse>{},
);
expect(execute).toBeTruthy();
});
});
@@ -0,0 +1,8 @@
export const mixnet = 'n14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9sjyvg3g';
export const gatewayowneraddress = 'n16evnn8glr0sham3matj8rg2s24m6x56ayk87ts';
// export const mixId = 436207616;
export const mix_id = 26;
export const nodeIdentityKey = 'ATmVJknZarDF6Yj53M7h8NS9LLCUvWuToXpk3pDvYUH1';
export const mixnodeowneraddress = 'n1fzv4jc7fanl9s0qj02ge2ezk3kts545kjtek47';
export const delegatorAddress = 'n1lemst75va9700tsrxq58adzujrh6h9s5x60h9h';
export const rewardingIntervalNonce = 1;
@@ -0,0 +1,180 @@
import expect from 'expect';
import ValidatorClient from '../../index';
import {
allunbondednodes,
contract,
delegation,
gateway,
layerDistribution,
mixnode,
mixnodebond,
ownedNode,
ownGateway,
rewardingnode,
saturation,
unbondednode,
} from '../expectedResponses';
import { delegatorAddress, gatewayowneraddress } from '../mock/testData';
const dotenv = require('dotenv');
dotenv.config();
describe('Mixnet queries', () => {
let client: ValidatorClient;
beforeEach(async () => {
client = await ValidatorClient.connectForQuery(
process.env.rpcAddress || '',
process.env.validatorAddress || '',
process.env.prefix || '',
process.env.mixnetContractAddress || '',
process.env.vestingContractAddress || '',
process.env.denom || '',
);
});
//
// CONTRACT
//
it('can query for an account balance', async () => {
const balance = await client.getBalance('n1ptg680vnmef2cd8l0s9uyc4f0hgf3x8sed6w77');
expect(Number.parseFloat(balance.amount)).toBeGreaterThan(0);
});
it('can query for stake saturation', async () => {
const stakeSaturation = await client.getStakeSaturation(7);
expect(Object.keys(stakeSaturation)).toEqual(Object.keys(saturation));
expect(stakeSaturation).toBeTruthy();
expect(stakeSaturation?.current_saturation).toBeTruthy();
});
it('can query for contract version', async () => {
const contractV = await client.getMixnetContractVersion();
expect(contractV).toBeTruthy();
});
it('can query for mixnet contract settings', async () => {
const settings = await client.getMixnetContractSettings();
expect(Object.keys(settings)).toEqual(Object.keys(contract));
expect(settings).toBeTruthy();
});
it('can query for reward pool', async () => {
const rewardPool = await client.getRewardParams();
// TODO add velidation here
expect(rewardPool).toBeTruthy();
});
it('can query for layer distribution', async () => {
const layer = await client.getLayerDistribution();
expect(Object.keys(layer)).toEqual(Object.keys(layerDistribution));
expect(layer).toBeTruthy();
});
//
// MIXNODES
//
it('can query for unbonded mixnodes', async () => {
const unbondedNodes = await client.getUnbondedMixNodes();
for (let i = 0; i < unbondedNodes.length; i++) {
expect(Object.keys(unbondedNodes[0])).toEqual(Object.keys(allunbondednodes));
expect(unbondedNodes).toBeTruthy();
}
});
it('can query for unbonded mixnode information', async () => {
const unbondedMixnodeInfo = await client.getUnbondedMixNodeInformation(1);
expect(Object.keys(unbondedMixnodeInfo)).toEqual(Object.keys(unbondednode));
expect(unbondedMixnodeInfo).toBeTruthy();
});
it('can query for mixnode rewarding details', async () => {
const rewardingDetails = await client.getMixnodeRewardingDetails(1);
expect(Object.keys(rewardingDetails)).toEqual(Object.keys(rewardingnode));
expect(rewardingDetails).toBeTruthy();
});
it('can query for owned mixnode', async () => {
const ownedMixnode = await client.getOwnedMixnode('n1ptg680vnmef2cd8l0s9uyc4f0hgf3x8sed6w77');
expect(Object.keys(ownedMixnode)).toEqual(Object.keys(ownedNode));
expect(ownedMixnode).toBeTruthy();
});
it('can query for all mixnode bonds', async () => {
const mixnodeBonds = await client.getMixNodeBonds();
expect(Object.keys(mixnodeBonds[0])).toEqual(Object.keys(mixnodebond));
expect(mixnodeBonds).toBeTruthy();
expect(Array.isArray(mixnodeBonds)).toBeTruthy();
});
it('can query for all mixnode details', async () => {
const mixnodeDetails = await client.getMixNodesDetailed();
expect(Object.keys(mixnodeDetails[0])).toEqual(Object.keys(mixnode));
expect(mixnodeDetails).toBeTruthy();
expect(Array.isArray(mixnodeDetails)).toBeTruthy();
});
it('can query for all active mixnodes', async () => {
const activeNodes = await client.getActiveMixnodes();
expect(Object.keys(activeNodes[0])).toEqual(Object.keys(mixnode));
expect(activeNodes).toBeTruthy();
expect(Array.isArray(activeNodes)).toBeTruthy();
});
it('can query for rewarded mixnodes', async () => {
const rewardNodes = await client.getRewardedMixnodes();
expect(Object.keys(rewardNodes[0])).toEqual(Object.keys(mixnode));
expect(rewardNodes).toBeTruthy();
});
//
// DELEGATIONS
//
it('can query for account delegations', async () => {
const delegations = await client.getAllNyxdDelegatorDelegations('n1fzv4jc7fanl9s0qj02ge2ezk3kts545kjtek47');
expect(Object.keys(delegations[0])).toEqual(Object.keys(delegation));
expect(delegations).toBeTruthy();
expect(Array.isArray(delegations)).toBeTruthy();
});
it('can query for all delegations', async () => {
const allDelegations = await client.getAllNyxdDelegations();
expect(Object.keys(allDelegations[0])).toEqual(Object.keys(delegation));
expect(allDelegations).toBeTruthy();
expect(Array.isArray(allDelegations)).toBeTruthy();
});
it('can query for all delegations on a node', async () => {
const mixnodeDelegations = await client.getAllNyxdSingleMixnodeDelegations(1);
expect(Object.keys(mixnodeDelegations[0])).toEqual(Object.keys(delegation));
expect(mixnodeDelegations).toBeTruthy();
});
it('can query for detailed delegations', async () => {
const detailedDelegation = await client.getDelegationDetails(7, delegatorAddress);
expect(Object.keys(detailedDelegation)).toEqual(Object.keys(detailedDelegation));
expect(detailedDelegation).toBeTruthy();
});
//
// GATEWAYS
//
it('can query for all gateways', async () => {
const gateways = await client.getAllNyxdGateways();
expect(Object.keys(gateways[0])).toEqual(Object.keys(gateway));
expect(gateways).toBeTruthy();
expect(Array.isArray(gateways)).toBeTruthy();
}).timeout(10000);
it('can query for owned gateway', async () => {
const gateway = await client.ownsGateway(gatewayowneraddress);
expect(Object.keys(gateway)).toEqual(Object.keys(ownGateway));
expect(gateway).toBeTruthy();
}).timeout(10000);
});
@@ -0,0 +1,28 @@
import expect from 'expect';
import ValidatorClient from '../../index';
const dotenv = require('dotenv');
dotenv.config();
describe('Vesting queries', () => {
let client: ValidatorClient;
beforeEach(async () => {
client = await ValidatorClient.connectForQuery(
process.env.rpcAddress || '',
process.env.validatorAddress || '',
process.env.prefix || '',
process.env.mixnetContractAddress || '',
process.env.vestingContractAddress || '',
process.env.denom || '',
);
});
it('can query for contract version', async () => {
const contract = await client.getVestingContractVersion();
expect(contract).toBeTruthy();
});
it('can get the balance on the account', () => {});
});
@@ -0,0 +1,107 @@
import expect from 'expect';
import ValidatorClient from '../../';
const dotenv = require('dotenv');
dotenv.config();
// TODO: implement for QA with .env for mnemonics
describe('Mixnet actions', () => {
let client: ValidatorClient;
beforeEach(async () => {
client = await ValidatorClient.connect(
process.env.mnemonic || '',
process.env.rpcAddress || '',
process.env.validatorAddress || '',
process.env.prefix || '',
process.env.mixnetContractAddress || '',
process.env.vestingContractAddress || '',
process.env.denom || '',
);
});
it('can send tokens', async () => {
const res = await client.send(client.address, [{ amount: '10000000', denom: 'unym' }]);
expect(res.transactionHash).toBeDefined();
}).timeout(10000);
it.skip('can delegate tokens', async () => {
const [_, secondMixnode] = await client.getActiveMixnodes();
if (secondMixnode) {
const res = await client.delegateToMixNode(
secondMixnode.bond_information.mix_id,
{
amount: '15000000',
denom: 'unym',
},
{ gas: '1000000', amount: [{ amount: '100000', denom: 'unym' }] },
);
expect(res.transactionHash).toBeDefined();
}
}).timeout(10000);
// Need to provide a mix id that can be undelegated from
it.skip('can undelegate from a mixnode', async () => {
const mixId = 8;
const res = await client.undelegateFromMixNode(mixId);
expect(res.transactionHash).toBeDefined();
});
it.skip('Can unbond a mixnode', async () => {
const res = await client.unbondMixNode();
expect(res.transactionHash).toBeDefined();
}).timeout(10000);
it.skip('Can bond a mixnode', async () => {
const res = await client.bondMixNode(
{
identity_key: '3P6pAcF2p3pYMqWdpHqhbavu3ifyaBs5Qw5UmmCGwimx',
sphinx_key: 'GQMQKwUThaggatA6oZteSWTsCQoUfmLtamJ7o9YkP9aE',
host: '109.74.195.67',
mix_port: 1789,
verloc_port: 1790,
http_api_port: 8000,
version: '1.1.4',
},
'3rXWCQBUj5JQB3UBUkZcXhCk9Zh3cjduMF8aFHPTG7KTkkhZzDJTNmE2p2Ph1g6iQW5vRGTpQzjXF33WDwvhzHk6',
{ profit_margin_percent: '0.1', interval_operating_cost: { amount: '40', denom: 'nym' } },
{ amount: '100_000_000', denom: 'unym' },
{ gas: '1000000', amount: [{ amount: '100000', denom: 'unym' }] },
);
expect(res.transactionHash).toBeDefined();
}).timeout(10000);
it.skip('can unbond a gateway', async () => {
const res = await client.unbondGateway();
expect(res.transactionHash).toBeDefined();
});
it.skip('can bond a gateway', async () => {
const res = await client.bondGateway(
{
identity_key: '36vfvEyBzo5cWEFbnP7fqgY39kFw9PQhvwzbispeNaxL',
sphinx_key: 'G65Fwc2JNAotuHQFqmDKhQNQL5rn3r9pupUdmxMygNUZ',
host: '151.236.220.82',
version: '1.1.4',
mix_port: 1789,
clients_port: 9000,
location: 'Cuba',
},
'3ipSJksWHehZm1YfuH5Ahtg7b22NnrP9hEs6iEDXfUS5uiUhpWmCjGR3b3NDHuxeGjpZYJNYJ52D8WCPK5ZR7Szj',
{ amount: '100_000_000', denom: 'unym' },
);
expect(res.transactionHash).toBeDefined();
});
it.skip('can update contract state params', async () => {
const res = await client.updateContractStateParams({
minimum_mixnode_pledge: '',
minimum_gateway_pledge: '',
mixnode_rewarded_set_size: 2,
mixnode_active_set_size: 2,
});
expect(res.transactionHash).toBeDefined();
});
});
-15
View File
@@ -1,15 +0,0 @@
{
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"esModuleInterop": true,
"strict": true,
"declaration": true,
"outDir": "./dist"
},
"exclude": [
"tests",
"dist",
"node_modules"
]
}
-157
View File
@@ -1,157 +0,0 @@
import { Coin } from '@cosmjs/stargate';
// TODO: ideally we'd have re-exported those using that fancy crate that builds ts types from rust
export type MixnetContractVersion = {
build_timestamp: string;
build_version: string;
commit_sha: string;
commit_timestamp: string;
commit_branch: string;
rustc_version: string;
};
export type PagedMixnodeResponse = {
nodes: MixNodeBond[];
per_page: number;
start_next_after?: string;
};
export type PagedGatewayResponse = {
nodes: GatewayBond[];
per_page: number;
start_next_after?: string;
};
export type MixOwnershipResponse = {
address: string;
mixnode?: MixNodeBond;
};
export type GatewayOwnershipResponse = {
address: string;
gateway?: GatewayBond;
};
export type ContractStateParams = {
// ideally I'd want to define those as `number` rather than `string`, but
// rust-side they are defined as Uint128 and that don't have
// native javascript representations and therefore are interpreted as strings after deserialization
minimum_mixnode_pledge: string;
minimum_gateway_pledge: string;
mixnode_rewarded_set_size: number;
mixnode_active_set_size: number;
};
export type LayerDistribution = {
gateways: number;
layer1: number;
layer2: number;
layer3: number;
};
export type Delegation = {
owner: string;
node_identity: string;
amount: Coin;
block_height: number;
proxy?: string;
};
export type PagedMixDelegationsResponse = {
delegations: Delegation[];
start_next_after?: string;
};
export type PagedDelegatorDelegationsResponse = {
delegations: Delegation[];
start_next_after?: string;
};
export type PagedAllDelegationsResponse = {
delegations: Delegation[];
start_next_after?: [string, string];
};
export type RewardingResult = {
operator_reward: string;
total_delegator_reward: string;
};
export type NodeRewardParams = {
period_reward_pool: string;
k: string;
reward_blockstamp: number;
circulating_supply: string;
uptime: string;
sybil_resistance_percent: number;
};
export type DelegatorRewardParams = {
node_reward_params: NodeRewardParams;
sigma: number;
profit_margin: number;
node_profit: number;
};
export type PendingDelegatorRewarding = {
running_results: RewardingResult;
next_start: string;
rewarding_params: DelegatorRewardParams;
};
export type RewardingStatus = { Complete: RewardingResult } | { PendingNextDelegatorPage: PendingDelegatorRewarding };
export type MixnodeRewardingStatusResponse = {
status?: RewardingStatus;
};
export enum Layer {
Gateway,
One,
Two,
Three,
}
export type MixNodeBond = {
owner: string;
mix_node: MixNode;
layer: Layer;
bond_amount: Coin;
total_delegation: Coin;
};
export type MixNode = {
host: string;
mix_port: number;
verloc_port: number;
http_api_port: number;
sphinx_key: string;
identity_key: string;
version: string;
profit_margin_percent: number;
};
export type GatewayBond = {
owner: string;
gateway: Gateway;
bond_amount: Coin;
total_delegation: Coin;
};
export type Gateway = {
host: string;
mix_port: number;
clients_port: number;
location: string;
sphinx_key: string;
identity_key: string;
version: string;
};
export type SendRequest = {
senderAddress: string;
recipientAddress: string;
transferAmount: readonly Coin[];
};
+11
View File
@@ -0,0 +1,11 @@
declare namespace NodeJS {
interface ProcessEnv {
rpcAddress: string;
validatorAddress: string;
prefix: string;
mixnetContractAddress: string;
vestingContractAddress: string;
denom: string;
mnemonic: string;
}
}
+259
View File
@@ -0,0 +1,259 @@
import { JsonObject } from '@cosmjs/cosmwasm-stargate';
import { Code, CodeDetails, Contract, ContractCodeHistoryEntry } from '@cosmjs/cosmwasm-stargate/build/cosmwasmclient';
import {
Account,
Block,
Coin,
DeliverTxResponse,
IndexedTx,
SearchTxFilter,
SearchTxQuery,
SequenceResponse,
} from '@cosmjs/stargate';
import {
MixNodeRewarding,
PagedMixNodeBondResponse,
PagedMixNodeDetailsResponse,
StakeSaturationResponse,
UnbondedMixnodeResponse,
} from '@nymproject/types';
export type MixnetContractVersion = {
build_timestamp: string;
build_version: string;
commit_sha: string;
commit_timestamp: string;
commit_branch: string;
rustc_version: string;
};
export type PagedMixnodeResponse = {
nodes: MixNodeBond[];
per_page: number;
start_next_after?: string;
};
export type PagedGatewayResponse = {
nodes: GatewayBond[];
per_page: number;
start_next_after?: string;
};
export type MixOwnershipResponse = {
address: string;
mixnode?: MixNodeBond;
};
export type GatewayOwnershipResponse = {
address: string;
gateway?: GatewayBond;
};
export type ContractStateParams = {
// ideally I'd want to define those as `number` rather than `string`, but
// rust-side they are defined as Uint128 and that don't have
// native javascript representations and therefore are interpreted as strings after deserialization
minimum_mixnode_pledge: string;
minimum_gateway_pledge: string;
mixnode_rewarded_set_size: number;
mixnode_active_set_size: number;
};
export type LayerDistribution = {
gateways: number;
layer1: number;
layer2: number;
layer3: number;
};
export type Delegation = {
owner: string;
node_identity: string;
amount: Coin;
block_height: number;
proxy?: string;
};
export type PagedMixDelegationsResponse = {
delegations: Delegation[];
start_next_after?: string;
};
export type PagedDelegatorDelegationsResponse = {
delegations: Delegation[];
start_next_after?: string;
};
export type PagedAllDelegationsResponse = {
delegations: Delegation[];
start_next_after?: [string, string];
};
export type RewardingResult = {
operator_reward: string;
total_delegator_reward: string;
};
export type NodeRewardParams = {
period_reward_pool: string;
k: string;
reward_blockstamp: number;
circulating_supply: string;
uptime: string;
sybil_resistance_percent: number;
};
export type DelegatorRewardParams = {
node_reward_params: NodeRewardParams;
sigma: number;
profit_margin: number;
node_profit: number;
};
export type PendingDelegatorRewarding = {
running_results: RewardingResult;
next_start: string;
rewarding_params: DelegatorRewardParams;
};
export type RewardingStatus = { Complete: RewardingResult } | { PendingNextDelegatorPage: PendingDelegatorRewarding };
export type MixnodeRewardingStatusResponse = {
status?: RewardingStatus;
};
export enum Layer {
Gateway,
One,
Two,
Three,
}
export type MixNodeBond = {
owner: string;
mix_node: MixNode;
layer: Layer;
bond_amount: Coin;
total_delegation: Coin;
};
export type MixNode = {
host: string;
mix_port: number;
verloc_port: number;
http_api_port: number;
sphinx_key: string;
identity_key: string;
version: string;
profit_margin_percent: number;
};
export type GatewayBond = {
owner: string;
gateway: Gateway;
bond_amount: Coin;
total_delegation: Coin;
};
export type Gateway = {
host: string;
mix_port: number;
clients_port: number;
location: string;
sphinx_key: string;
identity_key: string;
version: string;
};
export type SendRequest = {
senderAddress: string;
recipientAddress: string;
transferAmount: readonly Coin[];
};
export interface SmartContractQuery {
queryContractSmart(address: string, queryMsg: Record<string, unknown>): Promise<JsonObject>;
}
export interface ICosmWasmQuery {
// methods exposed by `CosmWasmClient`
getChainId(): Promise<string>;
getHeight(): Promise<number>;
getAccount(searchAddress: string): Promise<Account | null>;
getSequence(address: string): Promise<SequenceResponse>;
getBlock(height?: number): Promise<Block>;
getBalance(address: string, searchDenom: string): Promise<Coin>;
getTx(id: string): Promise<IndexedTx | null>;
searchTx(query: SearchTxQuery, filter?: SearchTxFilter): Promise<readonly IndexedTx[]>;
disconnect(): void;
broadcastTx(tx: Uint8Array, timeoutMs?: number, pollIntervalMs?: number): Promise<DeliverTxResponse>;
getCodes(): Promise<readonly Code[]>;
getCodeDetails(codeId: number): Promise<CodeDetails>;
getContracts(codeId: number): Promise<readonly string[]>;
getContract(address: string): Promise<Contract>;
getContractCodeHistory(address: string): Promise<readonly ContractCodeHistoryEntry[]>;
queryContractRaw(address: string, key: Uint8Array): Promise<Uint8Array | null>;
queryContractSmart(address: string, queryMsg: Record<string, unknown>): Promise<JsonObject>;
}
export interface INymdQuery {
// nym-specific implemented inside NymQuerier
getContractVersion(mixnetContractAddress: string): Promise<MixnetContractVersion>;
getMixNodeBonds(
mixnetContractAddress: string,
limit?: number,
startAfter?: string,
): Promise<PagedMixNodeBondResponse>;
getMixNodesDetailed(
mixnetContractAddress: string,
limit?: number,
startAfter?: string,
): Promise<PagedMixNodeDetailsResponse>;
getGatewaysPaged(mixnetContractAddress: string, limit?: number, startAfter?: string): Promise<PagedGatewayResponse>;
getOwnedMixnode(mixnetContractAddress: string, address: string): Promise<MixOwnershipResponse>;
ownsGateway(mixnetContractAddress: string, address: string): Promise<GatewayOwnershipResponse>;
getStateParams(mixnetContractAddress: string): Promise<ContractState>;
getAllNetworkDelegationsPaged(
mixnetContractAddress: string,
limit?: number,
startAfter?: [string, string],
): Promise<PagedAllDelegationsResponse>;
getMixNodeDelegationsPaged(
mixnetContractAddress: string,
mix_id: number,
limit?: number,
startAfter?: string,
): Promise<PagedMixDelegationsResponse>;
getDelegatorDelegationsPaged(
mixnetContractAddress: string,
delegator: string,
limit?: number,
startAfter?: string,
): Promise<PagedDelegatorDelegationsResponse>;
getDelegationDetails(mixnetContractAddress: string, mix_id: number, delegator: string): Promise<Delegation>;
getLayerDistribution(mixnetContractAddress: string): Promise<LayerDistribution>;
getStakeSaturation(mixnetContractAddress: string, mixId: number): Promise<StakeSaturationResponse>;
getUnbondedMixNodeInformation(mixnetContractAddress: string, mixId: number): Promise<UnbondedMixnodeResponse>;
getMixnodeRewardingDetails(mixnetContractAddress: string, mixId: number): Promise<MixNodeRewarding>;
}
export interface IVestingQuerier {
getVestingContractVersion(mixnetContractAddress: string): Promise<MixnetContractVersion>;
}
export interface MappedCoin {
readonly denom: string;
readonly fractionalDigits: number;
}
export interface CoinMap {
readonly [key: string]: MappedCoin;
}
export interface ContractState {
owner: string;
rewarding_validator_address: string;
vesting_contract_address: string;
rewarding_denom: string;
params: ContractStateParams;
}
@@ -1,11 +0,0 @@
import ValidatorClient from '../../validator/index';
import expect from 'expect';
describe('Query: balances', () => {
it('can query for an account balance', async () => {
const client = await ValidatorClient.connectForQuery(
'https://rpc.nymtech.net/', 'https://validator.nymtech.net/api/', 'n', 'n14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9sjyvg3g', 'n1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq73f2nw', 'nym');
const balance = await client.getBalance('n10yyd98e2tuwu0f7ypz9dy3hhjw7v772q6287gy');
expect(Number.parseFloat(balance.amount)).toBeGreaterThan(0);
}).timeout(5000);
})
-14
View File
@@ -1,14 +0,0 @@
import ValidatorClient from '../../dist';
import expect from 'expect';
// TODO: implement for QA with .env for mnemonics
// describe('Sign: send', () => {
// it('can send tokens', async () => {
// const client = await ValidatorClient.connect(
// '<ADD MNEMONIC HERE>',
// 'https://rpc.nyx.nodes.guru/', 'https://validator.nymtech.net/api/', 'n', 'n14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9sjyvg3g', 'n1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq73f2nw', 'nym');
// await client.send('<ADD ADDRESS HERE>')
// const balance = await client.getBalance('n10yyd98e2tuwu0f7ypz9dy3hhjw7v772q6287gy');
// expect(Number.parseFloat(balance.amount)).toBeGreaterThan(0);
// }).timeout(5000);
// })
+21 -22
View File
@@ -1,23 +1,22 @@
{
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"esModuleInterop": true,
"strict": true,
"declaration": true,
"outDir": "./dist",
"skipLibCheck": true
},
"typedocOptions": {
"entryPoints": [
"src/index.ts"
],
"out": "docs"
},
"exclude": [
"dist",
"examples",
"tests",
"node_modules"
]
}
"compilerOptions": {
"outDir": "./dist/nym-validator-client",
"module": "ES2020",
"target": "es2021",
"allowJs": false,
"strict": true,
"lib": ["es2021", "dom", "dom.iterable", "esnext"],
"moduleResolution": "node",
"skipDefaultLibCheck": true,
"esModuleInterop": true,
"declaration": true,
"skipLibCheck": true,
"noImplicitAny": true,
"typeRoots": ["./src/types/global.d.ts"]
},
"typedocOptions": {
"entryPoints": ["./src/index.ts"],
"out": "docs"
},
"exclude": ["dist", "./src/tests/**/*", "node_module"]
}
+6
View File
@@ -0,0 +1,6 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"module": "CommonJS"
}
}
+2 -2
View File
@@ -31,8 +31,8 @@ wasm-bindgen-futures = "0.4"
# internal
client-core = { path = "../client-core", default-features = false, features = ["wasm"] }
coconut-interface = { path = "../../common/coconut-interface" }
credentials = { path = "../../common/credentials" }
nym-coconut-interface = { path = "../../common/coconut-interface" }
nym-credentials = { path = "../../common/credentials" }
nym-crypto = { path = "../../common/crypto" }
nym-sphinx = { path = "../../common/nymsphinx" }
gateway-client = { path = "../../common/client-libs/gateway-client", default-features = false, features = ["wasm"] }
@@ -1,10 +0,0 @@
[package]
name = "bandwidth-claim-contract"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
schemars = "0.8"
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
@@ -1,45 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
// Serializable structures for what we find in common/crypto
#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialEq, Eq, JsonSchema)]
pub struct PublicKey([u8; 32]);
impl PublicKey {
pub fn new(bytes: [u8; 32]) -> Self {
PublicKey(bytes)
}
pub fn to_bytes(&self) -> [u8; 32] {
self.0
}
}
impl AsRef<[u8]> for PublicKey {
#[inline]
fn as_ref(&self) -> &[u8] {
&self.0
}
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
pub struct Signature([u8; 32], [u8; 32]);
impl Signature {
pub fn new(bytes: [u8; 64]) -> Self {
let mut sig1 = [0u8; 32];
let mut sig2 = [0u8; 32];
sig1.copy_from_slice(&bytes[..32]);
sig2.copy_from_slice(&bytes[32..]);
Signature(sig1, sig2)
}
pub fn to_bytes(&self) -> [u8; 64] {
let mut res = [0u8; 64];
res[..32].copy_from_slice(&self.0);
res[32..].copy_from_slice(&self.1);
res
}
}
@@ -1,3 +0,0 @@
pub mod keys;
pub mod msg;
pub mod payment;
@@ -1,30 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use crate::keys::PublicKey;
use crate::payment::LinkPaymentData;
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
pub struct InstantiateMsg {}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum ExecuteMsg {
LinkPayment { data: LinkPaymentData },
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum QueryMsg {
GetPayments {
limit: Option<u32>,
start_after: Option<PublicKey>,
},
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub struct MigrateMsg {}
@@ -1,73 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use crate::keys::{PublicKey, Signature};
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)]
pub struct Payment {
verification_key: PublicKey,
gateway_identity: PublicKey,
bandwidth: u64,
}
impl Payment {
pub fn new(verification_key: PublicKey, gateway_identity: PublicKey, bandwidth: u64) -> Self {
Payment {
verification_key,
gateway_identity,
bandwidth,
}
}
pub fn verification_key(&self) -> PublicKey {
self.verification_key
}
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
pub struct LinkPaymentData {
pub verification_key: PublicKey,
pub gateway_identity: PublicKey,
pub bandwidth: u64,
pub signature: Signature,
}
impl LinkPaymentData {
pub fn new(
verification_key: [u8; 32],
gateway_identity: [u8; 32],
bandwidth: u64,
signature: [u8; 64],
) -> Self {
LinkPaymentData {
verification_key: PublicKey::new(verification_key),
gateway_identity: PublicKey::new(gateway_identity),
bandwidth,
signature: Signature::new(signature),
}
}
}
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)]
pub struct PagedPaymentResponse {
pub payments: Vec<Payment>,
pub per_page: usize,
pub start_next_after: Option<PublicKey>,
}
impl PagedPaymentResponse {
pub fn new(
payments: Vec<Payment>,
per_page: usize,
start_next_after: Option<PublicKey>,
) -> Self {
PagedPaymentResponse {
payments,
per_page,
start_next_after,
}
}
}
+2 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-bin-common"
version = "0.1.0"
version = "0.2.0"
description = "Common code for nym binaries"
edition = { workspace = true }
authors = { workspace = true }
@@ -17,7 +17,7 @@ semver = "0.11"
serde = { workspace = true, features = ["derive"], optional = true }
[build-dependencies]
vergen = { version = "7", default-features = false, features = ["build", "git", "rustc", "cargo"] }
vergen = { version = "=7.4.3", default-features = false, features = ["build", "git", "rustc", "cargo"] }
[features]
default = []
+6 -1
View File
@@ -4,5 +4,10 @@
use vergen::{vergen, Config};
fn main() {
vergen(Config::default()).expect("failed to extract build metadata")
let mut config = Config::default();
if std::env::var("DOCS_RS").is_ok() {
// If we don't have access to git information, such as in a docs.rs build, don't error
*config.git_mut().skip_if_error_mut() = true;
}
vergen(config).expect("failed to extract build metadata");
}
View File
+4 -4
View File
@@ -14,12 +14,12 @@ log = { workspace = true }
thiserror = "1.0"
url = "2.2"
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
async-trait = { version = "0.1.51" }
async-trait = { workspace = true }
tokio = { version = "1.24.1", features = ["macros"] }
# internal
coconut-interface = { path = "../../coconut-interface" }
credentials = { path = "../../credentials" }
nym-coconut-interface = { path = "../../coconut-interface" }
nym-credentials = { path = "../../credentials" }
nym-crypto = { path = "../../crypto" }
gateway-requests = { path = "../../../gateway/gateway-requests" }
nym-network-defaults = { path = "../../network-defaults" }
@@ -47,7 +47,7 @@ features = ["net", "sync", "time"]
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-tungstenite]
version = "0.14"
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.credential-storage]
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.nym-credential-storage]
path = "../../credential-storage"
# wasm-only dependencies
@@ -7,7 +7,7 @@ use crate::error::GatewayClientError;
use crate::wasm_mockups::Storage;
#[cfg(not(target_arch = "wasm32"))]
#[cfg(not(feature = "mobile"))]
use credential_storage::storage::Storage;
use nym_credential_storage::storage::Storage;
#[cfg(not(target_arch = "wasm32"))]
#[cfg(feature = "mobile")]
@@ -22,7 +22,7 @@ use crate::wasm_mockups::StorageError;
#[cfg(not(target_arch = "wasm32"))]
#[cfg(not(feature = "mobile"))]
use credential_storage::error::StorageError;
use nym_credential_storage::error::StorageError;
#[cfg(target_arch = "wasm32")]
use crate::wasm_mockups::{Client, CosmWasmClient};
@@ -30,8 +30,8 @@ use std::str::FromStr;
#[cfg(not(target_arch = "wasm32"))]
use validator_client::{nyxd::CosmWasmClient, Client};
use {
coconut_interface::Base58,
credentials::coconut::{
nym_coconut_interface::Base58,
nym_credentials::coconut::{
bandwidth::prepare_for_spending, utils::obtain_aggregate_verification_key,
},
};
@@ -42,7 +42,7 @@ use crate::wasm_mockups::PersistentStorage;
#[cfg(not(target_arch = "wasm32"))]
#[cfg(not(feature = "mobile"))]
use credential_storage::PersistentStorage;
use nym_credential_storage::PersistentStorage;
#[cfg(not(target_arch = "wasm32"))]
#[cfg(feature = "mobile")]
@@ -69,17 +69,17 @@ where
pub async fn prepare_coconut_credential(
&self,
) -> Result<(coconut_interface::Credential, i64), GatewayClientError> {
) -> Result<(nym_coconut_interface::Credential, i64), GatewayClientError> {
let bandwidth_credential = self.storage.get_next_coconut_credential().await?;
let voucher_value = u64::from_str(&bandwidth_credential.voucher_value)
.map_err(|_| StorageError::InconsistentData)?;
let voucher_info = bandwidth_credential.voucher_info.clone();
let serial_number =
coconut_interface::Attribute::try_from_bs58(bandwidth_credential.serial_number)?;
nym_coconut_interface::Attribute::try_from_bs58(bandwidth_credential.serial_number)?;
let binding_number =
coconut_interface::Attribute::try_from_bs58(bandwidth_credential.binding_number)?;
nym_coconut_interface::Attribute::try_from_bs58(bandwidth_credential.binding_number)?;
let signature =
coconut_interface::Signature::try_from_bs58(bandwidth_credential.signature)?;
nym_coconut_interface::Signature::try_from_bs58(bandwidth_credential.signature)?;
let epoch_id = u64::from_str(&bandwidth_credential.epoch_id)
.map_err(|_| StorageError::InconsistentData)?;
@@ -9,13 +9,13 @@ pub use crate::packet_router::{
};
use crate::socket_state::{PartiallyDelegated, SocketState};
use crate::{cleanup_socket_message, try_decrypt_binary_message};
use coconut_interface::Credential;
use futures::{SinkExt, StreamExt};
use gateway_requests::authentication::encrypted_address::EncryptedAddressBytes;
use gateway_requests::iv::IV;
use gateway_requests::registration::handshake::{client_handshake, SharedKeys};
use gateway_requests::{BinaryRequest, ClientControlRequest, ServerResponse, PROTOCOL_VERSION};
use log::*;
use nym_coconut_interface::Credential;
use nym_crypto::asymmetric::identity;
use nym_network_defaults::{REMAINING_BANDWIDTH_THRESHOLD, TOKENS_TO_BURN};
use nym_sphinx::forwarding::packet::MixPacket;
@@ -33,7 +33,7 @@ use validator_client::nyxd::CosmWasmClient;
#[cfg(not(target_arch = "wasm32"))]
#[cfg(not(feature = "mobile"))]
use credential_storage::PersistentStorage;
use nym_credential_storage::PersistentStorage;
#[cfg(not(target_arch = "wasm32"))]
#[cfg(feature = "mobile")]
@@ -3,13 +3,13 @@
#[cfg(target_arch = "wasm32")]
use crate::wasm_mockups::StorageError;
#[cfg(not(feature = "mobile"))]
#[cfg(not(target_arch = "wasm32"))]
use credential_storage::error::StorageError;
use gateway_requests::registration::handshake::error::HandshakeError;
#[cfg(feature = "mobile")]
#[cfg(not(target_arch = "wasm32"))]
use mobile_storage::StorageError;
#[cfg(not(feature = "mobile"))]
#[cfg(not(target_arch = "wasm32"))]
use nym_credential_storage::error::StorageError;
use std::io;
use thiserror::Error;
use tungstenite::Error as WsError;
@@ -31,7 +31,7 @@ pub enum GatewayClientError {
CredentialStorageError(#[from] StorageError),
#[error("Coconut error - {0}")]
CoconutError(#[from] coconut_interface::CoconutError),
CoconutError(#[from] nym_coconut_interface::CoconutError),
// TODO: see if `JsValue` is a reasonable type for this
#[cfg(target_arch = "wasm32")]
@@ -48,7 +48,7 @@ pub enum GatewayClientError {
NoBandwidthControllerAvailable,
#[error("Credential error - {0}")]
CredentialError(#[from] credentials::error::Error),
CredentialError(#[from] nym_credentials::error::Error),
#[error("Connection was abruptly closed")]
ConnectionAbruptlyClosed,
+10 -8
View File
@@ -11,13 +11,13 @@ rust-version = "1.56"
base64 = "0.13"
colored = "2.0"
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-mixnet-contract-common = { path= "../../cosmwasm-smart-contracts/mixnet-contract" }
nym-vesting-contract-common = { path= "../../cosmwasm-smart-contracts/vesting-contract" }
coconut-bandwidth-contract-common = { path= "../../cosmwasm-smart-contracts/coconut-bandwidth-contract" }
multisig-contract-common = { path = "../../cosmwasm-smart-contracts/multisig-contract" }
group-contract-common = { path = "../../cosmwasm-smart-contracts/group-contract" }
nym-coconut-bandwidth-contract-common = { path= "../../cosmwasm-smart-contracts/coconut-bandwidth-contract" }
nym-multisig-contract-common = { path = "../../cosmwasm-smart-contracts/multisig-contract" }
nym-group-contract-common = { path = "../../cosmwasm-smart-contracts/group-contract" }
nym-vesting-contract = { path = "../../../contracts/vesting" }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
@@ -28,15 +28,16 @@ url = { version = "2.2", features = ["serde"] }
tokio = { version = "1.24.1", features = ["sync", "time"] }
futures = "0.3"
coconut-interface = { path = "../../coconut-interface" }
nym-coconut-interface = { path = "../../coconut-interface" }
nym-network-defaults = { path = "../../network-defaults" }
nym-api-requests = { path = "../../../nym-api/nym-api-requests" }
nym-execute = { path = "../../execute" }
# required for nyxd-client
# at some point it might be possible to make it wasm-compatible
# perhaps after https://github.com/cosmos/cosmos-rust/pull/97 is resolved (and tendermint-rs is updated)
async-trait = { version = "0.1.51", optional = true }
bip39 = { version = "1", features = ["rand"], optional = true }
async-trait = { workspace = true, optional = true }
bip39 = { workspace = true, features = ["rand"], optional = true }
nym-config = { path = "../../config", optional = true }
cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support", features = ["rpc", "bip32", "cosmwasm"], optional = true}
cw3 = { version = "0.13.4", optional = true }
@@ -46,7 +47,7 @@ flate2 = { version = "1.0.20", optional = true }
sha2 = { version = "0.9.5", optional = true }
itertools = { version = "0.10", optional = true }
cosmwasm-std = { version = "1.0.0", optional = true }
nym-execute = { path = "../../execute" }
zeroize = { version = "1.5.7", optional = true, features = ["zeroize_derive"] }
[dev-dependencies]
ts-rs = "6.1.2"
@@ -64,6 +65,7 @@ nyxd-client = [
"sha2",
"itertools",
"cosmwasm-std",
"zeroize"
]
generate-ts = []

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