Compare commits

...

96 Commits

Author SHA1 Message Date
farbanas 63254ecffe update versions and changelogs for the release 2023-07-18 13:43:30 +02:00
farbanas 407d280019 fixes to GH action 2023-07-18 11:23:15 +02:00
farbanas 1fafc126fb debugging 2023-07-18 11:06:45 +02:00
farbanas 9a51135d22 debugging 2023-07-18 10:43:27 +02:00
Tommy Verrall 1b2790da80 Merge pull request #3676 from nymtech/bugfix/#3630
[wallet] bugfix: don't send funds for pledge decrease simulation
2023-07-17 12:08:32 +02:00
Jędrzej Stuczyński f8943eebce preemptively resolving future clippy issue 2023-07-17 11:01:45 +01:00
Jędrzej Stuczyński d7b53cba40 don't send funds for pledge decrease simulation 2023-07-17 11:01:45 +01:00
mx 89d2f0ac12 Merge pull request #3665 from nymtech/dev-portal/communityupdates
Dev portal/communityupdates
2023-07-17 09:49:52 +00:00
wigy 110b4d384e fix: typo in Rust SDK docs (#3655) 2023-07-17 10:29:28 +01:00
Lorexia 4631c72c6b Update quickstart overview, delete project comments in community-applications-and-guides 2023-07-13 10:36:27 +02:00
Lorexia 12aa5f1f4f Add Minibolt to community projects list 2023-07-12 18:37:06 +02:00
Lorexia 825f25800a Add Nymster email info, update Nostr-Nym link, update deployed apps text 2023-07-12 14:44:51 +02:00
Lorexia d9b4d8fde6 Add preprocessors for build, update NIsNymUp issue, update DarkFi picture 2023-07-12 11:05:42 +02:00
Lorexia a98613d83c Update community application list, merge community and guides pages, update SUMMARY file, update overview file 2023-07-11 19:57:21 +02:00
mx c6b193eb4f Merge pull request #3657 from nymtech/serinko-dev-portal-patch
update NC-Matrix user manual
2023-07-10 08:17:17 +00:00
farbanas a881740c20 Merge branch 'master' into develop 2023-07-05 17:03:18 +02:00
Mark Sinclair 90cc68435c Update connect-desktop-ci.yml 2023-07-05 15:56:31 +01:00
farbanas c3ce9e24e2 Merge branch 'release/v1.1.23' 2023-07-05 16:28:58 +02:00
Mark Sinclair bdda08c0b4 GitHub Actions: Change artifact upload paths to wildcards 2023-07-05 14:28:10 +01:00
Mark Sinclair 0e48ff5e9e GitHub Action: upload artifact 2023-07-05 13:49:12 +01:00
Mark Sinclair 68d6ece6c2 GitHub Actions: add manual trigger 2023-07-05 13:11:03 +01:00
Mark Sinclair e17ae9dce0 Update nym-wallet-publish-ubuntu.yml 2023-07-05 13:09:21 +01:00
Mark Sinclair 493f036687 GitHub Actions: fix up installing project dependencies 2023-07-05 13:07:19 +01:00
Mark Sinclair 1f4f312e8e Removing pre-install target 2023-07-05 12:24:06 +01:00
Mark Sinclair d829837275 GitHub Actions: install project root dependencies first 2023-07-05 12:13:39 +01:00
Mark Sinclair 36253e3f36 GitHub Actions: fix up naming and versions 2023-07-05 12:13:21 +01:00
Mark Sinclair 7743554bb8 Update nym-wallet-publish-windows10.yml 2023-07-05 11:43:33 +01:00
Mark Sinclair de05e32845 Removing bash shell from GitHub Action 2023-07-04 17:45:23 +01:00
Mark Sinclair 136f4a8473 Revert adding copyfiles 2023-07-04 17:42:36 +01:00
Mark Sinclair 57e2628928 GitHub Actions adding bash to Windows publish workflow 2023-07-04 17:32:32 +01:00
Mark Sinclair 879e324696 Add another target for install and CI 2023-07-04 17:26:41 +01:00
Mark Sinclair 351552b863 Add back preinstall target 2023-07-04 17:22:54 +01:00
Mark Sinclair 847f3a0ecc Remove preinstall target that needs wasm-pack 2023-07-04 17:03:24 +01:00
Mark Sinclair 58958a2449 nym-wallet: use NPM published package and add -rc0 prefix to workspace SDK package 2023-07-04 17:01:05 +01:00
Mark Sinclair 3051d84e8d Revert GitHub Action changes to add wasm-pack to Typescript workflows 2023-07-04 17:00:11 +01:00
Mark Sinclair 2d7003dfae Add npm registry to setup-node to create .npmrc file with reg details 2023-07-04 16:06:55 +01:00
serinko 92b9edf0da edited NC-Matrix user manual
- changed setup for Mac
- fixed typo
2023-07-04 14:52:05 +00:00
Mark Sinclair a91c997aed Revert building SDK from root package.json 2023-07-04 15:48:44 +01:00
Mark Sinclair 5787653210 GitHub Action to publish SDK 2023-07-04 15:40:45 +01:00
Mark Sinclair adbeeb3e5f GitHub Action to publish SDK 2023-07-04 15:39:11 +01:00
Mark Sinclair 4899ccf4ef GitHub Action to publish SDK 2023-07-04 15:36:26 +01:00
Mark Sinclair 9e0bcc025f Fix up working directory in GH Action to publish to NPM 2023-07-04 15:34:37 +01:00
Mark Sinclair 16d6444169 Add GH Actions workflow to publish the SDK 2023-07-04 15:30:49 +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
Mark Sinclair 730f03de30 Update nym-wallet-publish-windows10.yml 2023-07-04 11:54:46 +01:00
Mark Sinclair 2e366a094d Update nym-wallet-publish-windows10.yml 2023-07-04 11:52:13 +01:00
Mark Sinclair b134334ec7 Fix package.json preinstall error 2023-07-04 11:43:42 +01:00
Mark Sinclair f95b9b7f4a Add preinstall targets to some package.json files to build pre-requisites 2023-07-04 11:37:00 +01:00
Mark Sinclair 11ed3b3e45 GitHub Actions: run Windows steps that use yarn in bash 2023-07-04 11:33:05 +01: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 a71e228a25 Add wasm-pack install to GitHub Actions 2023-07-04 09:34:35 +01:00
farbanas da9c2e5a7c Merge remote-tracking branch 'refs/remotes/origin/master' 2023-07-04 09:55:15 +02:00
farbanas e025564678 update changelog 2023-07-04 09:48:43 +02:00
mx cf6188d794 Merge pull request #3635 from nymtech/feature/v1-1-23-docs
Feature/v1 1 23 docs
2023-07-03 18:50:57 +00:00
mfahampshire dd7308d92f Merge branch 'release/v1.1.23' into feature/v1-1-23-docs 2023-07-03 17:39:27 +00:00
mfahampshire 73ca14aae2 added alias info + some restructuring 2023-07-03 17:29:58 +00:00
mfahampshire 0fee189a89 moved image 2023-07-03 17:29:29 +00:00
Pierre Dommerc a0c6efafd2 feat(nc): monitoring update (#3609) 2023-07-03 18:35:28 +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
serinko 9f1f765f73 created matrix<>nymconnect user manual 2023-07-03 17:13:50 +02: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 7bbb10e52c Merge branch 'release/v1.1.23' into feature/v1-1-23-docs 2023-07-03 09:47:55 +00:00
mfahampshire 1aad69a527 tidyup extra '/' in fixed last-changed git commit link 2023-07-03 09:43:04 +00:00
mfahampshire 4ad02d5328 fix attempt #3 2023-07-03 09:40:00 +00:00
mfahampshire 45b74008f1 fixed broken links to platform and wallet build instructions in readme 2023-07-03 09:36:23 +00:00
mfahampshire 2ddf9646ec push fix attempt 2 to get actual commit message on CI deployment 2023-07-03 09:31:56 +00:00
mfahampshire b258cd8733 fixed broken git url for last-changed mdbook plugin 2023-07-03 09:28:14 +00: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 4b13a5cf61 build: add workflow to push release data to strapi 2023-06-29 16:07:54 +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
mx 02a4452eca Merge pull request #3613 from nymtech/dev-portal-edit
created monero.md user manual
2023-06-29 11:38:58 +00:00
mx d47633faa8 Update documentation/dev-portal/src/tutorials/monero.md
Co-authored-by: Mark Sinclair <14054343+mmsinclair@users.noreply.github.com>
2023-06-29 13:26:41 +02:00
serinko a3da077ce9 reverted cargo.lock & changed video size 2023-06-29 13:26:40 +02:00
serinko 131952c036 changed video link 2023-06-29 13:26:27 +02:00
mx 8d2ef605ed quickfix 2023-06-29 13:26:27 +02:00
mx fbcc2907bb fixed dead image link + added monero x nymconnect page to sidebare 2023-06-29 13:26:27 +02:00
serinko c0bb5503cf removed redundant copy 2023-06-29 13:26:27 +02:00
serinko ef7beaa12f created user manuals & monero.md 2023-06-29 13:26:24 +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
Dave Hrycyszyn 0f41dda013 Made a few additions to upgrade notes 2023-06-28 12:23:32 +02:00
Dave Hrycyszyn 51d66c3e0c Made a few additions to upgrade notes 2023-06-28 11:21:24 +01:00
mx f14b40a769 hotfix on broken releases page link 2023-06-27 16:06:03 +02:00
mx d8f80434b4 * updated libwasmvm.so linking instructions
* added note on upgrading to v0.31.1
2023-06-27 16:02:31 +02:00
benedettadavico 2f98912778 fix nym-connect version 2023-06-20 13:56:38 +02:00
154 changed files with 4400 additions and 2047 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
+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: Setup yarn
run: npm install -g yarn
- name: Build
+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:
+8 -2
View File
@@ -20,11 +20,17 @@ 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
- name: Install project dependencies
run: cd ../.. && yarn --network-timeout 100000
- name: Install app dependencies
run: yarn
continue-on-error: true
- name: Set environment from the example
run: cp .env.sample .env
- run: yarn storybook:build
@@ -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
- name: Node
uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
- name: Install Rust stable
uses: actions-rs/toolchain@v1
with:
@@ -51,11 +58,15 @@ jobs:
security list-keychain -d user -s $KEYCHAIN_PATH
- name: Create env file
uses: timheuer/base64-to-file@v1.1
uses: timheuer/base64-to-file@v1.2
with:
fileName: '.env'
encodedString: ${{ secrets.WALLET_ADMIN_ADDRESS }}
- name: Install project dependencies
shell: bash
run: cd .. && yarn --network-timeout 100000
- name: Install app dependencies and build it
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -73,7 +84,7 @@ jobs:
uses: actions/upload-artifact@v3
with:
name: nym-connect_1.0.0_x64.dmg
path: nym-connect/desktop/target/release/bundle/dmg/nym-connect_1.0.0_x64.dmg
path: nym-connect/desktop/target/release/bundle/dmg/nym-connect_1*_x64.dmg
retention-days: 30
- name: Clean up keychain
@@ -81,10 +92,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
@@ -26,19 +33,25 @@ jobs:
sudo apt-get install -y webkit2gtk-4.0 libayatana-appindicator3-dev
continue-on-error: true
- name: Node v16
- name: Node
uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
- name: Install Rust stable
uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: Install project dependencies
shell: bash
run: cd .. && yarn --network-timeout 100000
- name: Install app dependencies
run: yarn
- name: Create env file
uses: timheuer/base64-to-file@v1.1
uses: timheuer/base64-to-file@v1.2
with:
fileName: '.env'
encodedString: ${{ secrets.WALLET_ADMIN_ADDRESS }}
@@ -53,13 +66,41 @@ jobs:
uses: actions/upload-artifact@v3
with:
name: nym-connect.AppImage.tar.gz
path: nym-connect/desktop/target/release/bundle/appimage/nym-connect_1.0.0_amd64.AppImage
path: nym-connect/desktop/target/release/bundle/appimage/nym-connect_1*_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
@@ -39,10 +46,10 @@ jobs:
Remove-Item -path certificate -include tempCert.txt
Import-PfxCertificate -FilePath certificate/certificate.pfx -CertStoreLocation Cert:\CurrentUser\My -Password (ConvertTo-SecureString -String $env:WINDOWS_CERTIFICATE_PASSWORD -Force -AsPlainText)
- name: Node v16
- name: Node
uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
- name: Install Rust stable
uses: actions-rs/toolchain@v1
@@ -50,15 +57,21 @@ jobs:
toolchain: stable
- name: Create env file
uses: timheuer/base64-to-file@v1.1
uses: timheuer/base64-to-file@v1.2
with:
fileName: '.env'
encodedString: ${{ secrets.WALLET_ADMIN_ADDRESS }}
- name: Install project dependencies
shell: bash
run: cd .. && yarn --network-timeout 100000
- name: Install app dependencies
shell: bash
run: yarn --network-timeout 100000
- name: Build and sign it
shell: bash
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ENABLE_CODE_SIGNING: ${{ secrets.WINDOWS_CERTIFICATE }}
@@ -72,13 +85,41 @@ jobs:
uses: actions/upload-artifact@v3
with:
name: nym-connect_1.0.0_x64_en-US.msi
path: nym-connect/desktop/target/release/bundle/msi/nym-connect_1.0.0_x64_en-US.msi
path: nym-connect/desktop/target/release/bundle/msi/nym-connect_1*_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
+46 -6
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
- name: Node
uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
- name: Install Rust stable
uses: actions-rs/toolchain@v1
with:
@@ -51,11 +58,15 @@ jobs:
security list-keychain -d user -s $KEYCHAIN_PATH
- name: Create env file
uses: timheuer/base64-to-file@v1.1
uses: timheuer/base64-to-file@v1.2
with:
fileName: '.env'
encodedString: ${{ secrets.WALLET_ADMIN_ADDRESS }}
- name: Install project dependencies
shell: bash
run: cd .. && yarn --network-timeout 100000
- name: Install app dependencies and build it
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -80,11 +91,40 @@ 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: |
ref=${{ github.ref_name }}
semver="${ref##nym-wallet-}" && semver="${semver##v}"
echo "version=${semver}" >> "$GITHUB_OUTPUT"
echo "filename=nym-wallet_${version}_x64.dmg" >> "$GITHUB_OUTPUT"
echo "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
@@ -25,19 +33,24 @@ jobs:
sudo apt-get install -y webkit2gtk-4.0
continue-on-error: true
- name: Node v16
- name: Node
uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
- name: Install Rust stable
uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: Install project dependencies
shell: bash
run: cd .. && yarn --network-timeout 100000
- name: Install app dependencies
run: yarn
- name: Create env file
uses: timheuer/base64-to-file@v1.1
uses: timheuer/base64-to-file@v1.2
with:
fileName: '.env'
encodedString: ${{ secrets.WALLET_ADMIN_ADDRESS }}
@@ -47,9 +60,48 @@ jobs:
env:
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
- name: Upload to release based on tag name
- name: Upload Artifact
uses: actions/upload-artifact@v3
with:
name: nym-wallet_1.0.0_amd64.AppImage.tar.gz
path: nym-wallet/target/release/bundle/appimage/nym-wallet*.AppImage.tar.gz
retention-days: 30
- 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/appimage/*.AppImage
nym-wallet/target/release/bundle/appimage/*.AppImage.tar.gz*
- id: release-info
name: Prepare release info
run: |
ref=${{ github.ref_name }}
semver="${ref##nym-wallet-}" && semver="${semver##v}"
echo "version=${semver}" >> "$GITHUB_OUTPUT"
echo "filename=nym-wallet_${version}_amd64.AppImage" >> "$GITHUB_OUTPUT"
echo "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
@@ -39,10 +46,10 @@ jobs:
Remove-Item -path certificate -include tempCert.txt
Import-PfxCertificate -FilePath certificate/certificate.pfx -CertStoreLocation Cert:\CurrentUser\My -Password (ConvertTo-SecureString -String $env:WINDOWS_CERTIFICATE_PASSWORD -Force -AsPlainText)
- name: Node v16
- name: Node
uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
- name: Install Rust stable
uses: actions-rs/toolchain@v1
@@ -50,15 +57,21 @@ jobs:
toolchain: stable
- name: Create env file
uses: timheuer/base64-to-file@v1.1
uses: timheuer/base64-to-file@v1.2
with:
fileName: '.env'
encodedString: ${{ secrets.WALLET_ADMIN_ADDRESS }}
- name: Install project dependencies
shell: bash
run: cd .. && yarn --network-timeout 100000
- name: Install app dependencies
shell: bash
run: yarn --network-timeout 100000
- name: Build and sign it
shell: bash
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ENABLE_CODE_SIGNING: ${{ secrets.WINDOWS_CERTIFICATE }}
@@ -75,10 +88,40 @@ 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: |
ref=${{ github.ref_name }}
semver="${ref##nym-wallet-}" && semver="${semver##v}"
echo "version=${semver}" >> "$GITHUB_OUTPUT"
echo "filename=nym-wallet_${version}_x64_en-US.msi" >> "$GITHUB_OUTPUT"
echo "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
+2 -2
View File
@@ -34,10 +34,10 @@ jobs:
profile: minimal
toolchain: stable
- name: Node v16
- name: Node
uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
- name: Install yarn for building application
run: yarn install
+212
View File
@@ -0,0 +1,212 @@
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 }}
+35
View File
@@ -0,0 +1,35 @@
name: Publish SDK to NPM
on:
workflow_dispatch:
jobs:
publish:
runs-on: [custom-runner-linux]
steps:
- uses: actions/checkout@v2
- name: Install Node
uses: actions/setup-node@v3
with:
node-version: 18
registry-url: 'https://registry.npmjs.org'
- 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: Install dependencies
run: yarn
- name: Build and publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }}
working-directory: ./sdk/typescript/packages/sdk
run: scripts/publish.sh
+1 -1
View File
@@ -1 +1 @@
16
18
+3 -11
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,19 +35,9 @@ 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
working-directory: clients/webassembly
- name: Build WASM
run: wasm-pack build
working-directory: clients/webassembly
- name: Install
run: yarn
- name: Build packages
+5 -1
View File
@@ -4,12 +4,16 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
## [Unreleased]
## [1.1.23] (2023-06-27)
## [v1.1.23] (2023-07-04)
- nym-cli: add client identity key signing support ([#3576])
- Feature/node tester package ([#3634])
- Add medium toggle to socks5 client ([#3615])
- Don't fully turn off background task when cover traffic is disabled ([#3596])
[#3576]: https://github.com/nymtech/nym/issues/3576
[#3634]: https://github.com/nymtech/nym/pull/3634
[#3615]: https://github.com/nymtech/nym/pull/3615
[#3596]: https://github.com/nymtech/nym/pull/3596
## [v1.1.22] (2023-06-20)
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"
+4 -5
View File
@@ -13,7 +13,7 @@ The platform is composed of multiple Rust crates. Top-level executable binary cr
* nym-gateway - acts sort of like a mailbox for mixnet messages, which removes the need for direct delivery to potentially offline or firewalled devices.
* nym-network-monitor - sends packets through the full system to check that they are working as expected, and stores node uptime histories as the basis of a rewards system ("mixmining" or "proof-of-mixing").
* nym-explorer - a (projected) block explorer and (existing) mixnet viewer.
* nym-wallet - a desktop wallet implemented using the [Tauri](https://tauri.studio/en/docs/about/intro) framework.
* nym-wallet - a desktop wallet implemented using the [Tauri](https://tauri.studio/en/docs/about/intro) framework.
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg?style=for-the-badge)](https://opensource.org/licenses/Apache-2.0)
[![Build Status](https://img.shields.io/github/actions/workflow/status/nymtech/nym/build.yml?branch=develop&style=for-the-badge&logo=github-actions)](https://github.com/nymtech/nym/actions?query=branch%3Adevelop)
@@ -21,7 +21,7 @@ The platform is composed of multiple Rust crates. Top-level executable binary cr
### Building
Platform build instructions are available on [our docs site](https://nymtech.net/docs/binaries/build-nym.html).
Platform build instructions are available on [our docs site](https://nymtech.net/docs/binaries/pre-built-binaries.html).
Wallet build instructions are also available on [our docs site](https://nymtech.net/docs/wallet/desktop-wallet.html).
### Developing
@@ -32,9 +32,9 @@ For Typescript components, please see [ts-packages](./ts-packages).
### Developer chat
> We used to use Keybase for developer chats, but we have since migrated to Matrix and Discord. We no longer check the old **nymtech.friends** Keybase team.
> We used to use Keybase for developer chats, but we have since migrated to Matrix and Discord. We no longer check the old **nymtech.friends** Keybase team.
You can chat to us in two places:
You can chat to us in two places:
* The #dev channel on [Matrix](https://matrix.to/#/#dev:nymtech.chat)
* The various developer channels on [Discord](https://discord.gg/nym)
@@ -84,4 +84,3 @@ where `s'` is stake `s` scaled over total token circulating supply.
### Licensing and copyright information
This program is available as open source under the terms of the Apache 2.0 license. However, some elements are being licensed under CC0-1.0 and MIT. For accurate information, please check individual files.
+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
@@ -79,7 +79,7 @@ copy-fonts = true
no-section-label = false
additional-css = ["theme/pagetoc.css", "././mdbook-admonish.css", "last-changed.css"]
additional-js = ["theme/pagetoc.js"]
git-repository-url = "https://github.com/nymtech/dev-portal"
git-repository-url = "https://github.com/nymtech/nym"
git-repository-icon = "fa-github"
# edit-url-template = "https://github.com/rust-lang/mdBook/edit/master/guide/{path}"
site-url = "/developers/"
+8 -4
View File
@@ -14,6 +14,11 @@
- [SOCKS Proxy (CLI)](quickstart/socks-proxy.md)
- [NymConnect Beta (GUI)](quickstart/nymconnect-gui.md)
# User Manuals
- [NymConnect Monero](tutorials/monero.md)
- [NymConnect Matrix](tutorials/matrix.md)
# Integrations
- [Integration Options](integrations/integration-options.md)
@@ -35,14 +40,13 @@
# Community Resources
- [Nym DevRel AMAs](community-resources/ama.md)
- [Community Applications](community-resources/community-applications.md)
- [Community Guides](community-resources/community-guides.md)
- [Community Applications and Guides](community-resources/community-applications-and-guides.md)
- [Change Service Grantee Information](info-request.md)
- [Rewards FAQ](community-resources/rewards-faq.md)
---
---
# Misc.
- [Glossary](glossary.md)
- [Code of Conduct](coc.md)
- [Licensing](licensing.md)
---
---
@@ -0,0 +1,116 @@
# Community Applications
We love seeing our developer community create applications using Nym. If you would like to share your application with the community, please submit a pull request to the `main` branch of the `nymtech/dev-portal` [repository](https://github.com/nymtech/dev-portal).
## <img src='../images/profile_picture/pastenym_ntv_pp.png' style="float: right; width: 75px; height: 75px;">Pastenym
>A [pastebin](https://pastebin.com) inspired project, offer a solution for sharing text with Nym products to offer full anonymity, even on metadata level.
* [Github](https://github.com/notrustverify/pastenym)
* [Deployed App](https://pastenym.ch)
## <img src='../images/profile_picture/pineappleproxy_pp.png' style="float: right; width: 75px; height: 75px;">Nostr-Nym
> [Nostr-nym](https://github.com/notrustverify/nostr-nym) offer a solution to use [Nostr](https://nostr.how/en/what-is-nostr) protocol by giving the possibility to run a relay on mixnet. By using a nostr client compatible with the mixnet, users can protect their privacy to be able to use Nostr has the want, without being observed.
* [Github](https://github.com/notrustverify/nostr-nym)
* [Deployed App](https://nostrnym.pnproxy.org/)
* [Documentation](https://blog.notrustverify.ch/nostr-relay-on-nym)
## <img src='../images/profile_picture/ethereum_rpc_spook_pp.png' style="float: right; width: 75px; height: 75px;"> Spook
> Ethereum RPC request mixer uses the Nym network mixing service to anonymize RPC requests to the Ethereum network without revealing sensitive data and metadata.
* [Github](https://github.com/EdenBlockVC/spook)
## <img src='../images/profile_picture/ethereum_transaction_broadcaster_root_pp.png' style="float: right; width: 75px; height: 75px;"> Ethereum Transaction Broadcaster
> Ethereum Transaction Broadcaster that uses the Nym Mixnet to provide privacy and anonymity for transactions on the Ethereum network command-line interface.
* [Github](https://github.com/noot/nym-ethtx)
## <img src='../images/profile_picture/nymdrive_saleel_pp.png' style="float: right; width: 75px; height: 75px;">NymDrive
> An open-source, decentralized, E2E encrypted, privacy friendly alternative to Google Drive/Dropbox, allowing for file encryption and decryption using the Nym Mixnet.
* [Github](https://github.com/saleel/nymdrive)
* [Demo](https://www.youtube.com/watch?v=5Rx73nw8NYI)
* [Presentation](https://docs.google.com/presentation/d/1MpvIK32Mx9VKLVfMTcvbeyrsKHHUsTvDQ-3n31dR0NE/edit#slide=id.p)
## <img src='../images/profile_picture/nym_dashboard_pp.svg' style="float: right; width: 75px; height: 75px;">Nym Dashboard
> Developed by No Trust Verify, this dashboard is a great tool to get information about the mixnet, gateways and mixnodes.
* [Deployed App](https://status.notrustverify.ch/d/CW3L7dVVk/nym-mixnet?orgId=1)
## <img src='../images/profile_picture/pastenym_ntv_pp.png' style="float: right; width: 75px; height: 75px;">Is Nym Up
> Explore whether we're up through IsNymUp, a tool that helps check the heath of the Nym network as well as some mixnet related statistics!
* [Deployed App](https://isnymup.com/)
## <img src='../images/profile_picture/darkfi_over_nym_pp.png' style="float: right; width: 75px; height: 75px;">DarkFi over Nym
> DarkFi leverages Nym's mixnet as a pluggable transport for IRCD, their p2p IRC variant. Users can anonymously connect to peers over the network, ensuring secure and private communication within the DarkFi ecosystem.
* [Github](https://github.com/darkrenaissance/darkfi)
* [Documentation](https://darkrenaissance.github.io/darkfi/clients/nym_outbound.html)
## <img src='../images/profile_picture/nymstr_email_pp.png' style="float: right; width: 75px; height: 75px;">Nymstr email
> Experience secure and private email communication with ease using Nymstr email, which enables seamless transmission of emails over a SOCKS5 proxy and our NYM mixnet!
* [Github](https://github.com/dial0ut/nymstr-email)
## <img src='../images/profile_picture/minibolt_pp.png' style="float: right; width: 75px; height: 75px;">Minibolt
> Anonymize your p2p inventory messages and mempool for your Bitcoin & Lightning full nodes on consumer PCs!
* [Github](https://github.com/minibolt-guide/minibolt)
* [Documentation](https://v2.minibolt.info/bonus-guides/system/nym-mixnet#proxying-bitcoin-core)
<br/> <br/>
# Community Guides
We aren't the only ones writing documentation: the Nym developer community is also a great source of guides and resources, some of which we've included here.
## <img src='../images/profile_picture/pastenym_ntv_pp.png' style="float: right; width: 75px; height: 75px;"> No Trust Verify
>No Trust Verify is a project that aims to build open-source, privacy-enhancing technologies that make it easier to use the Internet securely and anonymously. Their focus is on providing tools and services that make it simple for developers to create decentralized applications (dApps) that respect users' privacy.
* [Awesome Nym list](https://notrustverify.github.io/awesome-nym/) ([GitHub](https://github.com/notrustverify/awesome-nym))
* A lot of guides can be found on the [NTV Blog](https://blog.notrustverify.ch/)
## <img src='../images/profile_picture/pineappleproxy_pp.png' style="float: right; width: 75px; height: 75px;">The Way of the NYMJA
by Pineapple Proxy🍍
>Born out of a study group from Nym's Shipyard Academy, Pineapple Proxy has emerged as a cluster of motivated and skilled individuals who see the new internet taking shape. With vibecare at the heart of their approach, this zesty collective is on a mission to make privacy convenient for everyone via content, new tools, events, and novel experiences. They believe in collective intelligence, empathy, and collaboration as the means by which privacy will become a meaningful reality.
* [Website](https://pnproxy.org/welcome.html) ([GitHub](https://github.com/Pineapple-Proxy-DAO/web))
@@ -1,61 +0,0 @@
# Community Applications
We love seeing our developer community create applications using Nym. If you would like to share your application with the community, please submit a pull request to the `main` branch of the `nymtech/dev-portal` [repository](https://github.com/nymtech/dev-portal).
<!--Pastenym *start-->
## <img src='../images/profile_picture/pastenym_ntv_pp.png' style="float: right; width: 75px; height: 75px;">Pastenym
>A [pastebin](https://pastebin.com) inspired project, offer a solution for sharing text with Nym products to offer full anonymity, even on metadata level.
* [Github](https://github.com/notrustverify/pastenym)
* [Deployed Website App](https://pastenym.ch)
<!--Pastenym *end-->
<!--Pineapple *start-->
## <img src='../images/profile_picture/pineappleproxy_pp.png' style="float: right; width: 75px; height: 75px;">Nostr-Nym
> [Nostr-nym](https://github.com/notrustverify/nostr-nym) offer a solution to use [Nostr](https://nostr.how/) protocol by giving the possibility to run a relay on mixnet. By using a nostr client compatible with the mixnet, users can protect their privacy to be able to use Nostr has the want, without being observed.
* [Github](https://github.com/notrustverify/nostr-nym)
* Deployed Website App coming soon
<!--Pineapple *end-->
<!--Ethereum RPC Provider *start-->
## <img src='../images/profile_picture/ethereum_rpc_spook_pp.png' style="float: right; width: 75px; height: 75px;"> Spook
> Ethereum RPC request mixer uses the Nym network mixing service to anonymize RPC requests to the Ethereum network without revealing sensitive data and metadata.
* [Github](https://github.com/EdenBlockVC/spook)
<!--Ethereum RPC Provider *end-->
<!--Ethereum Transaction Broadcaster *start-->
## <img src='../images/profile_picture/ethereum_transaction_broadcaster_root_pp.png' style="float: right; width: 75px; height: 75px;"> Ethereum Transaction Broadcaster
> Ethereum Transaction Broadcaster that uses the Nym Mixnet to provide privacy and anonymity for transactions on the Ethereum network command-line interface.
* [Github](https://github.com/noot/nym-ethtx)
<!--Ethereum Transaction Broadcaster *end-->
<!--NymDrive *start-->
## <img src='../images/profile_picture/nymdrive_saleel_pp.png' style="float: right; width: 75px; height: 75px;">NymDrive
> An open-source, decentralized, E2E encrypted, privacy friendly alternative to Google Drive/Dropbox, allowing for file encryption and decryption using the Nym Mixnet.
* [Github](https://github.com/saleel/nymdrive)
* [Demo](https://www.youtube.com/watch?v=5Rx73nw8NYI)
* [Presentation](https://docs.google.com/presentation/d/1MpvIK32Mx9VKLVfMTcvbeyrsKHHUsTvDQ-3n31dR0NE/edit#slide=id.p)
<!--NymDrive *end-->
@@ -1,17 +0,0 @@
# Community Guides
We aren't the only ones writing documentation: the Nym developer community is also a great source of guides and resources, some of which we've included here.
## <img src='../images/profile_picture/pastenym_ntv_pp.png' style="float: right; width: 75px; height: 75px;"> No Trust Verify
>No Trust Verify is a project that aims to build open-source, privacy-enhancing technologies that make it easier to use the Internet securely and anonymously. Their focus is on providing tools and services that make it simple for developers to create decentralized applications (dApps) that respect users' privacy.
* [Awesome Nym list](https://notrustverify.github.io/awesome-nym/) ([GitHub](https://github.com/notrustverify/awesome-nym))
* A lot of guides can be found on the [NTV Blog](https://blog.notrustverify.ch/)
## <img src='../images/profile_picture/pineappleproxy_pp.png' style="float: right; width: 75px; height: 75px;">The Way of the NYMJA
by Pineapple Proxy🍍
>Born out of a study group from Nym's Shipyard Academy, Pineapple Proxy has emerged as a cluster of motivated and skilled individuals who see the new internet taking shape. With vibecare at the heart of their approach, this zesty collective is on a mission to make privacy convenient for everyone via content, new tools, events, and novel experiences. They believe in collective intelligence, empathy, and collaboration as the means by which privacy will become a meaningful reality.
* [Website](https://pnproxy.org/welcome.html) ([GitHub](https://github.com/Pineapple-Proxy-DAO/web))
Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 314 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

@@ -0,0 +1,57 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 20.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="351px" height="365px" viewBox="0 0 351 365" style="enable-background:new 0 0 351 365;" xml:space="preserve">
<style type="text/css">
.st0{fill:url(#SVGID_1_);}
</style>
<g id="Layer_1_1_">
</g>
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="175.5" y1="30%" x2="175.5" y2="99%">
<stop offset="0" style="stop-color:#F05A28"/>
<stop offset="1" style="stop-color:#FBCA0A"/>
</linearGradient>
<path class="st0" d="M342,161.2c-0.6-6.1-1.6-13.1-3.6-20.9c-2-7.7-5-16.2-9.4-25c-4.4-8.8-10.1-17.9-17.5-26.8
c-2.9-3.5-6.1-6.9-9.5-10.2c5.1-20.3-6.2-37.9-6.2-37.9c-19.5-1.2-31.9,6.1-36.5,9.4c-0.8-0.3-1.5-0.7-2.3-1
c-3.3-1.3-6.7-2.6-10.3-3.7c-3.5-1.1-7.1-2.1-10.8-3c-3.7-0.9-7.4-1.6-11.2-2.2c-0.7-0.1-1.3-0.2-2-0.3
c-8.5-27.2-32.9-38.6-32.9-38.6c-27.3,17.3-32.4,41.5-32.4,41.5s-0.1,0.5-0.3,1.4c-1.5,0.4-3,0.9-4.5,1.3c-2.1,0.6-4.2,1.4-6.2,2.2
c-2.1,0.8-4.1,1.6-6.2,2.5c-4.1,1.8-8.2,3.8-12.2,6c-3.9,2.2-7.7,4.6-11.4,7.1c-0.5-0.2-1-0.4-1-0.4c-37.8-14.4-71.3,2.9-71.3,2.9
c-3.1,40.2,15.1,65.5,18.7,70.1c-0.9,2.5-1.7,5-2.5,7.5c-2.8,9.1-4.9,18.4-6.2,28.1c-0.2,1.4-0.4,2.8-0.5,4.2
C18.8,192.7,8.5,228,8.5,228c29.1,33.5,63.1,35.6,63.1,35.6c0,0,0.1-0.1,0.1-0.1c4.3,7.7,9.3,15,14.9,21.9c2.4,2.9,4.8,5.6,7.4,8.3
c-10.6,30.4,1.5,55.6,1.5,55.6c32.4,1.2,53.7-14.2,58.2-17.7c3.2,1.1,6.5,2.1,9.8,2.9c10,2.6,20.2,4.1,30.4,4.5
c2.5,0.1,5.1,0.2,7.6,0.1l1.2,0l0.8,0l1.6,0l1.6-0.1l0,0.1c15.3,21.8,42.1,24.9,42.1,24.9c19.1-20.1,20.2-40.1,20.2-44.4l0,0
c0,0,0-0.1,0-0.3c0-0.4,0-0.6,0-0.6l0,0c0-0.3,0-0.6,0-0.9c4-2.8,7.8-5.8,11.4-9.1c7.6-6.9,14.3-14.8,19.9-23.3
c0.5-0.8,1-1.6,1.5-2.4c21.6,1.2,36.9-13.4,36.9-13.4c-3.6-22.5-16.4-33.5-19.1-35.6l0,0c0,0-0.1-0.1-0.3-0.2
c-0.2-0.1-0.2-0.2-0.2-0.2c0,0,0,0,0,0c-0.1-0.1-0.3-0.2-0.5-0.3c0.1-1.4,0.2-2.7,0.3-4.1c0.2-2.4,0.2-4.9,0.2-7.3l0-1.8l0-0.9
l0-0.5c0-0.6,0-0.4,0-0.6l-0.1-1.5l-0.1-2c0-0.7-0.1-1.3-0.2-1.9c-0.1-0.6-0.1-1.3-0.2-1.9l-0.2-1.9l-0.3-1.9
c-0.4-2.5-0.8-4.9-1.4-7.4c-2.3-9.7-6.1-18.9-11-27.2c-5-8.3-11.2-15.6-18.3-21.8c-7-6.2-14.9-11.2-23.1-14.9
c-8.3-3.7-16.9-6.1-25.5-7.2c-4.3-0.6-8.6-0.8-12.9-0.7l-1.6,0l-0.4,0c-0.1,0-0.6,0-0.5,0l-0.7,0l-1.6,0.1c-0.6,0-1.2,0.1-1.7,0.1
c-2.2,0.2-4.4,0.5-6.5,0.9c-8.6,1.6-16.7,4.7-23.8,9c-7.1,4.3-13.3,9.6-18.3,15.6c-5,6-8.9,12.7-11.6,19.6c-2.7,6.9-4.2,14.1-4.6,21
c-0.1,1.7-0.1,3.5-0.1,5.2c0,0.4,0,0.9,0,1.3l0.1,1.4c0.1,0.8,0.1,1.7,0.2,2.5c0.3,3.5,1,6.9,1.9,10.1c1.9,6.5,4.9,12.4,8.6,17.4
c3.7,5,8.2,9.1,12.9,12.4c4.7,3.2,9.8,5.5,14.8,7c5,1.5,10,2.1,14.7,2.1c0.6,0,1.2,0,1.7,0c0.3,0,0.6,0,0.9,0c0.3,0,0.6,0,0.9-0.1
c0.5,0,1-0.1,1.5-0.1c0.1,0,0.3,0,0.4-0.1l0.5-0.1c0.3,0,0.6-0.1,0.9-0.1c0.6-0.1,1.1-0.2,1.7-0.3c0.6-0.1,1.1-0.2,1.6-0.4
c1.1-0.2,2.1-0.6,3.1-0.9c2-0.7,4-1.5,5.7-2.4c1.8-0.9,3.4-2,5-3c0.4-0.3,0.9-0.6,1.3-1c1.6-1.3,1.9-3.7,0.6-5.3
c-1.1-1.4-3.1-1.8-4.7-0.9c-0.4,0.2-0.8,0.4-1.2,0.6c-1.4,0.7-2.8,1.3-4.3,1.8c-1.5,0.5-3.1,0.9-4.7,1.2c-0.8,0.1-1.6,0.2-2.5,0.3
c-0.4,0-0.8,0.1-1.3,0.1c-0.4,0-0.9,0-1.2,0c-0.4,0-0.8,0-1.2,0c-0.5,0-1,0-1.5-0.1c0,0-0.3,0-0.1,0l-0.2,0l-0.3,0
c-0.2,0-0.5,0-0.7-0.1c-0.5-0.1-0.9-0.1-1.4-0.2c-3.7-0.5-7.4-1.6-10.9-3.2c-3.6-1.6-7-3.8-10.1-6.6c-3.1-2.8-5.8-6.1-7.9-9.9
c-2.1-3.8-3.6-8-4.3-12.4c-0.3-2.2-0.5-4.5-0.4-6.7c0-0.6,0.1-1.2,0.1-1.8c0,0.2,0-0.1,0-0.1l0-0.2l0-0.5c0-0.3,0.1-0.6,0.1-0.9
c0.1-1.2,0.3-2.4,0.5-3.6c1.7-9.6,6.5-19,13.9-26.1c1.9-1.8,3.9-3.4,6-4.9c2.1-1.5,4.4-2.8,6.8-3.9c2.4-1.1,4.8-2,7.4-2.7
c2.5-0.7,5.1-1.1,7.8-1.4c1.3-0.1,2.6-0.2,4-0.2c0.4,0,0.6,0,0.9,0l1.1,0l0.7,0c0.3,0,0,0,0.1,0l0.3,0l1.1,0.1
c2.9,0.2,5.7,0.6,8.5,1.3c5.6,1.2,11.1,3.3,16.2,6.1c10.2,5.7,18.9,14.5,24.2,25.1c2.7,5.3,4.6,11,5.5,16.9c0.2,1.5,0.4,3,0.5,4.5
l0.1,1.1l0.1,1.1c0,0.4,0,0.8,0,1.1c0,0.4,0,0.8,0,1.1l0,1l0,1.1c0,0.7-0.1,1.9-0.1,2.6c-0.1,1.6-0.3,3.3-0.5,4.9
c-0.2,1.6-0.5,3.2-0.8,4.8c-0.3,1.6-0.7,3.2-1.1,4.7c-0.8,3.1-1.8,6.2-3,9.3c-2.4,6-5.6,11.8-9.4,17.1
c-7.7,10.6-18.2,19.2-30.2,24.7c-6,2.7-12.3,4.7-18.8,5.7c-3.2,0.6-6.5,0.9-9.8,1l-0.6,0l-0.5,0l-1.1,0l-1.6,0l-0.8,0
c0.4,0-0.1,0-0.1,0l-0.3,0c-1.8,0-3.5-0.1-5.3-0.3c-7-0.5-13.9-1.8-20.7-3.7c-6.7-1.9-13.2-4.6-19.4-7.8
c-12.3-6.6-23.4-15.6-32-26.5c-4.3-5.4-8.1-11.3-11.2-17.4c-3.1-6.1-5.6-12.6-7.4-19.1c-1.8-6.6-2.9-13.3-3.4-20.1l-0.1-1.3l0-0.3
l0-0.3l0-0.6l0-1.1l0-0.3l0-0.4l0-0.8l0-1.6l0-0.3c0,0,0,0.1,0-0.1l0-0.6c0-0.8,0-1.7,0-2.5c0.1-3.3,0.4-6.8,0.8-10.2
c0.4-3.4,1-6.9,1.7-10.3c0.7-3.4,1.5-6.8,2.5-10.2c1.9-6.7,4.3-13.2,7.1-19.3c5.7-12.2,13.1-23.1,22-31.8c2.2-2.2,4.5-4.2,6.9-6.2
c2.4-1.9,4.9-3.7,7.5-5.4c2.5-1.7,5.2-3.2,7.9-4.6c1.3-0.7,2.7-1.4,4.1-2c0.7-0.3,1.4-0.6,2.1-0.9c0.7-0.3,1.4-0.6,2.1-0.9
c2.8-1.2,5.7-2.2,8.7-3.1c0.7-0.2,1.5-0.4,2.2-0.7c0.7-0.2,1.5-0.4,2.2-0.6c1.5-0.4,3-0.8,4.5-1.1c0.7-0.2,1.5-0.3,2.3-0.5
c0.8-0.2,1.5-0.3,2.3-0.5c0.8-0.1,1.5-0.3,2.3-0.4l1.1-0.2l1.2-0.2c0.8-0.1,1.5-0.2,2.3-0.3c0.9-0.1,1.7-0.2,2.6-0.3
c0.7-0.1,1.9-0.2,2.6-0.3c0.5-0.1,1.1-0.1,1.6-0.2l1.1-0.1l0.5-0.1l0.6,0c0.9-0.1,1.7-0.1,2.6-0.2l1.3-0.1c0,0,0.5,0,0.1,0l0.3,0
l0.6,0c0.7,0,1.5-0.1,2.2-0.1c2.9-0.1,5.9-0.1,8.8,0c5.8,0.2,11.5,0.9,17,1.9c11.1,2.1,21.5,5.6,31,10.3
c9.5,4.6,17.9,10.3,25.3,16.5c0.5,0.4,0.9,0.8,1.4,1.2c0.4,0.4,0.9,0.8,1.3,1.2c0.9,0.8,1.7,1.6,2.6,2.4c0.9,0.8,1.7,1.6,2.5,2.4
c0.8,0.8,1.6,1.6,2.4,2.5c3.1,3.3,6,6.6,8.6,10c5.2,6.7,9.4,13.5,12.7,19.9c0.2,0.4,0.4,0.8,0.6,1.2c0.2,0.4,0.4,0.8,0.6,1.2
c0.4,0.8,0.8,1.6,1.1,2.4c0.4,0.8,0.7,1.5,1.1,2.3c0.3,0.8,0.7,1.5,1,2.3c1.2,3,2.4,5.9,3.3,8.6c1.5,4.4,2.6,8.3,3.5,11.7
c0.3,1.4,1.6,2.3,3,2.1c1.5-0.1,2.6-1.3,2.6-2.8C342.6,170.4,342.5,166.1,342,161.2z"/>
</svg>

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

@@ -36,7 +36,7 @@ In order to ensure uptime and reliability, it is recommended that you run some p
* If you're running a purely P2P application, then just integrating clients and having some method of sharing addresses should be enough to route your traffic through the mixnet.
* If you're wanting to place the mixnet between your users' application instances and a server-based backend, you can use the [network requester](https://nymtech.net/docs/nodes/network-requester-setup.html) service provider binary to proxy these requests to your application backend, with the mixnet 'between' the user and your service, in order to prevent metadata leakage being broadcast to the internet.
* If you're wanting to route RPC requests through the mixnet to a blockchain, you will need to look into setting up some sort of service that does the transaction broadcasting for you. You can find examples of such projects on the [community applications](../community-resources/community-applications.md) page.
* If you're wanting to route RPC requests through the mixnet to a blockchain, you will need to look into setting up some sort of service that does the transaction broadcasting for you. You can find examples of such projects on the [community applications](../community-resources/community-applications-and-guides.md) page.
## Example application traffic flow
### Initialization
@@ -2,9 +2,10 @@
There are multiple options to quickly connect to Nym and see the network in action without the need for any code changes to your application. At most, these involve running Nym as a second process alongside an existing application in order to send traffic through the mixnet.
Demo application:
Demo applications:
* a browser-based 'hello world' [chat application](https://chat-demo.nymtech.net). Either open in two browser windows and send messages to yourself, or share with a friend and send messages to each other through the mixnet!
* a Coconut-scheme based [Credential Library](https://coco-demo.nymtech.net/). This is a WASM implementation of our Coconut libraries which generate raw Coconut credentials. Test it to create and re-randomize your own credentials!
<br>
Proxy traffic with the Nym Socks5 client:
* set up a plug-and-play connection with the [NymConnect](./nymconnect-gui.md) GUI for proxying Telegram, Electrum, Keybase or Blockstream Green traffic through the mixnet (~2 minutes).
* [Download and run](./socks-proxy.md) the Nym Socks5 client via the CLI, for other desktop applications with SOCKS5 connection options (~30 minutes).
@@ -0,0 +1,80 @@
# Matrix NymConnect Integration
Chat applications became an essential part of human communication. Matrix chat has end to end encryption on protocol level and Element app users can sort their communication into spaces and rooms. Now the Matrix communities can rely on network privacy as NymConnect supports Matrix chat protocol.
Currently there is no option in Matrix's Element client to set a Socks5 proxy. In order to use Element via NymConnect users have to start it from the command-line. The setup is simple, for convenience a keyboard shortcut setting or terminal alias can be easily done.
## Setup & Run
Make sure you have installed and started **[NymConnect](https://nymtech.net/developers/quickstart/nymconnect-gui.html)** on your desktop.
To then start Matrix's Element client via a Socks5 proxy connected to NymConnect, open terminal and run:
```sh
element-desktop --proxy-server=socks5://127.0.0.1:1080
```
## Optimise setup with a keybinding / alias
### Keybinding
An eloquent solution to avoid entering a command every time is to setup your keybinding. Open your settings, navigate to `Keyboard Shortcuts` and choose to `Set Custom Shortcut`. `Name` and `Shortcut` fields are up to your preference, to the `Command` line add:
```sh
element-desktop --proxy-server=socks5://127.0.0.1:1080
```
Make sure your `Shortcut` isn't already taken by something else in the menu.
An example can look like this.
![](../images/element_nym_keybind.png)
Alternatively you can add a keybinding via the CLI, using whatever config files you edit for your given desktop environment / window manager.
### Create an alias
If you prefer to simply shorten the length of the command (or all your keybindings are already taken) then you can simply create an alias for this long-winded command (this example aliases that command to the single word `element`, but you can replace it with whatever you like):
**Linux**
```sh
alias element="element-desktop --proxy-server=socks5://127.0.0.1:1080"
```
To make this alias persist, then add this to your `.bashrc` or `.zshrc` file (usually located in your `$HOME` directory) and `source` that file. This can be done by appending the alias command directly to the shell config file with one command.
For `bash` enter:
```sh
alias element="element-desktop --proxy-server=socks5://127.0.0.1:1080" >> ~/.bashrc
```
For `zsh` enter:
```sh
alias element="element-desktop --proxy-server=socks5://127.0.0.1:1080" >> ~/.zshrc
```
You can add the alias manually by opening your `$HOME` directory, enable hidden files (press `ctrl` + `h`) and open `.bashrc` or `.zshrc` file (based on your terminal setup) in a text editor, paste the string `alias element="element-desktop --proxy-server=socks5://127.0.0.1:1080"` to the the end, save and exit. Start a new terminal and run `element`.
**Mac**
```sh
alias element="open -a Element --args --proxy-server=socks5://127.0.0.1:1080"
```
To make this alias persist, then add this to your `.zshrc` (or `.bashrc`/`.profile`) file (usually located in your `$HOME` directory) and `source` that file. This can be done by appending the alias command directly to the shell config file with one command.
For `zsh` enter:
```sh
alias element="open -a Element --args --proxy-server=socks5://127.0.0.1:1080" >> ~/.zshrc
```
For `.bashrc` or `.profile` just change the end of the command.
You can add the alias manually by opening your `$HOME` directory, enable hidden files (in Finder press `Shift` + `Command` + `.`) and open `.zshrc` file (or `.bashrc`/`.profile`) in a text editor, paste the string `alias element="open -a Element --args --proxy-server=socks5://127.0.0.1:1080"` to the the end, save and exit. Start a new terminal and run `element`.
**Now you can run Element through the Nym Mixnet with a single-word command.**
@@ -0,0 +1,42 @@
# Monero NymConnect Integration
*New Nym mixnet integration launched for Monero desktop to secure the right to financial privacy and censorship-resistance*
![](../images/monero_tutorial/monero.png)
Financial privacy is an important component of digital currencies and the use of Nym will provide users with the highest level of privacy at the infrastructure level. All users of digital currencies should be afforded equal rights to protection from financial surveillance.
A team made up of Monero community members have successfully set up a service provider to use Monero (using the Monero desktop wallet) over the Nym mixnet. This allows Monero users to easily use NymConnect to run Monero over the mixnet, thereby enhancing the privacy of Monero transactions.
## How can I use Monero over the Nym mixnet?
The mainnet service provider to Monero over the Nym mixnet is now ready for use via [NymConnect](https://nymtech.net/download-nymconnect/).
* Download and open the latest version of [NymConnect](https://nymtech.net/download-nymconnect/).
* Click on the top left options and go to Settings
* Go to “Select service provider” and turn it on
* For Mainnet, search for this provider or insert it manually:
```sh
i1TiuoNp4jp9weffCW7tPnkb4hRTPydRjX8iXFVaYDG.88Z1hruuvbzWpdCE2xYnTbPNrr49j4s7mmUQC5wvRRLZ@3EPuxwGn2WP2HdxybzoDa5QsohYSP76aQQRUJuPMvk23
```
* Go to the main NymConnect interface and connect to the mixnet
Then go to your Monero wallet (gui or otherwise) and change the settings to run over socks5 proxy:
**Monero desktop:**
* Settings -> Interface -> Socks5 proxy -> Add values: IP address `localhost`, Port `1080`
<iframe width="700" height="400" src="https://www.youtube.com/embed/oSHnk1BG_f0" title="Demo: Connect Your Monero Wallet to the Nym Mixnet via NymConnect" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
**CLI**
* **Monerod:** add `--proxy 127.0.0.1:1080 --bootstrap-daemon-proxy 127.0.0.1:1080` to args
* **Monero-wallet-{rpc, cli}:** add `--proxy 127.0.0.1:1080 --daemon-ssl-allow-any-cert` to args
Follow the instructions and the Monero mainnet will be connected through to the Nym mixnet.
For those who want to try it out in testnet, a stagenet service provider is also available: [https://nymtech.net/.wellknown/connect/service-providers.json](https://nymtech.net/.wellknown/connect/service-providers.json)
@@ -12,7 +12,7 @@ Simply fill in the fields in your browser and click `Send`. In your browser you
<img src="../../images/tutorial_image_5.png"/>
This small project can be used as a template to start conceptualizing and developing more complex PEApps. Stay tuned for more soon, and if you're searching for inspiration check out the [community apps](../../community-resources/community-applications.md) list!
This small project can be used as a template to start conceptualizing and developing more complex PEApps. Stay tuned for more soon, and if you're searching for inspiration check out the [community apps](../../community-resources/community-applications-and-guides.md) list!
@@ -62,7 +62,8 @@ h3:target::before,
h4:target::before,
h5:target::before,
h6:target::before {
display: inline-block;
/* display "none" in order to avoid default artifacts on the community-applications-and-guides page */
display: none;
content: "»";
margin-left: -30px;
width: 30px;
+4 -4
View File
@@ -48,9 +48,9 @@ assets_version = "2.0.0" # do not edit: managed by `mdbook-admonish install`
# https://gitlab.com/tglman/mdbook-variables/
[preprocessor.variables.variables]
minimum_rust_version = "1.66"
# old variables - this is still needed for links.. TODO change to develop?
platform_release_version = "v1.1.21"
# vars for links: TODO think on how to streamline updating
platform_release_version = "v1.1.22"
wallet_release_version = "v1.2.5"
[preprocessor.last-changed]
command = "mdbook-last-changed"
@@ -87,7 +87,7 @@ copy-fonts = true
no-section-label = false
additional-css = ["theme/pagetoc.css", "././mdbook-admonish.css", "last-changed.css"]
additional-js = ["theme/pagetoc.js"]
git-repository-url = "https://github.com/nymtech/docs"
git-repository-url = "https://github.com/nymtech/nym"
git-repository-icon = "fa-github"
# edit-url-template = "https://github.com/rust-lang/mdBook/edit/master/guide/{path}"
# site-url = "/docs/"
+2 -2
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
@@ -102,4 +102,4 @@ The following code shows how you can use the SDK to create and use a [credential
{{#include ../../../../sdk/rust/nym-sdk/examples/bandwidth.rs}}
```
You can read more about Coconut credentials (also referred to as `zk-Nym`) [here](../cococnut.md).
You can read more about Coconut credentials (also referred to as `zk-Nym`) [here](../coconut.md).
+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
+3 -1
View File
@@ -2,16 +2,18 @@
## [Unreleased]
## [v1.1.14] (2023-06-27)
## [v1.1.14] (2023-07-04)
- Nym connect fails to start when encountering an old config version ([#3588])
- NC desktop - apps section adjustments + add monero integration ([#2977])
- nym-connect: use different service provider directory when medium toggle enabled ([#3617])
- Fix medium toggle in nym-connect ([#3590])
- [bugfix] NC: load old gateway configuration if we're not registering ([#3586])
- nym-connect: medium speed setting ([#3585])
[#3588]: https://github.com/nymtech/nym/issues/3588
[#2977]: https://github.com/nymtech/nym/issues/2977
[#3617]: https://github.com/nymtech/nym/pull/3617
[#3590]: https://github.com/nymtech/nym/pull/3590
[#3586]: https://github.com/nymtech/nym/pull/3586
[#3585]: https://github.com/nymtech/nym/pull/3585
+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]]

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