Compare commits

..

100 Commits

Author SHA1 Message Date
Bogdan-Ștefan Neacșu a31f7de7c9 Use 1.. indices and fix the blind sign request multiple generation 2022-03-04 14:25:48 +02:00
Bogdan-Ștefan Neacșu e7c8de8eec Finish with executing contract for pool send 2022-03-03 12:11:04 +02:00
Bogdan-Ștefan Neacșu e27a2ae857 Add execute in client 2022-03-02 17:37:50 +02:00
Bogdan-Ștefan Neacșu 0de0ad91bd Do multisig call after verification 2022-03-02 17:32:30 +02:00
Bogdan-Ștefan Neacșu db2769b59b Do verification on validator-apis with hardcoded aggr vk 2022-03-02 16:16:23 +02:00
Bogdan-Ștefan Neacșu d1c12c0b22 Stub verify of cred in validator api 2022-03-02 14:48:11 +02:00
Bogdan-Ștefan Neacșu 2e0c0bfdc5 Pass the actual tx hash for checks 2022-03-02 13:36:55 +02:00
Bogdan-Ștefan Neacșu ccf97e8570 Add deposit button 2022-03-02 13:11:53 +02:00
Bogdan-Ștefan Neacșu 56a8b82c4d Conditional signature 2022-03-01 14:52:34 +02:00
Bogdan-Ștefan Neacșu 876beed97d Remove useless serde on attributes 2022-03-01 14:18:39 +02:00
Bogdan-Ștefan Neacșu de450f87de Endpoint for accepting deposit 2022-02-28 15:58:35 +02:00
Bogdan-Ștefan Neacșu 2d55d09f24 Use 3 signer authorities 2022-02-28 13:38:17 +02:00
Bogdan-Ștefan Neacșu aa8926ed5d Use correct deserialisation method 2022-02-28 13:28:48 +02:00
Bogdan-Ștefan Neacșu 5df820db6c Testing code 2022-02-25 13:43:01 +03:00
Bogdan-Ștefan Neacșu 20c144c236 Start from 0 instead of 1, to have the correct length 2022-02-24 18:27:55 +03:00
dependabot[bot] 6604d927c5 Bump ws from 6.2.1 to 6.2.2 in /clients/webassembly/js-example (#1126)
Bumps [ws](https://github.com/websockets/ws) from 6.2.1 to 6.2.2.
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/compare/6.2.1...6.2.2)

---
updated-dependencies:
- dependency-name: ws
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-02-23 13:56:04 +00:00
Jon Häggblad 4fedf05b64 Fix clippy warnings on beta channel 2022-02-23 14:45:41 +01:00
Raphaël Walther fa00f85a31 Removed condition in notification 2022-02-23 14:05:55 +01:00
Raphaël Walther abf5a2d307 Triggerd workflow 2022-02-23 12:30:26 +01:00
Raphaël Walther 8e0000cb0b Removed test workflow 2022-02-23 12:02:30 +01:00
Raphaël Walther cc27911671 Moved workflow to different runners 2022-02-23 11:29:53 +01:00
Bogdan-Ștefan Neacșu b598f1d1db Make sure run uses the args passed at init, in eth mode 2022-02-23 12:45:31 +03:00
Raphaël Walther d4f10c1d68 Triggered workflow 2022-02-23 09:24:22 +01:00
Raphaël Walther 1d7ff40d02 Added test workflow 2022-02-23 09:14:21 +01:00
Raphaël Walther 72703b36e1 Tested build workflow 2022-02-22 17:35:50 +01:00
Raphaël Walther ecf7a40010 Enabled sccache 2022-02-22 17:23:47 +01:00
Raphaël Walther 272e9187d2 Removed test workflow 2022-02-22 16:18:28 +01:00
Raphaël Walther 69a1f3773c Amended test workflow 2022-02-22 15:41:20 +01:00
Raphaël Walther 15c97b1cae Amended test workflow 2022-02-22 15:31:02 +01:00
Raphaël Walther 2660ef59f7 Amended test workflow 2022-02-22 13:29:21 +01:00
dependabot[bot] 12a06d9dfe Bump url-parse from 1.4.7 to 1.5.7 in /clients/webassembly/react-example (#1125)
Bumps [url-parse](https://github.com/unshiftio/url-parse) from 1.4.7 to 1.5.7.
- [Release notes](https://github.com/unshiftio/url-parse/releases)
- [Commits](https://github.com/unshiftio/url-parse/compare/1.4.7...1.5.7)

---
updated-dependencies:
- dependency-name: url-parse
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-02-22 09:56:29 +00:00
dependabot[bot] 6399cd24c8 Bump url-parse in /clients/native/examples/js-examples/websocket (#1124)
Bumps [url-parse](https://github.com/unshiftio/url-parse) from 1.5.4 to 1.5.7.
- [Release notes](https://github.com/unshiftio/url-parse/releases)
- [Commits](https://github.com/unshiftio/url-parse/compare/1.5.4...1.5.7)

---
updated-dependencies:
- dependency-name: url-parse
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-02-22 09:56:20 +00:00
dependabot[bot] f30b5162e8 Bump url-parse from 1.5.1 to 1.5.7 in /clients/webassembly/js-example (#1122)
Bumps [url-parse](https://github.com/unshiftio/url-parse) from 1.5.1 to 1.5.7.
- [Release notes](https://github.com/unshiftio/url-parse/releases)
- [Commits](https://github.com/unshiftio/url-parse/compare/1.5.1...1.5.7)

---
updated-dependencies:
- dependency-name: url-parse
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-02-22 09:56:15 +00:00
dependabot[bot] 229329ae4c Bump follow-redirects in /contracts/basic-bandwidth-generation (#1117)
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.14.7 to 1.14.8.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.14.7...v1.14.8)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-02-22 09:56:10 +00:00
dependabot[bot] a3bb5e466d Bump follow-redirects from 1.14.3 to 1.14.8 in /explorer (#1116)
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.14.3 to 1.14.8.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.14.3...v1.14.8)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-02-22 09:56:03 +00:00
dependabot[bot] 65d893a70b Bump follow-redirects from 1.14.5 to 1.14.8 in /nym-wallet (#1115)
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.14.5 to 1.14.8.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.14.5...v1.14.8)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-02-22 09:55:55 +00:00
dependabot[bot] e0944e5238 Bump follow-redirects in /clients/native/examples/js-examples/websocket (#1114)
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.14.7 to 1.14.8.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.14.7...v1.14.8)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-02-22 09:55:49 +00:00
dependabot[bot] b451d4b709 Bump follow-redirects from 1.14.7 to 1.14.8 in /testnet-faucet (#1113)
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.14.7 to 1.14.8.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.14.7...v1.14.8)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-02-22 09:55:42 +00:00
dependabot[bot] 1507e48158 Bump follow-redirects in /clients/webassembly/js-example (#1112)
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.14.1 to 1.14.8.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.14.1...v1.14.8)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-02-22 09:55:36 +00:00
dependabot[bot] 6e7a7ed4a6 Bump simple-get from 3.1.0 to 3.1.1 in /explorer (#1109)
Bumps [simple-get](https://github.com/feross/simple-get) from 3.1.0 to 3.1.1.
- [Release notes](https://github.com/feross/simple-get/releases)
- [Commits](https://github.com/feross/simple-get/compare/v3.1.0...v3.1.1)

---
updated-dependencies:
- dependency-name: simple-get
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-02-22 09:55:29 +00:00
Jędrzej Stuczyński af4801ac47 Removes migration code (#1071) 2022-02-22 09:54:57 +00:00
Raphaël Walther b748c55d4e Amended test workflow 2022-02-22 09:37:31 +01:00
Raphaël Walther 84e5bfc286 Amended test workflow 2022-02-21 17:37:12 +01:00
Raphaël Walther 96bd9ecf49 Amended test workflow 2022-02-21 15:48:10 +01:00
Raphaël Walther 092851976f Added test workflow 2022-02-21 13:23:04 +01:00
durch 9da1f82cca Restore cargo.lock 2022-02-20 21:58:01 +01:00
durch 8c60927fe2 Update tauri 2022-02-18 18:20:18 +01:00
durch aa6f5ad664 Update minor/patch versions 2022-02-18 12:18:28 +01:00
fmtabbara 201b5cc56f ui updates 2022-02-17 17:09:02 +00:00
fmtabbara 510553a697 update content 2022-02-17 14:56:38 +00:00
Raphaël Walther b81967c9f5 Set nightly notification on failure 2022-02-17 14:19:28 +01:00
Bogdan-Ștefan Neacşu fadff7888b Feature/reuse init owner (#970)
* Save gateway owner for later use in erc20 bandwidth request

* Pass owner in network monitor

* Switch to variable length owner address

* Add erc20 bridge contract in validator client

* Check bandwidth credential refers to gateway

* Check the owner of the gateway from the eth event

* Fix wasm client

* Hack to avoid unused warning on coconut path

* Hacked, one-time payment

* Remove print

* Update arg format

* Fix token check

* Fix native template

* Use utokens instead of full token

... when talking to eth

* Fix parse event for new field

* Fix socks5 template

* Add estimation of gas call

* Make fs backup more reliable

* Fix clippy

* Fix unused import

* Update waiting time

* Remove defaults from run, as it they should be set on init

* Remove debugging prints

* Replaced unwrap with error

* Fix build

* Make eth contract address dependent of network

* Use tokio for sleep

* Add approve before spending token on bandwidth

* Put bandwidth claim only at the beginning of the process
2022-02-17 11:56:11 +01:00
fmtabbara 24854f9254 Merge branch 'feature/faucet-release' into develop 2022-02-17 10:49:02 +00:00
Tommy Verrall 8ea7726e81 Merge pull request #1121 from nymtech/feature/update-sandbox
update contract address
2022-02-16 18:26:07 +00:00
Tommy Verrall ea32a897b2 update contract address 2022-02-16 17:38:27 +00:00
Raphaël Walther 5d7a14c5ab Added a custom runner for ubuntu only builds 2022-02-16 17:14:19 +01:00
Raphaël Walther cad68b8d6c Set test schedule 2022-02-16 16:42:09 +01:00
Raphaël Walther 1d125ed97f Set test schedule 2022-02-16 14:36:36 +01:00
Raphaël Walther 36d4ec5a72 Set test schedule 2022-02-16 13:49:38 +01:00
Raphaël Walther 44981b8621 Set test schedule 2022-02-16 13:27:40 +01:00
fmtabbara bd68797432 replace local storage with cookie 2022-02-16 12:23:44 +00:00
Mark Sinclair 636759789c Merge pull request #1119 from nymtech/feature/gh-actions-notifications
Refactor GitHub Actions notifications
2022-02-16 14:21:16 +02:00
Mark Sinclair c03a859223 Refactor GitHub Actions notifications 2022-02-16 14:09:12 +02:00
fmtabbara b269cdae31 refactor 2022-02-16 11:10:15 +00:00
Raphaël Walther 2177a396b9 Added assume yes option on apt-get install 2022-02-16 11:48:11 +01:00
Raphaël Walther 4c50dd64f4 Added test build 2022-02-16 11:19:41 +01:00
fmtabbara be9d3a48bb update error message 2022-02-16 10:19:09 +00:00
fmtabbara 37d890c634 refactor 💪 2022-02-15 22:15:10 +00:00
fmtabbara 6287583f4b component refactor 2022-02-15 21:35:34 +00:00
fmtabbara c7760c6c10 eslint check + additional validation 2022-02-15 18:46:03 +00:00
fmtabbara 2c0058c477 update env sample£ 2022-02-15 16:38:07 +00:00
fmtabbara 02fb2e56ca add validation 2022-02-15 16:34:41 +00:00
Jon Häggblad 8b2c72eb91 gateway: upgrade clap and use declarative cli argument parsing 2022-02-15 17:11:51 +01:00
durch 805eb31728 Fix typo 2022-02-15 16:21:13 +01:00
durch 7f45cbd5df Rename all -> test 2022-02-15 14:41:00 +01:00
fmtabbara 8ce709bbb4 update currency 2022-02-15 10:20:07 +00:00
Raphaël Walther 46db139f79 Set cron schedule 2022-02-14 18:09:31 +01:00
Raphaël Walther 069c5bf332 Added repo checkout on new job 2022-02-14 18:08:06 +01:00
Jess 54a4653e7d Merge pull request #1118 from nymtech/bugfix/gateway-bond
Change `pledge` to `bond` in gateway list
2022-02-14 15:07:49 +00:00
Raphaël Walther ea1f689c71 Added a job for notification 2022-02-14 15:59:35 +01:00
Mark Sinclair 3d60299b22 Change pledge to bond in gateway list 2022-02-14 16:42:04 +02:00
Raphaël Walther 5b5063676f Corrected wrong indentation 2022-02-14 14:20:54 +01:00
Raphaël Walther 833556020d Set runner OS to ubuntu-latest for notification 2022-02-14 14:16:27 +01:00
Raphaël Walther aa00eb70d5 Adedd env variables for notification 2022-02-14 12:19:13 +01:00
Drazen Urch 2caf79fba0 Feature/vesting get current period (#1111)
* Add GetCurrentPeriod Msg

* Add a few more query endpoints

* Enrich original vesting response
2022-02-14 12:13:55 +01:00
Raphaël Walther eda1822653 Removed beta and nightly rust builds 2022-02-14 10:28:12 +01:00
Raphaël Walther 0f1ff5ffd2 Added notifications to nightly builds 2022-02-14 09:04:02 +01:00
Raphaël Walther 9512fc34f0 Added workflow for daily builds 2022-02-11 14:21:06 +01:00
Drazen Urch 938eb2d0f0 Add UpdateMixnodeConfigOnBehalf to vestng contract (#1091)
* Add UpdateMixnodeConfigOnBehalf

* Rename one_u* function
2022-02-11 12:15:00 +01:00
dependabot[bot] ede51fa589 Bump simple-get in /contracts/basic-bandwidth-generation (#1110)
Bumps [simple-get](https://github.com/feross/simple-get) from 2.8.1 to 2.8.2.
- [Release notes](https://github.com/feross/simple-get/releases)
- [Commits](https://github.com/feross/simple-get/compare/v2.8.1...v2.8.2)

---
updated-dependencies:
- dependency-name: simple-get
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-02-11 11:03:26 +00:00
dependabot[bot] 7e0711ce12 Bump simple-get from 3.1.0 to 3.1.1 in /clients/tauri-client (#1108)
Bumps [simple-get](https://github.com/feross/simple-get) from 3.1.0 to 3.1.1.
- [Release notes](https://github.com/feross/simple-get/releases)
- [Commits](https://github.com/feross/simple-get/compare/v3.1.0...v3.1.1)

---
updated-dependencies:
- dependency-name: simple-get
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-02-11 11:02:32 +00:00
dependabot[bot] 822f40239f Bump simple-get from 3.1.0 to 3.1.1 in /nym-wallet (#1107)
Bumps [simple-get](https://github.com/feross/simple-get) from 3.1.0 to 3.1.1.
- [Release notes](https://github.com/feross/simple-get/releases)
- [Commits](https://github.com/feross/simple-get/compare/v3.1.0...v3.1.1)

---
updated-dependencies:
- dependency-name: simple-get
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-02-11 11:02:27 +00:00
Tommy Verrall 31149e6ba1 Merge pull request #1105 from nymtech/dependabot/npm_and_yarn/clients/webassembly/react-example/node-sass-7.0.0
Bump node-sass from 4.14.1 to 7.0.0 in /clients/webassembly/react-example
2022-02-11 10:36:16 +00:00
Drazen Urch ba233b6016 Fix hardcoded period logic (#1104) 2022-02-11 09:44:15 +01:00
dependabot[bot] 3d17ab6a4b Bump node-sass in /clients/webassembly/react-example
Bumps [node-sass](https://github.com/sass/node-sass) from 4.14.1 to 7.0.0.
- [Release notes](https://github.com/sass/node-sass/releases)
- [Changelog](https://github.com/sass/node-sass/blob/master/CHANGELOG.md)
- [Commits](https://github.com/sass/node-sass/compare/v4.14.1...v7.0.0)

---
updated-dependencies:
- dependency-name: node-sass
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-02-10 23:34:48 +00:00
Jędrzej Stuczyński f22bb282ab Fixed underflow in rewarding all delegators (#1099)
* Fixed underflow in rewarding all delegators

* Slightly simplifying the loop
2022-02-07 11:30:17 +00:00
Jędrzej Stuczyński d907b6b81a Fixes infinite loops in requests involving pagination (#1085)
* Fixes infinite loops in requests involving pagination

* Removed duplicate code and added extra explanation for the decision
2022-02-07 11:29:00 +00:00
Tommy Verrall 1903938035 Merge pull request #1094 from nymtech/feature/minor-rewarding-adjustments
Emit original bond as part of rewarding event
2022-02-03 14:58:13 +00:00
mfahampshire bb646c4f6a updated token contract address 2022-02-03 14:43:37 +01:00
Jędrzej Stuczyński 4c65ef3a2f Emit original bond as part of rewarding event 2022-02-03 09:52:44 +00:00
151 changed files with 4801 additions and 1986 deletions
+7 -31
View File
@@ -9,28 +9,14 @@ on:
- 'explorer/**'
jobs:
matrix_prep:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
# creates the matrix strategy from build_matrix_includes.json
- uses: actions/checkout@v2
- id: set-matrix
uses: JoshuaTheMiller/conditional-build-matrix@main
with:
inputFile: '.github/workflows/build_matrix_includes.json'
filter: '[?runOnEvent==`${{ github.event_name }}` || runOnEvent==`always`]'
build:
needs: matrix_prep
strategy:
matrix: ${{fromJson(needs.matrix_prep.outputs.matrix)}}
runs-on: ${{ matrix.os }}
continue-on-error: ${{ matrix.rust == 'nightly' || matrix.rust == 'beta' || matrix.os == 'windows-latest' }}
runs-on: [ self-hosted, custom-linux-exoscale ]
# Enable sccache via environment variable
env:
RUSTC_WRAPPER: /home/ubuntu/.cargo/bin/sccache
steps:
- name: Install Dependencies (Linux)
run: sudo apt-get update && sudo apt-get install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev squashfs-tools
if: matrix.os == 'ubuntu-latest'
run: sudo apt-get update && sudo apt-get -y install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev squashfs-tools
- name: Check out repository code
uses: actions/checkout@v2
@@ -39,7 +25,7 @@ jobs:
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ matrix.rust }}
toolchain: stable
override: true
components: rustfmt, clippy
@@ -63,25 +49,16 @@ jobs:
- uses: actions-rs/clippy-check@v1
name: Clippy checks
# if: matrix.os == 'ubuntu-latest' && matrix.rust == 'stable'
with:
token: ${{ secrets.GITHUB_TOKEN }}
args: --all-features
- name: Run clippy
uses: actions-rs/cargo@v1
if: ${{ matrix.rust != 'nightly' }}
with:
command: clippy
args: -- -D warnings
# COCONUT stuff
- name: Reclaim some disk space (because Windows is being annoying)
uses: actions-rs/cargo@v1
if: ${{ matrix.os == 'windows-latest' }}
with:
command: clean
- name: Build all binaries with coconut enabled
uses: actions-rs/cargo@v1
with:
@@ -96,7 +73,6 @@ jobs:
- name: Run clippy with coconut enabled
uses: actions-rs/cargo@v1
if: ${{ matrix.rust != 'nightly' }}
with:
command: clippy
args: --features=coconut -- -D warnings
args: --features=coconut -- -D warnings
+2 -33
View File
@@ -10,41 +10,10 @@
"rust":"stable",
"runOnEvent":"pull_request"
},
{
"os":"macos-latest",
"rust":"stable",
"runOnEvent":"pull_request"
},
{
"os":"ubuntu-latest",
"rust":"beta",
"runOnEvent":"pull_request"
},
{
"os":"windows-latest",
"rust":"beta",
"runOnEvent":"pull_request"
},
{
"os":"macos-latest",
"rust":"beta",
"runOnEvent":"pull_request"
},
{
"os":"ubuntu-latest",
"rust":"nightly",
"runOnEvent":"pull_request"
},
{
"os":"windows-latest",
"rust":"nightly",
"runOnEvent":"pull_request"
},
{
"os":"macos-latest",
"rust":"nightly",
"runOnEvent":"pull_request"
}
]
]
+3 -2
View File
@@ -41,9 +41,10 @@ jobs:
EXCLUDE: "/dist/, /node_modules/"
- name: Keybase - Node Install
run: npm install
working-directory: .github/workflows/support-files/messages
working-directory: .github/workflows/support-files
- name: Keybase - Send Notification
env:
NYM_NOTIFICATION_KIND: network-explorer
NYM_PROJECT_NAME: "Network Explorer"
NYM_CI_WWW_BASE: "${{ secrets.NYM_CI_WWW_BASE }}"
NYM_CI_WWW_LOCATION: "network-explorer-${{ env.GITHUB_REF_SLUG }}"
@@ -56,4 +57,4 @@ jobs:
IS_SUCCESS: "${{ job.status == 'success' }}"
uses: docker://keybaseio/client:stable-node
with:
args: .github/workflows/support-files/messages/entry_point_notifications.sh
args: .github/workflows/support-files/notifications/entry_point.sh
+121
View File
@@ -0,0 +1,121 @@
name: Nightly builds
on:
schedule:
- cron: '14 4 * * *'
jobs:
matrix_prep:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
# creates the matrix strategy from nightly_build_matrix_includes.json
- uses: actions/checkout@v2
- id: set-matrix
uses: JoshuaTheMiller/conditional-build-matrix@main
with:
inputFile: '.github/workflows/nightly_build_matrix_includes.json'
filter: '[?runOnEvent==`${{ github.event_name }}` || runOnEvent==`always`]'
build:
needs: matrix_prep
strategy:
matrix: ${{fromJson(needs.matrix_prep.outputs.matrix)}}
runs-on: ${{ matrix.os }}
continue-on-error: ${{ matrix.rust == 'nightly' || matrix.rust == 'beta' || matrix.rust == 'stable' }}
steps:
- name: Install Dependencies (Linux)
run: sudo apt-get update && sudo apt-get install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev squashfs-tools
if: matrix.os == 'ubuntu-latest'
- name: Check out repository code
uses: actions/checkout@v2
- name: Install rust toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ matrix.rust }}
override: true
components: rustfmt, clippy
- name: Build all binaries
uses: actions-rs/cargo@v1
with:
command: build
args: --all
- name: Run all tests
uses: actions-rs/cargo@v1
with:
command: test
args: --all
- name: Check formatting
uses: actions-rs/cargo@v1
with:
command: fmt
args: --all -- --check
- uses: actions-rs/clippy-check@v1
name: Clippy checks
with:
token: ${{ secrets.GITHUB_TOKEN }}
args: --all-features
- name: Run clippy
uses: actions-rs/cargo@v1
if: ${{ matrix.rust != 'nightly' }}
with:
command: clippy
args: -- -D warnings
# COCONUT stuff
- name: Reclaim some disk space (because Windows is being annoying)
uses: actions-rs/cargo@v1
if: ${{ matrix.os == 'windows-latest' }}
with:
command: clean
- name: Build all binaries with coconut enabled
uses: actions-rs/cargo@v1
with:
command: build
args: --all --features=coconut
- name: Run all tests with coconut enabled
uses: actions-rs/cargo@v1
with:
command: test
args: --all --features=coconut
- name: Run clippy with coconut enabled
uses: actions-rs/cargo@v1
if: ${{ matrix.rust != 'nightly' }}
with:
command: clippy
args: --features=coconut -- -D warnings
notification:
needs: build
runs-on: ubuntu-latest
steps:
- name: Collect jobs status
uses: technote-space/workflow-conclusion-action@v2
- name: Check out repository code
uses: actions/checkout@v2
- name: Keybase - Node Install
run: npm install
working-directory: .github/workflows/support-files
- name: Keybase - Send Notification
env:
NYM_NOTIFICATION_KIND: nightly
NYM_PROJECT_NAME: "Nym nightly build"
GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}"
GIT_BRANCH: "${GITHUB_REF##*/}"
KEYBASE_NYMBOT_USERNAME: "${{ secrets.KEYBASE_NYMBOT_USERNAME }}"
KEYBASE_NYMBOT_PAPERKEY: "${{ secrets.KEYBASE_NYMBOT_PAPERKEY }}"
KEYBASE_NYMBOT_TEAM: "${{ secrets.KEYBASE_NYMBOT_TEAM }}"
KEYBASE_NYM_CHANNEL: "ci-nightly"
IS_SUCCESS: "${{ env.WORKFLOW_CONCLUSION == 'success' }}"
uses: docker://keybaseio/client:stable-node
with:
args: .github/workflows/support-files/notifications/entry_point.sh
@@ -0,0 +1,50 @@
[
{
"os":"ubuntu-latest",
"rust":"stable",
"runOnEvent":"schedule"
},
{
"os":"windows-latest",
"rust":"stable",
"runOnEvent":"schedule"
},
{
"os":"macos-latest",
"rust":"stable",
"runOnEvent":"schedule"
},
{
"os":"ubuntu-latest",
"rust":"beta",
"runOnEvent":"schedule"
},
{
"os":"windows-latest",
"rust":"beta",
"runOnEvent":"schedule"
},
{
"os":"macos-latest",
"rust":"beta",
"runOnEvent":"schedule"
},
{
"os":"ubuntu-latest",
"rust":"nightly",
"runOnEvent":"schedule"
},
{
"os":"windows-latest",
"rust":"nightly",
"runOnEvent":"schedule"
},
{
"os":"macos-latest",
"rust":"nightly",
"runOnEvent":"schedule"
}
]
+4 -1
View File
@@ -10,7 +10,10 @@ on:
jobs:
nym-wallet-types:
runs-on: ubuntu-latest
runs-on: [ self-hosted, custom-linux-exoscale ]
# Enable sccache
env:
RUSTC_WRAPPER: /home/ubuntu/.cargo/bin/sccache
if: ${{ github.event_name != 'pull_request' }}
steps:
- name: Prepare
@@ -0,0 +1,35 @@
KEYBASE_NYM_CHANNEL=
KEYBASE_NYMBOT_USERNAME=
KEYBASE_NYMBOT_PAPERKEY=
NYM_NOTIFICATION_KIND=nightly
NYM_PROJECT_NAME=Nightly Build
#----------------------------------------------------------------
# Custom GitHub Actions mock env vars
IS_SUCCESS=true
#----------------------------------------------------------------
# GitHub Actions context mock env vars
GITHUB_SHA=abcdef
GITHUB_RUN_ID=123456
GITHUB_REPOSITORY=nymtech/nym
GITHUB_SERVER_URL=https://github.com
GIT_BRANCH_NAME=feature/testing-support-files
GIT_BRANCH=feature/testing-support-files
GIT_COMMIT_MESSAGE=This is the commit message
GITHUB_ACTOR=octocat
# add a Personal Access Token (PAT) generated from GitHub here for use in testing
GITHUB_TOKEN=
#----------------------------------------------------------------
# Network Explorer
NYM_CI_WWW_LOCATION=some-branch
NYM_CI_WWW_BASE=example.com
#----------------------------------------------------------------
# Nightly builds
WORKFLOW_CONCLUSION=success
SHOW_DEBUG=true
@@ -0,0 +1,5 @@
node_modules
.idea
# don't commit the lock file to avoid cross-platform issues
package-lock.json
+1
View File
@@ -0,0 +1 @@
16
+58
View File
@@ -0,0 +1,58 @@
# GitHub Actions Support Files
This is a collection of scripts and files to support GitHub Actions.
## Sending Notifications
These scripts send CI notifications to Keybase by creating messages from templates and env vars passed from GitHub Actions.
### Adding notifications to a GitHub Action
```
jobs:
build:
...
- name: Notifications - Node Install
run: npm install
working-directory: .github/workflows/support-files/notifications
- name: Notifications - Send
env:
NYM_NOTIFICATION_KIND: "my-component"
GIT_BRANCH: "${GITHUB_REF##*/}"
KEYBASE_NYMBOT_USERNAME: "${{ secrets.KEYBASE_NYMBOT_USERNAME }}"
KEYBASE_NYMBOT_PAPERKEY: "${{ secrets.KEYBASE_NYMBOT_PAPERKEY }}"
KEYBASE_NYMBOT_TEAM: "${{ secrets.KEYBASE_NYMBOT_TEAM }}"
KEYBASE_NYM_CHANNEL: "ci-network-explorer"
IS_SUCCESS: "${{ job.status == 'success' }}"
uses: docker://keybaseio/client:stable-node
with:
args: .github/workflows/support-files/notifications/entry_point.sh
```
Notifications are run by adding the snippet above to a GitHub Action, and:
1. Installing node packages needed at run time
2. Set the env vars as required:
- `NYM_NOTIFICATION_KIND` matches the directory in `.github/workflows/support-files/${NYM_NOTIFICATION_KIND}` to provide the templates and extra scripting in `index.js`
- Keybase credentials, channel and other env vars for the status of the build and repo
3. Replacing the default entry point shell script on the `keybaseio/client:stable-node` docker image to run `.github/workflows/support-files/notifications/entry_point.sh`
### Running locally
You will need:
- Node 16 LTS
- npm
Copy `.github/workflows/support-files/.env.example` to `.github/workflows/support-files/.env` and valid Keybase credentials.
Then run `npm install` to get dependencies.
Start development mode for the notification type you want either by passing the value as an env var called `NYM_NOTIFICATION_KIND` or set the `.env` file values correctly.
```bash
cd .github/workflows/support-files
npm install
cp .env.example .env
vi .env
npm run dev
```
+1
View File
@@ -0,0 +1 @@
require('./notifications/send_message');
@@ -1,2 +0,0 @@
node_modules
.idea
@@ -1,69 +0,0 @@
const Bot = require('keybase-bot');
const Handlebars = require('handlebars');
const fs = require('fs');
async function main() {
const data = { env: process.env };
// const data = { ...PASTE TEST DATA HERE ... }; // -- DEV: uncomment to set test data
// validation of environment
if(!(process.env.NYM_PROJECT_NAME || data.env.NYM_PROJECT_NAME)) {
throw new Error('Please set env var NYM_PROJECT_NAME with the project name for displaying in notification messages');
}
const keybaseChannel = process.env.KEYBASE_NYM_CHANNEL || data.env.KEYBASE_NYM_CHANNEL;
if(!keybaseChannel) {
throw new Error('Please set env var KEYBASE_NYM_CHANNEL with the channel name for the notification message');
}
// extract the git branch name
const GIT_BRANCH_NAME = (process.env.GITHUB_REF || data.env.GITHUB_REF).split('/').slice(2).join('/');
data.env.GIT_BRANCH_NAME = GIT_BRANCH_NAME;
const source = fs
.readFileSync(process.env.IS_SUCCESS === 'true' ? 'success' : 'failure')
.toString();
const template = Handlebars.compile(source);
const result = template(data);
// -- DEV: uncomment to show what is available in the handlebars template / show the result
// console.dir({ data }, { depth: null });
// console.log(result);
const bot = new Bot();
try {
const username = process.env.KEYBASE_NYMBOT_USERNAME;
const paperkey = process.env.KEYBASE_NYMBOT_PAPERKEY;
if(!username) {
throw new Error('Username is not defined. Please set env var KEYBASE_NYMBOT_USERNAME');
}
if(!paperkey) {
throw new Error('Paperkey is not defined. Please set env var KEYBASE_NYMBOT_PAPERKEY');
}
console.log(`Initialising keybase with user "${username}" and key: "${'*'.repeat(paperkey.length)}"...`);
await bot.init(username, paperkey, { verbose: false });
const channel = {
name: 'nymtech_bot',
membersType: 'team',
topicName: keybaseChannel,
topic_type: 'CHAT',
};
const message = {
body: result,
};
console.log(`Sending to ${channel.name}#${channel.topicName}...`);
await bot.chat.send(channel, message);
console.log('Message sent!');
} catch (error) {
console.error(error);
process.exitCode = -1;
} finally {
await bot.deinit();
}
}
main();
@@ -0,0 +1,29 @@
const Handlebars = require('handlebars');
const fs = require('fs');
const path = require('path');
async function addToContextAndValidate(context) {
if (!context.env.NYM_CI_WWW_LOCATION) {
throw new Error('Please ensure the env var NYM_CI_WWW_LOCATION is set');
}
if (!context.env.NYM_CI_WWW_BASE) {
throw new Error('Please ensure the env var NYM_CI_WWW_BASE is set');
}
}
async function getMessageBody(context) {
const source = fs
.readFileSync(
context.env.IS_SUCCESS === 'true'
? path.resolve(__dirname, 'templates', 'success')
: path.resolve(__dirname, 'templates', 'failure'),
)
.toString();
const template = Handlebars.compile(source);
return template(context);
}
module.exports = {
addToContextAndValidate,
getMessageBody,
};
@@ -5,7 +5,7 @@
> `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }}
> `build ` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }}
Commit message:
Commit message by `{{ env.GITHUB_ACTOR }}` at {{ timestamp }}:
```
{{ env.GIT_COMMIT_MESSAGE }}
```
@@ -0,0 +1,162 @@
const Handlebars = require('handlebars');
const fs = require('fs');
const path = require('path');
const { Octokit, App } = require('octokit');
async function addToContextAndValidate(context) {
if (!context.env.WORKFLOW_CONCLUSION) {
throw new Error('Please ensure the env var WORKFLOW_CONCLUSION is set');
}
if (!context.env.GITHUB_TOKEN) {
throw new Error('Please ensure the env var GITHUB_TOKEN is set');
}
if (!context.env.GITHUB_RUN_ID) {
throw new Error('Please ensure the env var GITHUB_RUN_ID is set');
}
if (!context.env.GITHUB_REPOSITORY) {
throw new Error('Please ensure the env var GITHUB_REPOSITORY is set');
}
}
async function getMessageBody(context) {
const source = fs
.readFileSync(
context.env.WORKFLOW_CONCLUSION === 'success'
? path.resolve(__dirname, 'templates', 'success')
: path.resolve(__dirname, 'templates', 'failure'),
)
.toString();
const template = Handlebars.compile(source);
// get job details from GitHub API
const octokit = new Octokit({ auth: context.env.GITHUB_TOKEN });
const [owner, repo] = context.env.GITHUB_REPOSITORY.split('/');
const {
data: { jobs },
} = await octokit.rest.actions.listJobsForWorkflowRun({
run_id: context.env.GITHUB_RUN_ID,
owner,
repo,
});
// uncomment this to see what is available for each job
if(process.env.SHOW_DEBUG) {
console.dir(jobs, { depth: null });
}
/*
a sample of the response is:
{
total_count: 10,
jobs: [
{
id: 5182940024,
run_id: 1840752095,
run_url: 'https://api.github.com/repos/nymtech/nym/actions/runs/1840752095',
run_attempt: 1,
node_id: 'CR_kwDODdjOis8AAAABNO1jeA',
head_sha: 'aa00eb70d57751bfa556bd3602df87c7473367fc',
url: 'https://api.github.com/repos/nymtech/nym/actions/jobs/5182940024',
html_url: 'https://github.com/nymtech/nym/runs/5182940024?check_suite_focus=true',
status: 'completed',
conclusion: 'success',
started_at: '2022-02-14T11:28:34Z',
completed_at: '2022-02-14T11:28:38Z',
name: 'matrix_prep',
steps: [
{
name: 'Set up job',
status: 'completed',
conclusion: 'success',
number: 1,
started_at: '2022-02-14T13:28:34.000+02:00',
completed_at: '2022-02-14T13:28:36.000+02:00'
},
{
name: 'Run actions/checkout@v2',
status: 'completed',
conclusion: 'success',
number: 2,
started_at: '2022-02-14T13:28:36.000+02:00',
completed_at: '2022-02-14T13:28:37.000+02:00'
},
...
],
check_run_url: 'https://api.github.com/repos/nymtech/nym/check-runs/5182940024',
labels: [ 'ubuntu-latest' ],
runner_id: 1,
runner_name: 'Hosted Agent',
runner_group_id: 2,
runner_group_name: 'GitHub Actions'
},
{
id: 5182943473,
run_id: 1840752095,
run_url: 'https://api.github.com/repos/nymtech/nym/actions/runs/1840752095',
run_attempt: 1,
node_id: 'CR_kwDODdjOis8AAAABNO1w8Q',
head_sha: 'aa00eb70d57751bfa556bd3602df87c7473367fc',
url: 'https://api.github.com/repos/nymtech/nym/actions/jobs/5182943473',
html_url: 'https://github.com/nymtech/nym/runs/5182943473?check_suite_focus=true',
status: 'completed',
conclusion: 'failure',
started_at: '2022-02-14T11:29:04Z',
completed_at: '2022-02-14T11:55:45Z',
name: 'build (macos-latest, stable, schedule)',
steps: [
{
name: 'Set up job',
status: 'completed',
conclusion: 'success',
number: 1,
started_at: '2022-02-14T13:29:04.000+02:00',
completed_at: '2022-02-14T13:29:26.000+02:00'
},
{
name: 'Install Dependencies (Linux)',
status: 'completed',
conclusion: 'skipped',
number: 2,
started_at: '2022-02-14T13:29:26.000+02:00',
completed_at: '2022-02-14T13:29:26.000+02:00'
},
{
name: 'Keybase - Send Notification',
status: 'completed',
conclusion: 'failure',
number: 15,
started_at: '2022-02-14T13:55:44.000+02:00',
completed_at: '2022-02-14T13:55:44.000+02:00'
},
],
check_run_url: 'https://api.github.com/repos/nymtech/nym/check-runs/5182943473',
labels: [ 'macos-latest' ],
runner_id: 4,
runner_name: 'GitHub Actions 4',
runner_group_id: 2,
runner_group_name: 'GitHub Actions'
},
...
]
}
*/
const jobResults = jobs
.map((job) => {
const icon = job.conclusion === 'success' ? '🟩' : '🟥';
// each job is converted into formatted markdown text
return `${icon} ${job.conclusion}: ${job.name} - ${job.html_url}`;
})
// and join with newlines for display in the template
.join('\n');
return template({ ...context, jobResults });
}
module.exports = {
addToContextAndValidate,
getMessageBody,
};
@@ -0,0 +1,9 @@
🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥
> :rocket: {{ env.NYM_PROJECT_NAME }}
> 🔴 **FAILURE** :cry:
> `when` {{ timestamp }}
> `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }}
> `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }}
> `build ` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }}
{{ jobResults }}
@@ -0,0 +1,9 @@
🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩
> :rocket: {{ env.NYM_PROJECT_NAME }}
> ✅ **SUCCESS**
> `when` {{ timestamp }}
> `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }}
> `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }}
> `build ` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }}
{{ jobResults }}
@@ -0,0 +1,153 @@
require('dotenv').config();
const Bot = require('keybase-bot');
let context = {
kinds: ['network-explorer', 'nightly'],
};
/**
* Validate that all required env and context vars are available
*/
function validateContext() {
if (!context.env.NYM_NOTIFICATION_KIND) {
throw new Error(
'Please set env var NYM_NOTIFICATION_KIND with the project kind that matches a directory in ".github/workflows/support-files"',
);
}
if (!context.kinds.includes(context.env.NYM_NOTIFICATION_KIND)) {
throw new Error(`Env var NYM_NOTIFICATION_KIND is not in ${context.kinds}`);
}
if (!context.env.NYM_PROJECT_NAME) {
throw new Error(
'Please set env var NYM_PROJECT_NAME with the project name for displaying in notification messages',
);
}
if (!context.env.KEYBASE_NYM_CHANNEL) {
throw new Error(
'Please set env var KEYBASE_NYM_CHANNEL with the channel name for the notification message',
);
}
if (!context.env.KEYBASE_NYMBOT_USERNAME) {
throw new Error(
'Username is not defined. Please set env var KEYBASE_NYMBOT_USERNAME',
);
}
if (!context.env.KEYBASE_NYMBOT_PAPERKEY) {
throw new Error(
'Paperkey is not defined. Please set env var KEYBASE_NYMBOT_PAPERKEY',
);
}
}
/**
* Creates a context that will be available in the templates for rendering notifications
*/
function createTemplateContext() {
const options = { dateStyle: 'full', timeStyle: 'long' };
context.timestamp = new Date().toLocaleString(undefined, options);
// add environment to template context and validate
context.env = process.env;
try {
validateContext();
} catch (e) {
if(process.env.SHOW_DEBUG) {
// recursively print the context for easy debugging and rethrow the error
console.dir({ context }, { depth: null });
}
throw e;
}
context.kind = context.env.NYM_NOTIFICATION_KIND;
context.keybase = {
channel: context.env.KEYBASE_NYM_CHANNEL,
username: context.env.KEYBASE_NYMBOT_USERNAME,
paperkey: context.env.KEYBASE_NYMBOT_PAPERKEY,
};
if (!context.env.GIT_BRANCH_NAME) {
context.env.GIT_BRANCH_NAME = context.env.GITHUB_REF.split('/')
.slice(2)
.join('/');
}
context.status = process.env.IS_SUCCESS === 'true' ? 'success' : 'failure';
}
async function sendKeybaseMessage(messageBody) {
const bot = new Bot();
try {
console.log(
`Initialising keybase with user "${
context.keybase.username
}" and key: "${'*'.repeat(context.keybase.paperkey.length)}"...`,
);
await bot.init(context.keybase.username, context.keybase.paperkey, {
verbose: false,
});
const channel = {
name: 'nymtech_bot',
membersType: 'team',
topicName: context.keybase.channel,
topic_type: 'CHAT',
};
const message = {
body: messageBody,
};
console.log(`Sending to ${channel.name}#${channel.topicName}...`);
await bot.chat.send(channel, message);
console.log('Message sent!');
} catch (error) {
console.error(error);
process.exitCode = -1;
} finally {
await bot.deinit();
}
}
/**
* Uses the `kind` set in the context to process the context and generate a notification message
* @returns {Promise<string>} A string notification message body
*/
async function processKindScript() {
const script = require(`../${context.kind}`);
if (!script.addToContextAndValidate) {
throw new Error(
`"./${context.kind}/index.js" does not export a method called "async addToContextAndValidate(context)"`,
);
}
if (!script.getMessageBody) {
throw new Error(
`"./${context.kind}/index.js" does not export a method called "async getMessageBody(context)"`,
);
}
// call the script to modify and validate the context
await script.addToContextAndValidate(context);
// let the script create a message body and return the result as a string for sending
return await script.getMessageBody(context);
}
/**
* The main function, as async so that await syntax is available
*/
async function main() {
createTemplateContext();
console.log(`Sending notification for kind "${context.kind}"...`);
const messageBody = await processKindScript();
if(process.env.SHOW_DEBUG) {
console.log('-----------------------------------------');
console.log(messageBody);
console.log('-----------------------------------------');
}
await sendKeybaseMessage(messageBody);
}
// call main function and let NodeJS handle the promise
main();
@@ -4,11 +4,14 @@
"version": "1.0.0",
"private": true,
"scripts": {
"format": "prettier --write send_message.js"
"dev": "node dev.js",
"format": "prettier --write **/*.js"
},
"dependencies": {
"dotenv": "^16.0.0",
"handlebars": "^4.7.7",
"keybase-bot": "^3.6.1"
"keybase-bot": "^3.6.1",
"octokit": "^1.7.1"
},
"devDependencies": {
"prettier": "2.3.2"
Generated
+117 -11
View File
@@ -68,16 +68,21 @@ dependencies = [
[[package]]
name = "anyhow"
version = "1.0.44"
version = "1.0.53"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61604a8f862e1d5c3229fdd78f8b02c68dcf73a4c4b05fd636d12240aaa242c1"
checksum = "94a45b455c14666b85fc40a019e8ab9eb75e3a124e05494f5397122bc9eb06e0"
[[package]]
name = "app"
version = "0.1.0"
dependencies = [
"bandwidth-claim-contract",
"bip39",
"coconut-interface",
"cosmwasm-std",
"credentials",
"cw3-flex-multisig",
"network-defaults",
"serde",
"serde_json",
"tauri",
@@ -1002,9 +1007,9 @@ dependencies = [
[[package]]
name = "cosmwasm-crypto"
version = "1.0.0-beta4"
version = "1.0.0-beta5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f903ebbabc0d4880dbc76148efb8be8fc29fa4bf294c440c3d70da1c8bcafff7"
checksum = "8904127a5b9e325ef5d6b2b3f997dcd74943cd35097139b1a4d15b1b6bccae66"
dependencies = [
"digest 0.9.0",
"ed25519-zebra",
@@ -1015,18 +1020,18 @@ dependencies = [
[[package]]
name = "cosmwasm-derive"
version = "1.0.0-beta4"
version = "1.0.0-beta5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "832bebef577ecb394603de8e2bf0de429b74aa364e17dec18e15ce37e71b0cae"
checksum = "a14364ac4d9d085867929d0cf3e94b1d2100121ce02c33c72961406830002613"
dependencies = [
"syn",
]
[[package]]
name = "cosmwasm-std"
version = "1.0.0-beta4"
version = "1.0.0-beta5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6238c45840cc9de5a39f0f619e3a4f7c38c5d2c6ac9e3e4d72751ee045e6d7da"
checksum = "e2ece12e5bbde434b93937d7b2107e6291f11d69ffa72398c50e8bab41d451d3"
dependencies = [
"base64",
"cosmwasm-crypto",
@@ -1336,6 +1341,99 @@ dependencies = [
"serde",
]
[[package]]
name = "cw-storage-plus"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c087ff98fb0475db4c2b5298a5fd12b2848d2854b39d1115d930ee6da24d1eed"
dependencies = [
"cosmwasm-std",
"schemars",
"serde",
]
[[package]]
name = "cw-utils"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3396c7aff5a0e3fb6dcc6cc89f56862c1d212b40d93ed725a6962955b1887ff"
dependencies = [
"cosmwasm-std",
"schemars",
"serde",
"thiserror",
]
[[package]]
name = "cw2"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f8a6500c396e33f6a7b05d35a5124eb3e394cdb6ca901f7e88332870407896c"
dependencies = [
"cosmwasm-std",
"cw-storage-plus 0.12.1",
"schemars",
"serde",
]
[[package]]
name = "cw3"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "017b6263414c58081cea97626f4d8da2dc8f7267337bba0eb5770260d26251e0"
dependencies = [
"cosmwasm-std",
"cw-utils",
"schemars",
"serde",
]
[[package]]
name = "cw3-fixed-multisig"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "949d65e60dbe8a24fc58b6b33143328af0abcc72e108a7ff80d2f3f8cabdf63b"
dependencies = [
"cosmwasm-std",
"cw-storage-plus 0.12.1",
"cw-utils",
"cw2",
"cw3",
"schemars",
"serde",
"thiserror",
]
[[package]]
name = "cw3-flex-multisig"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "297fe0521cf453ed58b582262cdb045862f126eb9390432e5689774badcfa370"
dependencies = [
"cosmwasm-std",
"cw-storage-plus 0.12.1",
"cw-utils",
"cw2",
"cw3",
"cw3-fixed-multisig",
"cw4",
"schemars",
"serde",
"thiserror",
]
[[package]]
name = "cw4"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f22da7845e99e5a277523d61c19bda3171ec3dd1084b9ca6ebac6b2fd0cdf084"
dependencies = [
"cosmwasm-std",
"cw-storage-plus 0.12.1",
"schemars",
"serde",
]
[[package]]
name = "darling"
version = "0.10.2"
@@ -2268,6 +2366,7 @@ dependencies = [
"log",
"network-defaults",
"nymsphinx",
"pemstore",
"rand 0.7.3",
"secp256k1",
"thiserror",
@@ -3845,10 +3944,11 @@ dependencies = [
name = "nym-gateway"
version = "0.12.1"
dependencies = [
"anyhow",
"bandwidth-claim-contract",
"bip39",
"bs58",
"clap 2.33.3",
"clap 3.0.10",
"coconut-interface",
"colored",
"config",
@@ -3866,6 +3966,7 @@ dependencies = [
"mixnode-common",
"network-defaults",
"nymsphinx",
"once_cell",
"pemstore",
"pretty_env_logger",
"rand 0.7.3",
@@ -3984,6 +4085,8 @@ version = "0.12.0"
dependencies = [
"anyhow",
"attohttpc 0.18.0",
"bandwidth-claim-contract",
"bip39",
"cfg-if 1.0.0",
"clap 2.33.3",
"coconut-interface",
@@ -3991,6 +4094,8 @@ dependencies = [
"console-subscriber",
"credentials",
"crypto",
"cw3",
"cw3-flex-multisig",
"dirs",
"dotenv",
"futures",
@@ -7723,7 +7828,7 @@ version = "0.1.0"
dependencies = [
"config",
"cosmwasm-std",
"cw-storage-plus",
"cw-storage-plus 0.11.1",
"mixnet-contract-common",
"schemars",
"serde",
@@ -7737,10 +7842,11 @@ version = "0.1.0"
dependencies = [
"config",
"cosmwasm-std",
"cw-storage-plus",
"cw-storage-plus 0.11.1",
"mixnet-contract-common",
"schemars",
"serde",
"ts-rs",
]
[[package]]
+13 -3
View File
@@ -1,8 +1,9 @@
all: clippy-all test wasm fmt
happy: clippy-happy test fmt
test: build clippy-all cargo-test wasm fmt
happy: fmt clippy-happy test
clippy-all: clippy-all-main clippy-all-contracts clippy-all-wallet
clippy-happy: clippy-happy-main clippy-happy-contracts clippy-happy-wallet
test: test-main test-contracts test-wallet
cargo-test: test-main test-contracts test-wallet
build: build-main build-contracts build-wallet
fmt: fmt-main fmt-contracts fmt-wallet
clippy-happy-main:
@@ -32,6 +33,15 @@ test-contracts:
test-wallet:
cargo test --manifest-path nym-wallet/Cargo.toml --all-features
build-main:
cargo build
build-contracts:
cargo build --manifest-path contracts/Cargo.toml
build-wallet:
cargo build --manifest-path nym-wallet/Cargo.toml
fmt-main:
cargo fmt --all
+30 -14
View File
@@ -121,12 +121,16 @@ impl<T: NymConfig> Config<T> {
self.client.testnet_mode = testnet_mode;
}
pub fn with_gateway_id<S: Into<String>>(&mut self, id: S) {
self.client.gateway_id = id.into();
pub fn with_gateway_endpoint<S: Into<String>>(&mut self, id: S, owner: S, listener: S) {
self.client.gateway_endpoint = GatewayEndpoint {
gateway_id: id.into(),
gateway_owner: owner.into(),
gateway_listener: listener.into(),
};
}
pub fn with_gateway_listener<S: Into<String>>(&mut self, gateway_listener: S) {
self.client.gateway_listener = gateway_listener.into();
pub fn with_gateway_id<S: Into<String>>(&mut self, id: S) {
self.client.gateway_endpoint.gateway_id = id.into();
}
#[cfg(not(feature = "coconut"))]
@@ -198,11 +202,15 @@ impl<T: NymConfig> Config<T> {
}
pub fn get_gateway_id(&self) -> String {
self.client.gateway_id.clone()
self.client.gateway_endpoint.gateway_id.clone()
}
pub fn get_gateway_owner(&self) -> String {
self.client.gateway_endpoint.gateway_owner.clone()
}
pub fn get_gateway_listener(&self) -> String {
self.client.gateway_listener.clone()
self.client.gateway_endpoint.gateway_listener.clone()
}
#[cfg(not(feature = "coconut"))]
@@ -272,6 +280,19 @@ impl<T: NymConfig> Default for Config<T> {
}
}
#[derive(Debug, Default, Deserialize, PartialEq, Serialize)]
struct GatewayEndpoint {
/// gateway_id specifies ID of the gateway to which the client should send messages.
/// If initially omitted, a random gateway will be chosen from the available topology.
gateway_id: String,
/// Address of the gateway owner to which the client should send messages.
gateway_owner: String,
/// Address of the gateway listener to which all client requests should be sent.
gateway_listener: String,
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
pub struct Client<T> {
/// Version of the client for which this configuration was created.
@@ -313,12 +334,8 @@ pub struct Client<T> {
/// sent but not received back.
reply_encryption_key_store_path: PathBuf,
/// gateway_id specifies ID of the gateway to which the client should send messages.
/// If initially omitted, a random gateway will be chosen from the available topology.
gateway_id: String,
/// Address of the gateway listener to which all client requests should be sent.
gateway_listener: String,
/// Information regarding how the client should send data to gateway.
gateway_endpoint: GatewayEndpoint,
/// Path to directory containing public/private keys used for bandwidth token purchase.
/// Those are saved in case of emergency, to be able to reclaim bandwidth tokens.
@@ -357,8 +374,7 @@ impl<T: NymConfig> Default for Client<T> {
gateway_shared_key_file: Default::default(),
ack_key_file: Default::default(),
reply_encryption_key_store_path: Default::default(),
gateway_id: "".to_string(),
gateway_listener: "".to_string(),
gateway_endpoint: Default::default(),
#[cfg(not(feature = "coconut"))]
backup_bandwidth_token_keys_dir: Default::default(),
#[cfg(not(feature = "coconut"))]
@@ -2441,9 +2441,9 @@
}
},
"node_modules/follow-redirects": {
"version": "1.14.7",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz",
"integrity": "sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==",
"version": "1.14.8",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.8.tgz",
"integrity": "sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==",
"dev": true,
"funding": [
{
@@ -6085,9 +6085,9 @@
}
},
"node_modules/url-parse": {
"version": "1.5.4",
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.4.tgz",
"integrity": "sha512-ITeAByWWoqutFClc/lRZnFplgXgEZr3WJ6XngMM/N9DMIm4K8zXPCZ1Jdu0rERwO84w1WC5wkle2ubwTA4NTBg==",
"version": "1.5.7",
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.7.tgz",
"integrity": "sha512-HxWkieX+STA38EDk7CE9MEryFeHCKzgagxlGvsdS7WBImq9Mk+PGwiT56w82WI3aicwJA8REp42Cxo98c8FZMA==",
"dev": true,
"dependencies": {
"querystringify": "^2.1.1",
@@ -8853,9 +8853,9 @@
}
},
"follow-redirects": {
"version": "1.14.7",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz",
"integrity": "sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==",
"version": "1.14.8",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.8.tgz",
"integrity": "sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==",
"dev": true
},
"for-in": {
@@ -11733,9 +11733,9 @@
}
},
"url-parse": {
"version": "1.5.4",
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.4.tgz",
"integrity": "sha512-ITeAByWWoqutFClc/lRZnFplgXgEZr3WJ6XngMM/N9DMIm4K8zXPCZ1Jdu0rERwO84w1WC5wkle2ubwTA4NTBg==",
"version": "1.5.7",
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.7.tgz",
"integrity": "sha512-HxWkieX+STA38EDk7CE9MEryFeHCKzgagxlGvsdS7WBImq9Mk+PGwiT56w82WI3aicwJA8REp42Cxo98c8FZMA==",
"dev": true,
"requires": {
"querystringify": "^2.1.1",
+11 -6
View File
@@ -59,12 +59,6 @@ eth_endpoint = '{{ client.eth_endpoint }}'
##### additional client config options #####
# ID of the gateway from which the client should be fetching messages.
gateway_id = '{{ client.gateway_id }}'
# Address of the gateway listener to which all client requests should be sent.
gateway_listener = '{{ client.gateway_listener }}'
# A gateway specific, optional, base58 stringified shared key used for
# communication with particular gateway.
gateway_shared_key_file = '{{ client.gateway_shared_key_file }}'
@@ -78,6 +72,17 @@ ack_key_file = '{{ client.ack_key_file }}'
# Absolute path to the home Nym Clients directory.
nym_root_directory = '{{ client.nym_root_directory }}'
[client.gateway_endpoint]
# ID of the gateway from which the client should be fetching messages.
gateway_id = '{{ client.gateway_endpoint.gateway_id }}'
# Address of the gateway owner to which the client should send messages.
gateway_owner = '{{ client.gateway_endpoint.gateway_owner }}'
# Address of the gateway listener to which all client requests should be sent.
gateway_listener = '{{ client.gateway_endpoint.gateway_listener }}'
##### socket config options #####
+5
View File
@@ -160,6 +160,10 @@ impl NymClient {
if gateway_id.is_empty() {
panic!("The identity of the gateway is unknown - did you run `nym-client` init?")
}
let gateway_owner = self.config.get_base().get_gateway_owner();
if gateway_owner.is_empty() {
panic!("The owner of the gateway is unknown - did you run `nym-client` init?")
}
let gateway_address = self.config.get_base().get_gateway_listener();
if gateway_address.is_empty() {
panic!("The address of the gateway is unknown - did you run `nym-client` init?")
@@ -185,6 +189,7 @@ impl NymClient {
gateway_address,
self.key_manager.identity_keypair(),
gateway_identity,
gateway_owner,
Some(self.key_manager.gateway_shared_key()),
mixnet_message_sender,
ack_sender,
+6 -6
View File
@@ -136,6 +136,7 @@ async fn register_with_gateway(
let mut gateway_client = GatewayClient::new_init(
gateway.clients_address(),
gateway.identity_key,
gateway.owner.clone(),
our_identity.clone(),
timeout,
);
@@ -255,15 +256,14 @@ pub async fn execute(matches: ArgMatches<'static>) {
chosen_gateway_id,
)
.await;
config
.get_base_mut()
.with_gateway_id(gateway_details.identity_key.to_base58_string());
let shared_keys =
register_with_gateway(&gateway_details, key_manager.identity_keypair()).await;
config
.get_base_mut()
.with_gateway_listener(gateway_details.clients_address());
config.get_base_mut().with_gateway_endpoint(
gateway_details.identity_key.to_base58_string(),
gateway_details.owner.clone(),
gateway_details.clients_address(),
);
key_manager.insert_gateway_shared_key(shared_keys);
let pathfinder = ClientKeyPathfinder::new_from_config(config.get_base());
+2 -2
View File
@@ -58,7 +58,7 @@ pub(crate) fn override_config(mut config: Config, matches: &ArgMatches<'_>) -> C
#[cfg(not(feature = "coconut"))]
if let Some(eth_endpoint) = matches.value_of(ETH_ENDPOINT_ARG_NAME) {
config.get_base_mut().with_eth_endpoint(eth_endpoint);
} else {
} else if !cfg!(feature = "eth") {
config
.get_base_mut()
.with_eth_endpoint(DEFAULT_ETH_ENDPOINT);
@@ -66,7 +66,7 @@ pub(crate) fn override_config(mut config: Config, matches: &ArgMatches<'_>) -> C
#[cfg(not(feature = "coconut"))]
if let Some(eth_private_key) = matches.value_of(ETH_PRIVATE_KEY_ARG_NAME) {
config.get_base_mut().with_eth_private_key(eth_private_key);
} else {
} else if !cfg!(feature = "eth") {
config
.get_base_mut()
.with_eth_private_key(DEFAULT_ETH_PRIVATE_KEY);
+11 -7
View File
@@ -59,12 +59,6 @@ eth_endpoint = '{{ client.eth_endpoint }}'
##### additional client config options #####
# ID of the gateway from which the client should be fetching messages.
gateway_id = '{{ client.gateway_id }}'
# Address of the gateway listener to which all client requests should be sent.
gateway_listener = '{{ client.gateway_listener }}'
# A gateway specific, optional, base58 stringified shared key used for
# communication with particular gateway.
gateway_shared_key_file = '{{ client.gateway_shared_key_file }}'
@@ -72,12 +66,22 @@ gateway_shared_key_file = '{{ client.gateway_shared_key_file }}'
# Path to file containing key used for encrypting and decrypting the content of an
# acknowledgement so that nobody besides the client knows which packet it refers to.
ack_key_file = '{{ client.ack_key_file }}'
##### advanced configuration options #####
# Absolute path to the home Nym Clients directory.
nym_root_directory = '{{ client.nym_root_directory }}'
[client.gateway_endpoint]
# ID of the gateway from which the client should be fetching messages.
gateway_id = '{{ client.gateway_endpoint.gateway_id }}'
# Address of the gateway owner to which the client should send messages.
gateway_owner = '{{ client.gateway_endpoint.gateway_owner }}'
# Address of the gateway listener to which all client requests should be sent.
gateway_listener = '{{ client.gateway_endpoint.gateway_listener }}'
##### socket config options #####
+5
View File
@@ -148,6 +148,10 @@ impl NymClient {
if gateway_id.is_empty() {
panic!("The identity of the gateway is unknown - did you run `nym-client` init?")
}
let gateway_owner = self.config.get_base().get_gateway_owner();
if gateway_owner.is_empty() {
panic!("The owner of the gateway is unknown - did you run `nym-client` init?")
}
let gateway_address = self.config.get_base().get_gateway_listener();
if gateway_address.is_empty() {
panic!("The address of the gateway is unknown - did you run `nym-client` init?")
@@ -173,6 +177,7 @@ impl NymClient {
gateway_address,
self.key_manager.identity_keypair(),
gateway_identity,
gateway_owner,
Some(self.key_manager.gateway_shared_key()),
mixnet_message_sender,
ack_sender,
+6 -6
View File
@@ -136,6 +136,7 @@ async fn register_with_gateway(
let mut gateway_client = GatewayClient::new_init(
gateway.clients_address(),
gateway.identity_key,
gateway.owner.clone(),
our_identity.clone(),
timeout,
);
@@ -256,15 +257,14 @@ pub async fn execute(matches: ArgMatches<'static>) {
chosen_gateway_id,
)
.await;
config
.get_base_mut()
.with_gateway_id(gateway_details.identity_key.to_base58_string());
let shared_keys =
register_with_gateway(&gateway_details, key_manager.identity_keypair()).await;
config
.get_base_mut()
.with_gateway_listener(gateway_details.clients_address());
config.get_base_mut().with_gateway_endpoint(
gateway_details.identity_key.to_base58_string(),
gateway_details.owner.clone(),
gateway_details.clients_address(),
);
key_manager.insert_gateway_shared_key(shared_keys);
let pathfinder = ClientKeyPathfinder::new_from_config(config.get_base());
+2 -2
View File
@@ -54,7 +54,7 @@ pub(crate) fn override_config(mut config: Config, matches: &ArgMatches<'_>) -> C
#[cfg(not(feature = "coconut"))]
if let Some(eth_endpoint) = matches.value_of(ETH_ENDPOINT_ARG_NAME) {
config.get_base_mut().with_eth_endpoint(eth_endpoint);
} else {
} else if !cfg!(feature = "eth") {
config
.get_base_mut()
.with_eth_endpoint(DEFAULT_ETH_ENDPOINT);
@@ -62,7 +62,7 @@ pub(crate) fn override_config(mut config: Config, matches: &ArgMatches<'_>) -> C
#[cfg(not(feature = "coconut"))]
if let Some(eth_private_key) = matches.value_of(ETH_PRIVATE_KEY_ARG_NAME) {
config.get_base_mut().with_eth_private_key(eth_private_key);
} else {
} else if !cfg!(feature = "eth") {
config
.get_base_mut()
.with_eth_private_key(DEFAULT_ETH_PRIVATE_KEY);
+17 -7
View File
@@ -11,6 +11,7 @@
"@tauri-apps/api": "^1.0.0-beta.4",
"compression": "^1.7.1",
"polka": "next",
"qrious": "^4.0.2",
"sirv": "^1.0.0"
},
"devDependencies": {
@@ -6758,6 +6759,11 @@
"node": ">=8"
}
},
"node_modules/qrious": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/qrious/-/qrious-4.0.2.tgz",
"integrity": "sha512-xWPJIrK1zu5Ypn898fBp8RHkT/9ibquV2Kv24S/JY9VYEhMBMKur1gHVsOiNUh7PHP9uCgejjpZUHUIXXKoU/g=="
},
"node_modules/query-string": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz",
@@ -7579,11 +7585,10 @@
"license": "MIT"
},
"node_modules/simple-get": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.0.tgz",
"integrity": "sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA==",
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz",
"integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==",
"dev": true,
"license": "MIT",
"dependencies": {
"decompress-response": "^4.2.0",
"once": "^1.3.1",
@@ -13474,6 +13479,11 @@
"escape-goat": "^2.0.0"
}
},
"qrious": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/qrious/-/qrious-4.0.2.tgz",
"integrity": "sha512-xWPJIrK1zu5Ypn898fBp8RHkT/9ibquV2Kv24S/JY9VYEhMBMKur1gHVsOiNUh7PHP9uCgejjpZUHUIXXKoU/g=="
},
"query-string": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz",
@@ -14036,9 +14046,9 @@
"dev": true
},
"simple-get": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.0.tgz",
"integrity": "sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA==",
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz",
"integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==",
"dev": true,
"requires": {
"decompress-response": "^4.2.0",
+6 -1
View File
@@ -15,6 +15,9 @@ build = "src/build.rs"
tauri-build = { version = "1.0.0-beta.2" }
[dependencies]
cw3-flex-multisig = "0.12.1"
cosmwasm-std = "1.0.0-beta5"
bip39 = "1.0.1"
serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
tauri = { version = "1.0.0-beta.4", features = [] }
@@ -23,7 +26,9 @@ url = "2.2"
coconut-interface = { path = "../../../common/coconut-interface" }
credentials = { path = "../../../common/credentials" }
validator-client = {path = "../../../common/client-libs/validator-client"}
validator-client = {path = "../../../common/client-libs/validator-client", features = ["nymd-client"] }
bandwidth-claim-contract = { path = "../../../common/bandwidth-claim-contract" }
network-defaults = { path = "../../../common/network-defaults" }
[features]
default = ["custom-protocol"]
+155 -42
View File
@@ -3,18 +3,28 @@
windows_subsystem = "windows"
)]
use bip39::Mnemonic;
use cosmwasm_std::{to_binary, CosmosMsg, WasmMsg};
use std::str::FromStr;
use std::sync::Arc;
use bandwidth_claim_contract::msg::ExecuteMsg;
use tokio::sync::RwLock;
use url::Url;
use coconut_interface::{
self, hash_to_scalar, Attribute, Credential, Parameters, Signature, Theta, VerificationKey,
self, hash_to_scalar, Attribute, Base58, Bytable, Credential, Parameters, Signature, Theta,
VerificationKey,
};
use credentials::{obtain_aggregate_signature, obtain_aggregate_verification_key};
use credentials::coconut::bandwidth::{
obtain_signature, verify_credential_remote, BandwidthVoucherAttributes,
};
use credentials::obtain_aggregate_verification_key;
use validator_client::nymd::{AccountId, CosmosCoin, Decimal, Denom, NymdClient};
struct State {
signatures: Vec<Signature>,
last_tx_hash: String,
n_attributes: u32,
params: Parameters,
serial_number: Attribute,
@@ -25,19 +35,12 @@ struct State {
}
impl State {
fn init(public_attributes_bytes: Vec<Vec<u8>>, private_attributes_bytes: Vec<Vec<u8>>) -> State {
let n_attributes = (public_attributes_bytes.len() + private_attributes_bytes.len()) as u32;
fn init(public_attributes: Vec<Attribute>, private_attributes: Vec<Attribute>) -> State {
let n_attributes = (public_attributes.len() + private_attributes.len()) as u32;
let params = Parameters::new(n_attributes).unwrap();
let public_attributes = public_attributes_bytes
.iter()
.map(hash_to_scalar)
.collect::<Vec<Attribute>>();
let private_attributes = private_attributes_bytes
.iter()
.map(hash_to_scalar)
.collect::<Vec<Attribute>>();
State {
signatures: Vec::new(),
last_tx_hash: String::new(),
n_attributes,
params,
serial_number: private_attributes[0],
@@ -61,15 +64,40 @@ fn parse_url_validators(raw: &[String]) -> Result<Vec<Url>, String> {
}
#[tauri::command]
async fn randomise_credential(
idx: usize,
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<Vec<Signature>, String> {
async fn deposit_funds(state: tauri::State<'_, Arc<RwLock<State>>>) -> Result<String, String> {
let nymd_url = Url::from_str("http://127.0.0.1:26657").unwrap();
let mnemonic = Mnemonic::from_str(&"sun surge soon stomach flavor country gorilla dress oblige stamp attract hip soldier agree steel prize nuclear know enjoy arm bargain always theme matter").unwrap();
let nymd_client = NymdClient::connect_with_mnemonic(
network_defaults::all::Network::SANDBOX,
nymd_url.as_ref(),
None,
None,
AccountId::from_str("nymt1sthrn5ep8ls5vzz8f9gp89khhmedahhdqdmmps").ok(),
mnemonic,
None,
)
.expect("Could not create nymd client");
let req = ExecuteMsg::BuyBandwidth {};
let funds = vec![CosmosCoin {
denom: Denom::from_str(network_defaults::sandbox::DENOM).unwrap(),
amount: Decimal::from(1000000u64),
}];
let last_tx_hash = nymd_client
.execute(
nymd_client.erc20_bridge_contract_address().unwrap(),
&req,
Default::default(),
"",
funds,
)
.await
.unwrap()
.transaction_hash
.to_string();
println!("Tx hash: {}", last_tx_hash);
let mut state = state.write().await;
let signature = state.signatures.remove(idx);
let (new_signature, _) = signature.randomise(&state.params);
state.signatures.insert(idx, new_signature);
Ok(state.signatures.clone())
state.last_tx_hash = last_tx_hash.clone();
Ok(last_tx_hash)
}
#[tauri::command]
@@ -145,26 +173,100 @@ async fn verify_credential(
// the API needs to be improved but at least it should compile (in theory)
let verification_key =
get_aggregated_verification_key(validator_urls.clone(), state.clone()).await?;
let parsed_urls = parse_url_validators(&validator_urls)?;
println!("Verification key {:?}", verification_key.to_bs58());
let theta = prove_credential(idx, validator_urls, state.clone()).await?;
let state = state.read().await;
let public_attributes_bytes = vec![
state.voucher_value.to_bytes().to_vec(),
state.voucher_info.to_bytes().to_vec(),
state.voucher_value.to_byte_vec(),
state.voucher_info.to_byte_vec(),
];
let credential = Credential::new(
let mut credential = Credential::new(
state.n_attributes,
theta,
theta.clone(),
public_attributes_bytes,
state
.signatures
.get(idx)
.ok_or("Got invalid signature idx")?,
0,
);
println!("Using verification key: {:?}", verification_key.to_bs58());
let local_check = credential.verify(&verification_key);
println!("Local check: {}", local_check);
if !local_check {
return Ok(false);
}
Ok(credential.verify(&verification_key))
let mnemonic = Mnemonic::from_str("sun surge soon stomach flavor country gorilla dress oblige stamp attract hip soldier agree steel prize nuclear know enjoy arm bargain always theme matter").unwrap();
let nymd_url = Url::from_str("http://127.0.0.1:26657").unwrap();
let nymd_client = NymdClient::connect_with_mnemonic(
network_defaults::all::Network::SANDBOX,
nymd_url.as_ref(),
None,
None,
AccountId::from_str("nymt1sthrn5ep8ls5vzz8f9gp89khhmedahhdqdmmps").ok(),
mnemonic,
None,
)
.expect("Could not create nymd client");
let req = cw3_flex_multisig::msg::ExecuteMsg::Propose {
title: "Spend coconut".to_string(),
description: "Propose to spend a coconut cred".to_string(),
msgs: vec![CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr: "nymt1sthrn5ep8ls5vzz8f9gp89khhmedahhdqdmmps".to_string(),
msg: to_binary(&ExecuteMsg::SpendCredential { amount: 1000000u64 }).unwrap(),
funds: vec![],
})],
latest: None,
};
let tx = nymd_client
.execute(
&AccountId::from_str("nymt1qwlgtx52gsdu7dtp0cekka5zehdl0uj3vqx3jd").unwrap(),
&req,
Default::default(),
"",
vec![],
)
.await
.unwrap();
let event = tx.logs[0]
.events
.iter()
.find(|event| event.ty == "wasm")
.unwrap();
let proposal_id = u64::from_str(
&event
.attributes
.iter()
.find(|attr| attr.key == "proposal_id")
.unwrap()
.value,
)
.unwrap();
println!("Got proposal id {}", proposal_id);
credential.set_proposal_id(proposal_id);
let remote_check = verify_credential_remote(&parsed_urls, credential)
.await
.unwrap();
println!("Remote check: {}", remote_check);
let req = cw3_flex_multisig::msg::ExecuteMsg::Execute { proposal_id };
nymd_client
.execute(
&AccountId::from_str("nymt1qwlgtx52gsdu7dtp0cekka5zehdl0uj3vqx3jd").unwrap(),
&req,
Default::default(),
"",
vec![],
)
.await
.unwrap();
Ok(remote_check)
}
#[tauri::command]
@@ -172,19 +274,25 @@ async fn get_credential(
validator_urls: Vec<String>,
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<Vec<Signature>, String> {
let guard = state.read().await;
let parsed_urls = parse_url_validators(&validator_urls)?;
let public_attributes = vec![guard.voucher_value, guard.voucher_info];
let private_attributes = vec![guard.serial_number, guard.binding_number];
let signature = {
let guard = state.read().await;
let parsed_urls = parse_url_validators(&validator_urls)?;
let bandwidth_credential_attributes = BandwidthVoucherAttributes {
serial_number: guard.serial_number,
binding_number: guard.binding_number,
voucher_value: guard.voucher_value,
voucher_info: guard.voucher_info,
};
let signature = obtain_aggregate_signature(
&guard.params,
&public_attributes,
&private_attributes,
&parsed_urls,
)
.await
.map_err(|err| format!("failed to obtain aggregate signature - {:?}", err))?;
obtain_signature(
&guard.params,
&bandwidth_credential_attributes,
&parsed_urls,
guard.last_tx_hash.clone(),
)
.await
.map_err(|err| format!("failed to obtain aggregate signature - {:?}", err))?
};
let mut state = state.write().await;
state.signatures.push(signature);
@@ -192,16 +300,21 @@ async fn get_credential(
}
fn main() {
let public_attributes = vec![b"public_key".to_vec()];
let private_attributes = vec![b"private_key".to_vec()];
let params = coconut_interface::Parameters::new(4).unwrap();
let bandwidth_credential_attributes = BandwidthVoucherAttributes {
serial_number: params.random_scalar(),
binding_number: params.random_scalar(),
voucher_value: Attribute::from(1000000u64),
voucher_info: hash_to_scalar("BandwidthVoucher"),
};
tauri::Builder::default()
.manage(Arc::new(RwLock::new(State::init(
public_attributes,
private_attributes,
bandwidth_credential_attributes.get_public_attributes(),
bandwidth_credential_attributes.get_private_attributes(),
))))
.invoke_handler(tauri::generate_handler![
get_credential,
randomise_credential,
deposit_funds,
delete_credential,
list_credentials,
verify_credential
+6 -11
View File
@@ -3,9 +3,10 @@
import {onMount} from "svelte";
import QRious from "qrious";
const validator_urls = ["http://localhost:8080"];
const validator_urls = ["http://localhost:8080", "http://localhost:8081", "http://localhost:8082"];
let signatures = [];
let qrVisible = false;
let tx_hash = "";
async function getCredential() {
signatures = await invoke("get_credential", {
@@ -13,10 +14,8 @@
});
}
async function randomiseCredential(idx) {
signatures = await invoke("randomise_credential", {
idx: idx,
});
async function depositFunds() {
tx_hash = await invoke("deposit_funds");
}
async function verifyCredential(idx) {
@@ -24,7 +23,7 @@
idx: idx,
validatorUrls: validator_urls,
});
alert(response);
qrVisible = !response;
}
async function deleteCredential(idx) {
@@ -58,6 +57,7 @@
<title>Coconut</title>
</svelte:head>
<button class="btn btn-success" on:click={depositFunds}>Deposit</button>
<button class="btn btn-success" on:click={getCredential}>Get Credential</button>
<hr />
<table class="table table-dark">
@@ -67,11 +67,6 @@
<td>
<div class="btn-group" role="group" aria-label="Basic example">
<button
class="btn btn-primary"
on:click={() => {
randomiseCredential(idx);
}}>Randomize</button
><button
class="btn btn-danger"
on:click={() => {
deleteCredential(idx);
+7 -7
View File
@@ -1813,7 +1813,7 @@ decompress-response@^3.2.0, decompress-response@^3.3.0:
decompress-response@^4.2.0:
version "4.2.1"
resolved "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz"
resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-4.2.1.tgz#414023cc7a302da25ce2ec82d0d5238ccafd8986"
integrity sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==
dependencies:
mimic-response "^2.0.0"
@@ -3130,7 +3130,7 @@ mimic-response@^1.0.0, mimic-response@^1.0.1:
mimic-response@^2.0.0:
version "2.1.0"
resolved "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz"
resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-2.1.0.tgz#d13763d35f613d09ec37ebb30bac0469c0ee8f43"
integrity sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==
mimic-response@^3.1.0:
@@ -4056,13 +4056,13 @@ signal-exit@^3.0.0, signal-exit@^3.0.2:
simple-concat@^1.0.0:
version "1.0.1"
resolved "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz"
resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f"
integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==
simple-get@^3.0.3, simple-get@^3.1.0:
version "3.1.0"
resolved "https://registry.npmjs.org/simple-get/-/simple-get-3.1.0.tgz"
integrity sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA==
version "3.1.1"
resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-3.1.1.tgz#cc7ba77cfbe761036fbfce3d021af25fc5584d55"
integrity sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==
dependencies:
decompress-response "^4.2.0"
once "^1.3.1"
@@ -4673,7 +4673,7 @@ wrap-ansi@^7.0.0:
wrappy@1:
version "1.0.2"
resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz"
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
write-file-atomic@^3.0.0:
+19 -16
View File
@@ -22,7 +22,10 @@
"webpack-dev-server": "^3.11.0"
}
},
"../pkg": {},
"../pkg": {
"name": "@nymproject/nym-client-wasm",
"version": "0.0.1"
},
"node_modules/@nymproject/nym-client-wasm": {
"resolved": "../pkg",
"link": true
@@ -2351,9 +2354,9 @@
}
},
"node_modules/follow-redirects": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.1.tgz",
"integrity": "sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==",
"version": "1.14.8",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.8.tgz",
"integrity": "sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==",
"dev": true,
"funding": [
{
@@ -5859,9 +5862,9 @@
}
},
"node_modules/url-parse": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.1.tgz",
"integrity": "sha512-HOfCOUJt7iSYzEx/UqgtwKRMC6EU91NFhsCHMv9oM03VJcVo2Qrp8T8kI9D7amFf1cu+/3CEhgb3rF9zL7k85Q==",
"version": "1.5.7",
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.7.tgz",
"integrity": "sha512-HxWkieX+STA38EDk7CE9MEryFeHCKzgagxlGvsdS7WBImq9Mk+PGwiT56w82WI3aicwJA8REp42Cxo98c8FZMA==",
"dev": true,
"dependencies": {
"querystringify": "^2.1.1",
@@ -6500,9 +6503,9 @@
"dev": true
},
"node_modules/ws": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz",
"integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==",
"version": "6.2.2",
"resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz",
"integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==",
"dev": true,
"dependencies": {
"async-limiter": "~1.0.0"
@@ -8542,9 +8545,9 @@
}
},
"follow-redirects": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.1.tgz",
"integrity": "sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==",
"version": "1.14.8",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.8.tgz",
"integrity": "sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==",
"dev": true
},
"for-in": {
@@ -11399,9 +11402,9 @@
}
},
"url-parse": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.1.tgz",
"integrity": "sha512-HOfCOUJt7iSYzEx/UqgtwKRMC6EU91NFhsCHMv9oM03VJcVo2Qrp8T8kI9D7amFf1cu+/3CEhgb3rF9zL7k85Q==",
"version": "1.5.7",
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.7.tgz",
"integrity": "sha512-HxWkieX+STA38EDk7CE9MEryFeHCKzgagxlGvsdS7WBImq9Mk+PGwiT56w82WI3aicwJA8REp42Cxo98c8FZMA==",
"dev": true,
"requires": {
"querystringify": "^2.1.1",
File diff suppressed because it is too large Load Diff
@@ -8,7 +8,7 @@
"@testing-library/react": "^9.5.0",
"@testing-library/user-event": "^7.2.1",
"bootstrap": "^4.5.0",
"node-sass": "^4.14.1",
"node-sass": "^7.0.0",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-scripts": "^4.0.0",
+1
View File
@@ -127,6 +127,7 @@ impl NymClient {
gateway.clients_address(),
Arc::clone(&client.identity),
gateway.identity_key,
gateway.owner.clone(),
None,
mixnet_messages_sender,
ack_sender,
@@ -0,0 +1,8 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
// event types
pub const VOUCHER_ACQUIRED_EVENT_TYPE: &str = "coconut_acquired";
// attributes that are used in multiple places
pub const VOUCHER_VALUE: &str = "value";
@@ -1,3 +1,4 @@
pub mod events;
pub mod keys;
pub mod msg;
pub mod payment;
@@ -14,6 +14,8 @@ pub struct InstantiateMsg {}
#[serde(rename_all = "snake_case")]
pub enum ExecuteMsg {
LinkPayment { data: LinkPaymentData },
BuyBandwidth {},
SpendCredential { amount: u64 },
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
@@ -23,6 +23,7 @@ credentials = { path = "../../credentials" }
crypto = { path = "../../crypto" }
gateway-requests = { path = "../../../gateway/gateway-requests" }
nymsphinx = { path = "../../nymsphinx" }
pemstore = { path = "../../pemstore" }
coconut-interface = { path = "../../coconut-interface", optional = true }
network-defaults = { path = "../../network-defaults" }
@@ -16,22 +16,27 @@ use crypto::asymmetric::identity::PublicKey;
use network_defaults::BANDWIDTH_VALUE;
#[cfg(not(feature = "coconut"))]
use network_defaults::{
eth_contract::ETH_JSON_ABI, ETH_BURN_FUNCTION_NAME, ETH_CONTRACT_ADDRESS, ETH_MIN_BLOCK_DEPTH,
TOKENS_TO_BURN,
eth_contract::ETH_ERC20_JSON_ABI, eth_contract::ETH_JSON_ABI, ETH_BURN_FUNCTION_NAME,
ETH_CONTRACT_ADDRESS, ETH_ERC20_APPROVE_FUNCTION_NAME, ETH_ERC20_CONTRACT_ADDRESS,
ETH_MIN_BLOCK_DEPTH, TOKENS_TO_BURN, UTOKENS_TO_BURN,
};
#[cfg(not(feature = "coconut"))]
use pemstore::traits::PemStorableKeyPair;
#[cfg(not(feature = "coconut"))]
use rand::rngs::OsRng;
#[cfg(not(feature = "coconut"))]
use secp256k1::SecretKey;
#[cfg(not(feature = "coconut"))]
use std::io::Write;
use std::io::{Read, Write};
#[cfg(not(feature = "coconut"))]
use std::str::FromStr;
#[cfg(not(feature = "coconut"))]
use web3::{
contract::{Contract, Options},
ethabi::Token,
signing::{Key, SecretKeyRef},
transports::Http,
types::{Address, Bytes, U256, U64},
types::{Address, U256, U64},
Web3,
};
@@ -50,6 +55,19 @@ pub fn eth_contract(web3: Web3<Http>) -> Contract<Http> {
.expect("Invalid json abi")
}
#[cfg(not(feature = "coconut"))]
pub fn eth_erc20_contract(web3: Web3<Http>) -> Contract<Http> {
Contract::from_json(
web3.eth(),
Address::from(ETH_ERC20_CONTRACT_ADDRESS),
json::parse(ETH_ERC20_JSON_ABI)
.expect("Invalid json abi")
.dump()
.as_bytes(),
)
.expect("Invalid json abi")
}
#[derive(Clone)]
pub struct BandwidthController {
#[cfg(feature = "coconut")]
@@ -59,6 +77,8 @@ pub struct BandwidthController {
#[cfg(not(feature = "coconut"))]
contract: Contract<Http>,
#[cfg(not(feature = "coconut"))]
erc20_contract: Contract<Http>,
#[cfg(not(feature = "coconut"))]
eth_private_key: SecretKey,
#[cfg(not(feature = "coconut"))]
backup_bandwidth_token_keys_dir: std::path::PathBuf,
@@ -84,12 +104,14 @@ impl BandwidthController {
Http::new(&eth_endpoint).map_err(|_| GatewayClientError::InvalidURL(eth_endpoint))?;
let web3 = web3::Web3::new(transport);
// Fail early, on invalid abi
let contract = eth_contract(web3);
let contract = eth_contract(web3.clone());
let erc20_contract = eth_erc20_contract(web3);
let eth_private_key = secp256k1::SecretKey::from_str(&eth_private_key)
.map_err(|_| GatewayClientError::InvalidEthereumPrivateKey)?;
Ok(BandwidthController {
contract,
erc20_contract,
eth_private_key,
backup_bandwidth_token_keys_dir,
})
@@ -107,6 +129,45 @@ impl BandwidthController {
Ok(())
}
#[cfg(not(feature = "coconut"))]
fn restore_keypair(&self) -> Result<identity::KeyPair, GatewayClientError> {
std::fs::create_dir_all(&self.backup_bandwidth_token_keys_dir)?;
let file = std::fs::read_dir(&self.backup_bandwidth_token_keys_dir)?
.find(|entry| {
entry
.as_ref()
.map(|entry| entry.path().is_file())
.unwrap_or(false)
})
.unwrap_or_else(|| Err(std::io::Error::from(std::io::ErrorKind::NotFound)))?;
let file_path = file.path();
let pub_key = file_path
.file_name()
.ok_or_else(|| std::io::Error::from(std::io::ErrorKind::NotFound))?
.to_str()
.ok_or_else(|| std::io::Error::from(std::io::ErrorKind::NotFound))?;
let mut priv_key = vec![];
std::fs::File::open(file_path.clone())?.read_to_end(&mut priv_key)?;
Ok(identity::KeyPair::from_keys(
identity::PrivateKey::from_bytes(&priv_key).unwrap(),
identity::PublicKey::from_base58_string(pub_key).unwrap(),
))
}
#[cfg(not(feature = "coconut"))]
fn mark_keypair_as_spent(&self, keypair: &identity::KeyPair) -> Result<(), GatewayClientError> {
let mut spent_dir = self.backup_bandwidth_token_keys_dir.clone();
spent_dir.push("spent");
std::fs::create_dir_all(&spent_dir)?;
let file_path_old = self
.backup_bandwidth_token_keys_dir
.join(keypair.public_key().to_base58_string());
let file_path_new = spent_dir.join(keypair.public_key().to_base58_string());
std::fs::rename(file_path_old, file_path_new)?;
Ok(())
}
#[cfg(feature = "coconut")]
pub async fn prepare_coconut_credential(
&self,
@@ -128,6 +189,7 @@ impl BandwidthController {
&params,
&bandwidth_credential_attributes,
&self.validator_endpoints,
String::new(),
)
.await?;
// the above would presumably be loaded from a file
@@ -145,17 +207,25 @@ impl BandwidthController {
pub async fn prepare_token_credential(
&self,
gateway_identity: PublicKey,
gateway_owner: String,
) -> Result<TokenCredential, GatewayClientError> {
let mut rng = OsRng;
let kp = identity::KeyPair::new(&mut rng);
self.backup_keypair(&kp)?;
let kp = match self.restore_keypair() {
Ok(kp) => kp,
Err(_) => {
let mut rng = OsRng;
let kp = identity::KeyPair::new(&mut rng);
self.backup_keypair(&kp)?;
kp
}
};
let verification_key = *kp.public_key();
let signed_verification_key = kp.private_key().sign(&verification_key.to_bytes());
self.buy_token_credential(verification_key, signed_verification_key)
self.buy_token_credential(verification_key, signed_verification_key, gateway_owner)
.await?;
self.mark_keypair_as_spent(&kp)?;
let message: Vec<u8> = verification_key
.to_bytes()
.iter()
@@ -177,28 +247,109 @@ impl BandwidthController {
&self,
verification_key: PublicKey,
signed_verification_key: identity::Signature,
gateway_owner: String,
) -> Result<(), GatewayClientError> {
// 0 means a transaction failure, 1 means success
let confirmations = if cfg!(debug_assertions) {
1
} else {
ETH_MIN_BLOCK_DEPTH
};
// 15 seconds per confirmation block + 10 seconds of network overhead
// 15 seconds per confirmation block + 10 seconds of network overhead + 20 seconds of wait for kill
log::info!(
"Waiting for Ethereum transaction. This should take about {} seconds",
confirmations * 15 + 10
(confirmations + 1) * 15 + 30
);
let mut options = Options::default();
let estimation = self
.erc20_contract
.estimate_gas(
ETH_ERC20_APPROVE_FUNCTION_NAME,
(
Token::Address(Address::from(ETH_CONTRACT_ADDRESS)),
Token::Uint(U256::from(UTOKENS_TO_BURN)),
),
SecretKeyRef::from(&self.eth_private_key).address(),
options.clone(),
)
.await?;
options.gas = Some(estimation);
log::info!("Calling ERC20 approve in 10 seconds with an estimated gas of {}. Kill the process if you want to abort", estimation);
#[cfg(not(target_arch = "wasm32"))]
tokio::time::sleep(tokio::time::Duration::from_secs(10)).await;
#[cfg(target_arch = "wasm32")]
if let Err(err) = fluvio_wasm_timer::Delay::new(std::time::Duration::from_secs(10)).await {
log::error!(
"the timer has gone away while waiting for possible kill! - {}",
err
);
}
let recipt = self
.erc20_contract
.signed_call_with_confirmations(
ETH_ERC20_APPROVE_FUNCTION_NAME,
(
Token::Address(Address::from(ETH_CONTRACT_ADDRESS)),
Token::Uint(U256::from(UTOKENS_TO_BURN)),
),
options,
1, // One confirmation is enough, as we'll be consuming the approved token next anyway
&self.eth_private_key,
)
.await?;
if Some(U64::from(0u64)) == recipt.status {
return Err(GatewayClientError::BurnTokenError(
web3::Error::InvalidResponse(format!(
"Approve transaction status is 0 (failure): {:?}",
recipt.logs,
)),
));
} else {
log::info!(
"Approved {} tokens for bandwidth use on Ethereum",
TOKENS_TO_BURN
);
}
let mut options = Options::default();
let estimation = self
.contract
.estimate_gas(
ETH_BURN_FUNCTION_NAME,
(
Token::Uint(U256::from(UTOKENS_TO_BURN)),
Token::Uint(U256::from(&verification_key.to_bytes())),
Token::Bytes(signed_verification_key.to_bytes().to_vec()),
Token::String(gateway_owner.clone()),
),
SecretKeyRef::from(&self.eth_private_key).address(),
options.clone(),
)
.await?;
options.gas = Some(estimation);
log::info!("Generating bandwidth on ETH contract in 10 seconds with an estimated gas of {}. \
Kill the process if you want to abort. Keep in mind that if you abort now, you'll still have \
some tokens approved for bandwidth spending from the previous action. \
If you don't want that, you'll need to manually decreaseAllowance to revert the approval.", estimation);
#[cfg(not(target_arch = "wasm32"))]
tokio::time::sleep(tokio::time::Duration::from_secs(10)).await;
#[cfg(target_arch = "wasm32")]
if let Err(err) = fluvio_wasm_timer::Delay::new(std::time::Duration::from_secs(10)).await {
log::error!(
"the timer has gone away while waiting for possible kill! - {}",
err
);
}
let recipt = self
.contract
.signed_call_with_confirmations(
ETH_BURN_FUNCTION_NAME,
(
U256::from(TOKENS_TO_BURN),
U256::from(&verification_key.to_bytes()),
Bytes(signed_verification_key.to_bytes().to_vec()),
Token::Uint(U256::from(UTOKENS_TO_BURN)),
Token::Uint(U256::from(&verification_key.to_bytes())),
Token::Bytes(signed_verification_key.to_bytes().to_vec()),
Token::String(gateway_owner),
),
Options::default(),
options,
confirmations,
&self.eth_private_key,
)
@@ -236,6 +387,15 @@ mod tests {
eth_contract(web3);
}
#[test]
fn parse_erc20_contract() {
let transport =
Http::new("https://rinkeby.infura.io/v3/00000000000000000000000000000000").unwrap();
let web3 = web3::Web3::new(transport);
// test no panic occurs
eth_erc20_contract(web3);
}
#[test]
fn check_event_name_constant_against_abi() {
let transport =
+23 -19
View File
@@ -20,6 +20,7 @@ use gateway_requests::iv::IV;
use gateway_requests::registration::handshake::{client_handshake, SharedKeys};
use gateway_requests::{BinaryRequest, ClientControlRequest, ServerResponse};
use log::*;
use network_defaults::{REMAINING_BANDWIDTH_THRESHOLD, TOKENS_TO_BURN};
use nymsphinx::forwarding::packet::MixPacket;
use rand::rngs::OsRng;
use std::convert::TryFrom;
@@ -44,6 +45,7 @@ pub struct GatewayClient {
bandwidth_remaining: i64,
gateway_address: String,
gateway_identity: identity::PublicKey,
gateway_owner: String,
local_identity: Arc<identity::KeyPair>,
shared_key: Option<Arc<SharedKeys>>,
connection: SocketState,
@@ -68,6 +70,7 @@ impl GatewayClient {
gateway_address: String,
local_identity: Arc<identity::KeyPair>,
gateway_identity: identity::PublicKey,
gateway_owner: String,
shared_key: Option<Arc<SharedKeys>>,
mixnet_message_sender: MixnetMessageSender,
ack_sender: AcknowledgementSender,
@@ -80,6 +83,7 @@ impl GatewayClient {
bandwidth_remaining: 0,
gateway_address,
gateway_identity,
gateway_owner,
local_identity,
shared_key,
connection: SocketState::NotConnected,
@@ -112,6 +116,7 @@ impl GatewayClient {
pub fn new_init(
gateway_address: String,
gateway_identity: identity::PublicKey,
gateway_owner: String,
local_identity: Arc<identity::KeyPair>,
response_timeout_duration: Duration,
) -> Self {
@@ -129,6 +134,7 @@ impl GatewayClient {
bandwidth_remaining: 0,
gateway_address,
gateway_identity,
gateway_owner,
local_identity,
shared_key: None,
connection: SocketState::NotConnected,
@@ -548,6 +554,8 @@ impl GatewayClient {
return self.try_claim_testnet_bandwidth().await;
}
let _gateway_owner = self.gateway_owner.clone();
#[cfg(feature = "coconut")]
let credential = self
.bandwidth_controller
@@ -560,7 +568,7 @@ impl GatewayClient {
.bandwidth_controller
.as_ref()
.unwrap()
.prepare_token_credential(self.gateway_identity)
.prepare_token_credential(self.gateway_identity, _gateway_owner)
.await?;
#[cfg(feature = "coconut")]
@@ -584,15 +592,10 @@ impl GatewayClient {
return Err(GatewayClientError::NotAuthenticated);
}
if self.estimate_required_bandwidth(&packets) > self.bandwidth_remaining {
// Try to claim more bandwidth first, and return an error only if that is still not
// enough (the current granularity for bandwidth should be sufficient)
self.claim_bandwidth().await?;
if self.estimate_required_bandwidth(&packets) > self.bandwidth_remaining {
return Err(GatewayClientError::NotEnoughBandwidth(
self.estimate_required_bandwidth(&packets),
self.bandwidth_remaining,
));
}
return Err(GatewayClientError::NotEnoughBandwidth(
self.estimate_required_bandwidth(&packets),
self.bandwidth_remaining,
));
}
if !self.connection.is_established() {
return Err(GatewayClientError::ConnectionNotEstablished);
@@ -659,15 +662,10 @@ impl GatewayClient {
return Err(GatewayClientError::NotAuthenticated);
}
if (mix_packet.sphinx_packet().len() as i64) > self.bandwidth_remaining {
// Try to claim more bandwidth first, and return an error only if that is still not
// enough
self.claim_bandwidth().await?;
if (mix_packet.sphinx_packet().len() as i64) > self.bandwidth_remaining {
return Err(GatewayClientError::NotEnoughBandwidth(
mix_packet.sphinx_packet().len() as i64,
self.bandwidth_remaining,
));
}
return Err(GatewayClientError::NotEnoughBandwidth(
mix_packet.sphinx_packet().len() as i64,
self.bandwidth_remaining,
));
}
if !self.connection.is_established() {
return Err(GatewayClientError::ConnectionNotEstablished);
@@ -737,6 +735,12 @@ impl GatewayClient {
}
let shared_key = self.perform_initial_authentication().await?;
if self.bandwidth_remaining < REMAINING_BANDWIDTH_THRESHOLD {
info!("Claiming more bandwidth for your tokens. This will use {} token(s) from your wallet. \
Stop the process now if you don't want that to happen.", TOKENS_TO_BURN);
self.claim_bandwidth().await?;
}
// this call is NON-blocking
self.start_listening_for_mixnet_messages()?;
@@ -8,7 +8,7 @@ use tungstenite::Error as WsError;
#[cfg(target_arch = "wasm32")]
use wasm_bindgen::JsValue;
#[cfg(not(feature = "coconut"))]
use web3::Error as Web3Error;
use web3::{contract::Error as Web3ContractError, Error as Web3Error};
#[derive(Debug, Error)]
pub enum GatewayClientError {
@@ -27,13 +27,17 @@ pub enum GatewayClientError {
NetworkErrorWasm(JsValue),
#[cfg(not(feature = "coconut"))]
#[error("Could not backup keypair - {0}")]
#[error("Keypair IO error - {0}")]
IOError(#[from] std::io::Error),
#[cfg(not(feature = "coconut"))]
#[error("Could not burn ERC20 token in Ethereum smart contract - {0}")]
BurnTokenError(#[from] Web3Error),
#[cfg(not(feature = "coconut"))]
#[error("Could not run web3 contract - {0}")]
Web3ContractError(#[from] Web3ContractError),
#[cfg(not(feature = "coconut"))]
#[error("Invalid Ethereum private key")]
InvalidEthereumPrivateKey,
@@ -39,7 +39,8 @@ flate2 = { version = "1.0.20", optional = true }
sha2 = { version = "0.9.5", optional = true }
itertools = { version = "0.10", optional = true }
cosmwasm-std = { version = "1.0.0-beta3", optional = true }
ts-rs = { version = "5.1", optional = true }
# Leaving it as * so that it inherits whatever the wallet is using
ts-rs = { version = "*", optional = true }
[features]
nymd-client = [
@@ -2,7 +2,10 @@
// SPDX-License-Identifier: Apache-2.0
use crate::{validator_api, ValidatorClientError};
use coconut_interface::{BlindSignRequestBody, BlindedSignatureResponse, VerificationKeyResponse};
use coconut_interface::{
BlindSignRequestBody, BlindedSignatureResponse, Credential, VerificationKeyResponse,
VerifyCredentialResponse,
};
use mixnet_contract_common::{GatewayBond, IdentityKeyRef, MixNodeBond};
use url::Url;
use validator_api_requests::models::{
@@ -35,6 +38,7 @@ pub struct Config {
nymd_url: Url,
mixnet_contract_address: Option<cosmrs::AccountId>,
vesting_contract_address: Option<cosmrs::AccountId>,
erc20_bridge_contract_address: Option<cosmrs::AccountId>,
mixnode_page_limit: Option<u32>,
gateway_page_limit: Option<u32>,
@@ -50,12 +54,14 @@ impl Config {
api_url: Url,
mixnet_contract_address: Option<cosmrs::AccountId>,
vesting_contract_address: Option<cosmrs::AccountId>,
erc20_bridge_contract_address: Option<cosmrs::AccountId>,
) -> Self {
Config {
network,
nymd_url,
mixnet_contract_address,
vesting_contract_address,
erc20_bridge_contract_address,
api_url,
mixnode_page_limit: None,
gateway_page_limit: None,
@@ -90,6 +96,7 @@ pub struct Client<C> {
network: network_defaults::all::Network,
mixnet_contract_address: Option<cosmrs::AccountId>,
vesting_contract_address: Option<cosmrs::AccountId>,
erc20_bridge_contract_address: Option<cosmrs::AccountId>,
mnemonic: Option<bip39::Mnemonic>,
mixnode_page_limit: Option<u32>,
@@ -114,6 +121,7 @@ impl Client<SigningNymdClient> {
config.nymd_url.as_str(),
config.mixnet_contract_address.clone(),
config.vesting_contract_address.clone(),
config.erc20_bridge_contract_address.clone(),
mnemonic.clone(),
None,
)?;
@@ -122,6 +130,7 @@ impl Client<SigningNymdClient> {
network: config.network,
mixnet_contract_address: config.mixnet_contract_address,
vesting_contract_address: config.vesting_contract_address,
erc20_bridge_contract_address: config.erc20_bridge_contract_address,
mnemonic: Some(mnemonic),
mixnode_page_limit: config.mixnode_page_limit,
gateway_page_limit: config.gateway_page_limit,
@@ -138,6 +147,7 @@ impl Client<SigningNymdClient> {
new_endpoint.as_ref(),
self.mixnet_contract_address.clone(),
self.vesting_contract_address.clone(),
self.erc20_bridge_contract_address.clone(),
self.mnemonic.clone().unwrap(),
None,
)?;
@@ -159,12 +169,24 @@ impl Client<QueryNymdClient> {
cosmrs::AccountId::from_str(network_defaults::DEFAULT_VESTING_CONTRACT_ADDRESS)
.unwrap()
})),
Some(
config
.erc20_bridge_contract_address
.clone()
.unwrap_or_else(|| {
cosmrs::AccountId::from_str(
network_defaults::DEFAULT_BANDWIDTH_CLAIM_CONTRACT_ADDRESS,
)
.unwrap()
}),
),
)?;
Ok(Client {
network: config.network,
mixnet_contract_address: config.mixnet_contract_address,
vesting_contract_address: config.vesting_contract_address,
erc20_bridge_contract_address: config.erc20_bridge_contract_address,
mnemonic: None,
mixnode_page_limit: config.mixnode_page_limit,
gateway_page_limit: config.gateway_page_limit,
@@ -180,6 +202,7 @@ impl Client<QueryNymdClient> {
new_endpoint.as_ref(),
self.mixnet_contract_address.clone(),
self.vesting_contract_address.clone(),
self.erc20_bridge_contract_address.clone(),
)?;
Ok(())
}
@@ -232,7 +255,7 @@ impl<C> Client<C> {
where
C: CosmWasmClient + Sync,
{
Ok(self.nymd.get_mixnet_contract_version().await?)
self.nymd.get_mixnet_contract_version().await
}
pub async fn get_rewarding_status(
@@ -551,6 +574,13 @@ impl<C> Client<C> {
) -> Result<VerificationKeyResponse, ValidatorClientError> {
Ok(self.validator_api.get_coconut_verification_key().await?)
}
pub async fn verify_credential(
&self,
request_body: &Credential,
) -> Result<VerifyCredentialResponse, ValidatorClientError> {
Ok(self.validator_api.verify_credential(request_body).await?)
}
}
pub struct ApiClient {
@@ -653,4 +683,11 @@ impl ApiClient {
) -> Result<VerificationKeyResponse, ValidatorClientError> {
Ok(self.validator_api.get_coconut_verification_key().await?)
}
pub async fn verify_credential(
&self,
request_body: &Credential,
) -> Result<VerifyCredentialResponse, ValidatorClientError> {
Ok(self.validator_api.verify_credential(request_body).await?)
}
}
@@ -1,7 +1,7 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::nymd::cosmwasm_client::helpers::create_pagination;
use crate::nymd::cosmwasm_client::helpers::{create_pagination, next_page_key};
use crate::nymd::cosmwasm_client::types::{
Account, Code, CodeDetails, Contract, ContractCodeHistoryEntry, ContractCodeId,
SequenceResponse, SimulateResponse,
@@ -150,8 +150,8 @@ pub trait CosmWasmClient: rpc::Client {
.await?;
raw_balances.append(&mut res.balances);
if let Some(pagination_info) = res.pagination {
pagination = Some(create_pagination(pagination_info.next_key))
if let Some(next_key) = next_page_key(res.pagination) {
pagination = Some(create_pagination(next_key))
} else {
break;
}
@@ -182,8 +182,8 @@ pub trait CosmWasmClient: rpc::Client {
.await?;
supply.append(&mut res.supply);
if let Some(pagination_info) = res.pagination {
pagination = Some(create_pagination(pagination_info.next_key))
if let Some(next_key) = next_page_key(res.pagination) {
pagination = Some(create_pagination(next_key))
} else {
break;
}
@@ -274,8 +274,8 @@ pub trait CosmWasmClient: rpc::Client {
.await?;
raw_codes.append(&mut res.code_infos);
if let Some(pagination_info) = res.pagination {
pagination = Some(create_pagination(pagination_info.next_key))
if let Some(next_key) = next_page_key(res.pagination) {
pagination = Some(create_pagination(next_key))
} else {
break;
}
@@ -319,8 +319,8 @@ pub trait CosmWasmClient: rpc::Client {
.await?;
raw_contracts.append(&mut res.contracts);
if let Some(pagination_info) = res.pagination {
pagination = Some(create_pagination(pagination_info.next_key))
if let Some(next_key) = next_page_key(res.pagination) {
pagination = Some(create_pagination(next_key))
} else {
break;
}
@@ -375,8 +375,8 @@ pub trait CosmWasmClient: rpc::Client {
.await?;
raw_entries.append(&mut res.entries);
if let Some(pagination_info) = res.pagination {
pagination = Some(create_pagination(pagination_info.next_key))
if let Some(next_key) = next_page_key(res.pagination) {
pagination = Some(create_pagination(next_key))
} else {
break;
}
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
use crate::nymd::error::NymdError;
use cosmrs::proto::cosmos::base::query::v1beta1::PageRequest;
use cosmrs::proto::cosmos::base::query::v1beta1::{PageRequest, PageResponse};
use cosmrs::rpc::endpoint::broadcast;
use flate2::write::GzEncoder;
use flate2::Compression;
@@ -53,3 +53,15 @@ pub(crate) fn create_pagination(key: Vec<u8>) -> PageRequest {
count_total: false,
}
}
pub(crate) fn next_page_key(pagination_info: Option<PageResponse>) -> Option<Vec<u8>> {
if let Some(next_page_info) = pagination_info {
// it turns out, even though `PageResponse` is always returned wrapped in an `Option`,
// the `next_key` can still be empty, so check whether we actually need to perform another call
if !next_page_info.next_key.is_empty() {
return Some(next_page_info.next_key);
}
}
None
}
@@ -17,7 +17,7 @@ pub struct Log {
// and launchpad cosmos validator was setting it to what essentially is just the raw version of what
// we received (and we don't care about launchpad, we, as the time of writing this, work on the stargate)
// log: String,
events: Vec<cosmwasm_std::Event>,
pub events: Vec<cosmwasm_std::Event>,
}
/// Searches in logs for the first event of the given event type and in that event
@@ -46,12 +46,14 @@ pub mod error;
pub mod fee;
pub mod traits;
pub mod wallet;
use cosmrs::rpc::endpoint::tx::Response as TxResponse;
#[derive(Debug)]
pub struct NymdClient<C> {
client: C,
mixnet_contract_address: Option<AccountId>,
vesting_contract_address: Option<AccountId>,
erc20_bridge_contract_address: Option<AccountId>,
client_address: Option<Vec<AccountId>>,
custom_gas_limits: HashMap<Operation, Gas>,
simulated_gas_multiplier: f32,
@@ -62,6 +64,7 @@ impl NymdClient<QueryNymdClient> {
endpoint: U,
mixnet_contract_address: Option<AccountId>,
vesting_contract_address: Option<AccountId>,
erc20_bridge_contract_address: Option<AccountId>,
) -> Result<NymdClient<QueryNymdClient>, NymdError>
where
U: TryInto<HttpClientUrl, Error = TendermintRpcError>,
@@ -70,6 +73,7 @@ impl NymdClient<QueryNymdClient> {
client: QueryNymdClient::new(endpoint)?,
mixnet_contract_address,
vesting_contract_address,
erc20_bridge_contract_address,
client_address: None,
custom_gas_limits: Default::default(),
simulated_gas_multiplier: DEFAULT_SIMULATED_GAS_MULTIPLIER,
@@ -83,6 +87,7 @@ impl NymdClient<SigningNymdClient> {
endpoint: U,
mixnet_contract_address: Option<AccountId>,
vesting_contract_address: Option<AccountId>,
erc20_bridge_contract_address: Option<AccountId>,
signer: DirectSecp256k1HdWallet,
gas_price: Option<GasPrice>,
) -> Result<NymdClient<SigningNymdClient>, NymdError>
@@ -99,6 +104,7 @@ impl NymdClient<SigningNymdClient> {
client: SigningNymdClient::connect_with_signer(endpoint, signer, gas_price)?,
mixnet_contract_address,
vesting_contract_address,
erc20_bridge_contract_address,
client_address: Some(client_address),
custom_gas_limits: Default::default(),
simulated_gas_multiplier: DEFAULT_SIMULATED_GAS_MULTIPLIER,
@@ -110,6 +116,7 @@ impl NymdClient<SigningNymdClient> {
endpoint: U,
mixnet_contract_address: Option<AccountId>,
vesting_contract_address: Option<AccountId>,
erc20_bridge_contract_address: Option<AccountId>,
mnemonic: bip39::Mnemonic,
gas_price: Option<GasPrice>,
) -> Result<NymdClient<SigningNymdClient>, NymdError>
@@ -128,6 +135,7 @@ impl NymdClient<SigningNymdClient> {
client: SigningNymdClient::connect_with_signer(endpoint, wallet, gas_price)?,
mixnet_contract_address,
vesting_contract_address,
erc20_bridge_contract_address,
client_address: Some(client_address),
custom_gas_limits: Default::default(),
simulated_gas_multiplier: DEFAULT_SIMULATED_GAS_MULTIPLIER,
@@ -148,6 +156,12 @@ impl<C> NymdClient<C> {
.ok_or(NymdError::NoContractAddressAvailable)
}
pub fn erc20_bridge_contract_address(&self) -> Result<&AccountId, NymdError> {
self.erc20_bridge_contract_address
.as_ref()
.ok_or(NymdError::NoContractAddressAvailable)
}
// now the question is as follows: will denom always be in the format of `u{prefix}`?
pub fn denom(&self) -> Result<Denom, NymdError> {
Ok(format!("u{}", self.mixnet_contract_address()?.prefix())
@@ -261,6 +275,13 @@ impl<C> NymdClient<C> {
self.client.get_balance(address, denom).await
}
pub async fn get_tx(&self, id: tx::Hash) -> Result<TxResponse, NymdError>
where
C: CosmWasmClient + Sync,
{
self.client.get_tx(id).await
}
pub async fn get_total_supply(&self) -> Result<Vec<Coin>, NymdError>
where
C: CosmWasmClient + Sync,
@@ -6,8 +6,10 @@ use crate::nymd::error::NymdError;
use crate::nymd::NymdClient;
use async_trait::async_trait;
use cosmwasm_std::{Coin, Timestamp};
use vesting_contract::vesting::{Account, PledgeData};
use vesting_contract_common::messages::QueryMsg as VestingQueryMsg;
use vesting_contract::vesting::Account;
use vesting_contract_common::{
messages::QueryMsg as VestingQueryMsg, OriginalVestingResponse, Period, PledgeData,
};
#[async_trait]
pub trait VestingQueryClient {
@@ -43,7 +45,10 @@ pub trait VestingQueryClient {
async fn vesting_end_time(&self, vesting_account_address: &str)
-> Result<Timestamp, NymdError>;
async fn original_vesting(&self, vesting_account_address: &str) -> Result<Coin, NymdError>;
async fn original_vesting(
&self,
vesting_account_address: &str,
) -> Result<OriginalVestingResponse, NymdError>;
async fn delegated_free(
&self,
@@ -60,6 +65,10 @@ pub trait VestingQueryClient {
async fn get_account(&self, address: &str) -> Result<Account, NymdError>;
async fn get_mixnode_pledge(&self, address: &str) -> Result<Option<PledgeData>, NymdError>;
async fn get_gateway_pledge(&self, address: &str) -> Result<Option<PledgeData>, NymdError>;
async fn get_current_vesting_period(
&self,
vesting_account_address: &str,
) -> Result<Period, NymdError>;
}
#[async_trait]
@@ -142,7 +151,10 @@ impl<C: CosmWasmClient + Sync + Send> VestingQueryClient for NymdClient<C> {
.await
}
async fn original_vesting(&self, vesting_account_address: &str) -> Result<Coin, NymdError> {
async fn original_vesting(
&self,
vesting_account_address: &str,
) -> Result<OriginalVestingResponse, NymdError> {
let request = VestingQueryMsg::GetOriginalVesting {
vesting_account_address: vesting_account_address.to_string(),
};
@@ -203,4 +215,13 @@ impl<C: CosmWasmClient + Sync + Send> VestingQueryClient for NymdClient<C> {
.query_contract_smart(self.vesting_contract_address()?, &request)
.await
}
async fn get_current_vesting_period(&self, address: &str) -> Result<Period, NymdError> {
let request = VestingQueryMsg::GetCurrentVestingPeriod {
address: address.to_string(),
};
self.client
.query_contract_smart(self.vesting_contract_address()?, &request)
.await
}
}
@@ -13,6 +13,11 @@ use vesting_contract_common::messages::{ExecuteMsg as VestingExecuteMsg, Vesting
#[async_trait]
pub trait VestingSigningClient {
async fn vesting_update_mixnode_config(
&self,
profix_margin_percent: u8,
) -> Result<ExecuteResult, NymdError>;
async fn update_mixnet_address(&self, address: &str) -> Result<ExecuteResult, NymdError>;
async fn vesting_bond_gateway(
@@ -316,4 +321,24 @@ impl<C: SigningCosmWasmClient + Sync + Send> VestingSigningClient for NymdClient
)
.await
}
async fn vesting_update_mixnode_config(
&self,
profit_margin_percent: u8,
) -> Result<ExecuteResult, NymdError> {
let fee = self.operation_fee(Operation::UpdateMixnodeConfig);
let req = VestingExecuteMsg::UpdateMixnodeConfig {
profit_margin_percent,
};
self.client
.execute(
self.address(),
self.vesting_contract_address()?,
&req,
fee,
"VestingContract::UpdateMixnetConfig",
vec![],
)
.await
}
}
@@ -3,7 +3,10 @@
use crate::validator_api::error::ValidatorAPIError;
use crate::validator_api::routes::{CORE_STATUS_COUNT, SINCE_ARG};
use coconut_interface::{BlindSignRequestBody, BlindedSignatureResponse, VerificationKeyResponse};
use coconut_interface::{
BlindSignRequestBody, BlindedSignatureResponse, Credential, VerificationKeyResponse,
VerifyCredentialResponse,
};
use mixnet_contract_common::{GatewayBond, IdentityKeyRef, MixNodeBond};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
@@ -270,6 +273,18 @@ impl Client {
)
.await
}
pub async fn verify_credential(
&self,
request_body: &Credential,
) -> Result<VerifyCredentialResponse, ValidatorAPIError> {
self.post_validator_api(
&[routes::API_VERSION, routes::COCONUT_VERIFY_CREDENTIAL],
NO_PARAMS,
request_body,
)
.await
}
}
// utility function that should solve the double slash problem in validator API forever.
@@ -12,6 +12,7 @@ pub const REWARDED: &str = "rewarded";
pub const COCONUT_BLIND_SIGN: &str = "blind-sign";
pub const COCONUT_VERIFICATION_KEY: &str = "verification-key";
pub const COCONUT_VERIFY_CREDENTIAL: &str = "verify-credential";
pub const STATUS_ROUTES: &str = "status";
pub const MIXNODE: &str = "mixnode";
+25 -7
View File
@@ -1,12 +1,12 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use getset::{CopyGetters, Getters};
use getset::{CopyGetters, Getters, Setters};
use serde::{Deserialize, Serialize};
pub use nymcoconut::*;
#[derive(Serialize, Deserialize, Getters, CopyGetters, Clone)]
#[derive(Serialize, Deserialize, Getters, CopyGetters, Setters, Clone)]
pub struct Credential {
#[getset(get = "pub")]
n_params: u32,
@@ -15,6 +15,8 @@ pub struct Credential {
public_attributes: Vec<Vec<u8>>,
#[getset(get = "pub")]
signature: Signature,
#[getset(get = "pub", set = "pub")]
proposal_id: u64,
}
impl Credential {
pub fn new(
@@ -22,12 +24,14 @@ impl Credential {
theta: Theta,
public_attributes: Vec<Vec<u8>>,
signature: &Signature,
proposal_id: u64,
) -> Credential {
Credential {
n_params,
theta,
public_attributes,
signature: *signature,
proposal_id,
}
}
@@ -37,11 +41,13 @@ impl Credential {
pub fn verify(&self, verification_key: &VerificationKey) -> bool {
let params = Parameters::new(self.n_params).unwrap();
let public_attributes = self
.public_attributes
.iter()
.map(hash_to_scalar)
.collect::<Vec<Attribute>>();
let mut public_attributes = vec![];
for attr in &self.public_attributes {
match Attribute::try_from_byte_slice(attr) {
Ok(attr) => public_attributes.push(attr),
Err(_) => return false,
}
}
nymcoconut::verify_credential(&params, verification_key, &self.theta, &public_attributes)
}
}
@@ -88,6 +94,7 @@ pub struct BlindSignRequestBody {
public_attributes: Vec<String>,
#[getset(get = "pub")]
total_params: u32,
tx_hash: String,
}
impl BlindSignRequestBody {
@@ -96,6 +103,7 @@ impl BlindSignRequestBody {
public_key: &nymcoconut::PublicKey,
public_attributes: &[Attribute],
total_params: u32,
tx_hash: String,
) -> BlindSignRequestBody {
BlindSignRequestBody {
blind_sign_request: blind_sign_request.clone(),
@@ -105,6 +113,7 @@ impl BlindSignRequestBody {
.map(|attr| attr.to_bs58())
.collect(),
total_params,
tx_hash,
}
}
@@ -114,6 +123,10 @@ impl BlindSignRequestBody {
.map(|x| Attribute::try_from_bs58(x).unwrap())
.collect()
}
pub fn tx_hash(&self) -> &str {
&self.tx_hash
}
}
#[derive(Serialize, Deserialize)]
@@ -137,3 +150,8 @@ impl VerificationKeyResponse {
VerificationKeyResponse { key }
}
}
#[derive(Serialize, Deserialize)]
pub struct VerifyCredentialResponse {
pub response: bool,
}
@@ -12,7 +12,8 @@ cosmwasm-std = "1.0.0-beta3"
serde = { version = "1.0", features = ["derive"] }
serde_repr = "0.1"
schemars = "0.8"
ts-rs = { version = "5.1", optional = true }
# Leaving it as * so that it inherits whatever the wallet is using
ts-rs = { version = "*", optional = true }
thiserror = "1.0"
network-defaults = { path = "../../network-defaults" }
fixed = { version = "1.1", features = ["serde"] }
@@ -52,6 +52,8 @@ pub const NEW_MIXNODE_ACTIVE_SET_SIZE_KEY: &str = "new_mixnode_active_set_size";
pub const INTERVAL_ID_KEY: &str = "interval_id";
pub const TOTAL_MIXNODE_REWARD_KEY: &str = "total_node_reward";
pub const OPERATOR_REWARD_KEY: &str = "operator_reward";
pub const TOTAL_PLEDGE_KEY: &str = "pledge";
pub const TOTAL_DELEGATIONS_KEY: &str = "delegated";
pub const LAMBDA_KEY: &str = "lambda";
pub const SIGMA_KEY: &str = "sigma";
pub const DISTRIBUTED_DELEGATION_REWARDS_KEY: &str = "distributed_delegation_rewards";
@@ -271,10 +273,13 @@ pub fn new_zero_uptime_mix_operator_rewarding_event(
.add_attribute(NO_REWARD_REASON_KEY, ZERO_UPTIME_VALUE)
}
#[allow(clippy::too_many_arguments)]
pub fn new_mix_operator_rewarding_event(
interval_id: u32,
identity: IdentityKeyRef<'_>,
node_reward_result: NodeRewardResult,
node_pledge: Uint128,
node_delegation: Uint128,
operator_reward: Uint128,
delegation_rewards_distributed: Uint128,
further_delegations: bool,
@@ -282,6 +287,8 @@ pub fn new_mix_operator_rewarding_event(
Event::new(OPERATOR_REWARDING_EVENT_TYPE)
.add_attribute(INTERVAL_ID_KEY, interval_id.to_string())
.add_attribute(NODE_IDENTITY_KEY, identity)
.add_attribute(TOTAL_PLEDGE_KEY, node_pledge)
.add_attribute(TOTAL_DELEGATIONS_KEY, node_delegation)
.add_attribute(
TOTAL_MIXNODE_REWARD_KEY,
node_reward_result.reward().to_string(),
@@ -23,6 +23,10 @@ pub enum ExecuteMsg {
UpdateMixnodeConfig {
profit_margin_percent: u8,
},
UpdateMixnodeConfigOnBehalf {
profit_margin_percent: u8,
owner: String,
},
BondGateway {
gateway: Gateway,
owner_signature: String,
@@ -11,4 +11,5 @@ mixnet-contract-common = { path = "../mixnet-contract" }
serde = { version = "1.0", features = ["derive"] }
schemars = "0.8"
cw-storage-plus = "0.11.1"
config = {path = "../../config"}
config = { path = "../../config" }
ts-rs = { version = "*", optional = true }
@@ -15,6 +15,7 @@ pub const VESTING_GATEWAY_BONDING_EVENT_TYPE: &str = "vesting_gateway_bonding";
pub const VESTING_GATEWAY_UNBONDING_EVENT_TYPE: &str = "vesting_gateway_unbonding";
pub const VESTING_MIXNODE_BONDING_EVENT_TYPE: &str = "vesting_mixnode_bonding";
pub const VESTING_MIXNODE_UNBONDING_EVENT_TYPE: &str = "vesting_mixnode_unbonding";
pub const VESTING_UPDATE_MIXNODE_CONFIG_EVENT_TYPE: &str = "vesting_update_mixnode_config";
pub const TRACK_MIXNODE_UNBOND_EVENT_TYPE: &str = "track_mixnode_unbond";
pub const TRACK_GATEWAY_UNBOND_EVENT_TYPE: &str = "track_gateway_unbond";
@@ -108,6 +109,10 @@ pub fn new_vesting_mixnode_bonding_event() -> Event {
Event::new(VESTING_MIXNODE_BONDING_EVENT_TYPE)
}
pub fn new_vesting_update_mixnode_config_event() -> Event {
Event::new(VESTING_UPDATE_MIXNODE_CONFIG_EVENT_TYPE)
}
pub fn new_vesting_mixnode_unbonding_event() -> Event {
Event::new(VESTING_MIXNODE_UNBONDING_EVENT_TYPE)
}
@@ -1,11 +1,70 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use config::defaults::DENOM;
use cosmwasm_std::Coin;
use cosmwasm_std::{Coin, Timestamp};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
pub mod events;
pub mod messages;
pub fn one_unym() -> Coin {
pub fn one_ucoin() -> Coin {
Coin::new(1, DENOM)
}
#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone, JsonSchema)]
pub enum Period {
Before,
In(usize),
After,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct PledgeData {
amount: Coin,
block_time: Timestamp,
}
impl PledgeData {
pub fn amount(&self) -> Coin {
self.amount.clone()
}
pub fn block_time(&self) -> Timestamp {
self.block_time
}
pub fn new(amount: Coin, block_time: Timestamp) -> Self {
Self { amount, block_time }
}
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct OriginalVestingResponse {
amount: Coin,
number_of_periods: usize,
period_duration: u64,
}
impl OriginalVestingResponse {
pub fn amount(&self) -> Coin {
self.amount.clone()
}
pub fn number_of_periods(&self) -> usize {
self.number_of_periods
}
pub fn period_duration(&self) -> u64 {
self.period_duration
}
pub fn new(amount: Coin, number_of_periods: usize, period_duration: u64) -> Self {
Self {
amount,
number_of_periods,
period_duration,
}
}
}
@@ -49,6 +49,9 @@ impl VestingSpecification {
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum ExecuteMsg {
UpdateMixnodeConfig {
profit_margin_percent: u8,
},
UpdateMixnetAddress {
address: String,
},
@@ -145,4 +148,7 @@ pub enum QueryMsg {
GetGateway {
address: String,
},
GetCurrentVestingPeriod {
address: String,
},
}
+19 -2
View File
@@ -14,7 +14,9 @@ use url::Url;
use crate::error::Error;
use super::utils::{obtain_aggregate_signature, prepare_credential_for_spending};
use super::utils::{
obtain_aggregate_signature, obtain_aggregate_verify_credential, prepare_credential_for_spending,
};
pub const PUBLIC_ATTRIBUTES: u32 = 2;
pub const PRIVATE_ATTRIBUTES: u32 = 2;
@@ -46,11 +48,26 @@ pub async fn obtain_signature(
params: &Parameters,
attributes: &BandwidthVoucherAttributes,
validators: &[Url],
tx_hash: String,
) -> Result<Signature, Error> {
let public_attributes = attributes.get_public_attributes();
let private_attributes = attributes.get_private_attributes();
obtain_aggregate_signature(params, &public_attributes, &private_attributes, validators).await
obtain_aggregate_signature(
params,
&public_attributes,
&private_attributes,
validators,
tx_hash,
)
.await
}
pub async fn verify_credential_remote(
validators: &[Url],
credential: Credential,
) -> Result<bool, Error> {
obtain_aggregate_verify_credential(validators, credential).await
}
pub fn prepare_for_spending(
+55 -18
View File
@@ -3,8 +3,8 @@
use coconut_interface::{
aggregate_signature_shares, aggregate_verification_keys, prepare_blind_sign,
prove_bandwidth_credential, Attribute, BlindSignRequestBody, Credential, Parameters, Signature,
SignatureShare, VerificationKey,
prove_bandwidth_credential, Attribute, Base58, BlindSignRequest, BlindSignRequestBody,
Credential, ElGamalKeyPair, Parameters, Signature, SignatureShare, VerificationKey,
};
use url::Url;
@@ -47,17 +47,41 @@ pub async fn obtain_aggregate_verification_key(
let mut client = validator_client::ApiClient::new(validators[0].clone());
let response = client.get_coconut_verification_key().await?;
indices.push(0);
indices.push(1);
shares.push(response.key);
for (id, validator_url) in validators.iter().enumerate().skip(1) {
client.change_validator_api(validator_url.clone());
let response = client.get_coconut_verification_key().await?;
indices.push(id as u64);
indices.push((id + 1) as u64);
shares.push(response.key);
}
let ret = aggregate_verification_keys(&shares, Some(&indices))?;
Ok(aggregate_verification_keys(&shares, Some(&indices))?)
Ok(ret)
}
pub async fn obtain_aggregate_verify_credential(
validators: &[Url],
credential: Credential,
) -> Result<bool, Error> {
if validators.is_empty() {
return Err(Error::NoValidatorsAvailable);
}
let mut ret = true;
let mut client = validator_client::ApiClient::new(validators[0].clone());
let response = client.verify_credential(&credential).await?;
ret &= response.response;
for validator_url in validators.iter().skip(1) {
client.change_validator_api(validator_url.clone());
let response = client.verify_credential(&credential).await?;
ret &= response.response;
}
Ok(ret)
}
async fn obtain_partial_credential(
@@ -66,20 +90,16 @@ async fn obtain_partial_credential(
private_attributes: &[Attribute],
client: &validator_client::ApiClient,
validator_vk: &VerificationKey,
blind_sign_request: &BlindSignRequest,
elgamal_keypair: &ElGamalKeyPair,
tx_hash: String,
) -> Result<Signature, Error> {
let elgamal_keypair = coconut_interface::elgamal_keygen(params);
let blind_sign_request = prepare_blind_sign(
params,
&elgamal_keypair,
private_attributes,
public_attributes,
)?;
let blind_sign_request_body = BlindSignRequestBody::new(
&blind_sign_request,
blind_sign_request,
elgamal_keypair.public_key(),
public_attributes,
(public_attributes.len() + private_attributes.len()) as u32,
tx_hash,
);
let blinded_signature = client
@@ -104,6 +124,7 @@ pub async fn obtain_aggregate_signature(
public_attributes: &[Attribute],
private_attributes: &[Attribute],
validators: &[Url],
tx_hash: String,
) -> Result<Signature, Error> {
if validators.is_empty() {
return Err(Error::NoValidatorsAvailable);
@@ -116,15 +137,26 @@ pub async fn obtain_aggregate_signature(
let validator_partial_vk = client.get_coconut_verification_key().await?;
validators_partial_vks.push(validator_partial_vk.key.clone());
let elgamal_keypair = coconut_interface::elgamal_keygen(params);
let blind_sign_request = prepare_blind_sign(
params,
&elgamal_keypair,
private_attributes,
public_attributes,
)?;
let first = obtain_partial_credential(
params,
public_attributes,
private_attributes,
&client,
&validator_partial_vk.key,
&blind_sign_request,
&elgamal_keypair,
tx_hash.clone(),
)
.await?;
shares.push(SignatureShare::new(first, 0));
shares.push(SignatureShare::new(first, 1));
for (id, validator_url) in validators.iter().enumerate().skip(1) {
client.change_validator_api(validator_url.clone());
@@ -136,9 +168,12 @@ pub async fn obtain_aggregate_signature(
private_attributes,
&client,
&validator_partial_vk.key,
&blind_sign_request,
&elgamal_keypair,
tx_hash.clone(),
)
.await?;
let share = SignatureShare::new(signature, id as u64);
let share = SignatureShare::new(signature, (id + 1) as u64);
shares.push(share)
}
@@ -147,11 +182,12 @@ pub async fn obtain_aggregate_signature(
attributes.extend_from_slice(public_attributes);
let mut indices: Vec<u64> = Vec::with_capacity(validators_partial_vks.len());
for i in 1..validators_partial_vks.len() {
indices.push(i as u64);
for i in 0..validators_partial_vks.len() {
indices.push((i + 1) as u64);
}
let verification_key =
aggregate_verification_keys(&validators_partial_vks, Some(indices.as_ref()))?;
println!("Verification key: {}", verification_key.to_bs58());
Ok(aggregate_signature_shares(
params,
@@ -183,5 +219,6 @@ pub fn prepare_credential_for_spending(
theta,
public_attributes,
signature,
0,
))
}
+542 -125
View File
@@ -4,129 +4,546 @@
// This should be modified whenever an updated Ethereum contract is uploaded
pub const ETH_JSON_ABI: &str = r#"
[
{
"inputs": [
{
"internalType": "contract ERC20Burnable",
"name": "_erc20",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint256",
"name": "Bandwidth",
"type": "uint256"
},
{
"indexed": true,
"internalType": "uint256",
"name": "VerificationKey",
"type": "uint256"
},
{
"indexed": false,
"internalType": "bytes",
"name": "SignedVerificationKey",
"type": "bytes"
}
],
"name": "Burned",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "previousOwner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "OwnershipTransferred",
"type": "event"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "verificationKey",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "signedVerificationKey",
"type": "bytes"
}
],
"name": "burnTokenForAccessCode",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "erc20",
"outputs": [
{
"internalType": "contract ERC20Burnable",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "owner",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "renounceOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "transferOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
]
{
"inputs": [
{
"internalType": "contract CosmosERC20",
"name": "_erc20",
"type": "address"
},
{
"internalType": "contract Gravity",
"name": "_gravityBridge",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint256",
"name": "Bandwidth",
"type": "uint256"
},
{
"indexed": true,
"internalType": "uint256",
"name": "VerificationKey",
"type": "uint256"
},
{
"indexed": false,
"internalType": "bytes",
"name": "SignedVerificationKey",
"type": "bytes"
},
{
"indexed": false,
"internalType": "string",
"name": "CosmosRecipient",
"type": "string"
}
],
"name": "BBCredentialPurchased",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "bool",
"name": "Enabled",
"type": "bool"
}
],
"name": "CredentialGenerationSwitch",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "previousOwner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "OwnershipTransferred",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "uint256",
"name": "NewBytesPerToken",
"type": "uint256"
}
],
"name": "RatioChanged",
"type": "event"
},
{
"inputs": [],
"name": "BytesPerToken",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "_amount",
"type": "uint256"
}
],
"name": "bandwidthFromToken",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "_newBytesPerTokenAmount",
"type": "uint256"
}
],
"name": "changeRatio",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "credentialGenerationEnabled",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "bool",
"name": "_generation",
"type": "bool"
}
],
"name": "credentialGenerationSwitch",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "erc20",
"outputs": [
{
"internalType": "contract CosmosERC20",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "_amount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "_verificationKey",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "_signedVerificationKey",
"type": "bytes"
},
{
"internalType": "string",
"name": "_cosmosRecipient",
"type": "string"
}
],
"name": "generateBasicBandwidthCredential",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "gravityBridge",
"outputs": [
{
"internalType": "contract Gravity",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "owner",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "renounceOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "transferOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
]
"#;
pub const ETH_ERC20_JSON_ABI: &str = r#"
[
{
"inputs": [
{
"internalType": "string",
"name": "name_",
"type": "string"
},
{
"internalType": "string",
"name": "symbol_",
"type": "string"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "Approval",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "from",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "to",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "Transfer",
"type": "event"
},
{
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"internalType": "address",
"name": "spender",
"type": "address"
}
],
"name": "allowance",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "approve",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "account",
"type": "address"
}
],
"name": "balanceOf",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "decimals",
"outputs": [
{
"internalType": "uint8",
"name": "",
"type": "uint8"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"internalType": "uint256",
"name": "subtractedValue",
"type": "uint256"
}
],
"name": "decreaseAllowance",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"internalType": "uint256",
"name": "addedValue",
"type": "uint256"
}
],
"name": "increaseAllowance",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "name",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "symbol",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "totalSupply",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "recipient",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "transfer",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "sender",
"type": "address"
},
{
"internalType": "address",
"name": "recipient",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "transferFrom",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "nonpayable",
"type": "function"
}
]
"#;
+15 -5
View File
@@ -17,6 +17,8 @@ cfg_if::cfg_if! {
pub const DEFAULT_MIXNET_CONTRACT_ADDRESS: &str = mainnet::MIXNET_CONTRACT_ADDRESS;
pub const DEFAULT_VESTING_CONTRACT_ADDRESS: &str = mainnet::VESTING_CONTRACT_ADDRESS;
pub const DEFAULT_BANDWIDTH_CLAIM_CONTRACT_ADDRESS: &str = mainnet::BANDWIDTH_CLAIM_CONTRACT_ADDRESS;
pub const ETH_CONTRACT_ADDRESS: [u8; 20] = mainnet::_ETH_CONTRACT_ADDRESS;
pub const ETH_ERC20_CONTRACT_ADDRESS: [u8; 20] = mainnet::_ETH_ERC20_CONTRACT_ADDRESS;
pub const DEFAULT_REWARDING_VALIDATOR_ADDRESS: &str = mainnet::REWARDING_VALIDATOR_ADDRESS;
pub fn default_validators() -> Vec<ValidatorDetails> {
@@ -33,6 +35,8 @@ cfg_if::cfg_if! {
pub const DEFAULT_MIXNET_CONTRACT_ADDRESS: &str = qa::MIXNET_CONTRACT_ADDRESS;
pub const DEFAULT_VESTING_CONTRACT_ADDRESS: &str = qa::VESTING_CONTRACT_ADDRESS;
pub const DEFAULT_BANDWIDTH_CLAIM_CONTRACT_ADDRESS: &str = qa::BANDWIDTH_CLAIM_CONTRACT_ADDRESS;
pub const ETH_CONTRACT_ADDRESS: [u8; 20] = qa::_ETH_CONTRACT_ADDRESS;
pub const ETH_ERC20_CONTRACT_ADDRESS: [u8; 20] = qa::_ETH_ERC20_CONTRACT_ADDRESS;
pub const DEFAULT_REWARDING_VALIDATOR: &str = qa::REWARDING_VALIDATOR_ADDRESS;
pub fn default_validators() -> Vec<ValidatorDetails> {
@@ -49,6 +53,8 @@ cfg_if::cfg_if! {
pub const DEFAULT_MIXNET_CONTRACT_ADDRESS: &str = sandbox::MIXNET_CONTRACT_ADDRESS;
pub const DEFAULT_VESTING_CONTRACT_ADDRESS: &str = sandbox::VESTING_CONTRACT_ADDRESS;
pub const DEFAULT_BANDWIDTH_CLAIM_CONTRACT_ADDRESS: &str = sandbox::BANDWIDTH_CLAIM_CONTRACT_ADDRESS;
pub const ETH_CONTRACT_ADDRESS: [u8; 20] = sandbox::_ETH_CONTRACT_ADDRESS;
pub const ETH_ERC20_CONTRACT_ADDRESS: [u8; 20] = sandbox::_ETH_ERC20_CONTRACT_ADDRESS;
pub const DEFAULT_REWARDING_VALIDATOR: &str = sandbox::REWARDING_VALIDATOR_ADDRESS;
pub fn default_validators() -> Vec<ValidatorDetails> {
@@ -106,18 +112,22 @@ pub fn default_api_endpoints() -> Vec<Url> {
.collect()
}
pub const ETH_CONTRACT_ADDRESS: [u8; 20] =
hex_literal::hex!("9fEE3e28c17dbB87310A51F13C4fbf4331A6f102");
// Name of the event triggered by the eth contract. If the event name is changed,
// this would also need to be changed; It is currently tested against the json abi
pub const ETH_EVENT_NAME: &str = "Burned";
pub const ETH_BURN_FUNCTION_NAME: &str = "burnTokenForAccessCode";
pub const ETH_EVENT_NAME: &str = "BBCredentialPurchased";
pub const ETH_BURN_FUNCTION_NAME: &str = "generateBasicBandwidthCredential";
pub const ETH_ERC20_APPROVE_FUNCTION_NAME: &str = "approve";
// Ethereum constants used for token bridge
/// How much bandwidth (in bytes) one token can buy
const BYTES_PER_TOKEN: u64 = 1024 * 1024 * 1024;
/// Threshold for claiming more bandwidth: 1 MB
pub const REMAINING_BANDWIDTH_THRESHOLD: i64 = 1024 * 1024;
/// How many ERC20 tokens should be burned to buy bandwidth
pub const TOKENS_TO_BURN: u64 = 10;
pub const TOKENS_TO_BURN: u64 = 1;
/// How many ERC20 utokens should be burned to buy bandwidth
pub const UTOKENS_TO_BURN: u64 = TOKENS_TO_BURN * 1000000;
/// Default bandwidth (in bytes) that we try to buy
pub const BANDWIDTH_VALUE: u64 = TOKENS_TO_BURN * BYTES_PER_TOKEN;
+4
View File
@@ -10,6 +10,10 @@ pub(crate) const MIXNET_CONTRACT_ADDRESS: &str = "n19lc9u84cz0yz3fww5283nucc9yvr
pub(crate) const VESTING_CONTRACT_ADDRESS: &str = "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0";
pub(crate) const BANDWIDTH_CLAIM_CONTRACT_ADDRESS: &str =
"n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0";
pub(crate) const _ETH_CONTRACT_ADDRESS: [u8; 20] =
hex_literal::hex!("0000000000000000000000000000000000000000");
pub(crate) const _ETH_ERC20_CONTRACT_ADDRESS: [u8; 20] =
hex_literal::hex!("0000000000000000000000000000000000000000");
pub(crate) const REWARDING_VALIDATOR_ADDRESS: &str = "n17zujduc46wvkwvp6f062mm5xhr7jc3fewvqu9e";
pub(crate) fn validators() -> Vec<ValidatorDetails> {
+6 -2
View File
@@ -6,10 +6,14 @@ use crate::ValidatorDetails;
pub(crate) const BECH32_PREFIX: &str = "nymt";
pub const DENOM: &str = "unymt";
pub(crate) const MIXNET_CONTRACT_ADDRESS: &str = "nymt14hj2tavq8fpesdwxxcu44rty3hh90vhuysqrsr";
pub(crate) const VESTING_CONTRACT_ADDRESS: &str = "nymt17p9rzwnnfxcjp32un9ug7yhhzgtkhvl9f8xzkv";
pub(crate) const MIXNET_CONTRACT_ADDRESS: &str = "nymt17x6pt4msccvawgxjeg5nmnygttu56tftg5l6j3";
pub(crate) const VESTING_CONTRACT_ADDRESS: &str = "nymt1t4dmskxea0avvrj8xtmu66hv7dkyg9s8059t3c";
pub(crate) const BANDWIDTH_CLAIM_CONTRACT_ADDRESS: &str =
"nymt17p9rzwnnfxcjp32un9ug7yhhzgtkhvl9f8xzkv";
pub(crate) const _ETH_CONTRACT_ADDRESS: [u8; 20] =
hex_literal::hex!("0000000000000000000000000000000000000000");
pub(crate) const _ETH_ERC20_CONTRACT_ADDRESS: [u8; 20] =
hex_literal::hex!("0000000000000000000000000000000000000000");
pub(crate) const REWARDING_VALIDATOR_ADDRESS: &str = "nymt1dn52nx8wv9wkqmrvj6tcmdzh4es6jt8tr7f6j9";
pub(crate) fn validators() -> Vec<ValidatorDetails> {
+6 -2
View File
@@ -7,10 +7,14 @@ pub(crate) const BECH32_PREFIX: &str = "nymt";
pub const DENOM: &str = "unymt";
pub(crate) const MIXNET_CONTRACT_ADDRESS: &str = "nymt1ghd753shjuwexxywmgs4xz7x2q732vcnstz02j";
pub(crate) const VESTING_CONTRACT_ADDRESS: &str = "nymt1nc5tatafv6eyq7llkr2gv50ff9e22mnfp9pc5s";
pub(crate) const VESTING_CONTRACT_ADDRESS: &str = "nymt14ejqjyq8um4p3xfqj74yld5waqljf88fn549lh";
pub(crate) const BANDWIDTH_CLAIM_CONTRACT_ADDRESS: &str =
"nymt17p9rzwnnfxcjp32un9ug7yhhzgtkhvl9f8xzkv";
pub(crate) const REWARDING_VALIDATOR_ADDRESS: &str = "nymt17zujduc46wvkwvp6f062mm5xhr7jc3fewvqu9e";
pub(crate) const _ETH_CONTRACT_ADDRESS: [u8; 20] =
hex_literal::hex!("8e0DcFF7F3085235C32E845f3667aEB3f1e83133");
pub(crate) const _ETH_ERC20_CONTRACT_ADDRESS: [u8; 20] =
hex_literal::hex!("E8883BAeF3869e14E4823F46662e81D4F7d2A81F");
pub(crate) const REWARDING_VALIDATOR_ADDRESS: &str = "nymt1jh0s6qu6tuw9ut438836mmn7f3f2wencrnmdj4";
pub(crate) fn validators() -> Vec<ValidatorDetails> {
vec![ValidatorDetails::new(
+3 -2
View File
@@ -1,10 +1,13 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
extern crate core;
use std::convert::TryInto;
use bls12_381::Scalar;
pub use crate::traits::Bytable;
pub use elgamal::elgamal_keygen;
pub use elgamal::ElGamalKeyPair;
pub use elgamal::PublicKey;
@@ -28,8 +31,6 @@ pub use scheme::SignatureShare;
pub use traits::Base58;
pub use utils::hash_to_scalar;
use crate::traits::Bytable;
pub mod elgamal;
mod error;
mod impls;
+11 -2
View File
@@ -206,10 +206,17 @@ pub fn verify_credential(
public_attributes: &[Attribute],
) -> bool {
if public_attributes.len() + theta.pi_v.private_attributes_len() > verification_key.beta.len() {
println!(
"Len fail: {} + {} > {}",
public_attributes.len(),
theta.pi_v.private_attributes_len(),
verification_key.beta.len()
);
return false;
}
if !theta.verify_proof(params, verification_key) {
println!("Proof fail");
return false;
}
@@ -230,12 +237,14 @@ pub fn verify_credential(
theta.blinded_message + signed_public_attributes
};
check_bilinear_pairing(
let ret = check_bilinear_pairing(
&theta.credential.0.to_affine(),
&G2Prepared::from(kappa.to_affine()),
&(theta.credential.1).to_affine(),
params.prepared_miller_g2(),
) && !bool::from(theta.credential.0.is_identity())
);
println!("Ret value {}", ret);
ret && !bool::from(theta.credential.0.is_identity())
}
// Used in tests only
+1 -3
View File
@@ -8,11 +8,9 @@ edition = "2021"
[lib]
crate-type = ["cdylib", "rlib"]
[dev-dependencies]
config = { path = "../../common/config"}
[dependencies]
bandwidth-claim-contract = { path = "../../common/bandwidth-claim-contract" }
config = { path = "../../common/config"}
cosmwasm-std = "1.0.0-beta3"
cosmwasm-storage = "1.0.0-beta3"
+14
View File
@@ -1,6 +1,8 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use config::defaults::DENOM;
use cosmwasm_std::{StdError, VerificationError};
use thiserror::Error;
@@ -24,4 +26,16 @@ pub enum ContractError {
#[error("The payment is not properly signed")]
BadSignature,
#[error("Received multiple coin types")]
MultipleDenoms,
#[error("No coin was sent for voucher")]
NoCoin,
#[error("Wrong coin denomination, you must send {}", DENOM)]
WrongDenom,
#[error("The sender is not authorized to perform this action")]
Unauthorized,
}
+4
View File
@@ -39,6 +39,10 @@ pub fn execute(
) -> Result<Response, ContractError> {
match msg {
ExecuteMsg::LinkPayment { data } => transactions::link_payment(deps, env, info, data),
ExecuteMsg::BuyBandwidth {} => transactions::buy_bandwidth(deps, env, info),
ExecuteMsg::SpendCredential { amount } => {
transactions::spend_credential(deps, env, info, amount)
}
}
}
+9
View File
@@ -11,6 +11,7 @@ use bandwidth_claim_contract::payment::Payment;
// buckets
const PREFIX_PAYMENTS: &[u8] = b"payments";
const PREFIX_STATUS: &[u8] = b"status";
const PREFIX_COCONUT: &[u8] = b"coconut";
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
pub enum Status {
@@ -31,6 +32,14 @@ pub fn status(storage: &mut dyn Storage) -> Bucket<'_, Status> {
bucket(storage, PREFIX_STATUS)
}
pub fn coconut(storage: &mut dyn Storage) -> Bucket<'_, Payment> {
bucket(storage, PREFIX_COCONUT)
}
pub fn coconut_read(storage: &dyn Storage) -> ReadonlyBucket<'_, Payment> {
bucket_read(storage, PREFIX_COCONUT)
}
#[cfg(test)]
mod tests {
use super::*;
+41 -2
View File
@@ -1,11 +1,13 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use cosmwasm_std::{DepsMut, Env, MessageInfo, Response};
use cosmwasm_std::{Addr, BankMsg, Coin, DepsMut, Env, Event, MessageInfo, Response};
use crate::error::ContractError;
use crate::storage::{payments, status, Status};
use crate::storage::{coconut, payments, status, Status};
use bandwidth_claim_contract::events::{VOUCHER_ACQUIRED_EVENT_TYPE, VOUCHER_VALUE};
use bandwidth_claim_contract::payment::{LinkPaymentData, Payment};
use config::defaults::DENOM;
pub(crate) fn link_payment(
deps: DepsMut<'_>,
@@ -44,6 +46,43 @@ pub(crate) fn link_payment(
Ok(Response::default())
}
pub(crate) fn buy_bandwidth(
_deps: DepsMut<'_>,
_env: Env,
info: MessageInfo,
) -> Result<Response, ContractError> {
if info.funds.is_empty() {
return Err(ContractError::NoCoin);
}
if info.funds.len() > 1 {
return Err(ContractError::MultipleDenoms);
}
if info.funds[0].denom != DENOM {
return Err(ContractError::WrongDenom);
}
let voucher_value = info.funds.last().unwrap();
let event =
Event::new(VOUCHER_ACQUIRED_EVENT_TYPE).add_attribute(VOUCHER_VALUE, voucher_value.amount);
Ok(Response::new().add_event(event))
}
pub(crate) fn spend_credential(
_deps: DepsMut<'_>,
_env: Env,
info: MessageInfo,
amount: u64,
) -> Result<Response, ContractError> {
if info.sender != Addr::unchecked(String::from("nymt1qwlgtx52gsdu7dtp0cekka5zehdl0uj3vqx3jd")) {
return Err(ContractError::Unauthorized);
}
let return_tokens = BankMsg::Send {
to_address: String::from("nymt1t6p4dl8nnlftvehz3jsklrd0aw458p4l6n9n4t"),
amount: vec![Coin::new(amount as u128, DENOM)],
};
Ok(Response::new().add_message(return_tokens))
}
#[cfg(test)]
mod tests {
use super::*;
@@ -5,7 +5,7 @@
"GRAVITY":"0xe58c12f8bA4a28e87841b10BdEdf80A47F86BA9B"
},
"mainnet":{
"NYM_ERC20":"0xCf6DeE9947fdDc958985E5283e63d41CBC83Ff61",
"NYM_ERC20":"0x525A8F6F3Ba4752868cde25164382BfbaE3990e1",
"NYMT": "0xE8883BAeF3869e14E4823F46662e81D4F7d2A81F",
"BANDWIDTH_GENERATOR":"",
"BANDWIDTH_GENERATOR_NYMT":"0xB3BF30DD53044c9050B7309031Bbf26b2cecF3be",
+7 -21
View File
@@ -6524,9 +6524,9 @@
}
},
"follow-redirects": {
"version": "1.14.7",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz",
"integrity": "sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==",
"version": "1.14.8",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.8.tgz",
"integrity": "sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==",
"dev": true
},
"for-each": {
@@ -14257,21 +14257,7 @@
"simple-concat": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
"integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==",
"dev": true,
"optional": true
},
"simple-get": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-2.8.1.tgz",
"integrity": "sha512-lSSHRSw3mQNUGPAYRqo7xy9dhKmxFXIjLjp4KHpf99GEH2VH7C3AM+Qfx6du6jhfUi6Vm7XnbEVEf7Wb6N8jRw==",
"dev": true,
"optional": true,
"requires": {
"decompress-response": "^3.3.0",
"once": "^1.3.1",
"simple-concat": "^1.0.0"
}
"integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q=="
},
"snapdragon": {
"version": "0.8.2",
@@ -19376,9 +19362,9 @@
"integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q=="
},
"simple-get": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-2.8.1.tgz",
"integrity": "sha512-lSSHRSw3mQNUGPAYRqo7xy9dhKmxFXIjLjp4KHpf99GEH2VH7C3AM+Qfx6du6jhfUi6Vm7XnbEVEf7Wb6N8jRw==",
"version": "2.8.2",
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-2.8.2.tgz",
"integrity": "sha512-Ijd/rV5o+mSBBs4F/x9oDPtTx9Zb6X9brmnXvMW4J7IR15ngi9q5xxqWBKU744jTZiaXtxaPL7uHG6vtN8kUkw==",
"requires": {
"decompress-response": "^3.3.0",
"once": "^1.3.1",
+12 -57
View File
@@ -122,6 +122,16 @@ pub fn execute(
info,
profit_margin_percent,
),
ExecuteMsg::UpdateMixnodeConfigOnBehalf {
profit_margin_percent,
owner,
} => crate::mixnodes::transactions::try_update_mixnode_config_on_behalf(
deps,
env,
info,
profit_margin_percent,
owner,
),
ExecuteMsg::BondGateway {
gateway,
owner_signature,
@@ -315,64 +325,9 @@ pub fn query(deps: Deps<'_>, env: Env, msg: QueryMsg) -> Result<QueryResponse, C
Ok(query_res?)
}
#[entry_point]
pub fn migrate(deps: DepsMut<'_>, env: Env, _msg: MigrateMsg) -> Result<Response, ContractError> {
use cw_storage_plus::Item;
use serde::{Deserialize, Serialize};
// needed migration:
/*
1. removal of rewarding_interval_starting_block field from ContractState
2. removal of latest_rewarding_interval_nonce field from ContractState
3. removal of rewarding_in_progress field from ContractState
4. interval_storage::CURRENT_INTERVAL.save(deps.storage, &Interval::default())?;
5. interval_storage::CURRENT_REWARDED_SET_HEIGHT.save(deps.storage, &env.block.height)?;
6. removal of active_set_work_factor fields from ContractStateParams
*/
#[derive(Serialize, Deserialize)]
pub struct OldContractStateParams {
pub minimum_mixnode_pledge: Uint128,
pub minimum_gateway_pledge: Uint128,
pub mixnode_rewarded_set_size: u32,
pub mixnode_active_set_size: u32,
pub active_set_work_factor: u8,
}
#[derive(Serialize, Deserialize)]
struct OldContractState {
pub owner: Addr, // only the owner account can update state
pub rewarding_validator_address: Addr,
pub params: OldContractStateParams,
pub rewarding_interval_starting_block: u64,
pub latest_rewarding_interval_nonce: u32,
pub rewarding_in_progress: bool,
}
let old_contract_state: Item<'_, OldContractState> = Item::new("config");
let old_state = old_contract_state.load(deps.storage)?;
let new_params = mixnet_contract_common::ContractStateParams {
minimum_mixnode_pledge: old_state.params.minimum_mixnode_pledge,
minimum_gateway_pledge: old_state.params.minimum_mixnode_pledge,
mixnode_rewarded_set_size: old_state.params.mixnode_rewarded_set_size,
mixnode_active_set_size: old_state.params.mixnode_active_set_size,
};
let new_state = crate::mixnet_contract_settings::models::ContractState {
owner: old_state.owner,
rewarding_validator_address: old_state.rewarding_validator_address,
params: new_params,
};
let rewarding_interval =
Interval::new(0, DEFAULT_FIRST_INTERVAL_START, REWARDING_INTERVAL_LENGTH);
mixnet_params_storage::CONTRACT_STATE.save(deps.storage, &new_state)?;
interval_storage::CURRENT_INTERVAL.save(deps.storage, &rewarding_interval)?;
interval_storage::CURRENT_REWARDED_SET_HEIGHT.save(deps.storage, &env.block.height)?;
pub fn migrate(_deps: DepsMut<'_>, _env: Env, _msg: MigrateMsg) -> Result<Response, ContractError> {
Ok(Default::default())
}
@@ -10,7 +10,7 @@ use cw_storage_plus::PrimaryKey;
use mixnet_contract_common::events::{new_delegation_event, new_undelegation_event};
use mixnet_contract_common::{Delegation, IdentityKey};
use vesting_contract_common::messages::ExecuteMsg as VestingContractExecuteMsg;
use vesting_contract_common::one_unym;
use vesting_contract_common::one_ucoin;
fn validate_delegation_stake(mut delegation: Vec<Coin>) -> Result<Coin, ContractError> {
// check if anything was put as delegation
@@ -213,7 +213,7 @@ pub(crate) fn _try_remove_delegation_from_mixnode(
amount: old_delegation.amount.clone(),
});
let track_undelegation_msg = wasm_execute(proxy, &msg, vec![one_unym()])?;
let track_undelegation_msg = wasm_execute(proxy, &msg, vec![one_ucoin()])?;
response = response.add_message(track_undelegation_msg);
}
@@ -12,7 +12,7 @@ use cosmwasm_std::{
use mixnet_contract_common::events::{new_gateway_bonding_event, new_gateway_unbonding_event};
use mixnet_contract_common::{Gateway, GatewayBond, Layer};
use vesting_contract_common::messages::ExecuteMsg as VestingContractExecuteMsg;
use vesting_contract_common::one_unym;
use vesting_contract_common::one_ucoin;
pub fn try_add_gateway(
deps: DepsMut<'_>,
@@ -177,7 +177,7 @@ pub(crate) fn _try_remove_gateway(
amount: gateway_bond.pledge_amount(),
};
let track_unbond_message = wasm_execute(proxy, &msg, vec![one_unym()])?;
let track_unbond_message = wasm_execute(proxy, &msg, vec![one_ucoin()])?;
response = response.add_message(track_unbond_message);
}
+49 -8
View File
@@ -14,7 +14,7 @@ use cosmwasm_std::{
use mixnet_contract_common::events::{new_mixnode_bonding_event, new_mixnode_unbonding_event};
use mixnet_contract_common::MixNode;
use vesting_contract_common::messages::ExecuteMsg as VestingContractExecuteMsg;
use vesting_contract_common::one_unym;
use vesting_contract_common::one_ucoin;
pub fn try_add_mixnode(
deps: DepsMut<'_>,
@@ -201,7 +201,7 @@ pub(crate) fn _try_remove_mixnode(
amount: mixnode_bond.pledge_amount(),
};
let track_unbond_message = wasm_execute(proxy, &msg, vec![one_unym()])?;
let track_unbond_message = wasm_execute(proxy, &msg, vec![one_ucoin()])?;
response = response.add_message(track_unbond_message);
}
@@ -222,14 +222,43 @@ pub(crate) fn try_update_mixnode_config(
profit_margin_percent: u8,
) -> Result<Response, ContractError> {
let owner = deps.api.addr_validate(info.sender.as_ref())?;
let mix_identity = storage::mixnodes()
_try_update_mixnode_config(deps, env, profit_margin_percent, owner, None)
}
pub(crate) fn try_update_mixnode_config_on_behalf(
deps: DepsMut,
env: Env,
info: MessageInfo,
profit_margin_percent: u8,
owner: String,
) -> Result<Response, ContractError> {
let owner = deps.api.addr_validate(&owner)?;
let proxy = deps.api.addr_validate(info.sender.as_ref())?;
_try_update_mixnode_config(deps, env, profit_margin_percent, owner, Some(proxy))
}
pub(crate) fn _try_update_mixnode_config(
deps: DepsMut,
env: Env,
profit_margin_percent: u8,
owner: Addr,
proxy: Option<Addr>,
) -> Result<Response, ContractError> {
let mixnode_bond = storage::mixnodes()
.idx
.owner
.item(deps.storage, owner.clone())?
.ok_or(ContractError::NoAssociatedMixNodeBond { owner })?
.1
.identity()
.clone();
.1;
if proxy != mixnode_bond.proxy {
return Err(ContractError::ProxyMismatch {
existing: mixnode_bond
.proxy
.map_or_else(|| "None".to_string(), |a| a.as_str().to_string()),
incoming: proxy.map_or_else(|| "None".to_string(), |a| a.as_str().to_string()),
});
}
// We don't have to check lower bound as its an u8
if profit_margin_percent > 100 {
@@ -238,7 +267,7 @@ pub(crate) fn try_update_mixnode_config(
));
}
storage::mixnodes().update(deps.storage, &mix_identity, |mixnode_bond_opt| {
storage::mixnodes().update(deps.storage, mixnode_bond.identity(), |mixnode_bond_opt| {
mixnode_bond_opt
.map(|mut mixnode_bond| {
mixnode_bond.mix_node.profit_margin_percent = profit_margin_percent;
@@ -248,7 +277,19 @@ pub(crate) fn try_update_mixnode_config(
.ok_or(ContractError::NoBondFound)
})?;
Ok(Response::new())
let mut response = Response::new();
if let Some(proxy) = proxy {
// Returns one_ucoin proxy had to send in order to execute the contract to contract transaction, this is potentially leaky as anyone can say that they're a proxy,
// and they could potentially leak 1 unym per transaction, altough I'm pretty sure transaction fees make that silly.
let return_one_ucoint = BankMsg::Send {
to_address: proxy.as_str().to_string(),
amount: vec![one_ucoin()],
};
response = response.add_message(return_one_ucoint);
}
Ok(response)
}
fn validate_mixnode_pledge(
@@ -260,6 +260,9 @@ pub(crate) fn try_reward_mixnode(
);
}
let node_pledge = current_bond.pledge_amount.amount;
let node_delegation = current_bond.total_delegation.amount;
// check if it has non-zero uptime
if params.uptime() == 0 {
storage::REWARDING_STATUS.save(
@@ -315,6 +318,8 @@ pub(crate) fn try_reward_mixnode(
interval_id,
&mix_identity,
node_reward_result,
node_pledge,
node_delegation,
operator_reward,
total_delegator_reward,
further_delegations,
+27 -2
View File
@@ -3,7 +3,7 @@ use crate::storage::{account_from_address, ADMIN, MIXNET_CONTRACT_ADDRESS};
use crate::traits::{
DelegatingAccount, GatewayBondingAccount, MixnodeBondingAccount, VestingAccount,
};
use crate::vesting::{populate_vesting_periods, Account, PledgeData};
use crate::vesting::{populate_vesting_periods, Account};
use config::defaults::DENOM;
use cosmwasm_std::{
coin, entry_point, to_binary, BankMsg, Coin, Deps, DepsMut, Env, MessageInfo, QueryResponse,
@@ -18,6 +18,7 @@ use vesting_contract_common::events::{
use vesting_contract_common::messages::{
ExecuteMsg, InitMsg, MigrateMsg, QueryMsg, VestingSpecification,
};
use vesting_contract_common::{OriginalVestingResponse, Period, PledgeData};
#[entry_point]
pub fn instantiate(
@@ -45,6 +46,9 @@ pub fn execute(
msg: ExecuteMsg,
) -> Result<Response, ContractError> {
match msg {
ExecuteMsg::UpdateMixnodeConfig {
profit_margin_percent,
} => try_update_mixnode_config(profit_margin_percent, info, deps),
ExecuteMsg::UpdateMixnetAddress { address } => {
try_update_mixnet_address(address, info, deps)
}
@@ -102,6 +106,15 @@ pub fn execute(
}
}
pub fn try_update_mixnode_config(
profit_margin_percent: u8,
info: MessageInfo,
deps: DepsMut,
) -> Result<Response, ContractError> {
let account = account_from_address(info.sender.as_str(), deps.storage, deps.api)?;
account.try_update_mixnode_config(profit_margin_percent, deps.storage)
}
// Only contract admin, set at init
pub fn try_update_mixnet_address(
address: String,
@@ -431,11 +444,23 @@ pub fn query(deps: Deps<'_>, env: Env, msg: QueryMsg) -> Result<QueryResponse, C
QueryMsg::GetAccount { address } => to_binary(&try_get_account(&address, deps)?),
QueryMsg::GetMixnode { address } => to_binary(&try_get_mixnode(&address, deps)?),
QueryMsg::GetGateway { address } => to_binary(&try_get_gateway(&address, deps)?),
QueryMsg::GetCurrentVestingPeriod { address } => {
to_binary(&try_get_current_vesting_period(&address, deps, env)?)
}
};
Ok(query_res?)
}
pub fn try_get_current_vesting_period(
address: &str,
deps: Deps<'_>,
env: Env,
) -> Result<Period, ContractError> {
let account = account_from_address(address, deps.storage, deps.api)?;
Ok(account.get_current_vesting_period(env.block.time))
}
pub fn try_get_mixnode(address: &str, deps: Deps<'_>) -> Result<Option<PledgeData>, ContractError> {
let account = account_from_address(address, deps.storage, deps.api)?;
account.load_mixnode_pledge(deps.storage)
@@ -509,7 +534,7 @@ pub fn try_get_end_time(
pub fn try_get_original_vesting(
vesting_account_address: &str,
deps: Deps<'_>,
) -> Result<Coin, ContractError> {
) -> Result<OriginalVestingResponse, ContractError> {
let account = account_from_address(vesting_account_address, deps.storage, deps.api)?;
Ok(account.get_original_vesting())
}
+1 -1
View File
@@ -1,9 +1,9 @@
use crate::errors::ContractError;
use crate::vesting::Account;
use crate::vesting::PledgeData;
use cosmwasm_std::{Addr, Api, Storage, Uint128};
use cw_storage_plus::{Item, Map};
use mixnet_contract_common::IdentityKey;
use vesting_contract_common::PledgeData;
type BlockHeight = u64;
+6
View File
@@ -19,6 +19,12 @@ pub trait MixnodeBondingAccount {
amount: Coin,
storage: &mut dyn Storage,
) -> Result<(), ContractError>;
fn try_update_mixnode_config(
&self,
profit_margin_percent: u8,
storage: &mut dyn Storage,
) -> Result<Response, ContractError>;
}
pub trait GatewayBondingAccount {
@@ -1,5 +1,6 @@
use crate::errors::ContractError;
use cosmwasm_std::{Addr, Coin, Env, Storage, Timestamp};
use vesting_contract_common::OriginalVestingResponse;
pub trait VestingAccount {
// locked_coins returns the set of coins that are not spendable (can still be delegated tough) (i.e. locked),
@@ -37,7 +38,7 @@ pub trait VestingAccount {
fn get_start_time(&self) -> Timestamp;
fn get_end_time(&self) -> Timestamp;
fn get_original_vesting(&self) -> Coin;
fn get_original_vesting(&self) -> OriginalVestingResponse;
fn get_delegated_free(
&self,
block_time: Option<Timestamp>,
@@ -8,7 +8,7 @@ use mixnet_contract_common::IdentityKey;
use vesting_contract_common::events::{
new_vesting_delegation_event, new_vesting_undelegation_event,
};
use vesting_contract_common::one_unym;
use vesting_contract_common::one_ucoin;
use super::Account;
@@ -70,7 +70,7 @@ impl DelegatingAccount for Account {
let undelegate_from_mixnode = wasm_execute(
MIXNET_CONTRACT_ADDRESS.load(storage)?,
&msg,
vec![one_unym()],
vec![one_ucoin()],
)?;
Ok(Response::new()
@@ -7,7 +7,7 @@ use mixnet_contract_common::{ExecuteMsg as MixnetExecuteMsg, Gateway};
use vesting_contract_common::events::{
new_vesting_gateway_bonding_event, new_vesting_gateway_unbonding_event,
};
use vesting_contract_common::one_unym;
use vesting_contract_common::one_ucoin;
use super::Account;
@@ -34,10 +34,7 @@ impl GatewayBondingAccount for Account {
self.owner_address().as_str().to_string(),
));
} else {
PledgeData {
block_time: env.block.time,
amount: pledge.amount,
}
PledgeData::new(pledge.clone(), env.block.time)
};
let msg = MixnetExecuteMsg::BondGatewayOnBehalf {
@@ -68,7 +65,7 @@ impl GatewayBondingAccount for Account {
let unbond_msg = wasm_execute(
MIXNET_CONTRACT_ADDRESS.load(storage)?,
&msg,
vec![one_unym()],
vec![one_ucoin()],
)?;
Ok(Response::new()
@@ -1,4 +1,3 @@
use super::PledgeData;
use crate::errors::ContractError;
use crate::storage::MIXNET_CONTRACT_ADDRESS;
use crate::traits::MixnodeBondingAccount;
@@ -6,12 +5,36 @@ use cosmwasm_std::{wasm_execute, Coin, Env, Response, Storage, Uint128};
use mixnet_contract_common::{ExecuteMsg as MixnetExecuteMsg, MixNode};
use vesting_contract_common::events::{
new_vesting_mixnode_bonding_event, new_vesting_mixnode_unbonding_event,
new_vesting_update_mixnode_config_event,
};
use vesting_contract_common::one_unym;
use vesting_contract_common::one_ucoin;
use vesting_contract_common::PledgeData;
use super::Account;
impl MixnodeBondingAccount for Account {
fn try_update_mixnode_config(
&self,
profit_margin_percent: u8,
storage: &mut dyn Storage,
) -> Result<Response, ContractError> {
let msg = MixnetExecuteMsg::UpdateMixnodeConfigOnBehalf {
profit_margin_percent,
owner: self.owner_address().into_string(),
};
let update_mixnode_config_msg = wasm_execute(
MIXNET_CONTRACT_ADDRESS.load(storage)?,
&msg,
vec![one_ucoin()],
)?;
Ok(Response::new()
.add_message(update_mixnode_config_msg)
.add_event(new_vesting_update_mixnode_config_event()))
}
fn try_bond_mixnode(
&self,
mix_node: MixNode,
@@ -34,10 +57,7 @@ impl MixnodeBondingAccount for Account {
self.owner_address().as_str().to_string(),
));
} else {
PledgeData {
block_time: env.block.time,
amount: pledge.amount,
}
PledgeData::new(pledge.clone(), env.block.time)
};
let msg = MixnetExecuteMsg::BondMixnodeOnBehalf {
@@ -68,7 +88,7 @@ impl MixnodeBondingAccount for Account {
let unbond_msg = wasm_execute(
MIXNET_CONTRACT_ADDRESS.load(storage)?,
&msg,
vec![one_unym()],
vec![one_ucoin()],
)?;
Ok(Response::new()
+19 -15
View File
@@ -1,4 +1,4 @@
use super::{PledgeData, VestingPeriod};
use super::VestingPeriod;
use crate::errors::ContractError;
use crate::storage::{
load_balance, load_bond_pledge, load_gateway_pledge, remove_bond_pledge, remove_delegation,
@@ -10,6 +10,7 @@ use cw_storage_plus::Bound;
use mixnet_contract_common::IdentityKey;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use vesting_contract_common::{Period, PledgeData};
mod delegating_account;
mod gateway_bonding_account;
@@ -60,6 +61,10 @@ impl Account {
self.periods.len()
}
pub fn period_duration(&self) -> u64 {
self.periods.get(0).unwrap().period_seconds
}
pub fn storage_key(&self) -> u32 {
self.storage_key
}
@@ -92,24 +97,23 @@ impl Account {
}
}
pub fn get_current_vesting_period(&self, block_time: Timestamp) -> usize {
pub fn get_current_vesting_period(&self, block_time: Timestamp) -> Period {
// Returns the index of the next vesting period. Unless the current time is somehow in the past or vesting has not started yet.
// In case vesting is over it will always return NUM_VESTING_PERIODS.
let period = match self
.periods
.iter()
.map(|period| period.start_time)
.collect::<Vec<u64>>()
.binary_search(&block_time.seconds())
{
Ok(u) => u,
Err(u) => u,
};
if period > 0 {
period - 1
if block_time.seconds() < self.periods.first().unwrap().start_time {
Period::Before
} else if self.periods.last().unwrap().end_time() < block_time {
Period::After
} else {
0
let mut index = 0;
for period in &self.periods {
if block_time < period.end_time() {
break;
}
index += 1;
}
Period::In(index)
}
}
@@ -3,6 +3,7 @@ use crate::storage::{delete_account, save_account, DELEGATIONS};
use crate::traits::VestingAccount;
use config::defaults::DENOM;
use cosmwasm_std::{Addr, Coin, Env, Order, Storage, Timestamp, Uint128};
use vesting_contract_common::{OriginalVestingResponse, Period};
use super::Account;
@@ -61,17 +62,15 @@ impl VestingAccount for Account {
let period = self.get_current_vesting_period(block_time);
let amount = match period {
// We're in the first period, or the vesting has not started yet.
0 => Coin {
Period::Before => Coin {
amount: Uint128::new(0),
denom: DENOM.to_string(),
},
// We always have 8 vesting periods, so periods 1-7 are special
1..=7 => Coin {
amount: Uint128::new(self.tokens_per_period()? * period as u128),
Period::In(idx) => Coin {
amount: Uint128::new(self.tokens_per_period()? * idx as u128),
denom: DENOM.to_string(),
},
_ => Coin {
Period::After => Coin {
amount: self.coin.amount,
denom: DENOM.to_string(),
},
@@ -85,7 +84,7 @@ impl VestingAccount for Account {
env: &Env,
) -> Result<Coin, ContractError> {
Ok(Coin {
amount: self.get_original_vesting().amount
amount: self.get_original_vesting().amount().amount
- self.get_vested_coins(block_time, env)?.amount,
denom: DENOM.to_string(),
})
@@ -99,8 +98,12 @@ impl VestingAccount for Account {
self.periods[(self.num_vesting_periods() - 1) as usize].end_time()
}
fn get_original_vesting(&self) -> Coin {
self.coin.clone()
fn get_original_vesting(&self) -> OriginalVestingResponse {
OriginalVestingResponse::new(
self.coin.clone(),
self.num_vesting_periods(),
self.period_duration(),
)
}
fn get_delegated_free(
@@ -111,8 +114,12 @@ impl VestingAccount for Account {
) -> Result<Coin, ContractError> {
let block_time = block_time.unwrap_or(env.block.time);
let period = self.get_current_vesting_period(block_time);
let max_vested = self.tokens_per_period()? * period as u128;
let start_time = self.periods[period].start_time;
let max_vested = self.get_vested_coins(Some(block_time), env)?;
let start_time = match period {
Period::Before => 0,
Period::After => u64::MAX,
Period::In(idx) => self.periods[idx as usize].start_time,
};
let coin = DELEGATIONS
.sub_prefix(self.storage_key())
@@ -123,7 +130,7 @@ impl VestingAccount for Account {
acc + amount
});
let amount = Uint128::new(coin.u128().min(max_vested));
let amount = Uint128::new(coin.u128().min(max_vested.amount.u128()));
Ok(Coin {
amount,
@@ -157,15 +164,19 @@ impl VestingAccount for Account {
) -> Result<Coin, ContractError> {
let block_time = block_time.unwrap_or(env.block.time);
let period = self.get_current_vesting_period(block_time);
let max_vested = self.tokens_per_period()? * period as u128;
let start_time = self.periods[period].start_time;
let max_vested = self.get_vested_coins(Some(block_time), env)?;
let start_time = match period {
Period::Before => 0,
Period::After => u64::MAX,
Period::In(idx) => self.periods[idx as usize].start_time,
};
let amount = if let Some(bond) = self
.load_mixnode_pledge(storage)?
.or(self.load_gateway_pledge(storage)?)
{
if bond.block_time.seconds() < start_time {
bond.amount
if bond.block_time().seconds() < start_time {
bond.amount().amount
} else {
Uint128::zero()
}
@@ -173,7 +184,7 @@ impl VestingAccount for Account {
Uint128::zero()
};
let amount = Uint128::new(amount.u128().min(max_vested));
let amount = Uint128::new(amount.u128().min(max_vested.amount.u128()));
Ok(Coin {
amount,
@@ -194,7 +205,7 @@ impl VestingAccount for Account {
.load_mixnode_pledge(storage)?
.or(self.load_gateway_pledge(storage)?)
{
let amount = bond.amount - bonded_free.amount;
let amount = bond.amount().amount - bonded_free.amount;
Ok(Coin {
amount,
denom: DENOM.to_string(),
+50 -26
View File
@@ -1,4 +1,4 @@
use cosmwasm_std::{Timestamp, Uint128};
use cosmwasm_std::Timestamp;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
@@ -19,12 +19,6 @@ impl VestingPeriod {
}
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct PledgeData {
amount: Uint128,
block_time: Timestamp,
}
pub fn populate_vesting_periods(
start_time: u64,
vesting_spec: VestingSpecification,
@@ -53,6 +47,7 @@ mod tests {
use cosmwasm_std::{coins, Addr, Coin, Timestamp, Uint128};
use mixnet_contract_common::{Gateway, MixNode};
use vesting_contract_common::messages::ExecuteMsg;
use vesting_contract_common::Period;
#[test]
fn test_account_creation() {
@@ -182,48 +177,63 @@ mod tests {
assert_eq!(account.periods().len(), num_vesting_periods as usize);
let current_period = account.get_current_vesting_period(Timestamp::from_seconds(0));
assert_eq!(0, current_period);
assert_eq!(Period::Before, current_period);
let block_time =
Timestamp::from_seconds(account.start_time().seconds() + vesting_period + 1);
let current_period = account.get_current_vesting_period(block_time);
assert_eq!(current_period, 1);
assert_eq!(current_period, Period::In(1));
let vested_coins = account.get_vested_coins(Some(block_time), &env).unwrap();
let vesting_coins = account.get_vesting_coins(Some(block_time), &env).unwrap();
assert_eq!(
vested_coins.amount,
Uint128::new(
account.get_original_vesting().amount.u128() / num_vesting_periods as u128
account.get_original_vesting().amount().amount.u128() / num_vesting_periods as u128
)
);
assert_eq!(
vesting_coins.amount,
Uint128::new(
account.get_original_vesting().amount.u128()
- account.get_original_vesting().amount.u128() / num_vesting_periods as u128
account.get_original_vesting().amount().amount.u128()
- account.get_original_vesting().amount().amount.u128()
/ num_vesting_periods as u128
)
);
let block_time =
Timestamp::from_seconds(account.start_time().seconds() + 5 * vesting_period + 1);
let current_period = account.get_current_vesting_period(block_time);
assert_eq!(current_period, 5);
assert_eq!(current_period, Period::In(5));
let vested_coins = account.get_vested_coins(Some(block_time), &env).unwrap();
let vesting_coins = account.get_vesting_coins(Some(block_time), &env).unwrap();
assert_eq!(
vested_coins.amount,
Uint128::new(
5 * account.get_original_vesting().amount.u128() / num_vesting_periods as u128
5 * account.get_original_vesting().amount().amount.u128()
/ num_vesting_periods as u128
)
);
assert_eq!(
vesting_coins.amount,
Uint128::new(
account.get_original_vesting().amount.u128()
- 5 * account.get_original_vesting().amount.u128()
account.get_original_vesting().amount().amount.u128()
- 5 * account.get_original_vesting().amount().amount.u128()
/ num_vesting_periods as u128
)
);
let vesting_over_period = num_vesting_periods + 1;
let block_time = Timestamp::from_seconds(
account.start_time().seconds() + vesting_over_period * vesting_period + 1,
);
let current_period = account.get_current_vesting_period(block_time);
assert_eq!(current_period, Period::After);
let vested_coins = account.get_vested_coins(Some(block_time), &env).unwrap();
let vesting_coins = account.get_vesting_coins(Some(block_time), &env).unwrap();
assert_eq!(
vested_coins.amount,
Uint128::new(account.get_original_vesting().amount().amount.u128())
);
assert_eq!(vesting_coins.amount, Uint128::zero());
}
#[test]
@@ -396,7 +406,7 @@ mod tests {
assert!(err.is_err());
let pledge = account.load_mixnode_pledge(&deps.storage).unwrap().unwrap();
assert_eq!(Uint128::new(500_000_000_000), pledge.amount);
assert_eq!(Uint128::new(500_000_000_000), pledge.amount().amount);
// Current period -> block_time: None
let bonded_free = account.get_pledged_free(None, &env, &deps.storage).unwrap();
@@ -405,7 +415,10 @@ mod tests {
let bonded_vesting = account
.get_pledged_vesting(None, &env, &deps.storage)
.unwrap();
assert_eq!(pledge.amount - bonded_free.amount, bonded_vesting.amount);
assert_eq!(
pledge.amount().amount - bonded_free.amount,
bonded_vesting.amount
);
// All periods
for (i, period) in account.periods().iter().enumerate() {
@@ -417,7 +430,8 @@ mod tests {
)
.unwrap();
assert_eq!(
(account.tokens_per_period().unwrap() * i as u128).min(pledge.amount.u128()),
(account.tokens_per_period().unwrap() * i as u128)
.min(pledge.amount().amount.u128()),
bonded_free.amount.u128()
);
@@ -428,7 +442,10 @@ mod tests {
&deps.storage,
)
.unwrap();
assert_eq!(pledge.amount - bonded_free.amount, bonded_vesting.amount);
assert_eq!(
pledge.amount().amount - bonded_free.amount,
bonded_vesting.amount
);
}
let bonded_free = account
@@ -438,7 +455,7 @@ mod tests {
&deps.storage,
)
.unwrap();
assert_eq!(pledge.amount, bonded_free.amount);
assert_eq!(pledge.amount().amount, bonded_free.amount);
let bonded_vesting = account
.get_pledged_vesting(
@@ -509,7 +526,7 @@ mod tests {
assert!(err.is_err());
let pledge = account.load_gateway_pledge(&deps.storage).unwrap().unwrap();
assert_eq!(Uint128::new(500_000_000_000), pledge.amount);
assert_eq!(Uint128::new(500_000_000_000), pledge.amount().amount);
// Current period -> block_time: None
let bonded_free = account.get_pledged_free(None, &env, &deps.storage).unwrap();
@@ -518,7 +535,10 @@ mod tests {
let bonded_vesting = account
.get_pledged_vesting(None, &env, &deps.storage)
.unwrap();
assert_eq!(pledge.amount - bonded_free.amount, bonded_vesting.amount);
assert_eq!(
pledge.amount().amount - bonded_free.amount,
bonded_vesting.amount
);
// All periods
for (i, period) in account.periods().iter().enumerate() {
@@ -530,7 +550,8 @@ mod tests {
)
.unwrap();
assert_eq!(
(account.tokens_per_period().unwrap() * i as u128).min(pledge.amount.u128()),
(account.tokens_per_period().unwrap() * i as u128)
.min(pledge.amount().amount.u128()),
bonded_free.amount.u128()
);
@@ -541,7 +562,10 @@ mod tests {
&deps.storage,
)
.unwrap();
assert_eq!(pledge.amount - bonded_free.amount, bonded_vesting.amount);
assert_eq!(
pledge.amount().amount - bonded_free.amount,
bonded_vesting.amount
);
}
let bonded_free = account
@@ -551,7 +575,7 @@ mod tests {
&deps.storage,
)
.unwrap();
assert_eq!(pledge.amount, bonded_free.amount);
assert_eq!(pledge.amount().amount, bonded_free.amount);
let bonded_vesting = account
.get_pledged_vesting(

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