Compare commits

...

28 Commits

Author SHA1 Message Date
Mark Sinclair dfee78e6c0 Merge pull request #3425 from nymtech/fix-sdk-examples
Fix TS SDK examples and make them independent
2023-07-05 14:53:44 +01:00
Mark Sinclair e17ae9dce0 Update nym-wallet-publish-ubuntu.yml 2023-07-05 13:09:21 +01:00
Mark Sinclair 997faeb1e6 Create sdk-publish.yml 2023-07-04 15:27:15 +01:00
Gala 565e7768e3 Update README.md 2023-07-04 14:33:09 +02:00
Pierre Dommerc 271a5fbab6 ci: gh actions strapi (#3616) 2023-07-04 11:56:23 +02:00
Jon Häggblad 90a97b398e name-service contract: signature check when registering (#3572)
* Add nonce state

* Update name-service types and make it build

* wip: convert tests

* Fixed all tests in names.rs

* Add TestName

* Move TestSetup to integration tests

* Tests in contract.rs done

* Move error mod to common crate

* All tests ported

* Update other crates in workspace

* rustfmt

* clippy

* Remove commented out code

* Shortcut for name.name
2023-07-04 11:49:54 +02:00
Jon Häggblad fc2236c3c8 Strip comments from allow lists in network-requester (#3624)
* service-provider: remove comments from standard allow list

* Remove comments from the local allow list
2023-07-04 11:21:47 +02:00
Mark Sinclair c3d3164533 Update nym-wallet-storybook.yml 2023-07-03 17:16:27 +01:00
Jędrzej Stuczyński fa2e0a9010 Merge branch 'release/v1.1.23' into develop 2023-07-03 16:58:27 +01:00
Fouad fcc5398aab Feature/node tester package (#3634)
* create node tester package dir

* start building node tester package

* refactor code + build updates

* fix up types

* add more methods and fix up types

* use node tester sdk inside wallet

* fix frontend state

* Use Node 18 instead of 16

* Fix up dependencies and yarn workspace

* Fix lint error

* Try to fix up linting error

* Remove explorer linting and move it to the existing action

* Add wasm-pack build to linting GH Action

* change lerna to use workspaces and fix linting errors

* Fix up node versions in GitHub Actions and add wasm-pack

* fix build:lint target in sdk

* exclude all worker.js from eslint for sdk

---------

Co-authored-by: Mark Sinclair <mmsinclair@users.noreply.github.com>
2023-07-03 16:53:39 +01:00
Jędrzej Stuczyński 6403d0055b Fixed compilation errors on latest nightly (#3638)
ref: https://github.com/rust-lang/rust/issues/113152
2023-07-03 16:01:10 +01:00
mx ed48a2ddd4 fixed broken example file import 2023-06-30 12:50:48 +02:00
Pierre Dommerc 7a1a7c003e feat(nc-desktop): add matrix and monero to providers list (ui) (#3623) 2023-06-30 12:13:52 +02:00
Pierre Dommerc ef36c29b91 feat(nc-desktop): add matrix and monero to providers list (ui) (#3623) 2023-06-30 12:13:04 +02:00
pierre 4025fed882 build: update workflow to push release data to strapi 2023-06-29 16:51:58 +02:00
pierre 9aaa74204b build: update workflow to push release data to strapi 2023-06-29 16:26:03 +02:00
pierre 9d7a6b2aec build: update workflow to push release data to strapi 2023-06-29 16:09:38 +02:00
pierre c9489fb48e build: add workflow to push release data to strapi 2023-06-29 15:57:02 +02:00
pierre 6c3653c128 build: new workflow placeholder 2023-06-29 15:44:56 +02:00
Jon Häggblad 2953837f25 Add medium toggle to socks5 client (#3615)
* Add medium toggle to socks5 client

* rustfmt
2023-06-28 23:47:00 +02:00
Jon Häggblad ee98820bb4 Use different service provider directory when medium toggle enabled (#3617) 2023-06-28 23:45:45 +02:00
Nadim Kobeissi 3515e4e1c3 Fix typo 2023-05-22 13:35:08 +02:00
Nadim Kobeissi 5ec20e5599 Firefox Extension example for Nym TypeScript SDK 2023-05-22 13:30:55 +02:00
Nadim Kobeissi 9f408d4c79 Do not inline WASM for ESM TypeScript SDK
This change is necessary for us to be able to use the TypeScript Nym SDK
in the context of Firefox extensions. See:

https://bugzilla.mozilla.org/show_bug.cgi?id=1294996
2023-05-22 13:30:02 +02:00
Nadim Kobeissi 31233b3b68 New TypeScript SDK example: Google Chrome
Example of a simple manifest v3 extension to load a Nym client within  a
popup view.
2023-05-19 14:28:30 +02:00
Nadim Kobeissi c4ea887319 Fix favicon path 2023-05-17 15:30:58 +02:00
Nadim Kobeissi 6a69449e43 Update build instructions 2023-05-17 15:30:32 +02:00
Nadim Kobeissi 7aac01cca1 Fix TS SDK examples and make them independent
The Nym TypeScript SDK comes with two main examples:

- `plain-html`
- `react-webpack-with-theme-example`

As of time of testing on the latest `develop` branch over at
@nymtech/nym, `react-webpack-with-theme-example` was broken because the
example was expected to load some of its packages from its own
`package.json` and the rest from the repository's root `package.json`,
which caused the problems outlined here:

https://stackoverflow.com/q/72413194

While addressing this issue, I noticed that the examples were not truly
independent from the rest of the repo. While I know that this is a
monorepo with mulitple workspaces and understand how that's supposed to
work, I think that specifically in the case of example folders, we need
to ensure the ability for these folders to work fully independently from
the rest of the repository, because example folders are overwhelmingly
likely to be copied out of the repo to be expected by curious third
parties to work while fully self-contained. Furthermore, this makes
resolving the above-mentioned original issue easier.

Therefore, in this commit, I did the following for both `plain-html` and
`react-webpack-with-theme-example`:

- Ensure that their `package.json`s are fully self-sufficient and that
  no packages would need to be loaded from the repository's root
  `package.json`.
- Ensure that their `tsconfig.json` are equally self-sufficient and do
  not reference the repository's root `tsconfig.json`.
- Ensure that the webpack configuration is present in each example,
  instead of being snuck into a hidden `.webpack` folder in the
  `sdk/typescript/examples` folder (this struck me as an ugly hack
  anyhow).
- Ensure that `react-webpack-with-theme-example` has direct access to a
  small SVG graphic of the Nym logo within its own folder.

I think these changes will both render the examples less likely to break
as the rest of the monorepo evolves. Freezing dependencies in time is
not appropriate for the monorepo, but it is totally appropriate for
examples since we don't really care about migrating them to the latest
best practices/security fixes for their dependencies as much.

Also, these changes will make the example folders more friendly for
third-party engineers, since they can just copy out the example folder
from the repo and have everything they need to understand how things
work within the example folder itself, instead of needing to hunt down
other files in the sprawling monorepo.
2023-05-16 15:35:13 +02:00
157 changed files with 4734 additions and 2012 deletions
+1 -1
View File
@@ -38,7 +38,7 @@ jobs:
- name: install npm
uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
- name: Matrix - Node Install
run: npm install
working-directory: .github/workflows/support-files
+7 -1
View File
@@ -16,9 +16,15 @@ jobs:
- uses: rlespinasse/github-slug-action@v3.x
- uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
- name: Setup yarn
run: npm install -g yarn
- name: Install Rust stable
uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: Install wasm-pack
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
- name: Build
run: yarn && yarn build && yarn build:ci
- name: Deploy branch to CI www (storybook)
+1 -1
View File
@@ -16,7 +16,7 @@ jobs:
- uses: rlespinasse/github-slug-action@v3.x
- uses: actions/setup-node@v3
with:
node-version: "16"
node-version: 18
- name: Install Rust stable
uses: actions-rs/toolchain@v1
with:
+1 -1
View File
@@ -16,7 +16,7 @@ jobs:
- uses: rlespinasse/github-slug-action@v3.x
- uses: actions/setup-node@v3
with:
node-version: "16"
node-version: 18
- name: Install Rust stable
uses: actions-rs/toolchain@v1
with:
+1 -1
View File
@@ -16,7 +16,7 @@ jobs:
- uses: rlespinasse/github-slug-action@v3.x
- uses: actions/setup-node@v3
with:
node-version: "16"
node-version: 18
- name: Install Rust stable
uses: actions-rs/toolchain@v1
with:
+1 -1
View File
@@ -16,7 +16,7 @@ jobs:
- uses: rlespinasse/github-slug-action@v3.x
- uses: actions/setup-node@v3
with:
node-version: "16"
node-version: 18
- name: Install Rust stable
uses: actions-rs/toolchain@v1
with:
+1 -1
View File
@@ -20,7 +20,7 @@ jobs:
- uses: rlespinasse/github-slug-action@v3.x
- uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
- name: Install Yarn
run: npm install -g yarn
- run: yarn
@@ -1,24 +0,0 @@
name: Linting for Network Explorer (eslint/prettier)
on:
pull_request:
paths:
- 'explorer/**'
defaults:
run:
working-directory: explorer
jobs:
build:
runs-on: custom-runner-linux
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v3
with:
node-version: 16
- name: Setup yarn
run: npm install -g yarn
- name: Run ESLint
# GitHub should automatically annotate the PR
run: yarn && yarn lint
+1 -1
View File
@@ -21,7 +21,7 @@ jobs:
- uses: rlespinasse/github-slug-action@v3.x
- uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
- name: Setup yarn
run: npm install -g yarn
continue-on-error: true
+1 -1
View File
@@ -152,7 +152,7 @@ jobs:
uses: actions/setup-node@v3
if: env.WORKFLOW_CONCLUSION == 'failure'
with:
node-version: 16
node-version: 18
- name: Matrix - Node Install
if: env.WORKFLOW_CONCLUSION == 'failure'
run: npm install
+1 -1
View File
@@ -167,7 +167,7 @@ jobs:
uses: actions/setup-node@v3
if: env.WORKFLOW_CONCLUSION == 'failure'
with:
node-version: 16
node-version: 18
- name: Matrix - Node Install
if: env.WORKFLOW_CONCLUSION == 'failure'
run: npm install
+1 -1
View File
@@ -167,7 +167,7 @@ jobs:
uses: actions/setup-node@v3
if: env.WORKFLOW_CONCLUSION == 'failure'
with:
node-version: 16
node-version: 18
- name: Matrix - Node Install
if: env.WORKFLOW_CONCLUSION == 'failure'
run: npm install
@@ -15,15 +15,22 @@ jobs:
fail-fast: false
matrix:
platform: [macos-latest]
runs-on: ${{ matrix.platform }}
outputs:
release_id: ${{ steps.create-release.outputs.id }}
release_date: ${{ fromJSON(steps.create-release.outputs.assets)[0].published_at }}
version: ${{ steps.release-info.outputs.version }}
filename: ${{ steps.release-info.outputs.filename }}
file_hash: ${{ steps.release-info.outputs.file_hash }}
steps:
- uses: actions/checkout@v2
- name: Node v16
uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
- name: Install Rust stable
uses: actions-rs/toolchain@v1
with:
@@ -81,10 +88,38 @@ jobs:
run: |
security delete-keychain $RUNNER_TEMP/app-signing.keychain-db
- name: Upload to release based on tag name
- id: create-release
name: Upload to release based on tag name
uses: softprops/action-gh-release@v1
if: github.event_name == 'release'
with:
files: |
nym-connect/desktop/target/release/bundle/dmg/*.dmg
nym-connect/desktop/target/release/bundle/macos/*.app.tar.gz*
- id: release-info
name: Prepare release info
run: |
semver="${${{ github.ref_name }}##nym-connect-}" && semver="${semver##v}"
echo "version=$semver" >> "$GITHUB_OUTPUT"
echo "filename=nym-connect_$version_x64.dmg" >> "$GITHUB_OUTPUT"
echo "file_hash=${{ hashFiles('nym-connect/desktop/target/release/bundle/dmg/nym-connect_*_x64.dmg') }}" >> "$GITHUB_OUTPUT"
push-release-data:
if: ${{ (startsWith(github.ref, 'refs/tags/nym-connect-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
uses: ./.github/workflows/push-release-data.yml
needs: publish-tauri
with:
release_tag: ${{ github.ref_name }}
release_id: ${{ needs.publish-tauri.outputs.release_id }}
release_date: ${{ needs.publish-tauri.outputs.release_date }}
download_base_url: https://github.com/nymtech/nym/releases/download/${{ github.ref_name }}
changelog_url: https://github.com/nymtech/nym/blob/${{ github.ref_name }}/nym-connect/desktop/CHANGELOG.md
archive_url: https://github.com/nymtech/nym/releases/download/${{ github.ref_name }}/nym-connect.app.tar.gz
sig_url: https://github.com/nymtech/nym/releases/download/${{ github.ref_name }}/nym-connect.app.tar.gz.sig
version: ${{ needs.publish-tauri.outputs.version }}
filename: ${{ needs.publish-tauri.outputs.filename }}
file_hash: ${{ needs.publish-tauri.outputs.file_hash }}
name: NymConnect
category: connect
platform: MacOS
secrets: inherit
@@ -15,8 +15,15 @@ jobs:
fail-fast: false
matrix:
platform: [custom-runner-linux]
runs-on: ${{ matrix.platform }}
outputs:
release_id: ${{ steps.create-release.outputs.id }}
release_date: ${{ fromJSON(steps.create-release.outputs.assets)[0].published_at }}
version: ${{ steps.release-info.outputs.version }}
filename: ${{ steps.release-info.outputs.filename }}
file_hash: ${{ steps.release-info.outputs.file_hash }}
steps:
- uses: actions/checkout@v2
@@ -29,7 +36,7 @@ jobs:
- name: Node v16
uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
- name: Install Rust stable
uses: actions-rs/toolchain@v1
@@ -56,10 +63,38 @@ jobs:
path: nym-connect/desktop/target/release/bundle/appimage/nym-connect_1.0.0_amd64.AppImage
retention-days: 30
- name: Upload to release based on tag name
- id: create-release
name: Upload to release based on tag name
uses: softprops/action-gh-release@v1
if: github.event_name == 'release'
with:
files: |
nym-connect/desktop/target/release/bundle/appimage/*.AppImage
nym-connect/desktop/target/release/bundle/appimage/*.AppImage.tar.gz*
- id: release-info
name: Prepare release info
run: |
semver="${${{ github.ref_name }}##nym-connect-}" && semver="${semver##v}"
echo "version=$semver" >> "$GITHUB_OUTPUT"
echo "filename=nym-connect_$version_amd64.AppImage" >> "$GITHUB_OUTPUT"
echo "file_hash=${{ hashFiles('nym-connect/desktop/target/release/bundle/appimage/nym-connect_*_amd64.AppImage') }}" >> "$GITHUB_OUTPUT"
push-release-data:
if: ${{ (startsWith(github.ref, 'refs/tags/nym-connect-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
uses: ./.github/workflows/push-release-data.yml
needs: publish-tauri
with:
release_tag: ${{ github.ref_name }}
release_id: ${{ needs.publish-tauri.outputs.release_id }}
release_date: ${{ needs.publish-tauri.outputs.release_date }}
download_base_url: https://github.com/nymtech/nym/releases/download/${{ github.ref_name }}
changelog_url: https://github.com/nymtech/nym/blob/${{ github.ref_name }}/nym-connect/desktop/CHANGELOG.md
archive_url: https://github.com/nymtech/nym/releases/download/${{ github.ref_name }}/nym-connect_${{ needs.publish-tauri.outputs.version }}_amd64.AppImage.tar.gz
sig_url: https://github.com/nymtech/nym/releases/download/${{ github.ref_name }}/nym-connect_${{ needs.publish-tauri.outputs.version }}_amd64.AppImage.tar.gz.sig
version: ${{ needs.publish-tauri.outputs.version }}
filename: ${{ needs.publish-tauri.outputs.filename }}
file_hash: ${{ needs.publish-tauri.outputs.file_hash }}
name: NymConnect
category: connect
platform: Ubuntu
secrets: inherit
@@ -15,8 +15,15 @@ jobs:
fail-fast: false
matrix:
platform: [windows10]
runs-on: ${{ matrix.platform }}
outputs:
release_id: ${{ steps.create-release.outputs.id }}
release_date: ${{ fromJSON(steps.create-release.outputs.assets)[0].published_at }}
version: ${{ steps.release-info.outputs.version }}
filename: ${{ steps.release-info.outputs.filename }}
file_hash: ${{ steps.release-info.outputs.file_hash }}
steps:
- name: Clean up first
continue-on-error: true
@@ -42,7 +49,7 @@ jobs:
- name: Node v16
uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
- name: Install Rust stable
uses: actions-rs/toolchain@v1
@@ -75,10 +82,38 @@ jobs:
path: nym-connect/desktop/target/release/bundle/msi/nym-connect_1.0.0_x64_en-US.msi
retention-days: 30
- name: Upload to release based on tag name
- id: create-release
name: Upload to release based on tag name
uses: softprops/action-gh-release@v1
if: github.event_name == 'release'
with:
files: |
nym-connect/desktop/target/release/bundle/msi/*.msi
nym-connect/desktop/target/release/bundle/msi/*.msi.zip*
- id: release-info
name: Prepare release info
run: |
semver="${${{ github.ref_name }}##nym-connect-}" && semver="${semver##v}"
echo "version=$semver" >> "$GITHUB_OUTPUT"
echo "filename=nym-connect_$version_x64_en-US.msi" >> "$GITHUB_OUTPUT"
echo "file_hash=${{ hashFiles('nym-connect/desktop/target/release/bundle/msi/nym-connect_*_x64_en-US.msi') }}" >> "$GITHUB_OUTPUT"
push-release-data:
if: ${{ (startsWith(github.ref, 'refs/tags/nym-connect-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
uses: ./.github/workflows/push-release-data.yml
needs: publish-tauri
with:
release_tag: ${{ github.ref_name }}
release_id: ${{ needs.publish-tauri.outputs.release_id }}
release_date: ${{ needs.publish-tauri.outputs.release_date }}
download_base_url: https://github.com/nymtech/nym/releases/download/${{ github.ref_name }}
changelog_url: https://github.com/nymtech/nym/blob/${{ github.ref_name }}/nym-connect/desktop/CHANGELOG.md
archive_url: https://github.com/nymtech/nym/releases/download/${{ github.ref_name }}/nym-connect_${{ needs.publish-tauri.outputs.version }}_x64_en-US.msi.zip
sig_url: https://github.com/nymtech/nym/releases/download/${{ github.ref_name }}/nym-connect_${{ needs.publish-tauri.outputs.version }}_x64_en-US.msi.zip.sig
version: ${{ needs.publish-tauri.outputs.version }}
filename: ${{ needs.publish-tauri.outputs.filename }}
file_hash: ${{ needs.publish-tauri.outputs.file_hash }}
name: NymConnect
category: connect
platform: Windows
secrets: inherit
+174 -8
View File
@@ -2,17 +2,17 @@ name: Publish Nym binaries
on:
workflow_dispatch:
inputs:
inputs:
add_tokio_unstable:
description: 'True to add RUSTFLAGS="--cfg tokio_unstable"'
required: true
default: false
type: boolean
type: boolean
release:
types: [created]
env:
NETWORK: mainnet
NETWORK: mainnet
jobs:
publish-nym:
@@ -21,15 +21,33 @@ jobs:
fail-fast: false
matrix:
platform: [custom-runner-linux]
runs-on: ${{ matrix.platform }}
outputs:
release_id: ${{ steps.create-release.outputs.id }}
release_date: ${{ fromJSON(steps.create-release.outputs.assets)[0].published_at }}
client_hash: ${{ steps.binary-hashes.outputs.client_hash }}
mixnode_hash: ${{ steps.binary-hashes.outputs.mixnode_hash }}
gateway_hash: ${{ steps.binary-hashes.outputs.gateway_hash }}
socks5_hash: ${{ steps.binary-hashes.outputs.socks5_hash }}
netreq_hash: ${{ steps.binary-hashes.outputs.netreq_hash }}
cli_hash: ${{ steps.binary-hashes.outputs.cli_hash }}
netstat_hash: ${{ steps.binary-hashes.outputs.netstat_hash }}
client_version: ${{ steps.binary-versions.outputs.client_version }}
mixnode_version: ${{ steps.binary-versions.outputs.mixnode_version }}
gateway_version: ${{ steps.binary-versions.outputs.gateway_version }}
socks5_version: ${{ steps.binary-versions.outputs.socks5_version }}
netreq_version: ${{ steps.binary-versions.outputs.netreq_version }}
cli_version: ${{ steps.binary-versions.outputs.cli_version }}
netstat_version: ${{ steps.binary-versions.outputs.netstat_version }}
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
run: sudo apt-get update && sudo apt-get -y install ripgrep libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools
continue-on-error: true
- name: Sets env vars for tokio if set in manual dispatch inputs
run: |
echo 'RUSTFLAGS="--cfg tokio_unstable"' >> $GITHUB_ENV
@@ -62,7 +80,8 @@ jobs:
target/release/nym-cli
retention-days: 30
- name: Upload to release based on tag name
- id: create-release
name: Upload to release based on tag name
uses: softprops/action-gh-release@v1
if: github.event_name == 'release'
with:
@@ -76,3 +95,150 @@ jobs:
target/release/nym-network-requester
target/release/nym-network-statistics
target/release/nym-cli
- id: release-info
name: Prepare release info
run: |
semver="${${{ github.ref_name }}##nym-binaries-}" && semver="${semver##v}"
echo "version=$semver" >> "$GITHUB_OUTPUT"
- id: binary-hashes
name: Generate binary hashes
run: |
echo "client_hash=${{ hashFiles('target/release/nym-client') }}" >> "$GITHUB_OUTPUT"
echo "mixnode_hash=${{ hashFiles('target/release/nym-mixnode') }}" >> "$GITHUB_OUTPUT"
echo "gateway_hash=${{ hashFiles('target/release/nym-gateway') }}" >> "$GITHUB_OUTPUT"
echo "socks5_hash=${{ hashFiles('target/release/nym-socks5-client') }}" >> "$GITHUB_OUTPUT"
echo "netreq_hash=${{ hashFiles('target/release/nym-network-requester') }}" >> "$GITHUB_OUTPUT"
echo "cli_hash=${{ hashFiles('target/release/nym-cli') }}" >> "$GITHUB_OUTPUT"
echo "netstat_hash=${{ hashFiles('target/release/nym-network-statistics') }}" >> "$GITHUB_OUTPUT"
- id: binary-versions
name: Get binary versions
run: |
v=$(rg '^version = "(.*)"' -or '$1' clients/native/Cargo.toml) && echo "client_version=$v" >> "$GITHUB_OUTPUT"
v=$(rg '^version = "(.*)"' -or '$1' mixnode/Cargo.toml) && echo "mixnode_version=$v" >> "$GITHUB_OUTPUT"
v=$(rg '^version = "(.*)"' -or '$1' gateway/Cargo.toml) && echo "gateway_version=$v" >> "$GITHUB_OUTPUT"
v=$(rg '^version = "(.*)"' -or '$1' clients/socks5/Cargo.toml) && echo "socks5_version=$v" >> "$GITHUB_OUTPUT"
v=$(rg '^version = "(.*)"' -or '$1' service-providers/network-requester/Cargo.toml) && echo "netreq_version=$v" >> "$GITHUB_OUTPUT"
v=$(rg '^version = "(.*)"' -or '$1' tools/nym-cli/Cargo.toml) && echo "cli_version=$v" >> "$GITHUB_OUTPUT"
v=$(rg '^version = "(.*)"' -or '$1' service-providers/network-statistics/Cargo.toml) && echo "netstat_version=$v" >> "$GITHUB_OUTPUT"
push-release-data-client:
if: ${{ (startsWith(github.ref, 'refs/tags/nym-binaries-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
uses: ./.github/workflows/push-release-data.yml
needs: publish-nym
with:
release_tag: ${{ github.ref_name }}
release_id: ${{ needs.publish-nym.outputs.release_id }}
release_date: ${{ needs.publish-nym.outputs.release_date }}
download_base_url: https://github.com/nymtech/nym/releases/download/${{ github.ref_name }}
changelog_url: https://github.com/nymtech/nym/blob/${{ github.ref_name }}/CHANGELOG.md
version: ${{ needs.publish-nym.outputs.client_version }}
filename: nym-client
file_hash: ${{ needs.publish-nym.outputs.client_hash }}
name: Client
category: binaries
secrets: inherit
push-release-data-mixnode:
if: ${{ (startsWith(github.ref, 'refs/tags/nym-binaries-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
uses: ./.github/workflows/push-release-data.yml
needs: publish-nym
with:
release_tag: ${{ github.ref_name }}
release_id: ${{ needs.publish-nym.outputs.release_id }}
release_date: ${{ needs.publish-nym.outputs.release_date }}
download_base_url: https://github.com/nymtech/nym/releases/download/${{ github.ref_name }}
changelog_url: https://github.com/nymtech/nym/blob/${{ github.ref_name }}/CHANGELOG.md
version: ${{ needs.publish-nym.outputs.mixnode_version }}
filename: nym-mixnode
file_hash: ${{ needs.publish-nym.outputs.mixnode_hash }}
name: Mixnode
category: binaries
secrets: inherit
push-release-data-gateway:
if: ${{ (startsWith(github.ref, 'refs/tags/nym-binaries-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
uses: ./.github/workflows/push-release-data.yml
needs: publish-nym
with:
release_tag: ${{ github.ref_name }}
release_id: ${{ needs.publish-nym.outputs.release_id }}
release_date: ${{ needs.publish-nym.outputs.release_date }}
download_base_url: https://github.com/nymtech/nym/releases/download/${{ github.ref_name }}
changelog_url: https://github.com/nymtech/nym/blob/${{ github.ref_name }}/CHANGELOG.md
version: ${{ needs.publish-nym.outputs.gateway_version }}
filename: nym-gateway
file_hash: ${{ needs.publish-nym.outputs.gateway_hash }}
name: Gateway
category: binaries
secrets: inherit
push-release-data-socks5:
if: ${{ (startsWith(github.ref, 'refs/tags/nym-binaries-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
uses: ./.github/workflows/push-release-data.yml
needs: publish-nym
with:
release_tag: ${{ github.ref_name }}
release_id: ${{ needs.publish-nym.outputs.release_id }}
release_date: ${{ needs.publish-nym.outputs.release_date }}
download_base_url: https://github.com/nymtech/nym/releases/download/${{ github.ref_name }}
changelog_url: https://github.com/nymtech/nym/blob/${{ github.ref_name }}/CHANGELOG.md
version: ${{ needs.publish-nym.outputs.socks5_version }}
filename: nym-socks5-client
file_hash: ${{ needs.publish-nym.outputs.socks5_hash }}
name: Socks5 Client
category: binaries
secrets: inherit
push-release-data-network-requester:
if: ${{ (startsWith(github.ref, 'refs/tags/nym-binaries-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
uses: ./.github/workflows/push-release-data.yml
needs: publish-nym
with:
release_tag: ${{ github.ref_name }}
release_id: ${{ needs.publish-nym.outputs.release_id }}
release_date: ${{ needs.publish-nym.outputs.release_date }}
download_base_url: https://github.com/nymtech/nym/releases/download/${{ github.ref_name }}
changelog_url: https://github.com/nymtech/nym/blob/${{ github.ref_name }}/CHANGELOG.md
version: ${{ needs.publish-nym.outputs.netreq_version }}
filename: nym-network-requester
file_hash: ${{ needs.publish-nym.outputs.netreq_hash }}
name: Network Requester
category: binaries
secrets: inherit
push-release-data-cli:
if: ${{ (startsWith(github.ref, 'refs/tags/nym-binaries-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
uses: ./.github/workflows/push-release-data.yml
needs: publish-nym
with:
release_tag: ${{ github.ref_name }}
release_id: ${{ needs.publish-nym.outputs.release_id }}
release_date: ${{ needs.publish-nym.outputs.release_date }}
download_base_url: https://github.com/nymtech/nym/releases/download/${{ github.ref_name }}
changelog_url: https://github.com/nymtech/nym/blob/${{ github.ref_name }}/CHANGELOG.md
version: ${{ needs.publish-nym.outputs.cli_version }}
filename: nym-cli
file_hash: ${{ needs.publish-nym.outputs.cli_hash }}
name: Cli
category: binaries
secrets: inherit
push-release-data-network-stat:
if: ${{ (startsWith(github.ref, 'refs/tags/nym-binaries-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
uses: ./.github/workflows/push-release-data.yml
needs: publish-nym
with:
release_tag: ${{ github.ref_name }}
release_id: ${{ needs.publish-nym.outputs.release_id }}
release_date: ${{ needs.publish-nym.outputs.release_date }}
download_base_url: https://github.com/nymtech/nym/releases/download/${{ github.ref_name }}
changelog_url: https://github.com/nymtech/nym/blob/${{ github.ref_name }}/CHANGELOG.md
version: ${{ needs.publish-nym.outputs.netstat_version }}
filename: nym-network-statistics
file_hash: ${{ needs.publish-nym.outputs.netstat_hash }}
name: Network Statistics
category: binaries
secrets: inherit
+38 -4
View File
@@ -15,15 +15,22 @@ jobs:
fail-fast: false
matrix:
platform: [macos-latest]
runs-on: ${{ matrix.platform }}
outputs:
release_id: ${{ steps.create-release.outputs.id }}
release_date: ${{ fromJSON(steps.create-release.outputs.assets)[0].published_at }}
version: ${{ steps.release-info.outputs.version }}
filename: ${{ steps.release-info.outputs.filename }}
file_hash: ${{ steps.release-info.outputs.file_hash }}
steps:
- uses: actions/checkout@v2
- name: Node v16
uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
- name: Install Rust stable
uses: actions-rs/toolchain@v1
with:
@@ -80,11 +87,38 @@ jobs:
if: ${{ always() }}
run: |
security delete-keychain $RUNNER_TEMP/app-signing.keychain-db
- name: Upload to release based on tag name
- id: create-release
name: Upload to release based on tag name
uses: softprops/action-gh-release@v1
if: github.event_name == 'release'
with:
files: |
nym-wallet/target/release/bundle/dmg/*.dmg
nym-wallet/target/release/bundle/macos/*.app.tar.gz*
- id: release-info
name: Prepare release info
run: |
semver="${${{ github.ref_name }}##nym-wallet-}" && semver="${semver##v}"
echo "version=$semver" >> "$GITHUB_OUTPUT"
echo "filename=nym-wallet_$version_x64.dmg" >> "$GITHUB_OUTPUT"
echo "file_hash=${{ hashFiles('nym-wallet/target/release/bundle/dmg/nym-wallet_*_x64.dmg') }}" >> "$GITHUB_OUTPUT"
push-release-data:
if: ${{ (startsWith(github.ref, 'refs/tags/nym-wallet-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
uses: ./.github/workflows/push-release-data.yml
needs: publish-tauri
with:
release_tag: ${{ github.ref_name }}
release_id: ${{ needs.publish-tauri.outputs.release_id }}
release_date: ${{ needs.publish-tauri.outputs.release_date }}
download_base_url: https://github.com/nymtech/nym/releases/download/${{ github.ref_name }}
changelog_url: https://github.com/nymtech/nym/blob/${{ github.ref_name }}/nym-wallet/CHANGELOG.md
archive_url: https://github.com/nymtech/nym/releases/download/${{ github.ref_name }}/nym-wallet.app.tar.gz
sig_url: https://github.com/nymtech/nym/releases/download/${{ github.ref_name }}/nym-wallet.app.tar.gz.sig
version: ${{ needs.publish-tauri.outputs.version }}
filename: ${{ needs.publish-tauri.outputs.filename }}
file_hash: ${{ needs.publish-tauri.outputs.file_hash }}
name: Wallet
category: wallet
platform: MacOS
secrets: inherit
@@ -1,5 +1,6 @@
name: Publish Nym Wallet (Ubuntu)
on:
workflow_dispatch:
release:
types: [created]
@@ -14,8 +15,15 @@ jobs:
fail-fast: false
matrix:
platform: [custom-runner-linux]
runs-on: ${{ matrix.platform }}
outputs:
release_id: ${{ steps.create-release.outputs.id }}
release_date: ${{ fromJSON(steps.create-release.outputs.assets)[0].published_at }}
version: ${{ steps.release-info.outputs.version }}
filename: ${{ steps.release-info.outputs.filename }}
file_hash: ${{ steps.release-info.outputs.file_hash }}
steps:
- uses: actions/checkout@v2
@@ -28,7 +36,7 @@ jobs:
- name: Node v16
uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
- name: Install Rust stable
uses: actions-rs/toolchain@v1
@@ -47,9 +55,37 @@ jobs:
env:
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
- name: Upload to release based on tag name
- id: create-release
name: Upload to release based on tag name
uses: softprops/action-gh-release@v1
with:
files: |
nym-wallet/target/release/bundle/appimage/*.AppImage
nym-wallet/target/release/bundle/appimage/*.AppImage.tar.gz*
- id: release-info
name: Prepare release info
run: |
semver="${${{ github.ref_name }}##nym-wallet-}" && semver="${semver##v}"
echo "version=$semver" >> "$GITHUB_OUTPUT"
echo "filename=nym-wallet_$version_amd64.AppImage" >> "$GITHUB_OUTPUT"
echo "file_hash=${{ hashFiles('nym-wallet/target/release/bundle/appimage/nym-wallet_*_amd64.AppImage') }}" >> "$GITHUB_OUTPUT"
push-release-data:
if: ${{ (startsWith(github.ref, 'refs/tags/nym-wallet-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
uses: ./.github/workflows/push-release-data.yml
needs: publish-tauri
with:
release_tag: ${{ github.ref_name }}
release_id: ${{ needs.publish-tauri.outputs.release_id }}
release_date: ${{ needs.publish-tauri.outputs.release_date }}
download_base_url: https://github.com/nymtech/nym/releases/download/${{ github.ref_name }}
changelog_url: https://github.com/nymtech/nym/blob/${{ github.ref_name }}/nym-wallet/CHANGELOG.md
archive_url: https://github.com/nymtech/nym/releases/download/${{ github.ref_name }}/nym-wallet_${{ needs.publish-tauri.outputs.version }}_amd64.AppImage.tar.gz
sig_url: https://github.com/nymtech/nym/releases/download/${{ github.ref_name }}/nym-wallet_${{ needs.publish-tauri.outputs.version }}_amd64.AppImage.tar.gz.sig
version: ${{ needs.publish-tauri.outputs.version }}
filename: ${{ needs.publish-tauri.outputs.filename }}
file_hash: ${{ needs.publish-tauri.outputs.file_hash }}
name: Wallet
category: wallet
platform: Ubuntu
secrets: inherit
@@ -15,8 +15,15 @@ jobs:
fail-fast: false
matrix:
platform: [windows10]
runs-on: ${{ matrix.platform }}
outputs:
release_id: ${{ steps.create-release.outputs.id }}
release_date: ${{ fromJSON(steps.create-release.outputs.assets)[0].published_at }}
version: ${{ steps.release-info.outputs.version }}
filename: ${{ steps.release-info.outputs.filename }}
file_hash: ${{ steps.release-info.outputs.file_hash }}
steps:
- name: Clean up first
continue-on-error: true
@@ -42,7 +49,7 @@ jobs:
- name: Node v16
uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
- name: Install Rust stable
uses: actions-rs/toolchain@v1
@@ -75,10 +82,38 @@ jobs:
path: nym-wallet/target/release/bundle/msi/nym-wallet_1.*.msi
retention-days: 30
- name: Upload to release based on tag name
- id: create-release
name: Upload to release based on tag name
uses: softprops/action-gh-release@v1
if: github.event_name == 'release'
with:
files: |
nym-wallet/target/release/bundle/msi/*.msi
nym-wallet/target/release/bundle/msi/*.msi.zip*
- id: release-info
name: Prepare release info
run: |
semver="${${{ github.ref_name }}##nym-wallet-}" && semver="${semver##v}"
echo "version=$semver" >> "$GITHUB_OUTPUT"
echo "filename=nym-wallet_$version_x64_en-US.msi" >> "$GITHUB_OUTPUT"
echo "file_hash=${{ hashFiles('nym-wallet/target/release/bundle/msi/nym-wallet_*_x64_en-US.msi') }}" >> "$GITHUB_OUTPUT"
push-release-data:
if: ${{ (startsWith(github.ref, 'refs/tags/nym-wallet-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
uses: ./.github/workflows/push-release-data.yml
needs: publish-tauri
with:
release_tag: ${{ github.ref_name }}
release_id: ${{ needs.publish-tauri.outputs.release_id }}
release_date: ${{ needs.publish-tauri.outputs.release_date }}
download_base_url: https://github.com/nymtech/nym/releases/download/${{ github.ref_name }}
changelog_url: https://github.com/nymtech/nym/blob/${{ github.ref_name }}/nym-wallet/CHANGELOG.md
archive_url: https://github.com/nymtech/nym/releases/download/${{ github.ref_name }}/nym-wallet_${{ needs.publish-tauri.outputs.version }}_x64_en-US.msi.zip
sig_url: https://github.com/nymtech/nym/releases/download/${{ github.ref_name }}/nym-wallet_${{ needs.publish-tauri.outputs.version }}_x64_en-US.msi.zip.sig
version: ${{ needs.publish-tauri.outputs.version }}
filename: ${{ needs.publish-tauri.outputs.filename }}
file_hash: ${{ needs.publish-tauri.outputs.file_hash }}
name: Wallet
category: wallet
platform: Windows
secrets: inherit
+2 -6
View File
@@ -16,7 +16,7 @@ jobs:
- uses: rlespinasse/github-slug-action@v3.x
- uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
- name: Setup yarn
run: npm install -g yarn
- name: Install Rust stable
@@ -24,11 +24,7 @@ jobs:
with:
toolchain: stable
- name: Install wasm-pack
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
working-directory: clients/webassembly
- name: Build WASM
run: wasm-pack build
working-directory: clients/webassembly
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
- name: Build dependencies
run: yarn && yarn build
- name: Build storybook
+1 -1
View File
@@ -37,7 +37,7 @@ jobs:
- name: Node v16
uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
- name: Install yarn for building application
run: yarn install
+211
View File
@@ -0,0 +1,211 @@
name: Push release data
env:
strapi_download_url: 'https://strapi.feat-nym-update-nym-web.websites.dev.nymte.ch/api/downloaders'
strapi_updater_url: 'https://strapi.feat-nym-update-nym-web.websites.dev.nymte.ch/api/updaters'
on:
workflow_call:
inputs:
release_tag:
required: true
description: Release tag
type: string
release_id:
required: true
description: Release ID
type: string
release_date:
required: true
description: Release date
type: string
download_base_url:
required: true
description: Download base URL
type: string
changelog_url:
required: true
description: Changelog URL
type: string
archive_url:
required: false
description: Binary archive URL
type: string
sig_url:
required: false
description: Archive signature URL
type: string
version:
required: true
description: Release version (semver)
type: string
filename:
required: true
description: Binary file name
type: string
file_hash:
required: true
description: Binary hash (sha256)
type: string
name:
required: true
description: Name
type: string
category:
required: true
description: Category
type: string
platform:
required: false
description: Platform
type: string
workflow_dispatch:
inputs:
# ⚠ since inputs are limited to 10 max for workflow_dispatch
# some properties were omitted
version:
required: true
description: Release version (semver)
type: string
default: '1.0.0'
release_id:
required: true
description: Release ID
type: string
default: '1234'
release_date:
required: true
description: Release date
type: string
default: '2023-06-26T10:09:16Z'
download_base_url:
required: true
description: Download base URL
type: string
default: 'https://github.com/nymtech/nym/releases/download/nym-wallet-v1.0.0'
changelog_url:
required: true
description: Changelog URL
type: string
default: 'https://github.com/nymtech/nym/blob/nym-wallet-v1.0.0/nym-wallet/CHANGELOG.md'
filename:
required: true
description: Binary file name
type: string
default: 'nym-wallet_1.0.0_amd64.AppImage'
file_hash:
required: true
description: Binary hash (sha256)
type: string
default: 'xxx'
name:
required: true
description: Name
type: string
default: 'Wallet'
category:
required: true
description: Category
default: 'wallet'
type: choice
options:
- wallet
- connect
- binaries
platform:
required: false
description: Platform
default: 'Ubuntu'
type: choice
options:
- Ubuntu
- Windows
- MacOS
jobs:
push-download-data:
name: Push download data to Strapi
runs-on: custom-runner-linux
steps:
- name: Release info
run: |
echo "version: ${{ inputs.version }}"
echo "tag: ${{ inputs.release_tag }}"
- id: get_sig
name: Get sig
if: ${{ inputs.sig_url != null }}
run: |
output=$(curl -LsSf ${{ inputs.sig_url }})
echo "sig=$output" >> "$GITHUB_OUTPUT"
- id: strapi-request
name: Strapi request
uses: fjogeleit/http-request-action@v1
with:
url: ${{ env.strapi_download_url }}
method: 'POST'
bearerToken: ${{ secrets.STRAPI_API_TOKEN_RELEASES }}
customHeaders: '{"Content-Type": "application/json"}'
data: |
{
"data": {
"releaseId": "${{ inputs.release_id }}",
"releaseDate": "${{ inputs.release_date }}",
"downloadBaseUrl": "${{ inputs.download_base_url }}",
"changelogUrl": "${{ inputs.changelog_url }}",
"version": "${{ inputs.version }}",
"filename": "${{ inputs.filename }}",
"name": "${{ inputs.name }}",
"category": "${{ inputs.category }}",
"platform": "${{ inputs.platform }}",
"sha256": "${{ inputs.file_hash }}",
"sig": "${{ steps.get_sig.outputs.sig }}"
}
}
- name: Strapi Response
run: |
echo ${{ steps.strapi-request.outputs.response }}
push-update-data:
name: Push update data to Strapi
runs-on: custom-runner-linux
# only push update data for tauri apps (desktop wallet and NC)
if: ${{ inputs.category == 'wallet' || inputs.category == 'connect' }}
steps:
- name: Release info
run: |
echo "version: ${{ inputs.version }}"
echo "tag: ${{ inputs.release_tag }}"
- id: get_sig
name: Get sig
if: ${{ inputs.sig_url != null }}
run: |
output=$(curl -LsSf ${{ inputs.sig_url }})
echo "sig=$output" >> "$GITHUB_OUTPUT"
- id: strapi-request
name: Strapi request
uses: fjogeleit/http-request-action@v1
with:
url: ${{ env.strapi_updater_url }}
method: 'POST'
bearerToken: ${{ secrets.STRAPI_API_TOKEN_RELEASES }}
customHeaders: '{"Content-Type": "application/json"}'
data: |
{
"data": {
"releaseId": "${{ inputs.release_id }}",
"releaseDate": "${{ inputs.release_date }}",
"downloadUrl": "${{ inputs.archive_url }}",
"changelog": "See ${{ inputs.changelog_url }} for the changelog",
"version": "${{ inputs.version }}",
"filename": "${{ inputs.filename }}",
"category": "${{ inputs.category }}",
"platform": "${{ inputs.platform }}",
"sha256": "${{ inputs.file_hash }}",
"sig": "${{ steps.get_sig.outputs.sig }}"
}
}
- name: Strapi Response
run: |
echo ${{ steps.strapi-request.outputs.response }}
+19
View File
@@ -0,0 +1,19 @@
name: Publish SDK to NPM
on:
workflow_dispatch:
defaults:
run:
working-directory: sdk/typescript/packages/sdk
jobs:
publish:
runs-on: [custom-runner-linux]
steps:
- name: Install Node
uses: actions/setup-node@v3
with:
node-version: 18
- name: Setup yarn
run: npm install -g yarn
+1 -1
View File
@@ -1 +1 @@
16
18
+3 -5
View File
@@ -11,6 +11,7 @@ on:
- 'nym-connect/mobile/package.json'
- 'nym-wallet/src/**'
- 'nym-wallet/package.json'
- 'explorer/**'
pull_request:
paths:
- 'ts-packages/**'
@@ -21,6 +22,7 @@ on:
- 'nym-connect/mobile/package.json'
- 'nym-wallet/src/**'
- 'nym-wallet/package.json'
- 'explorer/**'
jobs:
build:
@@ -33,7 +35,7 @@ jobs:
- uses: rlespinasse/github-slug-action@v3.x
- uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
- name: Setup yarn
run: npm install -g yarn
- name: Install Rust stable
@@ -42,10 +44,6 @@ jobs:
toolchain: stable
- name: Install wasm-pack
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
working-directory: clients/webassembly
- name: Build WASM
run: wasm-pack build
working-directory: clients/webassembly
- name: Install
run: yarn
- name: Build packages
Generated
+10 -5
View File
@@ -4041,8 +4041,12 @@ name = "nym-name-service-common"
version = "0.1.0"
dependencies = [
"cosmwasm-std",
"cw-controllers",
"cw-utils",
"nym-contracts-common",
"schemars",
"serde",
"thiserror",
]
[[package]]
@@ -4092,6 +4096,7 @@ dependencies = [
"pretty_env_logger",
"publicsuffix",
"rand 0.7.3",
"regex",
"reqwest",
"serde",
"serde_json",
@@ -5706,13 +5711,13 @@ dependencies = [
[[package]]
name = "regex"
version = "1.8.1"
version = "1.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370"
checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax 0.7.1",
"regex-syntax 0.7.2",
]
[[package]]
@@ -5732,9 +5737,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
[[package]]
name = "regex-syntax"
version = "0.7.1"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c"
checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78"
[[package]]
name = "reqwest"
+3 -3
View File
@@ -15,9 +15,9 @@ The credential binary is still experimental software. The infrastructure for usi
From the project's root directory, run:
```
cargo build -p credential
cargo build -p nym-credential-client
```
which generates the `credential` binary in `target/debug/credential`.
which generates the `nym-credential-client` binary in `target/debug/nym-credential-client`.
### Running
@@ -25,7 +25,7 @@ which generates the `credential` binary in `target/debug/credential`.
For example, you can get a credential worth 3 nym (3000000 unym) in a socks5 client that was already initialized like so:
```
./target/debug/credential --config-env-file envs/sandbox.env --client-home-directory ~/.nym/socks5-clients/cred_client --nyxd-url https://sandbox-validator1.nymtech.net --mnemonic $MNEMONIC --recovery-dir /tmp/recovery --amount 3000000
./target/debug/nym-credential-client --config-env-file envs/sandbox.env --client-home-directory ~/.nym/socks5-clients/cred_client --nyxd-url https://sandbox-validator1.nymtech.net --mnemonic $MNEMONIC --recovery-dir /tmp/recovery --amount 3000000
```
More information regarding how to run the binary can be found by running it with the `--help` argument.
+1
View File
@@ -94,6 +94,7 @@ impl From<Init> for OverrideConfig {
use_anonymous_replies: init_config.use_reply_surbs,
fastmode: init_config.fastmode,
no_cover: init_config.no_cover,
medium_toggle: false,
nyxd_urls: init_config.nyxd_urls,
enabled_credentials_mode: init_config.enabled_credentials_mode,
outfox: false,
+17 -1
View File
@@ -19,7 +19,7 @@ use nym_client_core::client::key_manager::persistence::OnDiskKeys;
use nym_client_core::config::GatewayEndpointConfig;
use nym_client_core::error::ClientCoreError;
use nym_config::OptionalSet;
use nym_sphinx::params::PacketType;
use nym_sphinx::params::{PacketSize, PacketType};
use std::error::Error;
pub mod init;
@@ -68,6 +68,7 @@ pub(crate) struct OverrideConfig {
use_anonymous_replies: Option<bool>,
fastmode: bool,
no_cover: bool,
medium_toggle: bool,
nyxd_urls: Option<Vec<url::Url>>,
enabled_credentials_mode: Option<bool>,
outfox: bool,
@@ -86,6 +87,10 @@ pub(crate) async fn execute(args: &Cli) -> Result<(), Box<dyn Error + Send + Syn
}
pub(crate) fn override_config(config: Config, args: OverrideConfig) -> Config {
let disable_cover_traffic_with_keepalive = args.medium_toggle;
let secondary_packet_size = args.medium_toggle.then_some(PacketSize::ExtendedPacket16);
let no_per_hop_delays = args.medium_toggle;
let packet_type = if args.outfox {
PacketType::Outfox
} else {
@@ -96,6 +101,17 @@ pub(crate) fn override_config(config: Config, args: OverrideConfig) -> Config {
BaseClientConfig::with_high_default_traffic_volume,
args.fastmode,
)
.with_base(
// NOTE: This interacts with disabling cover traffic fully, so we want to this to be set before
BaseClientConfig::with_disabled_cover_traffic_with_keepalive,
disable_cover_traffic_with_keepalive,
)
.with_base(
BaseClientConfig::with_secondary_packet_size,
secondary_packet_size,
)
.with_base(BaseClientConfig::with_no_per_hop_delays, no_per_hop_delays)
// NOTE: see comment above about the order of the other disble cover traffic config
.with_base(BaseClientConfig::with_disabled_cover_traffic, args.no_cover)
.with_base(BaseClientConfig::with_packet_type, packet_type)
.with_optional(Config::with_anonymous_replies, args.use_anonymous_replies)
+6
View File
@@ -60,6 +60,11 @@ pub(crate) struct Run {
#[clap(long, hide = true)]
no_cover: bool,
/// Enable medium mixnet traffic, for experiments only.
/// This includes things like disabling cover traffic, no per hop delays, etc.
#[clap(long, hide = true)]
medium_toggle: bool,
/// Set this client to work in a enabled credentials mode that would attempt to use gateway
/// with bandwidth credential requirement.
#[clap(long, hide = true)]
@@ -77,6 +82,7 @@ impl From<Run> for OverrideConfig {
use_anonymous_replies: run_config.use_anonymous_replies,
fastmode: run_config.fastmode,
no_cover: run_config.no_cover,
medium_toggle: run_config.medium_toggle,
nyxd_urls: run_config.nyxd_urls,
enabled_credentials_mode: run_config.enabled_credentials_mode,
outfox: run_config.outfox,
+1 -1
View File
@@ -1 +1 @@
16
18
+4
View File
@@ -2580,8 +2580,12 @@ name = "nym-name-service-common"
version = "0.1.0"
dependencies = [
"cosmwasm-std",
"cw-controllers",
"cw-utils",
"nym-contracts-common",
"schemars",
"serde",
"thiserror",
]
[[package]]
@@ -4,7 +4,7 @@ use nym_contracts_common::ContractBuildInformation;
use nym_name_service_common::{
msg::QueryMsg as NameQueryMsg,
response::{ConfigResponse, NamesListResponse, PagedNamesListResponse},
Address, NameEntry, NameId,
Address, NameId, RegisteredName,
};
use serde::Deserialize;
@@ -21,7 +21,7 @@ pub trait NameServiceQueryClient {
.await
}
async fn get_name_entry(&self, name_id: NameId) -> Result<NameEntry, NyxdError> {
async fn get_name_entry(&self, name_id: NameId) -> Result<RegisteredName, NyxdError> {
self.query_name_service_contract(NameQueryMsg::NameId { name_id })
.await
}
@@ -54,14 +54,14 @@ pub trait NameServiceQueryClient {
.await
}
async fn get_all_names(&self) -> Result<Vec<NameEntry>, NyxdError> {
async fn get_all_names(&self) -> Result<Vec<RegisteredName>, NyxdError> {
let mut services = Vec::new();
let mut start_after = None;
loop {
let mut paged_response = self.get_names_paged(start_after.take(), None).await?;
let last_id = paged_response.names.last().map(|serv| serv.name_id);
let last_id = paged_response.names.last().map(|serv| serv.id);
services.append(&mut paged_response.names);
if let Some(start_after_res) = last_id {
@@ -2,7 +2,8 @@
// SPDX-License-Identifier: Apache-2.0
use async_trait::async_trait;
use nym_name_service_common::{msg::ExecuteMsg as NameExecuteMsg, Address, NameId, NymName};
use nym_contracts_common::signing::MessageSignature;
use nym_name_service_common::{msg::ExecuteMsg as NameExecuteMsg, NameDetails, NameId, NymName};
use crate::nyxd::{
coin::Coin, cosmwasm_client::types::ExecuteResult, error::NyxdError, Fee, NyxdClient,
@@ -20,14 +21,17 @@ pub trait NameServiceSigningClient {
async fn register_name(
&self,
name: NymName,
address: Address,
name: NameDetails,
owner_signature: MessageSignature,
deposit: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_name_service_contract(
fee,
NameExecuteMsg::Register { name, address },
NameExecuteMsg::Register {
name,
owner_signature,
},
vec![deposit],
)
.await
@@ -1,6 +1,7 @@
use clap::Parser;
use log::{error, info};
use nym_name_service_common::{Address, Coin, NymName};
use nym_contracts_common::signing::MessageSignature;
use nym_name_service_common::{Address, Coin, NameDetails, NymName};
use nym_validator_client::nyxd::{error::NyxdError, traits::NameServiceSigningClient};
use tap::TapFallible;
@@ -16,9 +17,15 @@ pub struct Args {
#[clap(long)]
pub nym_address: String,
#[clap(long)]
pub signature: MessageSignature,
/// Deposit to be made to the service provider directory, in curent DENOMINATION (e.g. 'unym')
#[clap(long)]
pub deposit: u128,
#[clap(long)]
pub identity_key: String,
}
pub async fn register(args: Args, client: SigningClient) -> Result<(), NyxdError> {
@@ -29,12 +36,17 @@ pub async fn register(args: Args, client: SigningClient) -> Result<(), NyxdError
let name = NymName::new(&args.name).expect("invalid name");
let address = Address::new(&args.nym_address);
let name = NameDetails {
name,
address,
identity_key: args.identity_key,
};
let denom = client.current_chain_details().mix_denom.base.as_str();
let deposit = Coin::new(args.deposit, denom);
let res = client
.register_name(name, address, deposit.into(), None)
.register_name(name, args.signature, deposit.into(), None)
.await
.tap_err(|err| error!("Failed to register name: {err:#?}"))?;
@@ -34,8 +34,8 @@ pub async fn query(args: Args, client: &QueryClientWithNyxd) {
table.set_header(vec!["Name Id", "Owner", "Nym Address", "Name"]);
for name_entry in res.names {
table.add_row(vec![
name_entry.name_id.to_string(),
name_entry.name.owner.to_string(),
name_entry.id.to_string(),
name_entry.owner.to_string(),
name_entry.name.address.to_string(),
name_entry.name.name.to_string(),
]);
@@ -7,5 +7,9 @@ edition = "2021"
[dependencies]
cosmwasm-std = { workspace = true }
cw-controllers = { workspace = true }
cw-utils = { workspace = true }
nym-contracts-common = { path = "../contracts-common", version = "0.5.0" }
schemars = "0.8"
serde = { workspace = true, features = ["derive"] }
thiserror = { workspace = true }
@@ -1,8 +1,10 @@
use cosmwasm_std::{Addr, StdError};
use cw_controllers::AdminError;
use nym_name_service_common::{Address, NameId, NymName};
use nym_contracts_common::signing::verifier::ApiVerifierError;
use thiserror::Error;
use crate::{Address, NameId, NymName};
#[derive(Error, Debug, PartialEq)]
pub enum NameServiceError {
#[error("{0}")]
@@ -47,6 +49,21 @@ pub enum NameServiceError {
error_message: String,
},
#[error("Failed to recover ed25519 public key from its base58 representation - {0}")]
MalformedEd25519IdentityKey(String),
#[error("Failed to recover ed25519 signature from its base58 representation - {0}")]
MalformedEd25519Signature(String),
#[error("Provided ed25519 signature did not verify correctly")]
InvalidEd25519Signature,
#[error("failed to verify message signature: {source}")]
SignatureVerificationFailure {
#[from]
source: ApiVerifierError,
},
#[error("duplicate entries detected for name: {name}")]
DuplicateNames { name: NymName },
@@ -54,4 +71,4 @@ pub enum NameServiceError {
NameAlreadyRegistered { name: NymName },
}
pub(crate) type Result<T, E = NameServiceError> = std::result::Result<T, E>;
pub type Result<T, E = NameServiceError> = std::result::Result<T, E>;
@@ -1,6 +1,6 @@
use cosmwasm_std::{Coin, Event};
use crate::{NameId, RegisteredName};
use crate::RegisteredName;
pub enum NameEventType {
Register,
@@ -34,29 +34,29 @@ pub const OWNER: &str = "owner";
pub const DEPOSIT_REQUIRED: &str = "deposit_required";
pub fn new_register_event(name_id: NameId, name: RegisteredName) -> Event {
pub fn new_register_event(name: RegisteredName) -> Event {
Event::new(NameEventType::Register)
.add_attribute(ACTION, NameEventType::Register)
.add_attribute(NAME_ID, name_id.to_string())
.add_attribute(NAME, name.name.to_string())
.add_attribute(name.address.event_tag(), name.address.to_string())
.add_attribute(NAME_ID, name.id.to_string())
.add_attribute(NAME, name.name.name.to_string())
.add_attribute(name.name.address.event_tag(), name.name.address.to_string())
.add_attribute(OWNER, name.owner.to_string())
}
pub fn new_delete_id_event(name_id: NameId, name: RegisteredName) -> Event {
pub fn new_delete_id_event(name: RegisteredName) -> Event {
Event::new(NameEventType::DeleteId)
.add_attribute(ACTION, NameEventType::DeleteId)
.add_attribute(NAME_ID, name_id.to_string())
.add_attribute(NAME, name.name.to_string())
.add_attribute(name.address.event_tag(), name.address.to_string())
.add_attribute(NAME_ID, name.id.to_string())
.add_attribute(NAME, name.name.name.to_string())
.add_attribute(name.name.address.event_tag(), name.name.address.to_string())
}
pub fn new_delete_name_event(name_id: NameId, name: RegisteredName) -> Event {
pub fn new_delete_name_event(name: RegisteredName) -> Event {
Event::new(NameEventType::DeleteId)
.add_attribute(ACTION, NameEventType::DeleteName)
.add_attribute(NAME_ID, name_id.to_string())
.add_attribute(NAME, name.name.to_string())
.add_attribute(name.address.event_tag(), name.address.to_string())
.add_attribute(NAME_ID, name.id.to_string())
.add_attribute(NAME, name.name.name.to_string())
.add_attribute(name.name.address.event_tag(), name.name.address.to_string())
}
pub fn new_update_deposit_required_event(deposit_required: Coin) -> Event {
@@ -1,6 +1,8 @@
pub mod error;
pub mod events;
pub mod msg;
pub mod response;
pub mod signing_types;
pub mod types;
// Re-export all types at the top-level
@@ -1,5 +1,6 @@
use crate::{Address, NameId, NymName};
use crate::{Address, NameDetails, NameId, NymName};
use cosmwasm_std::Coin;
use nym_contracts_common::signing::MessageSignature;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
@@ -22,7 +23,10 @@ pub struct MigrateMsg {}
#[serde(rename_all = "snake_case")]
pub enum ExecuteMsg {
/// Announcing a name pointing to a nym-address
Register { name: NymName, address: Address },
Register {
name: NameDetails,
owner_signature: MessageSignature,
},
/// Delete a name entry by id
DeleteId { name_id: NameId },
/// Delete a name entry by name
@@ -38,8 +42,11 @@ impl ExecuteMsg {
pub fn default_memo(&self) -> String {
match self {
ExecuteMsg::Register { name, address } => {
format!("registering {address} as name: {name}")
ExecuteMsg::Register {
name,
owner_signature: _,
} => {
format!("registering {} as name: {}", name.address, name.name)
}
ExecuteMsg::DeleteId { name_id } => {
format!("deleting name with id {name_id}")
@@ -75,6 +82,9 @@ pub enum QueryMsg {
limit: Option<u32>,
start_after: Option<NameId>,
},
SigningNonce {
address: String,
},
Config {},
GetContractVersion {},
#[serde(rename = "get_cw2_contract_version")]
@@ -1,36 +1,22 @@
use crate::{msg::ExecuteMsg, NameEntry, NameId, RegisteredName};
use crate::{NameId, RegisteredName};
use cosmwasm_std::Coin;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
/// Like [`NameEntry`] but since it's a response type the name is an option depending on if
/// the name exists or not.
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
#[serde(rename_all = "snake_case")]
pub struct NameEntryResponse {
pub name_id: NameId,
pub name: Option<RegisteredName>,
}
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub struct NamesListResponse {
pub names: Vec<NameEntry>,
pub names: Vec<RegisteredName>,
}
impl NamesListResponse {
pub fn new(names: Vec<(NameId, RegisteredName)>) -> NamesListResponse {
NamesListResponse {
names: names
.into_iter()
.map(|(name_id, name)| NameEntry::new(name_id, name))
.collect(),
}
pub fn new(names: Vec<RegisteredName>) -> NamesListResponse {
NamesListResponse { names }
}
}
impl From<&[NameEntry]> for NamesListResponse {
fn from(names: &[NameEntry]) -> Self {
impl From<&[RegisteredName]> for NamesListResponse {
fn from(names: &[RegisteredName]) -> Self {
NamesListResponse {
names: names.to_vec(),
}
@@ -40,21 +26,17 @@ impl From<&[NameEntry]> for NamesListResponse {
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
#[serde(rename_all = "snake_case")]
pub struct PagedNamesListResponse {
pub names: Vec<NameEntry>,
pub names: Vec<RegisteredName>,
pub per_page: usize,
pub start_next_after: Option<NameId>,
}
impl PagedNamesListResponse {
pub fn new(
names: Vec<(NameId, RegisteredName)>,
names: Vec<RegisteredName>,
per_page: usize,
start_next_after: Option<NameId>,
) -> PagedNamesListResponse {
let names = names
.into_iter()
.map(|(name_id, name)| NameEntry::new(name_id, name))
.collect();
PagedNamesListResponse {
names,
per_page,
@@ -68,12 +50,3 @@ impl PagedNamesListResponse {
pub struct ConfigResponse {
pub deposit_required: Coin,
}
impl From<RegisteredName> for ExecuteMsg {
fn from(name: RegisteredName) -> Self {
ExecuteMsg::Register {
name: name.name,
address: name.address,
}
}
}
@@ -0,0 +1,32 @@
use cosmwasm_std::{Addr, Coin};
use nym_contracts_common::signing::{
ContractMessageContent, MessageType, Nonce, SignableMessage, SigningPurpose,
};
use serde::Serialize;
use crate::NameDetails;
pub type SignableNameRegisterMsg = SignableMessage<ContractMessageContent<NameRegister>>;
#[derive(Serialize)]
pub struct NameRegister {
name: NameDetails,
}
impl SigningPurpose for NameRegister {
fn message_type() -> MessageType {
MessageType::new("name-register")
}
}
pub fn construct_name_register_sign_payload(
nonce: Nonce,
sender: Addr,
deposit: Coin,
name: NameDetails,
) -> SignableNameRegisterMsg {
let payload = NameRegister { name };
let proxy = None;
let content = ContractMessageContent::new(sender, proxy, vec![deposit], payload);
SignableMessage::new(nonce, content)
}
@@ -1,24 +1,48 @@
use std::fmt::{Display, Formatter};
use cosmwasm_std::{Addr, Coin};
use nym_contracts_common::IdentityKey;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
/// The directory of services are indexed by [`ServiceId`].
/// The directory of names are indexed by [`NameId`].
pub type NameId = u32;
#[derive(Serialize, Deserialize, Clone, PartialEq, Debug, JsonSchema)]
pub struct RegisteredName {
/// Unique id assigned to the registerd name.
pub id: NameId,
/// The registerd name details.
pub name: NameDetails,
/// name owner.
pub owner: Addr,
/// Block height at which the name was added.
pub block_height: u64,
/// The deposit used to announce the name.
pub deposit: Coin,
}
impl RegisteredName {
// Shortcut for getting the actual name
pub fn entry(&self) -> &NymName {
&self.name.name
}
}
#[derive(Serialize, Deserialize, Clone, PartialEq, Debug, JsonSchema)]
pub struct NameDetails {
/// The name pointing to the nym address
pub name: NymName,
/// The address of the service.
/// The address of the name alias.
pub address: Address,
/// Service owner.
pub owner: Addr,
/// Block height at which the service was added.
pub block_height: u64,
/// The deposit used to announce the service.
pub deposit: Coin,
/// The identity key of the registered name.
pub identity_key: IdentityKey,
}
/// String representation of a nym address, which is of the form
@@ -68,6 +92,7 @@ pub enum NymNameError {
InvalidName,
}
/// Defines what names are allowed
fn is_valid_name_char(c: char) -> bool {
// Normal lowercase letters
(c.is_alphabetic() && c.is_lowercase())
@@ -98,20 +123,6 @@ impl Display for NymName {
}
}
/// [`RegisterdName`] together with the assigned [`NameId`].
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub struct NameEntry {
pub name_id: NameId,
pub name: RegisteredName,
}
impl NameEntry {
pub fn new(name_id: NameId, name: RegisteredName) -> Self {
Self { name_id, name }
}
}
#[cfg(test)]
mod tests {
use super::NymName;
@@ -7,9 +7,9 @@ edition = "2021"
[dependencies]
cosmwasm-std = { workspace = true }
cw-controllers = { workspace = true }
cw-utils = { workspace = true }
nym-contracts-common = { path = "../contracts-common", version = "0.5.0" }
schemars = "0.8"
serde = { workspace = true, features = ["derive"] }
thiserror = { workspace = true }
cw-utils = { workspace = true }
cw-controllers = { workspace = true }
+9 -2
View File
@@ -1411,6 +1411,7 @@ name = "nym-name-service"
version = "0.1.0"
dependencies = [
"anyhow",
"bs58",
"cosmwasm-std",
"cw-controllers",
"cw-multi-test",
@@ -1418,8 +1419,10 @@ dependencies = [
"cw-utils",
"cw2",
"nym-contracts-common",
"nym-crypto",
"nym-name-service-common",
"rand 0.8.5",
"rand_chacha 0.2.2",
"rstest",
"semver",
"serde",
@@ -1432,8 +1435,12 @@ name = "nym-name-service-common"
version = "0.1.0"
dependencies = [
"cosmwasm-std",
"cw-controllers",
"cw-utils",
"nym-contracts-common",
"schemars",
"serde",
"thiserror",
]
[[package]]
@@ -1646,9 +1653,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.52"
version = "1.0.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d0e1ae9e836cc3beddd63db0df682593d7e2d3d891ae8c9083d2113e1744224"
checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb"
dependencies = [
"unicode-ident",
]
+3
View File
@@ -7,6 +7,7 @@ edition = "2021"
crate-type = ["cdylib", "rlib"]
[dependencies]
bs58 = "0.4.0"
cosmwasm-std = { workspace = true }
cw-controllers = { workspace = true }
cw-storage-plus = { workspace = true }
@@ -24,5 +25,7 @@ vergen = { version = "=7.4.3", default-features = false, features = ["build", "g
[dev-dependencies]
anyhow = "1.0.40"
cw-multi-test = { workspace = true }
nym-crypto = { path = "../../common/crypto", features = ["asymmetric", "rand"] }
rand = "0.8.5"
rand_chacha = "0.2"
rstest = "0.17.0"
+2
View File
@@ -14,3 +14,5 @@ pub const NAMES_PK_NAMESPACE: &str = "nanames";
pub const NAMES_OWNER_IDX_NAMESPACE: &str = "naowner";
pub const NAMES_ADDRESS_IDX_NAMESPACE: &str = "naaddress";
pub const NAMES_NAME_IDX_NAMESPACE: &str = "naname";
pub const SIGNING_NONCES_NAMESPACE: &str = "nasn";
+168 -34
View File
@@ -1,6 +1,6 @@
use crate::{
error::{NameServiceError, Result},
state::{self, Config},
NameServiceError, Result,
};
use cosmwasm_std::{to_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response};
use nym_name_service_common::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg};
@@ -72,7 +72,10 @@ pub fn execute(
msg: ExecuteMsg,
) -> Result<Response, NameServiceError> {
match msg {
ExecuteMsg::Register { name, address } => execute::register(deps, env, info, name, address),
ExecuteMsg::Register {
name,
owner_signature,
} => execute::register(deps, env, info, name, owner_signature),
ExecuteMsg::DeleteId { name_id } => execute::delete_id(deps, info, name_id),
ExecuteMsg::DeleteName { name } => execute::delete_name(deps, info, name),
ExecuteMsg::UpdateDepositRequired { deposit_required } => {
@@ -90,6 +93,9 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> Result<Binary> {
QueryMsg::All { limit, start_after } => {
to_binary(&query::query_all_paged(deps, limit, start_after)?)
}
QueryMsg::SigningNonce { address } => {
to_binary(&query::query_current_signing_nonce(deps, address)?)
}
QueryMsg::Config {} => to_binary(&query::query_config(deps)?),
QueryMsg::GetContractVersion {} => to_binary(&query::query_contract_version()),
QueryMsg::GetCW2ContractVersion {} => to_binary(&cw2::get_contract_version(deps.storage)?),
@@ -102,16 +108,19 @@ mod tests {
use super::*;
use crate::test_helpers::{
assert::{assert_config, assert_empty, assert_name, assert_names, assert_not_found},
fixture::name_fixture,
helpers::{get_attribute, nyms},
assert::{
assert_config, assert_current_nonce, assert_empty, assert_name, assert_names,
assert_not_found,
},
fixture::new_name_details_with_sign,
helpers::{get_attribute, nyms, test_rng},
};
use cosmwasm_std::{
testing::{mock_dependencies, mock_env, mock_info},
Addr, Coin,
};
use nym_name_service_common::{msg::ExecuteMsg, NameEntry, NameId};
use nym_name_service_common::{msg::ExecuteMsg, NameId, RegisteredName};
const DENOM: &str = "unym";
@@ -135,23 +144,28 @@ mod tests {
}
#[test]
fn register_fails_incorrect_deposit() {
fn register_fails_deposit_too_small() {
let mut rng = test_rng();
let mut deps = mock_dependencies();
let msg = InstantiateMsg::new(nyms(100));
let info = mock_info("creator", &[]);
let admin = info.sender.clone();
let res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap();
assert_eq!(res.messages.len(), 0);
// Register
let msg: ExecuteMsg = name_fixture().into();
let owner = name_fixture().owner.to_string();
let deposit = nyms(99);
let owner = "steve";
let (name, owner_signature) =
new_name_details_with_sign(deps.as_mut(), &mut rng, "foo", "address", owner, deposit);
let msg = ExecuteMsg::Register {
name,
owner_signature,
};
assert_eq!(
execute(
deps.as_mut(),
mock_env(),
mock_info(&owner, &[nyms(99)]),
mock_info(owner, &[nyms(99)]),
msg.clone()
)
.unwrap_err(),
@@ -161,34 +175,135 @@ mod tests {
}
);
// Since we signed for 99unym deposit.
assert_eq!(
execute(
deps.as_mut(),
mock_env(),
mock_info(&owner, &[nyms(101)]),
mock_info(owner, &[nyms(100)]),
msg
)
.unwrap_err(),
NameServiceError::InvalidEd25519Signature,
);
}
#[test]
fn register_fails_deposit_too_large() {
let mut rng = test_rng();
let mut deps = mock_dependencies();
let msg = InstantiateMsg::new(nyms(100));
let info = mock_info("creator", &[]);
let res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap();
assert_eq!(res.messages.len(), 0);
let deposit = nyms(101);
let owner = "steve";
let (name, owner_signature) =
new_name_details_with_sign(deps.as_mut(), &mut rng, "foo", "address", owner, deposit);
let msg = ExecuteMsg::Register {
name,
owner_signature,
};
assert_eq!(
execute(
deps.as_mut(),
mock_env(),
mock_info(owner, &[nyms(101)]),
msg.clone()
)
.unwrap_err(),
NameServiceError::TooLargeDeposit {
funds: 101u128.into(),
deposit_required: 100u128.into(),
}
);
assert_config(deps.as_ref(), &admin, Coin::new(100, DENOM));
assert_empty(deps.as_ref());
// Since we signed for 101unym deposit.
assert_eq!(
execute(
deps.as_mut(),
mock_env(),
mock_info(owner, &[nyms(100)]),
msg
)
.unwrap_err(),
NameServiceError::InvalidEd25519Signature,
);
}
#[test]
fn register_success() {
fn register_fails_owner_mismatch() {
let mut rng = test_rng();
let mut deps = mock_dependencies();
let msg = InstantiateMsg::new(nyms(100));
let info = mock_info("creator", &[]);
let res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap();
assert_eq!(res.messages.len(), 0);
// Setup
let deposit = nyms(100);
let owner = "steve";
let (name, owner_signature) = new_name_details_with_sign(
deps.as_mut(),
&mut rng,
"my-name",
"my-address",
owner,
deposit,
);
// Register
let msg: ExecuteMsg = name_fixture().into();
let msg = ExecuteMsg::Register {
name,
owner_signature,
};
assert_eq!(
execute(
deps.as_mut(),
mock_env(),
mock_info("timmy", &[nyms(100)]),
msg.clone(),
)
.unwrap_err(),
NameServiceError::InvalidEd25519Signature,
);
assert!(execute(
deps.as_mut(),
mock_env(),
mock_info("steve", &[nyms(100)]),
msg
)
.is_ok());
}
#[test]
fn register_success() {
let mut rng = test_rng();
let mut deps = mock_dependencies();
let msg = InstantiateMsg::new(nyms(100));
let info = mock_info("creator", &[]);
let res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap();
assert_eq!(res.messages.len(), 0);
// Setup
let deposit = nyms(100);
let owner = "steve";
let (name, owner_signature) = new_name_details_with_sign(
deps.as_mut(),
&mut rng,
"my-name",
"my-address",
owner,
deposit.clone(),
);
// Register
let msg = ExecuteMsg::Register {
name: name.clone(),
owner_signature,
};
let info = mock_info("steve", &[nyms(100)]);
let res = execute(deps.as_mut(), mock_env(), info, msg).unwrap();
@@ -198,17 +313,24 @@ mod tests {
assert_eq!(id, expected_id);
assert_eq!(
get_attribute(&res, "register", "name"),
"my-service".to_string()
"my-name".to_string()
);
assert_eq!(
get_attribute(&res, "register", "nym_address"),
"client_id.client_key@gateway_id".to_string()
"my-address".to_string()
);
// Check that the nonce has been incremented, but only for the owner
assert_current_nonce(deps.as_ref(), &Addr::unchecked("steve"), 1);
assert_current_nonce(deps.as_ref(), &Addr::unchecked("timmy"), 0);
// The expected registered name
let expected_name = NameEntry {
name_id: expected_id,
name: name_fixture(),
let expected_name = RegisteredName {
id: expected_id,
name,
owner: Addr::unchecked(owner),
block_height: 12345,
deposit,
};
assert_names(deps.as_ref(), &[expected_name.clone()]);
assert_name(deps.as_ref(), &expected_name);
@@ -216,6 +338,7 @@ mod tests {
#[test]
fn delete() {
let mut rng = test_rng();
let mut deps = mock_dependencies();
let msg = InstantiateMsg::new(Coin::new(100, "unym"));
let info = mock_info("creator", &[]);
@@ -223,16 +346,31 @@ mod tests {
assert_eq!(res.messages.len(), 0);
// Register
let msg: ExecuteMsg = name_fixture().into();
let deposit = nyms(100);
let steve = "steve";
let (name, owner_signature) = new_name_details_with_sign(
deps.as_mut(),
&mut rng,
"my-name",
"my-address",
steve,
deposit.clone(),
);
let msg = ExecuteMsg::Register {
name: name.clone(),
owner_signature,
};
let info_steve = mock_info("steve", &[nyms(100)]);
assert_eq!(info_steve.sender, name_fixture().owner);
execute(deps.as_mut(), mock_env(), info_steve, msg).unwrap();
execute(deps.as_mut(), mock_env(), info_steve.clone(), msg).unwrap();
// The expected registerd name
let expected_id = 1;
let expected_name = NameEntry {
name_id: expected_id,
name: name_fixture(),
let expected_name = RegisteredName {
id: expected_id,
name,
owner: Addr::unchecked(steve),
block_height: 12345,
deposit,
};
assert_names(deps.as_ref(), &[expected_name]);
@@ -248,12 +386,8 @@ mod tests {
// Removing an non-existent name will fail
let msg = ExecuteMsg::delete_id(expected_id + 1);
let info_owner = MessageInfo {
sender: name_fixture().owner,
funds: vec![],
};
assert_eq!(
execute(deps.as_mut(), mock_env(), info_owner.clone(), msg).unwrap_err(),
execute(deps.as_mut(), mock_env(), info_steve.clone(), msg).unwrap_err(),
NameServiceError::NotFound {
name_id: expected_id + 1
}
@@ -261,7 +395,7 @@ mod tests {
// Remove as correct owner succeeds
let msg = ExecuteMsg::delete_id(expected_id);
let res = execute(deps.as_mut(), mock_env(), info_owner, msg).unwrap();
let res = execute(deps.as_mut(), mock_env(), info_steve, msg).unwrap();
assert_eq!(
get_attribute(&res, "delete_id", "name_id"),
expected_id.to_string()
+70 -19
View File
@@ -1,15 +1,19 @@
use crate::{
constants::{MAX_NUMBER_OF_NAMES_FOR_ADDRESS, MAX_NUMBER_OF_NAMES_PER_OWNER},
error::{NameServiceError, Result},
state,
state, NameServiceError, Result,
};
use cosmwasm_std::{Addr, BankMsg, Coin, Deps, DepsMut, Env, MessageInfo, Response, Uint128};
use nym_contracts_common::{
signing::{MessageSignature, Verifier},
IdentityKey,
};
use nym_name_service_common::{
events::{
new_delete_id_event, new_delete_name_event, new_register_event,
new_update_deposit_required_event,
},
Address, NameId, NymName, RegisteredName,
signing_types::construct_name_register_sign_payload,
Address, NameDetails, NameId, NymName, RegisteredName,
};
use super::query;
@@ -86,17 +90,54 @@ fn return_deposit(name_to_delete: &RegisteredName) -> BankMsg {
}
}
fn verify_register_signature(
deps: Deps<'_>,
sender: Addr,
deposit: Coin,
name: NameDetails,
signature: MessageSignature,
) -> Result<()> {
// recover the public key
let public_key = decode_ed25519_identity_key(&name.identity_key)?;
// reconstruct the payload
let nonce = state::get_signing_nonce(deps.storage, sender.clone())?;
let msg = construct_name_register_sign_payload(nonce, sender, deposit, name);
if deps.api.verify_message(msg, signature, &public_key)? {
Ok(())
} else {
Err(NameServiceError::InvalidEd25519Signature)
}
}
fn decode_ed25519_identity_key(encoded: &IdentityKey) -> Result<[u8; 32]> {
let mut public_key = [0u8; 32];
let used = bs58::decode(encoded)
.into(&mut public_key)
.map_err(|err| NameServiceError::MalformedEd25519IdentityKey(err.to_string()))?;
if used != 32 {
return Err(NameServiceError::MalformedEd25519IdentityKey(
"Too few bytes provided for the public key".into(),
));
}
Ok(public_key)
}
/// Register a new name. It will be assigned a new name id.
pub fn register(
deps: DepsMut,
env: Env,
info: MessageInfo,
name: NymName,
address: Address,
name: NameDetails,
owner_signature: MessageSignature,
) -> Result<Response> {
ensure_name_not_exists(deps.as_ref(), &name)?;
ensure_name_not_exists(deps.as_ref(), &name.name)?;
ensure_max_names_per_owner(deps.as_ref(), info.sender.clone())?;
ensure_max_names_per_address(deps.as_ref(), address.clone())?;
ensure_max_names_per_address(deps.as_ref(), name.address.clone())?;
let deposit_required = state::deposit_required(deps.storage)?;
let denom = deposit_required.denom.clone();
@@ -104,16 +145,29 @@ pub fn register(
.map_err(|err| NameServiceError::DepositRequired { source: err })?;
ensure_correct_deposit(will_deposit, deposit_required.amount)?;
let deposit = Coin::new(will_deposit.u128(), denom);
verify_register_signature(
deps.as_ref(),
info.sender.clone(),
deposit.clone(),
name.clone(),
owner_signature,
)?;
state::increment_signing_nonce(deps.storage, info.sender.clone())?;
let id = state::next_name_id_counter(deps.storage)?;
let new_name = RegisteredName {
address,
id,
name,
owner: info.sender,
block_height: env.block.height,
deposit: Coin::new(will_deposit.u128(), denom),
deposit,
};
let name_id = state::names::save(deps.storage, &new_name)?;
state::names::save(deps.storage, &new_name)?;
Ok(Response::new().add_event(new_register_event(name_id, new_name)))
Ok(Response::new().add_event(new_register_event(new_name)))
}
/// Delete an exsisting name.
@@ -127,23 +181,20 @@ pub fn delete_id(deps: DepsMut, info: MessageInfo, name_id: NameId) -> Result<Re
Ok(Response::new()
.add_message(return_deposit_msg)
.add_event(new_delete_id_event(name_id, name_to_delete)))
.add_event(new_delete_id_event(name_to_delete)))
}
/// Delete an existing name by name.
pub(crate) fn delete_name(deps: DepsMut, info: MessageInfo, name: NymName) -> Result<Response> {
let name_to_delete = query::query_name(deps.as_ref(), name)?;
ensure_sender_authorized(info, &name_to_delete.name)?;
ensure_sender_authorized(info, &name_to_delete)?;
state::names::remove_id(deps.storage, name_to_delete.name_id)?;
let return_deposit_msg = return_deposit(&name_to_delete.name);
state::names::remove_id(deps.storage, name_to_delete.id)?;
let return_deposit_msg = return_deposit(&name_to_delete);
Ok(Response::new()
.add_message(return_deposit_msg)
.add_event(new_delete_name_event(
name_to_delete.name_id,
name_to_delete.name,
)))
.add_event(new_delete_name_event(name_to_delete)))
}
/// Update the deposit required to register new names
+12 -9
View File
@@ -1,18 +1,17 @@
use cosmwasm_std::Deps;
use nym_contracts_common::ContractBuildInformation;
use nym_contracts_common::{signing::Nonce, ContractBuildInformation};
use nym_name_service_common::{
response::{ConfigResponse, NamesListResponse, PagedNamesListResponse},
Address, NameEntry, NameId, NymName,
Address, NameId, NymName, RegisteredName,
};
use crate::{
error::Result,
state::{self, names::PagedLoad},
Result,
};
pub fn query_id(deps: Deps, name_id: NameId) -> Result<NameEntry> {
let name = state::names::load_id(deps.storage, name_id)?;
Ok(NameEntry { name_id, name })
pub fn query_id(deps: Deps, name_id: NameId) -> Result<RegisteredName> {
state::names::load_id(deps.storage, name_id)
}
pub fn query_owner(deps: Deps, owner: String) -> Result<NamesListResponse> {
@@ -26,9 +25,8 @@ pub fn query_address(deps: Deps, address: Address) -> Result<NamesListResponse>
Ok(NamesListResponse::new(names))
}
pub fn query_name(deps: Deps, name: NymName) -> Result<NameEntry> {
state::names::load_name_entry(deps.storage, &name)
.map(|(name_id, name)| NameEntry::new(name_id, name))
pub fn query_name(deps: Deps, name: NymName) -> Result<RegisteredName> {
state::names::load_name(deps.storage, &name)
}
pub fn query_all_paged(
@@ -44,6 +42,11 @@ pub fn query_all_paged(
Ok(PagedNamesListResponse::new(names, limit, start_next_after))
}
pub fn query_current_signing_nonce(deps: Deps<'_>, address: String) -> Result<Nonce> {
let address = deps.api.addr_validate(&address)?;
state::get_signing_nonce(deps.storage, address)
}
pub fn query_config(deps: Deps) -> Result<ConfigResponse> {
let config = state::load_config(deps.storage)?;
Ok(config.into())
@@ -1,445 +0,0 @@
//! Integration tests using cw-multi-test.
use cosmwasm_std::Addr;
use nym_name_service_common::{
response::{ConfigResponse, PagedNamesListResponse},
Address, NameEntry, NymName, RegisteredName,
};
use crate::{
constants::NAME_DEFAULT_RETRIEVAL_LIMIT,
error::NameServiceError,
test_helpers::{fixture::name_entry, helpers::nyms, test_setup::TestSetup},
};
#[test]
fn instantiate_contract() {
TestSetup::new();
}
#[test]
fn query_config() {
assert_eq!(
TestSetup::new().query_config(),
ConfigResponse {
deposit_required: nyms(100),
}
);
}
#[test]
fn register_and_query_name() {
let mut setup = TestSetup::new();
assert_eq!(
setup.query_all(),
PagedNamesListResponse {
names: vec![],
per_page: NAME_DEFAULT_RETRIEVAL_LIMIT as usize,
start_next_after: None,
}
);
// Register a first name
let owner = Addr::unchecked("owner");
let name = NymName::new("steves-server").unwrap();
let nym_address = Address::new("nym-address");
assert_eq!(setup.contract_balance(), nyms(0));
assert_eq!(setup.balance(&owner), nyms(250));
setup.register(name.clone(), nym_address.clone(), owner.clone());
// Deposit is deposited to contract and deducted from owners's balance
assert_eq!(setup.contract_balance(), nyms(100));
assert_eq!(setup.balance(&owner), nyms(150));
// We can query the full name list
assert_eq!(
setup.query_all(),
PagedNamesListResponse {
names: vec![NameEntry {
name_id: 1,
name: RegisteredName {
address: nym_address.clone(),
name: name.clone(),
owner: owner.clone(),
block_height: 12345,
deposit: nyms(100),
},
}],
per_page: NAME_DEFAULT_RETRIEVAL_LIMIT as usize,
start_next_after: Some(1),
}
);
// ... and we can query by id
assert_eq!(
setup.query_id(1),
NameEntry {
name_id: 1,
name: RegisteredName {
address: nym_address.clone(),
name: name.clone(),
owner: owner.clone(),
block_height: 12345,
deposit: nyms(100),
},
}
);
// Register a second name
let owner2 = Addr::unchecked("owner2");
let name2 = NymName::new("another_server").unwrap();
let nym_address2 = Address::new("nymAddress2");
setup.register(name2.clone(), nym_address2.clone(), owner2.clone());
assert_eq!(setup.contract_balance(), nyms(200));
assert_eq!(
setup.query_all(),
PagedNamesListResponse {
names: vec![
name_entry(1, name, nym_address, owner),
name_entry(2, name2, nym_address2, owner2)
],
per_page: NAME_DEFAULT_RETRIEVAL_LIMIT as usize,
start_next_after: Some(2),
}
);
}
#[test]
fn cant_register_a_name_without_funds() {
let mut setup = TestSetup::new();
assert_eq!(setup.contract_balance(), nyms(0));
assert_eq!(setup.balance("owner"), nyms(250));
setup.register(
NymName::new("my_name").unwrap(),
Address::new("nymAddress"),
Addr::unchecked("owner"),
);
assert_eq!(setup.contract_balance(), nyms(100));
assert_eq!(setup.balance("owner"), nyms(150));
setup.register(
NymName::new("my_name2").unwrap(),
Address::new("nymAddress"),
Addr::unchecked("owner"),
);
assert_eq!(setup.contract_balance(), nyms(200));
assert_eq!(setup.balance("owner"), nyms(50));
let res = setup
.try_register(
NymName::new("my_name3").unwrap(),
Address::new("nymAddress"),
Addr::unchecked("owner"),
)
.unwrap_err();
assert_eq!(
res.downcast::<cosmwasm_std::StdError>().unwrap(),
cosmwasm_std::StdError::Overflow {
source: cosmwasm_std::OverflowError::new(
cosmwasm_std::OverflowOperation::Sub,
"50",
"100"
)
}
);
}
#[test]
fn delete_name() {
let mut setup = TestSetup::new();
setup.register(
NymName::new("my_name").unwrap(),
Address::new("nymAddress"),
Addr::unchecked("owner"),
);
assert_eq!(setup.contract_balance(), nyms(100));
assert_eq!(setup.balance("owner"), nyms(150));
assert!(!setup.query_all().names.is_empty());
setup.delete(1, Addr::unchecked("owner"));
// Deleting the name returns the deposit to the owner
assert_eq!(setup.contract_balance(), nyms(0));
assert_eq!(setup.balance("owner"), nyms(250));
assert!(setup.query_all().names.is_empty());
}
#[test]
fn only_owner_can_delete_name() {
let mut setup = TestSetup::new();
assert_eq!(setup.contract_balance(), nyms(0));
setup.register(
NymName::new("name").unwrap(),
Address::new("nymAddress"),
Addr::unchecked("owner"),
);
assert_eq!(setup.contract_balance(), nyms(100));
assert!(!setup.query_all().names.is_empty());
let delete_resp = setup
.try_delete(1, Addr::unchecked("not_owner"))
.unwrap_err();
assert_eq!(setup.contract_balance(), nyms(100));
assert_eq!(
delete_resp.downcast::<NameServiceError>().unwrap(),
NameServiceError::Unauthorized {
sender: Addr::unchecked("not_owner")
}
);
}
#[test]
fn cant_delete_name_that_does_not_exist() {
let mut setup = TestSetup::new();
setup.register(
NymName::new("foo").unwrap(),
Address::new("nymAddress"),
Addr::unchecked("owner"),
);
assert_eq!(setup.contract_balance(), nyms(100));
assert!(!setup.query_all().names.is_empty());
let delete_resp = setup.try_delete(0, Addr::unchecked("owner")).unwrap_err();
assert_eq!(setup.contract_balance(), nyms(100));
assert_eq!(
delete_resp.downcast::<NameServiceError>().unwrap(),
NameServiceError::NotFound { name_id: 0 }
);
let delete_resp = setup.try_delete(2, Addr::unchecked("owner")).unwrap_err();
assert_eq!(setup.contract_balance(), nyms(100));
assert_eq!(
delete_resp.downcast::<NameServiceError>().unwrap(),
NameServiceError::NotFound { name_id: 2 }
);
assert!(!setup.query_all().names.is_empty());
setup.delete(1, Addr::unchecked("owner"));
assert_eq!(setup.contract_balance(), nyms(0));
assert!(setup.query_all().names.is_empty());
}
#[test]
fn cant_register_the_same_name_multiple_times() {
let mut setup = TestSetup::new();
setup.register(
NymName::new("name").unwrap(),
Address::new("nymAddress"),
Addr::unchecked("owner"),
);
let resp = setup
.try_register(
NymName::new("name").unwrap(),
Address::new("nymAddress"),
Addr::unchecked("owner"),
)
.unwrap_err();
assert_eq!(
resp.downcast::<NameServiceError>().unwrap(),
NameServiceError::NameAlreadyRegistered {
name: NymName::new("name").unwrap()
}
);
}
#[test]
fn can_register_multiple_names_for_the_same_nym_address() {
let mut setup = TestSetup::new();
let name1 = NymName::new("name1").unwrap();
let name2 = NymName::new("name2").unwrap();
let address = Address::new("nymaddress");
let owner = Addr::unchecked("owner");
setup.register(name1.clone(), address.clone(), owner.clone());
setup.register(name2.clone(), address.clone(), owner.clone());
assert_eq!(
setup.query_all().names,
vec![
name_entry(1, name1, address.clone(), owner.clone()),
name_entry(2, name2, address, owner)
],
);
}
#[test]
fn register_multiple_names_and_deleting_by_name() {
let mut setup = TestSetup::new();
let owner1 = Addr::unchecked("wealthy_owner_1");
let owner2 = Addr::unchecked("wealthy_owner_2");
let nym_address1 = Address::new("nymaddress1");
let nym_address2 = Address::new("nymaddress2");
let name1 = NymName::new("name1").unwrap();
let name2 = NymName::new("name2").unwrap();
let name3 = NymName::new("name3").unwrap();
let name4 = NymName::new("name4").unwrap();
let name5 = NymName::new("name5").unwrap();
// We register the same address three times, but with different owners
assert_eq!(setup.contract_balance(), nyms(0));
assert_eq!(setup.balance(&owner1), nyms(1000));
setup.register(name1.clone(), nym_address1.clone(), owner1.clone());
setup.register(name2.clone(), nym_address1.clone(), owner1.clone());
setup.register(name3.clone(), nym_address2.clone(), owner1.clone());
setup.register(name4.clone(), nym_address1.clone(), owner2.clone());
setup.register(name5.clone(), nym_address2.clone(), owner2.clone());
assert_eq!(setup.contract_balance(), nyms(500));
assert_eq!(setup.balance(&owner1), nyms(700));
assert_eq!(
setup.query_all(),
PagedNamesListResponse {
names: vec![
name_entry(1, name1.clone(), nym_address1.clone(), owner1.clone()),
name_entry(2, name2.clone(), nym_address1.clone(), owner1.clone()),
name_entry(3, name3.clone(), nym_address2.clone(), owner1.clone()),
name_entry(4, name4.clone(), nym_address1.clone(), owner2.clone()),
name_entry(5, name5.clone(), nym_address2.clone(), owner2.clone()),
],
per_page: NAME_DEFAULT_RETRIEVAL_LIMIT as usize,
start_next_after: Some(5),
}
);
setup.delete_name(name1, owner1.clone());
assert_eq!(setup.contract_balance(), nyms(400));
assert_eq!(setup.balance(&owner1), nyms(800));
assert_eq!(
setup.query_all(),
PagedNamesListResponse {
names: vec![
name_entry(2, name2, nym_address1.clone(), owner1.clone()),
name_entry(3, name3, nym_address2.clone(), owner1),
name_entry(4, name4, nym_address1, owner2.clone()),
name_entry(5, name5, nym_address2, owner2),
],
per_page: NAME_DEFAULT_RETRIEVAL_LIMIT as usize,
start_next_after: Some(5),
}
);
}
#[test]
fn check_paging() {
let mut setup = TestSetup::new();
let owner1 = Addr::unchecked("wealthy_owner_1");
let owner2 = Addr::unchecked("wealthy_owner_2");
let nym_address1 = Address::new("nymAddress1");
let nym_address2 = Address::new("nymAddress2");
let name1 = NymName::new("name1").unwrap();
let name2 = NymName::new("name2").unwrap();
let name3 = NymName::new("name3").unwrap();
let name4 = NymName::new("name4").unwrap();
let name5 = NymName::new("name5").unwrap();
// We register the same address three times, but with different owners
assert_eq!(setup.contract_balance(), nyms(0));
assert_eq!(setup.balance(&owner1), nyms(1000));
setup.register(name1.clone(), nym_address1.clone(), owner1.clone());
setup.register(name2.clone(), nym_address1.clone(), owner1.clone());
setup.register(name3.clone(), nym_address2.clone(), owner1.clone());
setup.register(name4.clone(), nym_address1.clone(), owner2.clone());
setup.register(name5.clone(), nym_address2.clone(), owner2.clone());
assert_eq!(setup.contract_balance(), nyms(500));
assert_eq!(setup.balance(&owner1), nyms(700));
assert_eq!(
setup.query_all(),
PagedNamesListResponse {
names: vec![
name_entry(1, name1.clone(), nym_address1.clone(), owner1.clone()),
name_entry(2, name2.clone(), nym_address1.clone(), owner1.clone()),
name_entry(3, name3.clone(), nym_address2.clone(), owner1.clone()),
name_entry(4, name4.clone(), nym_address1.clone(), owner2.clone()),
name_entry(5, name5, nym_address2.clone(), owner2.clone()),
],
per_page: NAME_DEFAULT_RETRIEVAL_LIMIT as usize,
start_next_after: Some(5),
}
);
setup.delete_name(name1, owner1.clone());
assert_eq!(
setup.query_all_with_limit(Some(2), None),
PagedNamesListResponse {
names: vec![
name_entry(2, name2, nym_address1.clone(), owner1.clone()),
name_entry(3, name3.clone(), nym_address2.clone(), owner1.clone()),
],
per_page: 2,
start_next_after: Some(3),
}
);
assert_eq!(
setup.query_all_with_limit(Some(2), Some(2)),
PagedNamesListResponse {
names: vec![
name_entry(3, name3, nym_address2, owner1),
name_entry(4, name4, nym_address1, owner2),
],
per_page: 2,
start_next_after: Some(4),
}
);
}
#[test]
fn name_id_is_not_resused_when_deleting_and_then_adding_a_new_names() {
let mut setup = TestSetup::new();
setup.register(
NymName::new("myname1").unwrap(),
Address::new("nymAddress1"),
Addr::unchecked("owner1"),
);
setup.register(
NymName::new("myname2").unwrap(),
Address::new("nymAddress2"),
Addr::unchecked("owner2"),
);
setup.register(
NymName::new("myname3").unwrap(),
Address::new("nymAddress3"),
Addr::unchecked("owner3"),
);
setup.delete(1, Addr::unchecked("owner1"));
setup.delete(3, Addr::unchecked("owner3"));
assert_eq!(
setup.query_all().names,
vec![name_entry(
2,
NymName::new("myname2").unwrap(),
Address::new("nymAddress2"),
Addr::unchecked("owner2")
)]
);
setup.register(
NymName::new("myname4").unwrap(),
Address::new("nymAddress4"),
Addr::unchecked("owner4"),
);
assert_eq!(
setup.query_all().names,
vec![
name_entry(
2,
NymName::new("myname2").unwrap(),
Address::new("nymAddress2"),
Addr::unchecked("owner2")
),
name_entry(
4,
NymName::new("myname4").unwrap(),
Address::new("nymAddress4"),
Addr::unchecked("owner4")
)
]
);
}
@@ -0,0 +1,148 @@
use cosmwasm_std::Addr;
use nym_name_service_common::{response::PagedNamesListResponse, Address, NymName};
use rstest::rstest;
use crate::{
constants::NAME_DEFAULT_RETRIEVAL_LIMIT,
test_helpers::{fixture::new_name, helpers::nyms},
NameServiceError,
};
use super::test_setup::TestSetup;
#[rstest::fixture]
fn setup() -> TestSetup {
TestSetup::new()
}
#[rstest]
fn delete_name(mut setup: TestSetup) {
setup.sign_and_register(
&NymName::new("my_name").unwrap(),
&Address::new("address"),
&Addr::unchecked("owner"),
&nyms(100),
);
assert_eq!(setup.contract_balance(), nyms(100));
assert_eq!(setup.balance("owner"), nyms(150));
assert!(!setup.query_all().names.is_empty());
setup.delete(1, Addr::unchecked("owner"));
// Deleting the name returns the deposit to the owner
assert_eq!(setup.contract_balance(), nyms(0));
assert_eq!(setup.balance("owner"), nyms(250));
assert!(setup.query_all().names.is_empty());
}
#[rstest]
fn only_owner_can_delete_name(mut setup: TestSetup) {
assert_eq!(setup.contract_balance(), nyms(0));
setup.sign_and_register(
&NymName::new("name").unwrap(),
&Address::new("nymAddress"),
&Addr::unchecked("owner"),
&nyms(100),
);
assert_eq!(setup.contract_balance(), nyms(100));
assert!(!setup.query_all().names.is_empty());
let delete_resp = setup
.try_delete(1, Addr::unchecked("not_owner"))
.unwrap_err();
assert_eq!(setup.contract_balance(), nyms(100));
assert_eq!(
delete_resp.downcast::<NameServiceError>().unwrap(),
NameServiceError::Unauthorized {
sender: Addr::unchecked("not_owner")
}
);
}
#[rstest]
fn cant_delete_name_that_does_not_exist(mut setup: TestSetup) {
setup.sign_and_register(
&NymName::new("foo").unwrap(),
&Address::new("nymAddress"),
&Addr::unchecked("owner"),
&nyms(100),
);
assert_eq!(setup.contract_balance(), nyms(100));
assert!(!setup.query_all().names.is_empty());
let delete_resp = setup.try_delete(0, Addr::unchecked("owner")).unwrap_err();
assert_eq!(setup.contract_balance(), nyms(100));
assert_eq!(
delete_resp.downcast::<NameServiceError>().unwrap(),
NameServiceError::NotFound { name_id: 0 }
);
let delete_resp = setup.try_delete(2, Addr::unchecked("owner")).unwrap_err();
assert_eq!(setup.contract_balance(), nyms(100));
assert_eq!(
delete_resp.downcast::<NameServiceError>().unwrap(),
NameServiceError::NotFound { name_id: 2 }
);
assert!(!setup.query_all().names.is_empty());
setup.delete(1, Addr::unchecked("owner"));
assert_eq!(setup.contract_balance(), nyms(0));
assert!(setup.query_all().names.is_empty());
}
#[rstest]
fn register_multiple_names_and_deleting_by_name(mut setup: TestSetup) {
let owner1 = Addr::unchecked("wealthy_owner_1");
let owner2 = Addr::unchecked("wealthy_owner_2");
let address1 = Address::new("address1");
let address2 = Address::new("address2");
let name1 = NymName::new("name1").unwrap();
let name2 = NymName::new("name2").unwrap();
let name3 = NymName::new("name3").unwrap();
let name4 = NymName::new("name4").unwrap();
let name5 = NymName::new("name5").unwrap();
// We register the same address three times, but with different owners
assert_eq!(setup.contract_balance(), nyms(0));
assert_eq!(setup.balance(&owner1), nyms(1000));
let s1 = setup.sign_and_register(&name1, &address1, &owner1, &nyms(100));
let s2 = setup.sign_and_register(&name2, &address1, &owner1, &nyms(100));
let s3 = setup.sign_and_register(&name3, &address2, &owner1, &nyms(100));
let s4 = setup.sign_and_register(&name4, &address1, &owner2, &nyms(100));
let s5 = setup.sign_and_register(&name5, &address2, &owner2, &nyms(100));
assert_eq!(setup.contract_balance(), nyms(500));
assert_eq!(setup.balance(&owner1), nyms(700));
assert_eq!(
setup.query_all(),
PagedNamesListResponse {
names: vec![
new_name(1, &name1, &address1, &owner1, s1.identity_key()),
new_name(2, &name2, &address1, &owner1, s2.identity_key()),
new_name(3, &name3, &address2, &owner1, s3.identity_key()),
new_name(4, &name4, &address1, &owner2, s4.identity_key()),
new_name(5, &name5, &address2, &owner2, s5.identity_key()),
],
per_page: NAME_DEFAULT_RETRIEVAL_LIMIT as usize,
start_next_after: Some(5),
}
);
setup.delete_name(name1, owner1.clone());
assert_eq!(setup.contract_balance(), nyms(400));
assert_eq!(setup.balance(&owner1), nyms(800));
assert_eq!(
setup.query_all(),
PagedNamesListResponse {
names: vec![
new_name(2, &name2, &address1, &owner1, s2.identity_key()),
new_name(3, &name3, &address2, &owner1, s3.identity_key()),
new_name(4, &name4, &address1, &owner2, s4.identity_key()),
new_name(5, &name5, &address2, &owner2, s5.identity_key()),
],
per_page: NAME_DEFAULT_RETRIEVAL_LIMIT as usize,
start_next_after: Some(5),
}
);
}
@@ -0,0 +1,13 @@
//! Integration tests using cw-multi-test.
mod delete;
mod name_id;
mod query;
mod register;
mod test_name;
mod test_setup;
#[test]
fn instantiate_contract() {
test_setup::TestSetup::new();
}
@@ -0,0 +1,70 @@
use cosmwasm_std::Addr;
use nym_name_service_common::{Address, NymName};
use crate::test_helpers::{fixture::new_name, helpers::nyms};
use super::test_setup::TestSetup;
#[test]
fn name_id_is_not_resused_when_deleting_and_then_adding_a_new_names() {
let mut setup = TestSetup::new();
setup.sign_and_register(
&NymName::new("myname1").unwrap(),
&Address::new("nymAddress1"),
&Addr::unchecked("owner1"),
&nyms(100),
);
let s2 = setup.sign_and_register(
&NymName::new("myname2").unwrap(),
&Address::new("nymAddress2"),
&Addr::unchecked("owner2"),
&nyms(100),
);
setup.sign_and_register(
&NymName::new("myname3").unwrap(),
&Address::new("nymAddress3"),
&Addr::unchecked("owner3"),
&nyms(100),
);
setup.delete(1, Addr::unchecked("owner1"));
setup.delete(3, Addr::unchecked("owner3"));
assert_eq!(
setup.query_all().names,
vec![new_name(
2,
&NymName::new("myname2").unwrap(),
&Address::new("nymAddress2"),
&Addr::unchecked("owner2"),
s2.identity_key(),
)]
);
let s4 = setup.sign_and_register(
&NymName::new("myname4").unwrap(),
&Address::new("nymAddress4"),
&Addr::unchecked("owner4"),
&nyms(100),
);
assert_eq!(
setup.query_all().names,
vec![
new_name(
2,
&NymName::new("myname2").unwrap(),
&Address::new("nymAddress2"),
&Addr::unchecked("owner2"),
s2.identity_key(),
),
new_name(
4,
&NymName::new("myname4").unwrap(),
&Address::new("nymAddress4"),
&Addr::unchecked("owner4"),
s4.identity_key(),
)
]
);
}
@@ -0,0 +1,88 @@
use cosmwasm_std::Addr;
use nym_name_service_common::{
response::{ConfigResponse, PagedNamesListResponse},
Address, NymName,
};
use crate::{
constants::NAME_DEFAULT_RETRIEVAL_LIMIT,
test_helpers::{fixture::new_name, helpers::nyms},
};
use super::test_setup::TestSetup;
#[test]
fn query_config() {
assert_eq!(
TestSetup::new().query_config(),
ConfigResponse {
deposit_required: nyms(100),
}
);
}
#[test]
fn check_paging() {
let mut setup = TestSetup::new();
let owner1 = Addr::unchecked("wealthy_owner_1");
let owner2 = Addr::unchecked("wealthy_owner_2");
let address1 = Address::new("nymAddress1");
let address2 = Address::new("nymAddress2");
let name1 = NymName::new("name1").unwrap();
let name2 = NymName::new("name2").unwrap();
let name3 = NymName::new("name3").unwrap();
let name4 = NymName::new("name4").unwrap();
let name5 = NymName::new("name5").unwrap();
// We register the same address three times, but with different owners
assert_eq!(setup.contract_balance(), nyms(0));
assert_eq!(setup.balance(&owner1), nyms(1000));
let s1 = setup.sign_and_register(&name1, &address1, &owner1, &nyms(100));
let s2 = setup.sign_and_register(&name2, &address1, &owner1, &nyms(100));
let s3 = setup.sign_and_register(&name3, &address2, &owner1, &nyms(100));
let s4 = setup.sign_and_register(&name4, &address1, &owner2, &nyms(100));
let s5 = setup.sign_and_register(&name5, &address2, &owner2, &nyms(100));
assert_eq!(setup.contract_balance(), nyms(500));
assert_eq!(setup.balance(&owner1), nyms(700));
assert_eq!(
setup.query_all(),
PagedNamesListResponse {
names: vec![
new_name(1, &name1, &address1, &owner1, s1.identity_key()),
new_name(2, &name2, &address1, &owner1, s2.identity_key()),
new_name(3, &name3, &address2, &owner1, s3.identity_key()),
new_name(4, &name4, &address1, &owner2, s4.identity_key()),
new_name(5, &name5, &address2, &owner2, s5.identity_key()),
],
per_page: NAME_DEFAULT_RETRIEVAL_LIMIT as usize,
start_next_after: Some(5),
}
);
setup.delete_name(name1, owner1.clone());
assert_eq!(
setup.query_all_with_limit(Some(2), None),
PagedNamesListResponse {
names: vec![
new_name(2, &name2, &address1, &owner1, s2.identity_key()),
new_name(3, &name3, &address2, &owner1, s3.identity_key()),
],
per_page: 2,
start_next_after: Some(3),
}
);
assert_eq!(
setup.query_all_with_limit(Some(2), Some(2)),
PagedNamesListResponse {
names: vec![
new_name(3, &name3, &address2, &owner1, s3.identity_key()),
new_name(4, &name4, &address1, &owner2, s4.identity_key()),
],
per_page: 2,
start_next_after: Some(4),
}
);
}
@@ -0,0 +1,269 @@
use cosmwasm_std::Addr;
use nym_name_service_common::{
error::NameServiceError, response::PagedNamesListResponse, Address, NameDetails, NymName,
RegisteredName,
};
use rstest::rstest;
use crate::{
constants::NAME_DEFAULT_RETRIEVAL_LIMIT,
test_helpers::{fixture::new_name, helpers::nyms},
};
use super::test_setup::TestSetup;
#[rstest::fixture]
fn setup() -> TestSetup {
TestSetup::new()
}
#[rstest]
fn basic_register(mut setup: TestSetup) {
assert_eq!(
setup.query_all(),
PagedNamesListResponse {
names: vec![],
per_page: NAME_DEFAULT_RETRIEVAL_LIMIT as usize,
start_next_after: None,
}
);
// Register a first name
let owner = Addr::unchecked("owner");
let name = NymName::new("steves-server").unwrap();
let nym_address = Address::new("nym-address");
assert_eq!(setup.contract_balance(), nyms(0));
assert_eq!(setup.balance(&owner), nyms(250));
assert_eq!(setup.query_signing_nonce(owner.to_string()), 0);
let reg_name = setup.new_name(&name, &nym_address);
let payload = setup.payload_to_sign(&owner, &nyms(100), &reg_name.name);
let reg_name = reg_name.sign(payload);
setup.register(&reg_name, &owner);
// Deposit is deposited to contract and deducted from owners's balance
assert_eq!(setup.contract_balance(), nyms(100));
assert_eq!(setup.balance(&owner), nyms(150));
// The signing nonce has been incremented
assert_eq!(setup.query_signing_nonce(owner.to_string()), 1);
// We can query the full name list
assert_eq!(
setup.query_all(),
PagedNamesListResponse {
names: vec![RegisteredName {
id: 1,
name: NameDetails {
name: name.clone(),
address: nym_address.clone(),
identity_key: reg_name.identity_key().to_string(),
},
owner: owner.clone(),
block_height: 12345,
deposit: nyms(100),
}],
per_page: NAME_DEFAULT_RETRIEVAL_LIMIT as usize,
start_next_after: Some(1),
}
);
// ... and we can query by id
assert_eq!(
setup.query_id(1),
RegisteredName {
id: 1,
name: reg_name.details().clone(),
owner: owner.clone(),
block_height: 12345,
deposit: nyms(100),
}
);
// Register a second name
let owner2 = Addr::unchecked("owner2");
let name2 = NymName::new("another_server").unwrap();
let nym_address2 = Address::new("nymAddress2");
let reg_name2 = setup.new_signed_name(&name2, &nym_address2, &owner2, &nyms(100));
setup.register(&reg_name2, &owner2);
assert_eq!(setup.contract_balance(), nyms(200));
assert_eq!(
setup.query_all(),
PagedNamesListResponse {
names: vec![
new_name(1, &name, &nym_address, &owner, reg_name.identity_key()),
new_name(2, &name2, &nym_address2, &owner2, reg_name2.identity_key()),
],
per_page: NAME_DEFAULT_RETRIEVAL_LIMIT as usize,
start_next_after: Some(2),
}
);
}
#[rstest]
fn register_fails_when_owner_mismatch(mut setup: TestSetup) {
let owner = Addr::unchecked("owner");
let name = NymName::new("steves-server").unwrap();
let nym_address = Address::new("nym-address");
let reg_name = setup.new_signed_name(&name, &nym_address, &owner, &nyms(100));
let res = setup
.try_register(&reg_name, &Addr::unchecked("owner2"))
.unwrap_err();
assert_eq!(
res.downcast::<NameServiceError>().unwrap(),
NameServiceError::InvalidEd25519Signature
);
}
#[rstest]
fn signing_nonce_is_increased_when_registering(mut setup: TestSetup) {
let owner1 = Addr::unchecked("owner1");
let owner2 = Addr::unchecked("owner2");
assert_eq!(setup.query_signing_nonce(owner1.to_string()), 0);
assert_eq!(setup.query_signing_nonce(owner2.to_string()), 0);
setup.sign_and_register(
&NymName::new("myname1").unwrap(),
&Address::new("address1"),
&owner1,
&nyms(100),
);
assert_eq!(setup.query_signing_nonce(owner1.to_string()), 1);
assert_eq!(setup.query_signing_nonce(owner2.to_string()), 0);
setup.sign_and_register(
&NymName::new("myname2").unwrap(),
&Address::new("address2"),
&owner2,
&nyms(100),
);
assert_eq!(setup.query_signing_nonce(owner1.to_string()), 1);
assert_eq!(setup.query_signing_nonce(owner2.to_string()), 1);
setup.sign_and_register(
&NymName::new("myname3").unwrap(),
&Address::new("address3"),
&owner2,
&nyms(100),
);
assert_eq!(setup.query_signing_nonce(owner1.to_string()), 1);
assert_eq!(setup.query_signing_nonce(owner2.to_string()), 2);
}
#[rstest]
fn creating_two_names_in_a_row_without_announcing_fails(mut setup: TestSetup) {
let owner = Addr::unchecked("wealthy_owner_1");
let name1 = NymName::new("steves-server1").unwrap();
let name2 = NymName::new("steves-server2").unwrap();
let address1 = Address::new("nymAddress1");
let address2 = Address::new("nymAddress2");
let deposit = nyms(100);
let s1 = setup.new_signed_name(&name1, &address1, &owner, &deposit);
// This second name will be signed with the same nonce
let s2 = setup.new_signed_name(&name2, &address2, &owner, &deposit);
// Announce the first service works, and this increments the nonce
setup.register(&s1, &owner);
// Now the nonce has been incremented, and the signature will not match
let resp: NameServiceError = setup
.try_register(&s2, &owner)
.unwrap_err()
.downcast()
.unwrap();
assert_eq!(resp, NameServiceError::InvalidEd25519Signature,);
}
#[rstest]
fn cant_register_a_name_without_funds(mut setup: TestSetup) {
assert_eq!(setup.contract_balance(), nyms(0));
assert_eq!(setup.balance("owner"), nyms(250));
let name1 = setup.new_signed_name(
&NymName::new("my_name").unwrap(),
&Address::new("nymAddress"),
&Addr::unchecked("owner"),
&nyms(100),
);
setup.register(&name1, &Addr::unchecked("owner"));
assert_eq!(setup.contract_balance(), nyms(100));
assert_eq!(setup.balance("owner"), nyms(150));
let name2 = setup.new_signed_name(
&NymName::new("my_name2").unwrap(),
&Address::new("nymAddress"),
&Addr::unchecked("owner"),
&nyms(100),
);
setup.register(&name2, &Addr::unchecked("owner"));
assert_eq!(setup.contract_balance(), nyms(200));
assert_eq!(setup.balance("owner"), nyms(50));
let name3 = setup.new_signed_name(
&NymName::new("my_name3").unwrap(),
&Address::new("nymAddress"),
&Addr::unchecked("owner"),
&nyms(100),
);
let res = setup
.try_register(&name3, &Addr::unchecked("owner"))
.unwrap_err();
assert_eq!(
res.downcast::<cosmwasm_std::StdError>().unwrap(),
cosmwasm_std::StdError::Overflow {
source: cosmwasm_std::OverflowError::new(
cosmwasm_std::OverflowOperation::Sub,
"50",
"100"
)
}
);
}
#[rstest]
fn cant_register_the_same_name_multiple_times(mut setup: TestSetup) {
let name1 = setup.new_signed_name(
&NymName::new("name").unwrap(),
&Address::new("nymAddress"),
&Addr::unchecked("owner"),
&nyms(100),
);
setup.register(&name1, &Addr::unchecked("owner"));
let resp = setup
.try_register(&name1, &Addr::unchecked("owner"))
.unwrap_err();
assert_eq!(
resp.downcast::<NameServiceError>().unwrap(),
NameServiceError::NameAlreadyRegistered {
name: NymName::new("name").unwrap()
}
);
}
#[rstest]
fn can_register_multiple_names_for_the_same_nym_address(mut setup: TestSetup) {
let name1 = NymName::new("name1").unwrap();
let name2 = NymName::new("name2").unwrap();
let address = Address::new("nymaddress");
let owner = Addr::unchecked("owner");
let reg_name1 = setup.new_signed_name(&name1, &address, &owner, &nyms(100));
setup.register(&reg_name1, &owner);
let reg_name2 = setup.new_signed_name(&name2, &address, &owner, &nyms(100));
setup.register(&reg_name2, &owner);
assert_eq!(
setup.query_all().names,
vec![
new_name(1, &name1, &address, &owner, reg_name1.identity_key()),
new_name(2, &name2, &address, &owner, reg_name2.identity_key()),
],
);
}
//
@@ -0,0 +1,75 @@
use nym_contracts_common::{signing::MessageSignature, IdentityKey};
use nym_crypto::asymmetric::identity;
use nym_name_service_common::{
signing_types::SignableNameRegisterMsg, Address, NameDetails, NymName,
};
use rand_chacha::ChaCha20Rng;
use crate::test_helpers::signing::ed25519_sign_message;
pub struct TestName {
pub name: NameDetails,
pub keys: identity::KeyPair,
pub rng: ChaCha20Rng,
}
impl TestName {
pub fn new(rng: &mut ChaCha20Rng, name: NymName, address: Address) -> Self {
let keys = identity::KeyPair::new(rng);
let name = NameDetails {
name,
address,
identity_key: keys.public_key().to_base58_string(),
};
Self {
name,
keys,
rng: rng.clone(),
}
}
pub fn identity_key(&self) -> &IdentityKey {
&self.name.identity_key
}
pub fn details(&self) -> &NameDetails {
&self.name
}
pub fn sign(self, payload: SignableNameRegisterMsg) -> SignedTestName {
let owner_signature = ed25519_sign_message(payload, self.keys.private_key());
SignedTestName {
name: self.name,
keys: self.keys,
owner_signature,
}
}
}
impl From<TestName> for NameDetails {
fn from(test_name: TestName) -> Self {
test_name.name
}
}
pub struct SignedTestName {
pub name: NameDetails,
pub keys: identity::KeyPair,
pub owner_signature: MessageSignature,
}
impl SignedTestName {
pub fn identity_key(&self) -> &IdentityKey {
&self.name.identity_key
}
pub fn details(&self) -> &NameDetails {
&self.name
}
}
impl From<SignedTestName> for NameDetails {
fn from(signed_name: SignedTestName) -> Self {
signed_name.name
}
}
@@ -1,13 +1,18 @@
use cosmwasm_std::{coins, Addr, Coin, Uint128};
use cw_multi_test::{App, AppBuilder, AppResponse, ContractWrapper, Executor};
use nym_contracts_common::signing::Nonce;
use nym_name_service_common::{
msg::{ExecuteMsg, InstantiateMsg, QueryMsg},
response::{ConfigResponse, PagedNamesListResponse},
Address, NameEntry, NameId, NymName,
signing_types::{construct_name_register_sign_payload, SignableNameRegisterMsg},
Address, NameDetails, NameId, NymName, RegisteredName,
};
use rand_chacha::ChaCha20Rng;
use serde::de::DeserializeOwned;
use crate::test_helpers::helpers::get_app_attribute;
use crate::test_helpers::helpers::{get_app_attribute, test_rng};
use super::test_name::{SignedTestName, TestName};
const DENOM: &str = "unym";
const ADDRESSES: &[&str] = &[
@@ -19,6 +24,7 @@ const WEALTHY_ADDRESSES: &[&str] = &["wealthy_owner_1", "wealthy_owner_2"];
pub struct TestSetup {
app: App,
addr: Addr,
rng: ChaCha20Rng,
}
impl Default for TestSetup {
@@ -44,7 +50,8 @@ impl TestSetup {
let code = ContractWrapper::new(crate::execute, crate::instantiate, crate::query);
let code_id = app.store_code(Box::new(code));
let addr = Self::instantiate(&mut app, code_id);
TestSetup { app, addr }
let rng = test_rng();
TestSetup { app, addr, rng }
}
fn instantiate(app: &mut App, code_id: u64) -> Addr {
@@ -76,7 +83,7 @@ impl TestSetup {
self.query(&QueryMsg::Config {})
}
pub fn query_id(&self, name_id: NameId) -> NameEntry {
pub fn query_id(&self, name_id: NameId) -> RegisteredName {
self.query(&QueryMsg::NameId { name_id })
}
@@ -92,16 +99,48 @@ impl TestSetup {
self.query(&QueryMsg::All { limit, start_after })
}
pub fn query_signing_nonce(&self, address: String) -> Nonce {
self.query(&QueryMsg::SigningNonce { address })
}
pub fn new_name(&mut self, name: &NymName, address: &Address) -> TestName {
TestName::new(&mut self.rng, name.clone(), address.clone())
}
pub fn payload_to_sign(
&mut self,
owner: &Addr,
deposit: &Coin,
name: &NameDetails,
) -> SignableNameRegisterMsg {
let nonce = self.query_signing_nonce(owner.to_string());
construct_name_register_sign_payload(nonce, owner.clone(), deposit.clone(), name.clone())
}
pub fn new_signed_name(
&mut self,
name: &NymName,
address: &Address,
owner: &Addr,
deposit: &Coin,
) -> SignedTestName {
let name = self.new_name(name, address);
let payload = self.payload_to_sign(owner, deposit, name.details());
name.sign(payload)
}
pub fn try_register(
&mut self,
name: NymName,
address: Address,
owner: Addr,
name: &SignedTestName,
owner: &Addr,
) -> anyhow::Result<AppResponse> {
self.app.execute_contract(
owner,
owner.clone(),
self.addr.clone(),
&ExecuteMsg::Register { name, address },
&ExecuteMsg::Register {
name: name.name.clone(),
owner_signature: name.owner_signature.clone(),
},
&[Coin {
denom: DENOM.to_string(),
amount: Uint128::new(100),
@@ -109,8 +148,8 @@ impl TestSetup {
)
}
pub fn register(&mut self, name: NymName, address: Address, owner: Addr) -> AppResponse {
let resp = self.try_register(name, address, owner).unwrap();
pub fn register(&mut self, name: &SignedTestName, owner: &Addr) -> AppResponse {
let resp = self.try_register(name, owner).unwrap();
assert_eq!(
get_app_attribute(&resp, "wasm-register", "action"),
"register"
@@ -118,6 +157,19 @@ impl TestSetup {
resp
}
// Convenience function for creating a new signed name, and regsitering it
pub fn sign_and_register(
&mut self,
name: &NymName,
address: &Address,
owner: &Addr,
deposit: &Coin,
) -> SignedTestName {
let signed_name = self.new_signed_name(name, address, owner, deposit);
self.register(&signed_name, owner);
signed_name
}
pub fn try_delete(&mut self, name_id: NameId, owner: Addr) -> anyhow::Result<AppResponse> {
self.app.execute_contract(
owner,
+2 -3
View File
@@ -3,16 +3,15 @@
#![warn(clippy::expect_used)]
#![warn(clippy::unwrap_used)]
use crate::error::Result;
pub use nym_name_service_common::error::{NameServiceError, Result};
use nym_name_service_common::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg};
#[cfg(not(feature = "library"))]
use cosmwasm_std::entry_point;
use cosmwasm_std::{Binary, Deps, DepsMut, Env, MessageInfo, Response};
use error::NameServiceError;
mod contract;
mod error;
mod state;
pub mod constants;
+1 -1
View File
@@ -1,7 +1,7 @@
use cosmwasm_std::{Addr, Deps, DepsMut};
use cw_controllers::Admin;
use crate::{constants::ADMIN_KEY, error::Result};
use crate::{constants::ADMIN_KEY, Result};
const ADMIN: Admin = Admin::new(ADMIN_KEY);
+1 -1
View File
@@ -3,7 +3,7 @@ use cw_storage_plus::Item;
use nym_name_service_common::response::ConfigResponse;
use serde::{Deserialize, Serialize};
use crate::{constants::CONFIG_KEY, error::Result};
use crate::{constants::CONFIG_KEY, Result};
const CONFIG: Item<Config> = Item::new(CONFIG_KEY);
+2
View File
@@ -2,7 +2,9 @@ pub mod admin;
pub mod config;
pub mod name_id_counter;
pub mod names;
pub mod nonce;
pub(crate) use admin::{assert_admin, set_admin};
pub(crate) use config::{deposit_required, load_config, save_config, Config};
pub(crate) use name_id_counter::next_name_id_counter;
pub(crate) use nonce::{get_signing_nonce, increment_signing_nonce};
@@ -2,7 +2,7 @@ use cosmwasm_std::Storage;
use cw_storage_plus::Item;
use nym_name_service_common::NameId;
use crate::{constants::NAME_ID_COUNTER_KEY, error::Result};
use crate::{constants::NAME_ID_COUNTER_KEY, Result};
const NAME_ID_COUNTER: Item<NameId> = Item::new(NAME_ID_COUNTER_KEY);
@@ -16,33 +16,56 @@ pub(crate) fn next_name_id_counter(store: &mut dyn Storage) -> Result<NameId> {
#[cfg(test)]
mod tests {
use nym_name_service_common::NameEntry;
use cosmwasm_std::Addr;
use nym_name_service_common::RegisteredName;
use crate::test_helpers::{
assert::assert_names,
fixture::name_fixture_name,
helpers::{delete_name_id, instantiate_test_contract, register_name},
helpers::{nyms, test_rng},
transactions::{delete_name_id, instantiate_test_contract, register_name},
};
#[test]
fn get_next_name_id() {
let mut deps = instantiate_test_contract();
let mut rng = test_rng();
assert_eq!(register_name(deps.as_mut(), &name_fixture_name("foo")), 1);
assert_names(
deps.as_ref(),
&[NameEntry::new(1, name_fixture_name("foo"))],
);
assert_eq!(register_name(deps.as_mut(), &name_fixture_name("bar")), 2);
assert_eq!(register_name(deps.as_mut(), &name_fixture_name("baz")), 3);
let (id1, name1) = register_name(deps.as_mut(), &mut rng, "foo", "addr1", "steve");
let (id2, name2) = register_name(deps.as_mut(), &mut rng, "bar", "addr2", "steve");
let (id3, name3) = register_name(deps.as_mut(), &mut rng, "baz", "addr3", "steve");
assert_eq!(id1, 1);
assert_eq!(id2, 2);
assert_eq!(id3, 3);
assert_eq!(name1.name.as_str(), "foo");
assert_eq!(name1.address.as_str(), "addr1");
assert_eq!(name2.name.as_str(), "bar");
assert_eq!(name2.address.as_str(), "addr2");
assert_eq!(name3.name.as_str(), "baz");
assert_eq!(name3.address.as_str(), "addr3");
assert_names(
deps.as_ref(),
&[
NameEntry::new(1, name_fixture_name("foo")),
NameEntry::new(2, name_fixture_name("bar")),
NameEntry::new(3, name_fixture_name("baz")),
RegisteredName {
id: 1,
name: name1,
owner: Addr::unchecked("steve"),
block_height: 12345,
deposit: nyms(100),
},
RegisteredName {
id: 2,
name: name2,
owner: Addr::unchecked("steve"),
block_height: 12345,
deposit: nyms(100),
},
RegisteredName {
id: 3,
name: name3,
owner: Addr::unchecked("steve"),
block_height: 12345,
deposit: nyms(100),
},
],
);
}
@@ -50,34 +73,28 @@ mod tests {
#[test]
fn deleted_name_id_is_not_reused() {
let mut deps = instantiate_test_contract();
let mut rng = test_rng();
// Register two names
assert_eq!(register_name(deps.as_mut(), &name_fixture_name("one")), 1);
assert_eq!(register_name(deps.as_mut(), &name_fixture_name("two")), 2);
assert_names(
deps.as_ref(),
&[
NameEntry::new(1, name_fixture_name("one")),
NameEntry::new(2, name_fixture_name("two")),
],
);
let (_, name1) = register_name(deps.as_mut(), &mut rng, "one", "sdfjkhsdfhr", "steve");
register_name(deps.as_mut(), &mut rng, "two", "suereljer", "steve");
// Delete the last entry
delete_name_id(deps.as_mut(), 2, "steve");
assert_names(
deps.as_ref(),
&[NameEntry::new(1, name_fixture_name("one"))],
&[RegisteredName {
id: 1,
name: name1,
owner: Addr::unchecked("steve"),
block_height: 12345,
deposit: nyms(100),
}],
);
// Create a third entry. The index should not reuse the previous entry that we just
// deleted.
assert_eq!(register_name(deps.as_mut(), &name_fixture_name("two")), 3);
assert_names(
deps.as_ref(),
&[
NameEntry::new(1, name_fixture_name("one")),
NameEntry::new(3, name_fixture_name("two")),
],
);
let (id3, _) = register_name(deps.as_mut(), &mut rng, "three", "ufd", "steve");
assert_eq!(id3, 3);
}
}
+159 -222
View File
@@ -8,7 +8,7 @@ use crate::{
NAMES_ADDRESS_IDX_NAMESPACE, NAMES_NAME_IDX_NAMESPACE, NAMES_OWNER_IDX_NAMESPACE,
NAMES_PK_NAMESPACE, NAME_DEFAULT_RETRIEVAL_LIMIT, NAME_MAX_RETRIEVAL_LIMIT,
},
error::{NameServiceError, Result},
NameServiceError, Result,
};
struct NameIndex<'a> {
@@ -28,9 +28,9 @@ impl<'a> IndexList<RegisteredName> for NameIndex<'a> {
fn names<'a>() -> IndexedMap<'a, NameId, RegisteredName, NameIndex<'a>> {
let indexes = NameIndex {
name: UniqueIndex::new(|d| d.name.to_string(), NAMES_NAME_IDX_NAMESPACE),
name: UniqueIndex::new(|d| d.name.name.to_string(), NAMES_NAME_IDX_NAMESPACE),
address: MultiIndex::new(
|d| d.address.to_string(),
|d| d.name.address.to_string(),
NAMES_PK_NAMESPACE,
NAMES_ADDRESS_IDX_NAMESPACE,
),
@@ -43,19 +43,29 @@ fn names<'a>() -> IndexedMap<'a, NameId, RegisteredName, NameIndex<'a>> {
IndexedMap::new(NAMES_PK_NAMESPACE, indexes)
}
pub fn save(store: &mut dyn Storage, new_name: &RegisteredName) -> Result<NameId> {
let name_id = super::next_name_id_counter(store)?;
pub fn save(store: &mut dyn Storage, new_name: &RegisteredName) -> Result<()> {
let name_id = new_name.id;
names().save(store, name_id, new_name)?;
Ok(name_id)
Ok(())
}
#[cfg(test)]
pub fn save_all(state: &mut dyn Storage, names: &[RegisteredName]) -> Result<Vec<NameId>> {
let mut ids = vec![];
pub fn save_all(state: &mut dyn Storage, names: &[RegisteredName]) -> Result<()> {
for name in names {
ids.push(save(state, name)?);
save(state, name)?;
}
Ok(ids)
Ok(())
}
pub fn remove_id(store: &mut dyn Storage, name_id: NameId) -> Result<()> {
Ok(names().remove(store, name_id)?)
}
#[cfg(test)]
pub fn remove_name(store: &mut dyn Storage, name: NymName) -> Result<NameId> {
let registered_name = load_name(store, &name)?;
remove_id(store, registered_name.id)?;
Ok(registered_name.id)
}
pub fn has_name_id(store: &dyn Storage, name_id: NameId) -> bool {
@@ -66,23 +76,6 @@ pub fn has_name(store: &dyn Storage, name: &NymName) -> bool {
load_name(store, name).is_ok()
}
// Get the (key, name) entry for a given name
pub fn load_name_entry(store: &dyn Storage, name: &NymName) -> Result<(NameId, RegisteredName)> {
names()
.idx
.name
.range(store, None, None, Order::Ascending)
.find(|entry| {
if let Ok(entry) = entry {
&entry.1.name == name
} else {
false
}
})
.ok_or(NameServiceError::NameNotFound { name: name.clone() })?
.map_err(NameServiceError::from)
}
pub fn load_id(store: &dyn Storage, name_id: NameId) -> Result<RegisteredName> {
names().load(store, name_id).map_err(|err| match err {
StdError::NotFound { .. } => NameServiceError::NotFound { name_id },
@@ -99,45 +92,33 @@ pub fn load_name(store: &dyn Storage, name: &NymName) -> Result<RegisteredName>
.ok_or(NameServiceError::NameNotFound { name: name.clone() })
}
pub fn load_address(
store: &dyn Storage,
address: &Address,
) -> Result<Vec<(NameId, RegisteredName)>> {
pub fn load_address(store: &dyn Storage, address: &Address) -> Result<Vec<RegisteredName>> {
names()
.idx
.address
.prefix(address.to_string())
.range(store, None, None, Order::Ascending)
.take(MAX_NUMBER_OF_NAMES_FOR_ADDRESS as usize)
.map(|res| res.map(|(_, name)| name))
.collect::<StdResult<Vec<_>>>()
.map_err(NameServiceError::from)
}
pub fn load_owner(store: &dyn Storage, owner: Addr) -> Result<Vec<(NameId, RegisteredName)>> {
pub fn load_owner(store: &dyn Storage, owner: Addr) -> Result<Vec<RegisteredName>> {
names()
.idx
.owner
.prefix(owner)
.range(store, None, None, Order::Ascending)
.take(MAX_NUMBER_OF_NAMES_PER_OWNER as usize)
.map(|res| res.map(|(_, name)| name))
.collect::<StdResult<Vec<_>>>()
.map_err(NameServiceError::from)
}
pub fn remove_id(store: &mut dyn Storage, name_id: NameId) -> Result<()> {
Ok(names().remove(store, name_id)?)
}
#[cfg(test)]
pub fn remove_name(store: &mut dyn Storage, name: NymName) -> Result<NameId> {
let name_info = load_name_entry(store, &name)?;
remove_id(store, name_info.0)?;
Ok(name_info.0)
}
#[derive(Debug, PartialEq)]
pub struct PagedLoad {
pub names: Vec<(NameId, RegisteredName)>,
pub names: Vec<RegisteredName>,
pub limit: usize,
pub start_next_after: Option<NameId>,
}
@@ -156,9 +137,10 @@ pub fn load_all_paged(
let names = names()
.range(store, start, None, Order::Ascending)
.take(limit)
.map(|res| res.map(|(_, name)| name))
.collect::<StdResult<Vec<_>>>()?;
let start_next_after = names.last().map(|name| name.0);
let start_next_after = names.last().map(|name| name.id);
Ok(PagedLoad {
names,
@@ -169,8 +151,6 @@ pub fn load_all_paged(
#[cfg(test)]
mod tests {
use std::iter::zip;
use cosmwasm_std::{
testing::{MockApi, MockQuerier},
MemoryStorage, OwnedDeps,
@@ -178,11 +158,11 @@ mod tests {
use rstest::rstest;
use crate::{
error::NameServiceError,
test_helpers::{
fixture::{name_fixture, name_fixture_full},
helpers::instantiate_test_contract,
transactions::instantiate_test_contract,
},
NameServiceError,
};
use super::*;
@@ -197,64 +177,55 @@ mod tests {
#[rstest::fixture]
fn uniq_names() -> Vec<RegisteredName> {
vec![
name_fixture_full("one", "address_one", "owner_one"),
name_fixture_full("two", "address_two", "owner_two"),
name_fixture_full("three", "address_three", "owner_three"),
name_fixture_full(1, "one", "address_one", "owner_one"),
name_fixture_full(2, "two", "address_two", "owner_two"),
name_fixture_full(3, "three", "address_three", "owner_three"),
]
}
#[rstest::fixture]
fn overlapping_addresses() -> Vec<RegisteredName> {
vec![
name_fixture_full("one", "address_one", "owner_one"),
name_fixture_full("two", "address_two", "owner_two"),
name_fixture_full("three", "address_two", "owner_three"),
name_fixture_full(1, "one", "address_one", "owner_one"),
name_fixture_full(2, "two", "address_two", "owner_two"),
name_fixture_full(3, "three", "address_two", "owner_three"),
]
}
#[rstest::fixture]
fn overlapping_owners() -> Vec<RegisteredName> {
vec![
name_fixture_full("one", "address_one", "owner_one"),
name_fixture_full("two", "address_two", "owner_two"),
name_fixture_full("three", "address_three", "owner_two"),
name_fixture_full(1, "one", "address_one", "owner_one"),
name_fixture_full(2, "two", "address_two", "owner_two"),
name_fixture_full(3, "three", "address_three", "owner_two"),
]
}
fn assert_not_registered(store: &dyn Storage, names: Vec<RegisteredName>, ids: Vec<NameId>) {
let names: Vec<(NameId, RegisteredName)> = zip(ids, names).collect();
fn assert_not_registered(store: &dyn Storage, names: Vec<RegisteredName>) {
let loaded = load_all_paged(store, None, None).unwrap();
for (id, name) in &names {
assert!(!has_name_id(store, *id));
assert!(!has_name(store, &name.name));
assert!(!loaded.names.iter().any(|(i, _)| i == id));
assert!(!loaded.names.iter().any(|(_, n)| n == name));
for name in &names {
assert!(!has_name_id(store, name.id));
assert!(!has_name(store, name.entry()));
assert!(!loaded.names.iter().any(|l_name| l_name.id == name.id));
assert!(!loaded.names.iter().any(|l_name| l_name == name));
}
}
fn assert_registered(store: &dyn Storage, names: Vec<RegisteredName>, ids: Vec<NameId>) {
assert!(names.len() == ids.len());
let names: Vec<(NameId, RegisteredName)> = zip(ids, names).collect();
fn assert_registered(store: &dyn Storage, names: Vec<RegisteredName>) {
let loaded = load_all_paged(store, None, None).unwrap();
for (id, name) in &names {
assert!(has_name_id(store, *id));
assert!(has_name(store, &name.name));
assert!(loaded.names.iter().filter(|(i, _)| i == id).count() == 1);
assert!(loaded.names.iter().filter(|(_, n)| n == name).count() == 1);
for name in &names {
assert!(has_name_id(store, name.id));
assert!(has_name(store, name.entry()));
assert!(loaded.names.iter().filter(|n| n == &name).count() == 1);
}
}
fn assert_only_these_registered(
store: &dyn Storage,
names: Vec<RegisteredName>,
ids: Vec<NameId>,
) {
let last_id = *ids.last().unwrap();
let names: Vec<(NameId, RegisteredName)> = zip(ids, names).collect();
for (id, name) in &names {
assert!(has_name_id(store, *id));
assert!(has_name(store, &name.name));
fn assert_only_these_registered(store: &dyn Storage, names: Vec<RegisteredName>) {
for name in &names {
assert!(has_name_id(store, name.id));
assert!(has_name(store, name.entry()));
}
let last_id = names.last().unwrap().id;
assert_eq!(
load_all_paged(store, None, None).unwrap(),
PagedLoad {
@@ -267,102 +238,107 @@ mod tests {
#[rstest]
fn single_basic_save_works(mut deps: TestDeps) {
save(deps.as_mut().storage, &name_fixture()).unwrap();
save(deps.as_mut().storage, &name_fixture(1)).unwrap();
}
#[rstest]
fn save_same_name_twice_fails(mut deps: TestDeps) {
save(deps.as_mut().storage, &name_fixture()).unwrap();
save(deps.as_mut().storage, &name_fixture(1)).unwrap();
assert!(matches!(
save(deps.as_mut().storage, &name_fixture()).unwrap_err(),
save(deps.as_mut().storage, &name_fixture(2)).unwrap_err(),
NameServiceError::Std(StdError::GenericErr { .. })
));
}
#[rstest]
fn remove_id_works(mut deps: TestDeps) {
save(deps.as_mut().storage, &name_fixture(1)).unwrap();
assert!(has_name_id(&deps.storage, 1));
assert!(has_name(&deps.storage, name_fixture(1).entry()));
remove_id(deps.as_mut().storage, 1).unwrap();
assert!(!has_name_id(&deps.storage, 1));
assert!(!has_name(&deps.storage, name_fixture(1).entry()));
}
#[rstest]
fn remove_name_works(mut deps: TestDeps) {
save(deps.as_mut().storage, &name_fixture(1)).unwrap();
assert!(has_name_id(&deps.storage, 1));
assert!(has_name(&deps.storage, name_fixture(1).entry()));
remove_name(deps.as_mut().storage, name_fixture(1).name.name).unwrap();
assert!(!has_name_id(&deps.storage, 1));
assert!(!has_name(&deps.storage, name_fixture(1).entry()));
}
#[rstest]
fn has_name_works(mut deps: TestDeps) {
assert!(!has_name(&deps.storage, &name_fixture().name));
save(deps.as_mut().storage, &name_fixture()).unwrap();
assert!(has_name(&deps.storage, &name_fixture().name));
assert!(!has_name(&deps.storage, name_fixture(1).entry()));
save(deps.as_mut().storage, &name_fixture(1)).unwrap();
assert!(has_name(&deps.storage, name_fixture(1).entry()));
}
#[rstest]
fn has_name_id_works(mut deps: TestDeps) {
assert!(!has_name_id(&deps.storage, 1));
save(deps.as_mut().storage, &name_fixture()).unwrap();
save(deps.as_mut().storage, &name_fixture(1)).unwrap();
assert!(has_name_id(&deps.storage, 1));
}
#[rstest]
fn has_name_id_with_incorrect_id_fails(mut deps: TestDeps) {
assert!(!has_name_id(&deps.storage, 2));
save(deps.as_mut().storage, &name_fixture()).unwrap();
save(deps.as_mut().storage, &name_fixture(1)).unwrap();
assert!(!has_name_id(&deps.storage, 2));
}
#[rstest]
fn load_name_entry_works(mut deps: TestDeps) {
assert_eq!(
load_name_entry(deps.as_ref().storage, &name_fixture().name).unwrap_err(),
NameServiceError::NameNotFound {
name: name_fixture().name
}
);
save(deps.as_mut().storage, &name_fixture()).unwrap();
assert_eq!(
load_name_entry(deps.as_ref().storage, &name_fixture().name).unwrap(),
(1, name_fixture())
);
}
#[rstest]
fn load_id_works(mut deps: TestDeps) {
assert_eq!(
load_id(deps.as_ref().storage, 1).unwrap_err(),
NameServiceError::NotFound { name_id: 1 }
);
save(deps.as_mut().storage, &name_fixture()).unwrap();
assert_eq!(load_id(deps.as_ref().storage, 1).unwrap(), name_fixture(),);
save(deps.as_mut().storage, &name_fixture(1)).unwrap();
assert_eq!(load_id(deps.as_ref().storage, 1).unwrap(), name_fixture(1),);
}
#[rstest]
fn load_name_works(mut deps: TestDeps) {
assert_eq!(
load_name(deps.as_ref().storage, &name_fixture().name).unwrap_err(),
load_name(deps.as_ref().storage, name_fixture(1).entry()).unwrap_err(),
NameServiceError::NameNotFound {
name: name_fixture().name
name: name_fixture(1).name.name,
}
);
save(deps.as_mut().storage, &name_fixture()).unwrap();
save(deps.as_mut().storage, &name_fixture(1)).unwrap();
assert_eq!(
load_name(deps.as_ref().storage, &name_fixture().name).unwrap(),
name_fixture(),
load_name(deps.as_ref().storage, name_fixture(1).entry()).unwrap(),
name_fixture(1),
);
}
#[rstest]
fn load_address_works(mut deps: TestDeps) {
assert_eq!(
load_address(deps.as_ref().storage, &name_fixture().address).unwrap(),
load_address(deps.as_ref().storage, &name_fixture(1).name.address).unwrap(),
vec![],
);
save(deps.as_mut().storage, &name_fixture()).unwrap();
save(deps.as_mut().storage, &name_fixture(1)).unwrap();
assert_eq!(
load_address(deps.as_ref().storage, &name_fixture().address).unwrap(),
vec![(1, name_fixture())],
load_address(deps.as_ref().storage, &name_fixture(1).name.address).unwrap(),
vec![name_fixture(1)],
);
}
#[rstest]
fn load_owner_works(mut deps: TestDeps) {
assert_eq!(
load_owner(deps.as_ref().storage, name_fixture().owner).unwrap(),
load_owner(deps.as_ref().storage, name_fixture(1).owner).unwrap(),
vec![],
);
save(deps.as_mut().storage, &name_fixture()).unwrap();
save(deps.as_mut().storage, &name_fixture(1)).unwrap();
assert_eq!(
load_owner(deps.as_ref().storage, name_fixture().owner).unwrap(),
vec![(1, name_fixture())],
load_owner(deps.as_ref().storage, name_fixture(1).owner).unwrap(),
vec![name_fixture(1)],
);
}
@@ -380,60 +356,19 @@ mod tests {
assert_eq!(
load_all_paged(&deps.storage, None, None).unwrap(),
PagedLoad {
names: vec![(1, uniq_names[0].clone())],
names: vec![uniq_names[0].clone()],
limit: NAME_DEFAULT_RETRIEVAL_LIMIT as usize,
start_next_after: Some(1),
}
);
}
#[rstest]
fn remove_id_works(mut deps: TestDeps) {
save(deps.as_mut().storage, &name_fixture()).unwrap();
assert!(has_name_id(&deps.storage, 1));
assert!(has_name(&deps.storage, &name_fixture().name));
remove_id(deps.as_mut().storage, 1).unwrap();
assert!(!has_name_id(&deps.storage, 1));
assert!(!has_name(&deps.storage, &name_fixture().name));
}
#[rstest]
fn remove_name_works(mut deps: TestDeps) {
save(deps.as_mut().storage, &name_fixture()).unwrap();
assert!(has_name_id(&deps.storage, 1));
assert!(has_name(&deps.storage, &name_fixture().name));
remove_name(deps.as_mut().storage, name_fixture().name).unwrap();
assert!(!has_name_id(&deps.storage, 1));
assert!(!has_name(&deps.storage, &name_fixture().name));
}
#[rstest]
fn save_set_of_unique_names_works(mut deps: TestDeps, uniq_names: Vec<RegisteredName>) {
let num = uniq_names.len() as NameId;
let ids = (1..=num).collect::<Vec<NameId>>();
assert_not_registered(&deps.storage, uniq_names.clone(), ids.clone());
let saved_ids = save_all(deps.as_mut().storage, &uniq_names).unwrap();
assert_eq!(saved_ids, ids);
assert_registered(&deps.storage, uniq_names.clone(), ids.clone());
assert_only_these_registered(&deps.storage, uniq_names, ids);
}
#[rstest]
fn save_set_of_unique_names_generates_ids(mut deps: TestDeps, uniq_names: Vec<RegisteredName>) {
let num = uniq_names.len() as NameId;
let ids = save_all(deps.as_mut().storage, &uniq_names).unwrap();
assert_eq!(ids, (1..=num).collect::<Vec<NameId>>());
}
#[rstest]
fn load_name_entry_for_unique_set_works(mut deps: TestDeps, uniq_names: Vec<RegisteredName>) {
assert_not_registered(&deps.storage, uniq_names.clone());
save_all(deps.as_mut().storage, &uniq_names).unwrap();
for (id, name) in uniq_names.iter().enumerate() {
assert_eq!(
load_name_entry(deps.as_ref().storage, &name.name).unwrap(),
(id as NameId + 1, name.clone()),
);
}
assert_registered(&deps.storage, uniq_names.clone());
assert_only_these_registered(&deps.storage, uniq_names);
}
#[rstest]
@@ -441,7 +376,7 @@ mod tests {
save_all(deps.as_mut().storage, &uniq_names).unwrap();
for name in uniq_names {
assert_eq!(
load_name(deps.as_ref().storage, &name.name).unwrap(),
load_name(deps.as_ref().storage, name.entry()).unwrap(),
name.clone(),
);
}
@@ -452,17 +387,14 @@ mod tests {
mut deps: TestDeps,
uniq_names: Vec<RegisteredName>,
) {
let ids = save_all(deps.as_mut().storage, &uniq_names).unwrap();
remove_id(deps.as_mut().storage, ids[1]).unwrap();
save_all(deps.as_mut().storage, &uniq_names).unwrap();
remove_id(deps.as_mut().storage, 2).unwrap();
assert_eq!(
load_all_paged(deps.as_ref().storage, None, None).unwrap(),
PagedLoad {
names: vec![
(1, name_fixture_full("one", "address_one", "owner_one")),
(
3,
name_fixture_full("three", "address_three", "owner_three")
),
name_fixture_full(1, "one", "address_one", "owner_one"),
name_fixture_full(3, "three", "address_three", "owner_three"),
],
limit: NAME_DEFAULT_RETRIEVAL_LIMIT as usize,
start_next_after: Some(3),
@@ -476,16 +408,13 @@ mod tests {
uniq_names: Vec<RegisteredName>,
) {
save_all(deps.as_mut().storage, &uniq_names).unwrap();
remove_name(deps.as_mut().storage, uniq_names[1].name.clone()).unwrap();
remove_name(deps.as_mut().storage, uniq_names[1].entry().clone()).unwrap();
assert_eq!(
load_all_paged(deps.as_ref().storage, None, None).unwrap(),
PagedLoad {
names: vec![
(1, name_fixture_full("one", "address_one", "owner_one")),
(
3,
name_fixture_full("three", "address_three", "owner_three")
),
name_fixture_full(1, "one", "address_one", "owner_one"),
name_fixture_full(3, "three", "address_three", "owner_three"),
],
limit: NAME_DEFAULT_RETRIEVAL_LIMIT as usize,
start_next_after: Some(3),
@@ -498,9 +427,9 @@ mod tests {
mut deps: TestDeps,
overlapping_addresses: Vec<RegisteredName>,
) {
let ids = save_all(deps.as_mut().storage, &overlapping_addresses).unwrap();
assert_registered(&deps.storage, overlapping_addresses.clone(), ids.clone());
assert_only_these_registered(&deps.storage, overlapping_addresses, ids);
save_all(deps.as_mut().storage, &overlapping_addresses).unwrap();
assert_registered(&deps.storage, overlapping_addresses.clone());
assert_only_these_registered(&deps.storage, overlapping_addresses);
}
#[rstest]
@@ -513,9 +442,9 @@ mod tests {
load_all_paged(deps.as_ref().storage, None, None).unwrap(),
PagedLoad {
names: vec![
(1, name_fixture_full("one", "address_one", "owner_one")),
(2, name_fixture_full("two", "address_two", "owner_two")),
(3, name_fixture_full("three", "address_two", "owner_three")),
name_fixture_full(1, "one", "address_one", "owner_one"),
name_fixture_full(2, "two", "address_two", "owner_two"),
name_fixture_full(3, "three", "address_two", "owner_three"),
],
limit: NAME_DEFAULT_RETRIEVAL_LIMIT as usize,
start_next_after: Some(3),
@@ -524,8 +453,8 @@ mod tests {
assert_eq!(
load_address(deps.as_ref().storage, &Address::new("address_two")).unwrap(),
vec![
(2, name_fixture_full("two", "address_two", "owner_two")),
(3, name_fixture_full("three", "address_two", "owner_three")),
name_fixture_full(2, "two", "address_two", "owner_two"),
name_fixture_full(3, "three", "address_two", "owner_three"),
]
);
}
@@ -535,14 +464,14 @@ mod tests {
mut deps: TestDeps,
overlapping_addresses: Vec<RegisteredName>,
) {
let ids = save_all(deps.as_mut().storage, &overlapping_addresses).unwrap();
remove_id(deps.as_mut().storage, ids[1]).unwrap();
save_all(deps.as_mut().storage, &overlapping_addresses).unwrap();
remove_id(deps.as_mut().storage, 2).unwrap();
assert_eq!(
load_all_paged(deps.as_ref().storage, None, None).unwrap(),
PagedLoad {
names: vec![
(1, name_fixture_full("one", "address_one", "owner_one")),
(3, name_fixture_full("three", "address_two", "owner_three")),
name_fixture_full(1, "one", "address_one", "owner_one"),
name_fixture_full(3, "three", "address_two", "owner_three"),
],
limit: NAME_DEFAULT_RETRIEVAL_LIMIT as usize,
start_next_after: Some(3),
@@ -556,13 +485,17 @@ mod tests {
overlapping_addresses: Vec<RegisteredName>,
) {
save_all(deps.as_mut().storage, &overlapping_addresses).unwrap();
remove_name(deps.as_mut().storage, overlapping_addresses[1].name.clone()).unwrap();
remove_name(
deps.as_mut().storage,
overlapping_addresses[1].name.name.clone(),
)
.unwrap();
assert_eq!(
load_all_paged(deps.as_ref().storage, None, None).unwrap(),
PagedLoad {
names: vec![
(1, name_fixture_full("one", "address_one", "owner_one")),
(3, name_fixture_full("three", "address_two", "owner_three")),
name_fixture_full(1, "one", "address_one", "owner_one"),
name_fixture_full(3, "three", "address_two", "owner_three"),
],
limit: NAME_DEFAULT_RETRIEVAL_LIMIT as usize,
start_next_after: Some(3),
@@ -575,9 +508,9 @@ mod tests {
mut deps: TestDeps,
overlapping_owners: Vec<RegisteredName>,
) {
let ids = save_all(deps.as_mut().storage, &overlapping_owners).unwrap();
assert_registered(&deps.storage, overlapping_owners.clone(), ids.clone());
assert_only_these_registered(&deps.storage, overlapping_owners, ids);
save_all(deps.as_mut().storage, &overlapping_owners).unwrap();
assert_registered(&deps.storage, overlapping_owners.clone());
assert_only_these_registered(&deps.storage, overlapping_owners);
}
#[rstest]
@@ -590,9 +523,9 @@ mod tests {
load_all_paged(deps.as_ref().storage, None, None).unwrap(),
PagedLoad {
names: vec![
(1, name_fixture_full("one", "address_one", "owner_one")),
(2, name_fixture_full("two", "address_two", "owner_two")),
(3, name_fixture_full("three", "address_three", "owner_two")),
name_fixture_full(1, "one", "address_one", "owner_one"),
name_fixture_full(2, "two", "address_two", "owner_two"),
name_fixture_full(3, "three", "address_three", "owner_two"),
],
limit: NAME_DEFAULT_RETRIEVAL_LIMIT as usize,
start_next_after: Some(3),
@@ -601,8 +534,8 @@ mod tests {
assert_eq!(
load_owner(deps.as_ref().storage, Addr::unchecked("owner_two")).unwrap(),
vec![
(2, name_fixture_full("two", "address_two", "owner_two")),
(3, name_fixture_full("three", "address_three", "owner_two")),
name_fixture_full(2, "two", "address_two", "owner_two"),
name_fixture_full(3, "three", "address_three", "owner_two"),
]
);
}
@@ -612,16 +545,16 @@ mod tests {
mut deps: TestDeps,
overlapping_owners: Vec<RegisteredName>,
) {
let ids = save_all(deps.as_mut().storage, &overlapping_owners).unwrap();
assert_registered(&deps.storage, overlapping_owners.clone(), ids.clone());
assert_only_these_registered(&deps.storage, overlapping_owners, ids);
save_all(deps.as_mut().storage, &overlapping_owners).unwrap();
assert_registered(&deps.storage, overlapping_owners.clone());
assert_only_these_registered(&deps.storage, overlapping_owners);
remove_id(deps.as_mut().storage, 2).unwrap();
assert_eq!(
load_all_paged(deps.as_ref().storage, None, None).unwrap(),
PagedLoad {
names: vec![
(1, name_fixture_full("one", "address_one", "owner_one")),
(3, name_fixture_full("three", "address_three", "owner_two")),
name_fixture_full(1, "one", "address_one", "owner_one"),
name_fixture_full(3, "three", "address_three", "owner_two"),
],
limit: NAME_DEFAULT_RETRIEVAL_LIMIT as usize,
start_next_after: Some(3),
@@ -634,16 +567,16 @@ mod tests {
mut deps: TestDeps,
overlapping_owners: Vec<RegisteredName>,
) {
let ids = save_all(deps.as_mut().storage, &overlapping_owners).unwrap();
assert_registered(&deps.storage, overlapping_owners.clone(), ids.clone());
assert_only_these_registered(&deps.storage, overlapping_owners.clone(), ids);
remove_name(deps.as_mut().storage, overlapping_owners[1].name.clone()).unwrap();
save_all(deps.as_mut().storage, &overlapping_owners).unwrap();
assert_registered(&deps.storage, overlapping_owners.clone());
assert_only_these_registered(&deps.storage, overlapping_owners.clone());
remove_name(deps.as_mut().storage, overlapping_owners[1].entry().clone()).unwrap();
assert_eq!(
load_all_paged(deps.as_ref().storage, None, None).unwrap(),
PagedLoad {
names: vec![
(1, name_fixture_full("one", "address_one", "owner_one")),
(3, name_fixture_full("three", "address_three", "owner_two")),
name_fixture_full(1, "one", "address_one", "owner_one"),
name_fixture_full(3, "three", "address_three", "owner_two"),
],
limit: NAME_DEFAULT_RETRIEVAL_LIMIT as usize,
start_next_after: Some(3),
@@ -658,8 +591,8 @@ mod tests {
load_all_paged(&deps.storage, Some(2), None).unwrap(),
PagedLoad {
names: vec![
(1, name_fixture_full("one", "address_one", "owner_one")),
(2, name_fixture_full("two", "address_two", "owner_two")),
name_fixture_full(1, "one", "address_one", "owner_one"),
name_fixture_full(2, "two", "address_two", "owner_two"),
],
limit: 2,
start_next_after: Some(2),
@@ -668,10 +601,12 @@ mod tests {
assert_eq!(
load_all_paged(&deps.storage, Some(1), Some(2)).unwrap(),
PagedLoad {
names: vec![(
names: vec![name_fixture_full(
3,
name_fixture_full("three", "address_three", "owner_three")
),],
"three",
"address_three",
"owner_three"
)],
limit: 1,
start_next_after: Some(3),
}
@@ -679,10 +614,12 @@ mod tests {
assert_eq!(
load_all_paged(&deps.storage, Some(2), Some(2)).unwrap(),
PagedLoad {
names: vec![(
names: vec![name_fixture_full(
3,
name_fixture_full("three", "address_three", "owner_three")
),],
"three",
"address_three",
"owner_three"
)],
limit: 2,
start_next_after: Some(3),
}
+71
View File
@@ -0,0 +1,71 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::{constants::SIGNING_NONCES_NAMESPACE, Result};
use cosmwasm_std::{Addr, Storage};
use cw_storage_plus::Map;
use nym_contracts_common::signing::Nonce;
pub const NONCES: Map<'_, Addr, Nonce> = Map::new(SIGNING_NONCES_NAMESPACE);
pub fn get_signing_nonce(storage: &dyn Storage, address: Addr) -> Result<Nonce> {
let nonce = NONCES.may_load(storage, address)?.unwrap_or(0);
Ok(nonce)
}
fn update_signing_nonce(storage: &mut dyn Storage, address: Addr, value: Nonce) -> Result<()> {
NONCES
.save(storage, address, &value)
.map_err(|err| err.into())
}
pub fn increment_signing_nonce(storage: &mut dyn Storage, address: Addr) -> Result<()> {
// get the current nonce
let nonce = get_signing_nonce(storage, address.clone())?;
// increment it for the next use
update_signing_nonce(storage, address, nonce + 1)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test_helpers::transactions::instantiate_test_contract;
use cosmwasm_std::{
testing::{MockApi, MockQuerier},
MemoryStorage, OwnedDeps,
};
use rstest::rstest;
type TestDeps = OwnedDeps<MemoryStorage, MockApi, MockQuerier>;
#[rstest::fixture]
fn deps() -> TestDeps {
instantiate_test_contract()
}
fn addr(s: &str) -> Addr {
Addr::unchecked(s)
}
#[rstest]
fn getting_signing_nonce_doesnt_increment_it(deps: TestDeps) {
assert_eq!(get_signing_nonce(&deps.storage, addr("gunnar")).unwrap(), 0);
assert_eq!(get_signing_nonce(&deps.storage, addr("gunnar")).unwrap(), 0);
}
#[rstest]
fn increment_works(mut deps: TestDeps) {
assert_eq!(get_signing_nonce(&deps.storage, addr("gunnar")).unwrap(), 0);
increment_signing_nonce(&mut deps.storage, addr("gunnar")).unwrap();
assert_eq!(get_signing_nonce(&deps.storage, addr("gunnar")).unwrap(), 1);
}
#[rstest]
fn incrementing_is_independent(mut deps: TestDeps) {
increment_signing_nonce(&mut deps.storage, addr("gunnar")).unwrap();
assert_eq!(get_signing_nonce(&deps.storage, addr("gunnar")).unwrap(), 1);
assert_eq!(get_signing_nonce(&deps.storage, addr("bjorn")).unwrap(), 0);
}
}
@@ -1,11 +1,12 @@
use cosmwasm_std::{from_binary, testing::mock_env, Addr, Coin, Deps};
use nym_contracts_common::signing::Nonce;
use nym_name_service_common::{
msg::QueryMsg,
response::{ConfigResponse, PagedNamesListResponse},
NameEntry, NameId,
NameId, RegisteredName,
};
use crate::{constants::NAME_DEFAULT_RETRIEVAL_LIMIT, error::NameServiceError};
use crate::{constants::NAME_DEFAULT_RETRIEVAL_LIMIT, NameServiceError};
pub fn assert_config(deps: Deps, admin: &Addr, deposit_required: Coin) {
crate::state::assert_admin(deps, admin).unwrap();
@@ -14,10 +15,10 @@ pub fn assert_config(deps: Deps, admin: &Addr, deposit_required: Coin) {
assert_eq!(config, ConfigResponse { deposit_required });
}
pub fn assert_names(deps: Deps, expected_names: &[NameEntry]) {
pub fn assert_names(deps: Deps, expected_names: &[RegisteredName]) {
let res = crate::contract::query(deps, mock_env(), QueryMsg::all()).unwrap();
let names: PagedNamesListResponse = from_binary(&res).unwrap();
let start_next_after = expected_names.iter().last().map(|s| s.name_id);
let start_next_after = expected_names.iter().last().map(|s| s.id);
assert_eq!(
names,
PagedNamesListResponse {
@@ -28,16 +29,16 @@ pub fn assert_names(deps: Deps, expected_names: &[NameEntry]) {
);
}
pub fn assert_name(deps: Deps, expected_name: &NameEntry) {
pub fn assert_name(deps: Deps, expected_name: &RegisteredName) {
let res = crate::contract::query(
deps,
mock_env(),
QueryMsg::NameId {
name_id: expected_name.name_id,
name_id: expected_name.id,
},
)
.unwrap();
let names: NameEntry = from_binary(&res).unwrap();
let names: RegisteredName = from_binary(&res).unwrap();
assert_eq!(&names, expected_name);
}
@@ -63,3 +64,16 @@ pub fn assert_not_found(deps: Deps, expected_id: NameId) {
}
));
}
pub fn assert_current_nonce(deps: Deps, address: &Addr, expected_nonce: Nonce) {
let res = crate::contract::query(
deps,
mock_env(),
QueryMsg::SigningNonce {
address: address.to_string(),
},
)
.unwrap();
let nonce: Nonce = from_binary(&res).unwrap();
assert_eq!(nonce, expected_nonce);
}
@@ -1,29 +1,103 @@
use cosmwasm_std::Addr;
use nym_name_service_common::{Address, NameEntry, NameId, NymName, RegisteredName};
use cosmwasm_std::{Addr, Coin, DepsMut};
use nym_contracts_common::{signing::MessageSignature, IdentityKeyRef};
use nym_crypto::asymmetric::identity;
use nym_name_service_common::{Address, NameDetails, NameId, NymName, RegisteredName};
use rand_chacha::rand_core::{CryptoRng, RngCore};
use super::helpers::nyms;
use super::{
helpers::nyms,
signing::{ed25519_sign_message, name_register_sign_payload},
};
pub fn name_fixture_full(name: &str, nym_address: &str, owner: &str) -> RegisteredName {
pub fn new_name(
name_id: NameId,
name: &NymName,
address: &Address,
owner: &Addr,
identity_key: IdentityKeyRef,
) -> RegisteredName {
RegisteredName {
name: NymName::new(name).unwrap(),
address: Address::new(nym_address),
owner: Addr::unchecked(owner),
id: name_id,
name: NameDetails {
name: name.clone(),
address: address.clone(),
identity_key: identity_key.to_string(),
},
owner: owner.clone(),
block_height: 12345,
deposit: nyms(100),
}
}
pub fn name_fixture() -> RegisteredName {
name_fixture_full("my-service", "client_id.client_key@gateway_id", "steve")
pub fn name_fixture(id: NameId) -> RegisteredName {
new_name(
id,
&NymName::new("my-service").unwrap(),
&Address::new("client_id.client_key@gateway_id"),
&Addr::unchecked("steve"),
"identity",
)
}
pub fn name_fixture_name(name: &str) -> RegisteredName {
name_fixture_full(name, "client_id.client_key@gateway_id", "steve")
#[allow(unused)]
pub fn name_fixture_with_name(id: NameId, name: &str, address: &str) -> RegisteredName {
new_name(
id,
&NymName::new(name).unwrap(),
&Address::new(address),
&Addr::unchecked("steve"),
"identity",
)
}
pub fn name_entry(name_id: NameId, name: NymName, address: Address, owner: Addr) -> NameEntry {
NameEntry {
name_id,
name: name_fixture_full(name.as_str(), address.as_str(), owner.as_str()),
}
pub fn name_fixture_full(id: NameId, name: &str, address: &str, owner: &str) -> RegisteredName {
new_name(
id,
&NymName::new(name).unwrap(),
&Address::new(address),
&Addr::unchecked(owner),
"identity",
)
}
// Create a new name, using a correctly generted identity key
pub fn new_name_details<R>(
rng: &mut R,
name: &str,
nym_address: &str,
) -> (NameDetails, identity::KeyPair)
where
R: RngCore + CryptoRng,
{
let keypair = identity::KeyPair::new(rng);
(
NameDetails {
name: NymName::new(name).unwrap(),
address: Address::new(nym_address),
identity_key: keypair.public_key().to_base58_string(),
},
keypair,
)
}
// Create a new service, with a correctly generated identity key, and sign it
pub fn new_name_details_with_sign<R>(
deps: DepsMut<'_>,
rng: &mut R,
name: &str,
nym_address: &str,
owner: &str,
deposit: Coin,
) -> (NameDetails, MessageSignature)
where
R: RngCore + CryptoRng,
{
// Service
let (name, keypair) = new_name_details(rng, name, nym_address);
// Sign
let sign_msg = name_register_sign_payload(deps.as_ref(), owner, name.clone(), deposit);
let owner_signature = ed25519_sign_message(sign_msg, keypair.private_key());
(name, owner_signature)
}
@@ -1,19 +1,16 @@
use cosmwasm_std::{
coin, coins,
testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier},
Coin, DepsMut, Event, MemoryStorage, OwnedDeps, Response,
};
use cosmwasm_std::{Coin, Event, Response};
use cw_multi_test::AppResponse;
use nym_name_service_common::{
events::{NameEventType, NAME_ID},
msg::{ExecuteMsg, InstantiateMsg},
NameId, NymName, RegisteredName,
};
use rand_chacha::{rand_core::SeedableRng, ChaCha20Rng};
pub fn nyms(amount: u64) -> Coin {
Coin::new(amount.into(), "unym")
}
pub fn test_rng() -> ChaCha20Rng {
let dummy_seed = [42u8; 32];
ChaCha20Rng::from_seed(dummy_seed)
}
pub fn get_event_types(response: &Response, event_type: &str) -> Vec<Event> {
response
.events
@@ -55,53 +52,3 @@ pub fn get_app_attribute(response: &AppResponse, event_type: &str, key: &str) ->
.value
.clone()
}
#[allow(unused)]
pub fn get_app_attributes(response: &AppResponse, event_type: &str, key: &str) -> Vec<String> {
get_app_event_types(response, event_type)
.iter()
.map(|ev| {
ev.attributes
.iter()
.find(|attr| attr.key == key)
.unwrap()
.value
.clone()
})
.collect::<Vec<_>>()
}
pub fn instantiate_test_contract() -> OwnedDeps<MemoryStorage, MockApi, MockQuerier> {
let mut deps = mock_dependencies();
let msg = InstantiateMsg {
deposit_required: coin(100, "unym"),
};
let env = mock_env();
let info = mock_info("creator", &[]);
let res = crate::instantiate(deps.as_mut(), env, info, msg).unwrap();
assert_eq!(res.messages.len(), 0);
deps
}
pub fn register_name(deps: DepsMut<'_>, name: &RegisteredName) -> NameId {
let msg: ExecuteMsg = name.clone().into();
let info = mock_info(name.owner.as_str(), &coins(100, "unym"));
let res = crate::execute(deps, mock_env(), info, msg).unwrap();
let name_id: NameId = get_attribute(&res, &NameEventType::Register.to_string(), NAME_ID)
.parse()
.unwrap();
name_id
}
pub fn delete_name_id(deps: DepsMut<'_>, name_id: NameId, owner: &str) {
let msg = ExecuteMsg::DeleteId { name_id };
let info = mock_info(owner, &[]);
crate::execute(deps, mock_env(), info, msg).unwrap();
}
#[allow(unused)]
pub fn delete_name(deps: DepsMut<'_>, name: NymName, owner: &str) {
let msg = ExecuteMsg::DeleteName { name };
let info = mock_info(owner, &[]);
crate::execute(deps, mock_env(), info, msg).unwrap();
}
@@ -1,4 +1,5 @@
pub mod assert;
pub mod fixture;
pub mod helpers;
pub mod test_setup;
pub mod signing;
pub mod transactions;
@@ -0,0 +1,39 @@
use cosmwasm_std::{Addr, Coin, Deps};
use nym_contracts_common::signing::{
MessageSignature, SignableMessage, SigningAlgorithm, SigningPurpose,
};
use nym_crypto::asymmetric::identity;
use nym_name_service_common::{
signing_types::{construct_name_register_sign_payload, SignableNameRegisterMsg},
NameDetails,
};
use serde::Serialize;
use crate::state;
pub fn name_register_sign_payload(
deps: Deps<'_>,
owner: &str,
name: NameDetails,
deposit: Coin,
) -> SignableNameRegisterMsg {
let owner = Addr::unchecked(owner);
let nonce = state::get_signing_nonce(deps.storage, owner.clone()).unwrap();
construct_name_register_sign_payload(nonce, owner, deposit, name)
}
pub fn ed25519_sign_message<T: Serialize + SigningPurpose>(
message: SignableMessage<T>,
private_key: &identity::PrivateKey,
) -> MessageSignature {
match message.algorithm {
SigningAlgorithm::Ed25519 => {
let plaintext = message.to_plaintext().unwrap();
let signature = private_key.sign(&plaintext);
MessageSignature::from(signature.to_bytes().as_ref())
}
SigningAlgorithm::Secp256k1 => {
unimplemented!()
}
}
}
@@ -0,0 +1,72 @@
use cosmwasm_std::{
coin,
testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier},
DepsMut, MemoryStorage, OwnedDeps,
};
use nym_name_service_common::{
events::{NameEventType, NAME_ID},
msg::{ExecuteMsg, InstantiateMsg},
NameDetails, NameId, NymName,
};
use rand_chacha::rand_core::{CryptoRng, RngCore};
use super::helpers::{get_attribute, nyms};
pub fn instantiate_test_contract() -> OwnedDeps<MemoryStorage, MockApi, MockQuerier> {
let mut deps = mock_dependencies();
let msg = InstantiateMsg {
deposit_required: coin(100, "unym"),
};
let env = mock_env();
let info = mock_info("creator", &[]);
let res = crate::instantiate(deps.as_mut(), env, info, msg).unwrap();
assert_eq!(res.messages.len(), 0);
deps
}
pub fn register_name<R>(
mut deps: DepsMut<'_>,
rng: &mut R,
name: &str,
nym_address: &str,
owner: &str,
) -> (NameId, NameDetails)
where
R: RngCore + CryptoRng,
{
let deposit = nyms(100);
let (name, owner_signature) = super::fixture::new_name_details_with_sign(
deps.branch(),
rng,
name,
nym_address,
owner,
deposit.clone(),
);
// Register
let msg = ExecuteMsg::Register {
name: name.clone(),
owner_signature,
};
let info = mock_info(owner, &[deposit]);
let res = crate::execute(deps, mock_env(), info, msg).unwrap();
let name_id: NameId = get_attribute(&res, &NameEventType::Register.to_string(), NAME_ID)
.parse()
.unwrap();
(name_id, name)
}
pub fn delete_name_id(deps: DepsMut<'_>, name_id: NameId, owner: &str) {
let msg = ExecuteMsg::DeleteId { name_id };
let info = mock_info(owner, &[]);
crate::execute(deps, mock_env(), info, msg).unwrap();
}
#[allow(unused)]
pub fn delete_name(deps: DepsMut<'_>, name: NymName, owner: &str) {
let msg = ExecuteMsg::DeleteName { name };
let info = mock_info(owner, &[]);
crate::execute(deps, mock_env(), info, msg).unwrap();
}
+1 -1
View File
@@ -50,7 +50,7 @@ As seen in the example above, the `mixnet::MixnetClientBuilder::new()` function
If you're integrating mixnet functionality into an existing app and want to integrate saving client configs and keys into your existing storage logic, you can manually perform the actions taken automatically above (`examples/manually_handle_keys_and_config.rs`)
```rust,noplayground
{{#include ../../../../sdk/rust/nym-sdk/examples/manually_handle_keys_and_config.rs}}
{{#include ../../../../sdk/rust/nym-sdk/examples/manually_handle_storage.rs}}
```
### Anonymous replies with SURBs
+1 -1
View File
@@ -1 +1 @@
16
18
+2 -8
View File
@@ -1,10 +1,4 @@
{
"packages": [
"ts-packages/*",
"nym-wallet",
"nym-connect/**",
"sdk/typescript/examples/docs",
"sdk/typescript/packages/**"
],
"version": "0.0.0"
"version": "0.0.0",
"useWorkspaces": true
}
+2 -2
View File
@@ -6,7 +6,7 @@ use nym_mixnet_contract_common::{
families::FamilyHead, GatewayBond, IdentityKey, Interval, MixId, MixNodeDetails,
RewardingParams,
};
use nym_name_service_common::NameEntry;
use nym_name_service_common::RegisteredName;
use nym_service_provider_directory_common::Service;
use std::collections::HashSet;
@@ -26,7 +26,7 @@ pub(crate) struct ValidatorCacheData {
pub(crate) mix_to_family: Cache<Vec<(IdentityKey, FamilyHead)>>,
pub(crate) service_providers: Cache<Vec<Service>>,
pub(crate) registered_names: Cache<Vec<NameEntry>>,
pub(crate) registered_names: Cache<Vec<RegisteredName>>,
}
impl ValidatorCacheData {
+3 -3
View File
@@ -5,7 +5,7 @@ use nym_mixnet_contract_common::{
families::FamilyHead, GatewayBond, IdentityKey, Interval, MixId, MixNodeBond, MixNodeDetails,
RewardingParams,
};
use nym_name_service_common::NameEntry;
use nym_name_service_common::RegisteredName;
use nym_service_provider_directory_common::Service;
use rocket::fairing::AdHoc;
use std::{
@@ -53,7 +53,7 @@ impl NymContractCache {
current_interval: Interval,
mix_to_family: Vec<(IdentityKey, FamilyHead)>,
services: Option<Vec<Service>>,
names: Option<Vec<NameEntry>>,
names: Option<Vec<RegisteredName>>,
) {
match time::timeout(Duration::from_millis(100), self.inner.write()).await {
Ok(mut cache) => {
@@ -303,7 +303,7 @@ impl NymContractCache {
}
}
pub(crate) async fn names(&self) -> Cache<Vec<NameEntry>> {
pub(crate) async fn names(&self) -> Cache<Vec<RegisteredName>> {
match time::timeout(Duration::from_millis(100), self.inner.read()).await {
Ok(cache) => cache.registered_names.clone(),
Err(err) => {
+1 -1
View File
@@ -1 +1 @@
14
18
+4
View File
@@ -3564,8 +3564,12 @@ name = "nym-name-service-common"
version = "0.1.0"
dependencies = [
"cosmwasm-std",
"cw-controllers",
"cw-utils",
"nym-contracts-common",
"schemars",
"serde",
"thiserror",
]
[[package]]
@@ -10,39 +10,58 @@ use nym_contracts_common::types::Percent;
static SERVICE_PROVIDER_WELLKNOWN_URL: &str =
"https://nymtech.net/.wellknown/connect/service-providers.json";
// List of network-requesters running with medium toggle enabled, for testing
static SERVICE_PROVIDER_WELLKNOWN_URL_MEDIUM: &str =
"https://nymtech.net/.wellknown/connect/service-providers-medium.json";
static HARBOUR_MASTER_URL: &str = "https://harbourmaster.nymtech.net/v1/services/?size=100";
static GATEWAYS_DETAILED_URL: &str =
"https://validator.nymtech.net/api/v1/status/gateways/detailed";
fn get_services_url() -> &'static str {
std::env::var("NYM_CONNECT_ENABLE_MEDIUM")
.is_ok()
.then(|| SERVICE_PROVIDER_WELLKNOWN_URL_MEDIUM)
.unwrap_or(SERVICE_PROVIDER_WELLKNOWN_URL)
}
#[tauri::command]
pub async fn get_services() -> Result<Vec<DirectoryServiceProvider>> {
log::trace!("Fetching services");
let services_res = reqwest::get(SERVICE_PROVIDER_WELLKNOWN_URL)
.await?
.json::<Vec<DirectoryService>>()
.await?;
log::trace!("Received: {:#?}", services_res);
let all_services = fetch_services().await?;
log::trace!("Received: {:#?}", all_services);
log::trace!("Fetching gateways");
let gateway_res = reqwest::get(GATEWAYS_DETAILED_URL)
.await?
.json::<Vec<GatewayBondAnnotated>>()
.await?;
log::trace!("Received: {:#?}", gateway_res);
// Early return if we're running with medium toggle enabled
if std::env::var("NYM_CONNECT_ENABLE_MEDIUM").is_ok() {
return Ok(all_services.into_iter().flat_map(|sp| sp.items).collect());
}
// TODO: get paged
log::trace!("Fetching active services");
let active_services = reqwest::get(HARBOUR_MASTER_URL)
.await?
.json::<PagedResult<HarbourMasterService>>()
.await?;
let active_services = fetch_active_services().await?;
log::trace!("Active: {:#?}", active_services);
let mut filtered: Vec<DirectoryService> = vec![];
let filtered_services = filter_out_inactive(all_services, active_services);
for service in &services_res {
let items = service
log::trace!("Fetching gateways");
let gateway_res = get_gateways_detailed().await?;
log::trace!("Received: {:#?}", gateway_res);
// Use only services that are active AND have a performance of >= 90%
let filtered_services_with_good_gateway =
filter_out_poor_gateways(filtered_services, gateway_res);
Ok(filtered_services_with_good_gateway)
}
fn filter_out_inactive(
services_res: Vec<DirectoryService>,
active_services: PagedResult<HarbourMasterService>,
) -> Vec<DirectoryService> {
let mut filtered: Vec<DirectoryService> = vec![];
for service_type in &services_res {
let items = service_type
.items
.clone()
.into_iter()
@@ -53,33 +72,48 @@ pub async fn get_services() -> Result<Vec<DirectoryServiceProvider>> {
.any(|active| active.service_provider_client_id == sp.address)
})
.collect_vec();
log::trace!("service = {} has {} items", service.id, items.len());
log::trace!("service = {} has {} items", service_type.id, items.len());
filtered.push(DirectoryService {
id: service.id.clone(),
description: service.description.clone(),
id: service_type.id.clone(),
description: service_type.description.clone(),
items,
})
}
filtered
}
fn filter_out_poor_gateways(
services: Vec<DirectoryService>,
gateway_res: Vec<GatewayBondAnnotated>,
) -> Vec<DirectoryServiceProvider> {
let perf_threshold = Percent::from_percentage_value(90).unwrap();
// Use only services that are active AND have a performance of >= 90%
let services_with_good_performance: Vec<DirectoryServiceProvider> = filtered
.iter_mut()
.fold(vec![], |mut acc, sp| {
acc.append(&mut sp.items);
acc
})
services
.into_iter()
.flat_map(|sp| sp.items)
.filter(|sp| {
gateway_res.iter().any(|gateway| {
gateway.gateway_bond.gateway.identity_key == sp.gateway
&& gateway.performance >= perf_threshold
})
})
.collect();
.collect()
}
Ok(services_with_good_performance)
async fn fetch_services() -> Result<Vec<DirectoryService>> {
let services_url = get_services_url();
let services_res = reqwest::get(services_url)
.await?
.json::<Vec<DirectoryService>>()
.await?;
Ok(services_res)
}
async fn fetch_active_services() -> Result<PagedResult<HarbourMasterService>> {
let active_services = reqwest::get(HARBOUR_MASTER_URL)
.await?
.json::<PagedResult<HarbourMasterService>>()
.await?;
Ok(active_services)
}
#[tauri::command]
+5 -8
View File
@@ -3,34 +3,31 @@ import { Divider, Typography } from '@mui/material';
import { Box } from '@mui/system';
const appsSchema = {
messagingApps: ['Telegram', 'Keybase'],
wallets: ['Blockstream', 'Electrum'],
messagingApps: ['Matrix', 'Telegram', 'Keybase'],
wallets: ['Monero', 'Blockstream', 'Electrum'],
};
export const CompatibleApps = () => (
<Box>
<Typography fontSize="small" color="grey.600" sx={{ mb: 2 }}>
<Typography fontWeight="bold" variant="body2" sx={{ mb: 2 }}>
Supported apps
</Typography>
<Typography color="nym.highlight" sx={{ mb: 2 }}>
Messaging apps
</Typography>
<Divider sx={{ mb: 2 }} />
<Box sx={{ mb: 4 }}>
<Box sx={{ mb: 2 }}>
{appsSchema.messagingApps.map((app) => (
<Typography variant="body2" color="grey.400" sx={{ mb: 2 }} key={app}>
{app}
</Typography>
))}
</Box>
<Divider sx={{ mb: 2 }} />
<Typography color="nym.highlight" sx={{ mb: 2 }}>
Wallets
</Typography>
<Divider sx={{ mb: 2 }} />
<Box sx={{ mb: 4 }}>
{appsSchema.wallets.map((wallet) => (
<Typography variant="body2" color="grey.400" sx={{ mb: 2 }} key={wallet}>
+1 -1
View File
@@ -1 +1 @@
14
18
+1 -1
View File
@@ -1 +1 @@
16
18
+4
View File
@@ -3086,8 +3086,12 @@ name = "nym-name-service-common"
version = "0.1.0"
dependencies = [
"cosmwasm-std",
"cw-controllers",
"cw-utils",
"nym-contracts-common",
"schemars",
"serde",
"thiserror",
]
[[package]]
+1 -1
View File
@@ -31,6 +31,7 @@
"@nymproject/mui-theme": "^1.0.0",
"@nymproject/react": "^1.0.0",
"@nymproject/types": "^1.0.0",
"@nymproject/sdk": "1",
"@storybook/react": "^6.5.15",
"@tauri-apps/api": "^1.2.0",
"@tauri-apps/tauri-forage": "^1.0.0-beta.2",
@@ -41,7 +42,6 @@
"lodash": "^4.17.21",
"notistack": "^2.0.3",
"npm-run-all": "^4.1.5",
"nym-client-wasm": "1.1.1",
"qrcode.react": "^1.0.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
@@ -1,6 +1,6 @@
import React from 'react';
import { TestStatus } from '@nymproject/sdk';
import { Divider, Typography } from '@mui/material';
import { TestStatus } from 'src/pages/bonding/node-settings/node-test/types';
import { ResultsCard, ResultsCardDetail } from './ResultsCard';
export const Packets = ({
@@ -1,7 +1,6 @@
import React from 'react';
import { TestStatus } from '@nymproject/sdk';
import { Grid } from '@mui/material';
import { TestStatus } from 'src/pages/bonding/node-settings/node-test/types';
import { Packets } from './Packets';
import { NodeScore } from './NodeScore';
import { Overview } from './Overview';
@@ -1,55 +0,0 @@
import { WasmGateway, WasmMixNode, WasmNymTopology } from 'nym-client-wasm';
export const createDummyTopology = () => {
const l1Mixnode = new WasmMixNode(
1,
'n1fzv4jc7fanl9s0qj02ge2ezk3kts545kjtek47',
'178.79.143.65',
1789,
'4Yr4qmEHd9sgsuQ83191FR2hD88RfsbMmB4tzhhZWriz',
'8ndjk5oZ6HxUZNScLJJ7hk39XtUqGexdKgW7hSX6kpWG',
1,
'1.10.0',
);
const l2Mixnode = new WasmMixNode(
2,
'n1z93z44vf8ssvdhujjvxcj4rd5e3lz0l60wdk70',
'109.74.197.180',
1789,
'7sVjiMrPYZrDWRujku9QLxgE8noT7NTgBAqizCsu7AoK',
'GepXwRnKZDd8x2nBWAajGGBVvF3mrpVMQBkgfrGuqRCN',
2,
'1.10.0',
);
const l3Mixnode = new WasmMixNode(
3,
'n1ptg680vnmef2cd8l0s9uyc4f0hgf3x8sed6w77',
'176.58.101.80',
1789,
'FoM5Mx9Pxk1g3zEqkS3APgtBeTtTo3M8k7Yu4bV6kK1R',
'DeYjrDC2AcQRVFshiKnbUo6bRvPyZ33QGYR2DLeFJ9qD',
3,
'1.10.0',
);
const gateway = new WasmGateway(
'n16evnn8glr0sham3matj8rg2s24m6x56ayk87ts',
'85.159.212.96',
1789,
9000,
'336yuXAeGEgedRfqTJZsG2YV7P13QH1bHv1SjCZYarc9',
'BtYjoWihiuFihGKQypmpSspbhmWDPxzqeTVSd8ciCpWL',
'1.10.1',
);
const mixnodes = new Map();
mixnodes.set(1, [l1Mixnode]);
mixnodes.set(2, [l2Mixnode]);
mixnodes.set(3, [l3Mixnode]);
const gateways = [gateway];
return new WasmNymTopology(mixnodes, gateways);
};
@@ -1,19 +1,20 @@
import React, { useContext, useEffect, useRef, useState } from 'react';
import React, { useCallback, useContext, useEffect, useRef, useState } from 'react';
import { Box, Button } from '@mui/material';
import { Download } from '@mui/icons-material';
import { NodeTestResultResponse, NodeTester, TestStatus, createNodeTesterClient } from '@nymproject/sdk';
import { format } from 'date-fns';
import { AppContext, useBondingContext } from 'src/context';
import { LoadingModal } from 'src/components/Modals/LoadingModal';
import { Results } from 'src/components/TestNode/Results';
import { ErrorModal } from 'src/components/Modals/ErrorModal';
import { PrintResults } from 'src/components/TestNode/PrintResults';
import { Download } from '@mui/icons-material';
import { format } from 'date-fns';
import { NodeTestEvent, NodeTestResult, TestStatus } from './types';
import { MAINNET_VALIDATOR_URL, QA_VALIDATOR_URL } from 'src/constants';
export const NodeTestPage = () => {
const [nodeTestWorker, setNodeTestWorker] = useState<Worker>();
const [nodeTestClient, setNodeTestClient] = useState<NodeTester>();
const [error, setError] = useState<string>();
const [isLoading, setIsLoading] = useState(false);
const [results, setResults] = useState<NodeTestResult>();
const [results, setResults] = useState<NodeTestResultResponse>();
const [printResults, setPrintResults] = useState(false);
const [testDate, setTestDate] = useState<string>();
@@ -23,15 +24,6 @@ export const NodeTestPage = () => {
const { network } = useContext(AppContext);
const { bondedNode } = useBondingContext();
const loadWorker = () => {
try {
const worker: Worker = new Worker(new URL('./worker.ts', import.meta.url));
setNodeTestWorker(worker);
} catch (e) {
setError('Error loading worker');
}
};
const handleTestTimeout = () => {
clearTimeout(timerRef.current);
timerRef.current = setTimeout(() => {
@@ -43,56 +35,52 @@ export const NodeTestPage = () => {
}, 15000);
};
const handleWorkerMessages = (ev: MessageEvent<NodeTestEvent>) => {
const eventKind = ev.data.kind;
if (eventKind === 'Error') {
setError(ev.data.args.message);
testStateRef.current = 'Stopped';
}
if (eventKind === 'DisplayTesterResults') {
setResults(ev.data.args.result);
testStateRef.current = 'Complete';
}
setIsLoading(false);
};
const handleTestNode = async () => {
setError(undefined);
setResults(undefined);
setIsLoading(true);
setTestDate(format(new Date(), 'dd/MM/yyyy HH:mm'));
if (nodeTestWorker) {
if (nodeTestClient && bondedNode) {
setResults(undefined);
setTestDate(format(new Date(), 'dd/MM/yyyy HH:mm'));
setIsLoading(true);
setError(undefined);
testStateRef.current = 'Running';
nodeTestWorker.postMessage({
kind: 'TestPacket',
args: {
mixnodeIdentity: bondedNode?.identityKey,
network,
},
} as NodeTestEvent);
handleTestTimeout();
try {
const result = await nodeTestClient.tester.startTest(bondedNode.identityKey);
setResults(result);
testStateRef.current = 'Complete';
} catch (e) {
setError('Node test failed, please try again');
testStateRef.current = 'Stopped';
console.log(e);
} finally {
setIsLoading(false);
}
}
};
useEffect(() => {
loadWorker();
return () => {
if (nodeTestWorker) {
nodeTestWorker.terminate();
}
};
const loadNodeTestClient = useCallback(async () => {
try {
const nodeTesterId = new Date().toISOString(); // make a new tester id for each session
const validator = network === 'MAINNET' ? MAINNET_VALIDATOR_URL : QA_VALIDATOR_URL;
const client = await createNodeTesterClient();
await client.tester.init(validator, nodeTesterId);
setNodeTestClient(client);
} catch (e) {
console.log(e);
setError('Failed to load node tester client, please try again');
}
}, []);
useEffect(() => {
if (nodeTestWorker) {
nodeTestWorker.addEventListener('message', (e) => handleWorkerMessages(e));
}
loadNodeTestClient();
return () => nodeTestWorker?.removeEventListener('message', handleWorkerMessages);
}, [nodeTestWorker]);
return () => {
clearTimeout(timerRef.current);
if (nodeTestClient) {
nodeTestClient.tester.disconnectFromGateway();
nodeTestClient.terminate();
}
};
}, [loadNodeTestClient]);
return (
<Box p={4}>
@@ -1,38 +0,0 @@
import { Network } from 'src/types';
export type NodeTestResult = {
score: number;
sentPackets: number;
receivedPackets: number;
receivedAcks: number;
duplicatePackets: number;
duplicateAcks: number;
};
type Error = {
kind: 'Error';
args: { message: string };
};
type WorkerLoaded = {
kind: 'WorkerLoaded';
};
type DisplayTesterResults = {
kind: 'DisplayTesterResults';
args: {
result: NodeTestResult;
};
};
type TestPacket = {
kind: 'TestPacket';
args: {
mixnodeIdentity: string;
network: Network;
};
};
export type TestStatus = 'Stopped' | 'Running' | 'Complete';
export type NodeTestEvent = Error | DisplayTesterResults | TestPacket | WorkerLoaded;
@@ -1,102 +0,0 @@
// Copyright 2020-2023 Nym Technologies SA
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/* eslint-disable no-restricted-globals */
import { NymNodeTester, set_panic_hook, current_network_topology, NodeTestResult } from 'nym-client-wasm';
import { Network } from 'src/types';
import { MAINNET_VALIDATOR_URL, QA_VALIDATOR_URL } from 'src/constants';
import { NodeTestEvent } from './types';
console.log('Initializing worker');
const postMessage = (data: NodeTestEvent) => self.postMessage(data);
postMessage({
kind: 'WorkerLoaded',
});
const printAndDisplayTestResult = (result: NodeTestResult) => {
result.log_details();
postMessage({
kind: 'DisplayTesterResults',
args: {
result: {
score: result.score(),
sentPackets: result.sent_packets,
receivedPackets: result.received_packets,
receivedAcks: result.received_acks,
duplicatePackets: result.duplicate_packets,
duplicateAcks: result.duplicate_acks,
},
},
});
};
const buildTester = async (network: Network) => {
const validator = network === 'QA' ? QA_VALIDATOR_URL : MAINNET_VALIDATOR_URL;
const topology = await current_network_topology(validator);
const nodeTester = await new NymNodeTester(topology, network);
return nodeTester;
};
async function testNode() {
self.onmessage = async (event: MessageEvent<NodeTestEvent>) => {
const eventKind = event.data.kind;
switch (eventKind) {
case 'TestPacket': {
const { mixnodeIdentity, network } = event.data.args;
const nodeTester = await buildTester(network);
try {
console.log(`Testing mixnode identity: ${mixnodeIdentity}, on network: ${network}.`);
const result = await nodeTester.test_node(mixnodeIdentity);
printAndDisplayTestResult(result);
await nodeTester.disconnect_from_gateway();
console.log('Disconnected from gateway');
} catch (e) {
const errorMessage = e instanceof Error ? e.message : 'Node test error';
console.log(errorMessage);
nodeTester.disconnect_from_gateway();
postMessage({
kind: 'Error',
args: { message: errorMessage },
});
}
break;
}
default:
return undefined;
}
return undefined;
};
}
async function main() {
// sets up better stack traces in case of in-rust panics
set_panic_hook();
// run test on simplified and dedicated tester:
await testNode();
}
// Let's get started!
main();
+6 -4
View File
@@ -10,14 +10,14 @@
"nym-connect/**",
"explorer",
"types",
"clients/validator",
"clients/webassembly/**"
"clients/validator"
],
"scripts": {
"clean": "lerna run clean",
"build": "run-s build:types build:packages",
"build:types": "lerna run --scope @nymproject/types build --stream",
"build:packages": "run-s build:packages:theme build:packages:react",
"build:packages": "run-s build:packages:theme build:packages:react build:packages:sdk",
"build:packages:sdk": "lerna run --scope @nymproject/sdk build:lint",
"build:packages:theme": "lerna run --scope @nymproject/mui-theme build",
"build:packages:react": "lerna run --scope @nymproject/react build",
"build:react-example": "lerna run --scope @nymproject/react-webpack-with-theme-example build --stream",
@@ -28,7 +28,9 @@
"lint:fix": "lerna run lint:fix --stream",
"tsc": "lerna run tsc --stream",
"types:lint:fix": "lerna run lint:fix --scope @nymproject/types --scope @nymproject/nym-wallet-app",
"audit:fix": "npm_config_yes=true npx yarn-audit-fix -- --dry-run"
"audit:fix": "npm_config_yes=true npx yarn-audit-fix -- --dry-run",
"preinstall": "yarn install:copy-placeholders",
"install:copy-placeholders": "cp scripts/build/yarn/wasm-placeholder/package.json sdk/typescript/packages/nym-client-wasm"
},
"devDependencies": {
"lerna": "^6.6.2",
@@ -0,0 +1,5 @@
{
"name": "@nymproject/nym-client-wasm",
"version": "1.0.0",
"sideEffects": false
}
+1 -1
View File
@@ -27,7 +27,7 @@ module.exports = {
'plugin:jest/recommended',
'plugin:jest/style',
],
ignorePatterns: ['dist/**/*', 'dist', 'node_modules', '**/wasm/worker.js'],
ignorePatterns: ['dist/**/*', 'dist', 'node_modules', '**/worker.js'],
rules: {
'jest/prefer-strict-equal': 'error',
'jest/prefer-to-have-length': 'warn',
@@ -0,0 +1,22 @@
# Nym Chrome Extension Example
This is an example of how Nym can be used within the context of a Chrome extension.
## Running the example
1. Copy a build of the Nym TypeScript SDK (ESM version) into `./sdk`.
2. Navigate to `chrome://extensions` in Google Chrome.
3. Enable "Developer mode" (top right of the page).
4. Click on "Load unpacked" (top left of the page).
5. Load this extension folder.
## How does it work?
The Nym Mixnet Client runs a [Web Worker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) that wraps
a WASM library that builds and encrypts Sphinx packets in the browser to send over the Nym mixnet:
![Sphinx packet](../docs/worker.svg)
The WASM code encrypts each layer of the Sphinx packet in the browser, before sending the Sphinx packet over a websocket to the ingress gateway:
![Sphinx packet](../docs/sphinx.svg)
Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

@@ -0,0 +1,15 @@
{
"name": "Nym Chrome Extension Example",
"description": "An example demonstrating how to integrate the Nym TypeScript SDK in the context of a Google Chrome browser extension.",
"version": "1.0",
"manifest_version": 3,
"permissions": [],
"content_security_policy": {
"extension_pages": "script-src 'self' 'wasm-unsafe-eval'; object-src 'self';"
},
"action": {
"default_title": "Nym Chrome Extension Example",
"default_icon": "icon.png",
"default_popup": "popup.html"
}
}
@@ -0,0 +1,8 @@
body {
width: 800px;
min-height: 400px;
}
#editdialog input {
width: 100%;
}
@@ -0,0 +1,21 @@
<html>
<head>
<link rel="stylesheet" href="popup.css" />
<script type="module" src="src/main.js"></script>
</head>
<body>
<p><label>Sender:</label><input disabled="true" size="85" id="sender" value=""></p>
<p><label>Recipient:</label><input size="85" id="recipient" value=""></p>
<p><label>Message:</label><input id="message" value="Hello mixnet!"></p>
<p><button id="send-button">Send</button></p>
<p>Send messages from your browser, through the mixnet, and to the recipient using the "send" button.</p>
<p><span style="color: blue;">Sent</span> messages show in blue, <span style="color: green;">received</span> messages show in green.</p>
<hr>
<p></p>
<div id="output"></div>
<p></p>
</body>
</html>

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