Compare commits

..

197 Commits

Author SHA1 Message Date
Bogdan-Ștefan Neacşu 099de36c60 Revert "Remove rustls feature on workspace deps (#4422)"
This reverts commit 2bff66e2c7.
2024-03-06 12:39:36 +02:00
dependabot[bot] 9e5890a0d7 Bump ip in /clients/native/examples/js-examples/websocket (#4421)
Bumps [ip](https://github.com/indutny/node-ip) from 1.1.5 to 1.1.9.
- [Commits](https://github.com/indutny/node-ip/compare/v1.1.5...v1.1.9)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-06 08:15:03 +01:00
dependabot[bot] 3bda5f59a3 Bump ip from 2.0.0 to 2.0.1 (#4417)
Bumps [ip](https://github.com/indutny/node-ip) from 2.0.0 to 2.0.1.
- [Commits](https://github.com/indutny/node-ip/compare/v2.0.0...v2.0.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-06 07:58:31 +01:00
import this 154dfa089b [DOC]: Hotfix - remove unexisting page (#4438)
* [DOC]: Hotfix - remove unexisting page

* update modules and run build

* installed with nvm 18

* yarn version solved

* remove extra files

* attempt to resolve module versioning

* Update package versions and fix keplr example

---------

Co-authored-by: Mark Sinclair <mmsinclair@users.noreply.github.com>
2024-03-05 15:07:38 +00:00
Jon Häggblad bd0cbbc18a Fix typo in macro invocation (#4441) 2024-03-04 14:48:43 +01:00
Jędrzej Stuczyński ed0e7a7a25 Merge pull request #4439 from nymtech/chore/move-nym-id
Chore/move nym
2024-03-04 12:19:41 +00:00
Jon Häggblad 5b35cfcfb2 build-information: pick up vergen from consuming crate (#4424) 2024-03-04 12:16:48 +01:00
Bogdan-Ștefan Neacşu d3ba008b88 Add IPPair constructor (#4440)
* Add IPPair constructor

* Rename
2024-03-04 12:56:10 +02:00
Jędrzej Stuczyński a04a782dbf renamed nym-id-lib to nym-id 2024-03-04 10:08:10 +00:00
Jędrzej Stuczyński f5d9fda0b1 moved and renamed nym-id to nym-id-cli 2024-03-04 09:38:29 +00:00
Bogdan-Ștefan Neacşu aebd386382 Add IPv6 support to IPPR (#4431) 2024-03-01 19:28:21 +02:00
Bogdan-Ștefan Neacşu 9a6f96b5e0 Fix windows build (#4426)
* Fix windows build

* Fix in another place too

* Install clang

* With sudo

* Revert "cargo update -p rustls@0.21.7 (#4404)"

This reverts commit ecc47cd418.
2024-03-01 12:07:38 +02:00
Jędrzej Stuczyński 5a3ff0f9f7 Merge pull request #4436 from nymtech/feature/nym-id-scaffold
Feature/nym id scaffold
2024-03-01 09:14:40 +00:00
Jędrzej Stuczyński 160db34651 clippy 2024-02-29 16:53:50 +00:00
Jędrzej Stuczyński ae20d2afb8 removed old test code 2024-02-29 16:04:07 +00:00
Jędrzej Stuczyński 41b7a2a20d cargo fmt 2024-02-29 15:57:48 +00:00
Jędrzej Stuczyński 208ec4574b using the shared code for credentials import 2024-02-29 15:29:41 +00:00
Jon Häggblad 2bff66e2c7 Remove rustls feature on workspace deps (#4422)
* Remove rustls feature on workspace deps

* Cargo.lock for nym-connect and nym-wallet
2024-02-28 18:45:00 +02:00
Jędrzej Stuczyński 1aad5fc1bf created nym-id for importing credentials 2024-02-28 11:22:46 +00:00
import this cb3e73fbd7 [DOC]/operators: Validator rewards (#4427)
* initialise token economics chapter

* initialise validator rewards page

* add todo points

* syntax edits

* docs: minor fixes

* add currency overview

* create bash scripts for nyx stake

* add nymvisor url

* final version of validator rewards

* final version of validator rewards

* final version of validator rewards

---------

Co-authored-by: Sachin Kamath <github@skamath.me>
2024-02-27 14:17:44 +00:00
Tommy Verrall eabb36b975 Merge pull request #4425 from nymtech/fix/nym-vpn/testing
[DOC]: Fix NymVPN desktop setup guide
2024-02-22 11:00:23 +01:00
serinko 2eed8e3f6c syntax edit 2024-02-22 10:40:38 +01:00
serinko bfac3e0b89 add gui-mac to summary 2024-02-22 10:23:00 +01:00
serinko 90680ceb16 add moving application step 2024-02-22 10:14:14 +01:00
Tommy Verrall f9c5684d6c Merge pull request #4414 from nymtech/qa/remove-deb-build
temporarily remove the debian builder from gh action
2024-02-22 09:49:44 +01:00
serinko ffb053fe4a desktop auto script update 2024-02-22 06:28:12 +01:00
serinko e83be64a52 mac desktop manual steps 2024-02-22 06:23:18 +01:00
serinko 32c897f789 add mac desktop manual setup 2024-02-21 11:33:51 +01:00
Bogdan-Ștefan Neacşu 9ff37d2f9f Propagate gateway ws fd into sdk (#4398)
* Propagate gateway ws fd into sdk

* Wrap fd in a more general struct
2024-02-21 12:27:18 +02:00
serinko a6ebfb521d remove redundant part 2024-02-21 11:20:57 +01:00
import this ac23ef924a [DOC]: Publish Nymvisor guide (#4423) 2024-02-21 09:27:12 +00:00
Tommy Verrall 5a770614dd formatting 2024-02-20 19:48:04 +01:00
Tommy Verrall 8f8cd79a65 amend workflow to input event instead 2024-02-20 19:45:22 +01:00
Jon Häggblad d8f73ef97a Update Cargo.lock 2024-02-20 19:03:54 +01:00
Tommy Verrall c7fb89bd5e Merge pull request #4412 from nymtech/master
Merge Master into Develop from Latest 2024.1-marabou release
2024-02-20 18:59:01 +01:00
benedettadavico 3c2d47ad18 update cargo.lock 2024-02-20 18:34:23 +01:00
Tommy Verrall 6f13720530 Merge pull request #4416 from nymtech/qa/merge-conflicts
make check on conflicts
2024-02-20 17:59:10 +01:00
Tommy Verrall 0efd7a2318 make check on conflicts 2024-02-20 17:45:23 +01:00
Lawrence Stalder 2ca2b9e032 Merge pull request #4413 from nymtech/fix-localnet-script
fix: localnet script fix typo to attach tmux session
2024-02-20 16:33:51 +01:00
Tommy Verrall d92a8ea028 temporarily remove the debian builder from gh action
- it's not needed but in the future we may want to reactivate it, currently commenting it out
2024-02-20 16:25:58 +01:00
Lawrence Stalder 7483d10701 fix: localnet script fix typo to attach tmux session 2024-02-20 16:02:53 +01:00
Jędrzej Stuczyński ca75c06f4c Merge pull request #4396 from nymtech/feature/freepass-combined
freepass credentials
2024-02-20 12:26:24 +00:00
import this 73632a0ae7 [DOC]: Update nym-vpn commands (#4386)
* update nym-vpn commands

* update testing flow

* bumped up scripts and version url to 0.0.4

* correct cli script version -> 0.0.2

* update extract commands

* update extract commands

* update gui and cli auto scripts

* correct curl url

* minor fixes to formatting

* add final bash script for desktop

* syntax change

* commenting mac manual steps

* comment off mac manual steps from summary

* Update SUMMARY.md

* Update troubleshooting.md

---------

Co-authored-by: mfahampshire <mfahampshire@pm.me>
2024-02-20 12:08:36 +00:00
Tommy Verrall 3d3dd80247 Merge pull request #4411 from nymtech/release/2024.1-marabou
Release/2024.1 marabou
2024-02-20 12:22:20 +01:00
Jędrzej Stuczyński 1d481db179 additional log for dkg address 2024-02-20 11:07:04 +00:00
Jędrzej Stuczyński cae97663c1 additional gateway logs 2024-02-20 11:03:44 +00:00
Tommy Verrall 795329b874 Merge pull request #4410 from nymtech/qa/debian-test
Debian Package Pre/Post install
2024-02-20 10:58:42 +01:00
Tommy Verrall 87ea3fcfc4 remove extra line 2024-02-20 09:29:26 +01:00
Tommy Verrall 289343d1c8 one last tweak 2024-02-20 09:26:00 +01:00
Jędrzej Stuczyński f96f74f2f1 removed unused imports 2024-02-19 18:27:16 +00:00
Jędrzej Stuczyński 3ec2ea904f fixed local expiration check 2024-02-19 17:55:36 +00:00
Jędrzej Stuczyński 04373589b1 added import-credential command to network requester 2024-02-19 17:51:19 +00:00
Jędrzej Stuczyński 1a8814ccdc changed nonces to be random bytes to prevent replay attacks 2024-02-19 17:45:39 +00:00
Jędrzej Stuczyński d62a41b9c1 fixed client route used for free pass 2024-02-19 17:09:16 +00:00
Jędrzej Stuczyński d3e30e98f9 preventing spending credentials with outdated gateways 2024-02-19 16:01:29 +00:00
Jędrzej Stuczyński 88a49dfc7e making sure the retrieved credentials haven't expired 2024-02-19 15:26:51 +00:00
Tommy Verrall 66a54aeab3 small formatting 2024-02-19 14:51:23 +01:00
Tommy Verrall d6afa74284 debhelper 2024-02-19 14:40:14 +01:00
Tommy Verrall 49e2be5b04 a condition was not being met for new installs
therefore, input a preinst script too to back up
2024-02-19 14:37:35 +01:00
Tommy Verrall 1cfddb942b remove line 2024-02-19 13:43:56 +01:00
Tommy Verrall 49c43617c9 include a pretty print of the service file here 2024-02-19 13:41:38 +01:00
Jędrzej Stuczyński ff01fc79e3 removed duplicate code 2024-02-19 12:19:54 +00:00
Jędrzej Stuczyński 5cf53b7002 fixed logging 2024-02-19 12:11:50 +00:00
Jędrzej Stuczyński 387d07fb93 additional logs 2024-02-19 11:43:17 +00:00
Jędrzej Stuczyński dcd6dcc6e3 restored accidentally removed lazy static in socks5 lib 2024-02-19 11:43:16 +00:00
Jędrzej Stuczyński e7d0c1812a added import commands for client binaries 2024-02-19 11:43:15 +00:00
Jędrzej Stuczyński 7bbac26676 replaced usage of lazy_static to oncelock for build information 2024-02-19 11:42:57 +00:00
Jędrzej Stuczyński 688ac2efb5 added nym-cli command for importing credentials 2024-02-19 11:42:57 +00:00
Jędrzej Stuczyński f348e6972a clippy 2024-02-19 11:42:57 +00:00
Jędrzej Stuczyński dd97eb13a8 locally marking credentials as spent 2024-02-19 11:42:57 +00:00
Jędrzej Stuczyński 92d9cb7dab added database code for the serial number storage 2024-02-19 11:42:56 +00:00
Jędrzej Stuczyński 5a4dfafe9f cargo fmt 2024-02-19 11:42:56 +00:00
Jędrzej Stuczyński fa93c4598f removing redundant epoch_id field 2024-02-19 11:42:56 +00:00
Jędrzej Stuczyński edbcade5f5 clippy 2024-02-19 11:42:56 +00:00
Jędrzej Stuczyński 3f0194a9aa nym-cli commands for issuing free passes 2024-02-19 11:42:56 +00:00
Jędrzej Stuczyński c2517ac63b clippy 2024-02-19 11:42:55 +00:00
Jędrzej Stuczyński 3fa74c90ff cargo fmt 2024-02-19 11:42:55 +00:00
Jędrzej Stuczyński 96f3192694 validating request attributes 2024-02-19 11:42:55 +00:00
Jędrzej Stuczyński f61b898c4f storage implementation 2024-02-19 11:42:55 +00:00
Jędrzej Stuczyński c9ff550311 nym-api logic for issuing free passes (minus storage impl) 2024-02-19 11:42:53 +00:00
Jędrzej Stuczyński 740cc72ec8 request type for obtaining free pass 2024-02-19 11:41:42 +00:00
benedettadavico 6e7bac1e7e cargo fmt 2024-02-19 11:41:32 +00:00
benedettadavico 691884e20a add return statement 2024-02-19 11:41:32 +00:00
Jędrzej Stuczyński 400d71bf07 ibid 2024-02-19 11:41:32 +00:00
benedettadavico ffe55ba072 running cargo fmt 2024-02-19 11:41:32 +00:00
Jędrzej Stuczyński 00f1ce98ba gateway downgrading advertised protocol for incompatible clients 2024-02-19 11:41:31 +00:00
Jędrzej Stuczyński b02bbdef19 fixed SQL type for epoch_id 2024-02-19 11:41:31 +00:00
Jędrzej Stuczyński 78e1d84905 restored OldV1Credential::as_bytes to be available to non-test code 2024-02-19 11:41:31 +00:00
Jędrzej Stuczyński 2638952f5a reintroduced handling of old v1 credentials 2024-02-19 11:41:31 +00:00
Jędrzej Stuczyński 9a3bd7a2a9 clippy and fixing tests 2024-02-19 11:41:31 +00:00
Jędrzej Stuczyński ad9aee0ec0 missing serialization 2024-02-19 11:41:31 +00:00
Jędrzej Stuczyński f687ebb0f5 persisting the issued credentials 2024-02-19 11:41:31 +00:00
Jędrzej Stuczyński ddf2770c8e reintroduced recovery of vouchers 2024-02-19 11:41:30 +00:00
Jędrzej Stuczyński 16c942d72e removed nym-api placeholders 2024-02-19 11:41:30 +00:00
Jędrzej Stuczyński 0ee727bac1 gateway handling of both credential types 2024-02-19 11:41:30 +00:00
Jędrzej Stuczyński 675cf3d7da removed usage of coconut-interface crate 2024-02-19 11:41:29 +00:00
Jędrzej Stuczyński 9a0cbf5072 wip in removing the Credential type for more strongly typed alternative 2024-02-19 11:39:54 +00:00
Jędrzej Stuczyński 6f3dd9f778 wip 2024-02-19 11:39:53 +00:00
Jędrzej Stuczyński 7a7fbce8ea using bincode serialization 2024-02-19 11:39:53 +00:00
Jędrzej Stuczyński 36242fa257 serde for 'IssuanceBandwidthCredential' 2024-02-19 11:39:53 +00:00
Jędrzej Stuczyński b764fcc756 revamped BandwidthVoucher to allow for different kinds of bandwidth credentials 2024-02-19 11:39:53 +00:00
Jędrzej Stuczyński ac676760d4 Merge pull request #4399 from nymtech/bugfix/signing-rewards
Bugfix/signing rewards
2024-02-19 11:30:42 +00:00
Jędrzej Stuczyński 20819331f3 Merge pull request #4405 from nymtech/bugfix/further-dkg-changes
Bugfix/further dkg changes
2024-02-19 11:29:53 +00:00
Jędrzej Stuczyński 6b6980c523 missing schema 2024-02-16 17:41:46 +00:00
Jędrzej Stuczyński 8b0953624f being less aggressive in contract polling 2024-02-16 17:39:22 +00:00
Jędrzej Stuczyński 24a260fbc9 missing trait implementation in test 2024-02-16 17:36:23 +00:00
Jędrzej Stuczyński 510ad11c98 nym-api using the new query 2024-02-16 17:33:07 +00:00
Jędrzej Stuczyński 627334cfe2 added dkg contract query to check if state can be advanced 2024-02-16 17:16:39 +00:00
Jędrzej Stuczyński d4c98e3ff5 clippy test 2024-02-16 16:47:01 +00:00
Jędrzej Stuczyński 9821dd994b updated schema 2024-02-16 16:38:03 +00:00
Jędrzej Stuczyński a977310225 fixed existing dkg contract tests 2024-02-16 16:27:58 +00:00
Tommy Verrall 8e16678f74 fix syntax 2024-02-16 16:39:01 +01:00
Tommy Verrall 52c46f371e shell 2024-02-16 16:18:11 +01:00
Tommy Verrall 3010d5192f add helper 2024-02-16 16:00:18 +01:00
Tommy Verrall 721ad9d8bb remove helper 2024-02-16 15:55:43 +01:00
Tommy Verrall 85803ec11c change some of the logic 2024-02-16 15:25:25 +01:00
Tommy Verrall 83da1f228b debian package changer
- instead of dealing with the complexities of initing the builds, it's a complex beast for automagically guessing a user config for a binary
- therefore, find their existing binary, move the executable from /usr/bin/ then find and replace it with their current set up
- a user then can do sudo apt install nym-gateway && systemctl restart nym-gateway.service
- script tells the user a few key things too
2024-02-16 14:05:53 +01:00
Jędrzej Stuczyński c663ba08f2 fixed dkg incorrectly setting state deadlines 2024-02-16 09:16:59 +00:00
Jędrzej Stuczyński 92bf31d9f4 fixed dkg progress not being recorded 2024-02-16 09:16:58 +00:00
Jędrzej Stuczyński 646f522142 fixed nym-api tests 2024-02-16 09:16:58 +00:00
Jędrzej Stuczyński be3dd2c250 setting threshold value upon entering dealing exchange 2024-02-16 09:16:58 +00:00
Jędrzej Stuczyński db826c4fb4 missing DkgExecuteMsg client impls 2024-02-16 09:16:58 +00:00
Jędrzej Stuczyński b960dc8aaf removed 'SurpassedThreshold' message 2024-02-16 09:16:58 +00:00
Jędrzej Stuczyński da70ae70a5 nym-api updates 2024-02-16 09:16:58 +00:00
Jędrzej Stuczyński 914b8a6dc2 updated the validator client 2024-02-16 09:16:58 +00:00
Jędrzej Stuczyński ad2552ec78 schema 2024-02-16 09:16:57 +00:00
Jędrzej Stuczyński 45686f7ca6 queries 2024-02-16 09:16:57 +00:00
Jędrzej Stuczyński 27554f52e3 revamped dealers storage structure (for txs) 2024-02-16 09:16:57 +00:00
Jędrzej Stuczyński 29edc8799a dkg reset/resharing triggered by admin messages instead 2024-02-16 09:16:57 +00:00
Jędrzej Stuczyński 46875cdf2f moved epoch advancement logic into separate file 2024-02-16 09:16:57 +00:00
Jędrzej Stuczyński 629081b5ec fixed reset mode not being triggered when enough parties left 2024-02-16 09:16:57 +00:00
Jędrzej Stuczyński d2c77d7f64 integration test for failed DKG redoing 2024-02-16 09:16:57 +00:00
Jędrzej Stuczyński eab7eb03c7 reduced tick rate logging 2024-02-16 09:16:56 +00:00
Jon Häggblad ecc47cd418 cargo update -p rustls@0.21.7 (#4404) 2024-02-15 16:06:09 +01:00
benedetta davico 71c975d20c Update publish-nym-binaries.yml 2024-02-15 11:46:20 +01:00
benedettadavico f0705cd1f9 Update workflow to add nymvisor binary 2024-02-15 10:56:13 +01:00
Stefano Piermatteo b6d5f780d2 [DOC]: Add landing page howto (#4378)
* add html snippet

* add reverse proxy

* fix typos
fix variables conventions
fix markdown

* add Avril 14th sentence

* fix syntax
2024-02-15 07:40:36 +00:00
Jędrzej Stuczyński 0b46e5b753 improved startup log regarding the epoch 2024-02-14 16:36:37 +00:00
Jędrzej Stuczyński 2c65460164 additional logs 2024-02-14 16:10:14 +00:00
Jędrzej Stuczyński e86419540c don't try to send empty rewarding txs 2024-02-14 16:10:14 +00:00
benedettadavico 3771cb9188 Update changelog and bump versions 2024-02-14 11:55:30 +01:00
mx e8f6d6e55d fixed theme bug? (#4401)
Co-authored-by: mfahampshire <mfahampshire@pm.me>
2024-02-14 09:59:19 +01:00
Jędrzej Stuczyński 536b892c91 fixed epoch id being advanced at wrong point 2024-02-14 08:49:08 +00:00
Jędrzej Stuczyński a40cd73dec Merge pull request #4402 from nymtech/bugfix/post-ephemera-nym-api
fixed nym-api config template
2024-02-13 17:41:38 +00:00
Jędrzej Stuczyński d7255374de fixed nym-api config template 2024-02-13 17:41:07 +00:00
Jędrzej Stuczyński 0b6cb236d8 allow running in monitor only mode without any tokens 2024-02-13 15:22:03 +00:00
Jędrzej Stuczyński f0361a200b log errors on failing to determine rewarding amounts and advance epochs regardless 2024-02-13 15:20:36 +00:00
Jędrzej Stuczyński f1c5e8bdc0 attempt to re-create websocket creation on failure 2024-02-13 14:41:57 +00:00
Jędrzej Stuczyński b03d737393 making sure to stop nym-rewarder if nyxd scraper has terminated 2024-02-13 12:03:06 +00:00
Jon Häggblad 3088b69711 Merge pull request #3503 from nymtech/jon/feat/test-rustls
Replace openssl with rustls
2024-02-13 08:55:47 +01:00
Jon Häggblad 412b7b9898 Remove sdk-version-bump from main workspace temporarily
In the upcoming cargo-edit version then the dependency on ureq is
dropped and also the implicit dependency on openssl
2024-02-13 08:29:39 +01:00
Jon Häggblad 30754a7a4a Switch tungstenite to rustls 2024-02-13 08:28:09 +01:00
Jon Häggblad e99b04f1c6 Remove explicit openssl dependency 2024-02-13 08:28:09 +01:00
Jon Häggblad 279fea9a0b Switch reqwest to rustls 2024-02-13 08:28:09 +01:00
Jon Häggblad c2aba223b8 Add openssl to cargo deny ban 2024-02-13 08:28:09 +01:00
Jędrzej Stuczyński 501f314266 Merge pull request #4356 from nymtech/chore/remove-ephemera
Chore/remove ephemera
2024-02-12 18:31:58 +00:00
Jędrzej Stuczyński 3ecd2af216 fixed test imports 2024-02-12 17:14:25 +00:00
Jon Häggblad 9b44674f43 Remove sdk-version-bump from main workspace temporarily
In the upcoming cargo-edit version then the dependency on ureq is
dropped and also the implicit dependency on openssl
2024-02-12 14:14:30 +01:00
Jon Häggblad 588839740f Switch tungstenite to rustls 2024-02-12 14:14:30 +01:00
Jon Häggblad 4353bab636 Remove explicit openssl dependency 2024-02-12 14:14:30 +01:00
Jon Häggblad 05957c366f Switch reqwest to rustls 2024-02-12 14:14:30 +01:00
Jon Häggblad 60e14f866e Add openssl to cargo deny ban 2024-02-12 14:14:30 +01:00
Jon Häggblad cec05a99f4 Tweak packet rate log string 2024-02-12 13:05:30 +01:00
Jon Häggblad d487f4d98c Merge pull request #4389 from nymtech/jon/handle-multiple-ip-packet-in-ipr
Handle multiple IP packets in ip-packet-router
2024-02-12 12:39:40 +01:00
Jon Häggblad b9e9809938 Extract out handle_responses 2024-02-12 12:14:51 +01:00
Jędrzej Stuczyński 9b50188d7d Merge pull request #4391 from nymtech/chore/reexport-types
re-export cosmrs' cosmwasm types
2024-02-12 09:14:33 +00:00
Jon Häggblad 0e3dbece8b Fix unit test 2024-02-12 08:21:32 +01:00
Jędrzej Stuczyński 052f7649a8 re-export cosmrs' cosmwasm types 2024-02-11 18:57:32 +00:00
Jon Häggblad 3fde9e648f Add health request response 2024-02-10 23:53:03 +01:00
Jon Häggblad 0b37b9fb1c Add ping pong request response 2024-02-10 23:35:36 +01:00
Jon Häggblad e273bfc25e Add message for unrequested disconnect on the IPR 2024-02-10 23:27:07 +01:00
Jon Häggblad d2ef94f1bd Add buffer timeout to connect request 2024-02-10 23:13:50 +01:00
Jon Häggblad 92ab794294 Encode packets in connection handler 2024-02-10 23:07:45 +01:00
Jon Häggblad 3f0210d56a Handle incoming multi-ip packets in IPR 2024-02-10 22:40:21 +01:00
Jon Häggblad 9b53473bee Tweak retransmission log info (#4387) 2024-02-09 18:22:25 +01:00
Tommy Verrall 5fdae14cb9 Merge pull request #4385 from nymtech/bugfix/gateway-vk-caching-without-coconut
[bugfix] remove hard failure on dkg contract queries in case it doesn't exist
2024-02-09 18:11:05 +01:00
Jędrzej Stuczyński ccb4d7fd5e comment regarding removal of ephemera 2024-02-09 14:40:24 +00:00
Jędrzej Stuczyński a8e520d13b removed unused import 2024-02-09 14:38:43 +00:00
Jędrzej Stuczyński 148db2f350 replaced uses of 'serde_derive' with 'serde' 2024-02-09 14:38:02 +00:00
Jędrzej Stuczyński 2f4fad3ce3 [bugfix] remove hard failure on dkg contract queries in case it doesn't exist 2024-02-09 11:39:27 +00:00
Jon Häggblad cc604c5f18 Merge pull request #4380 from nymtech/jon/ipr-connected-client-handler
Connected client handler in the IPR
2024-02-09 11:37:13 +01:00
Jon Häggblad d0aece501f Add missing deploy step to ci-build-upload-binaries 2024-02-09 11:28:32 +01:00
Jon Häggblad 22b5670396 Update release/publish workflow names to match filenames (#4383) 2024-02-09 11:26:39 +01:00
benedetta davico 4ebbf175fc Merge branch 'develop' into chore/remove-ephemera 2024-02-09 11:24:52 +01:00
Jon Häggblad 79e9399dfe Add nightly schedule trigger for ci-build-upload-binaries 2024-02-09 11:17:34 +01:00
Jon Häggblad 8450df28df Tweak logging 2024-02-09 10:58:49 +01:00
Jon Häggblad 0b23d1624f Switch to JoinHandle 2024-02-09 09:49:18 +01:00
Jon Häggblad 2026ffd61f Error logging 2024-02-09 09:49:18 +01:00
Jon Häggblad 48e5aecda1 Don't unwrap on failed to send close signal 2024-02-09 09:49:18 +01:00
Jon Häggblad d8e484b77e Disconnect stopped client handlers 2024-02-09 09:49:18 +01:00
Jon Häggblad d4ca2a7220 Implement drop for client handlers too 2024-02-09 09:49:18 +01:00
Jon Häggblad 2f0074821c Downgrade some logging after checking it works 2024-02-09 09:49:18 +01:00
Jon Häggblad d5e332ad39 Deduplicate and clean up 2024-02-09 09:49:18 +01:00
Jon Häggblad 14bf5645b1 Add missing module 2024-02-09 09:49:18 +01:00
Jon Häggblad a11582749c Add connected_client_handler 2024-02-09 09:49:18 +01:00
Jędrzej Stuczyński 339c6c6d24 finally using the correct feature in nym-node for utoipa 2024-01-26 17:21:24 +00:00
Jędrzej Stuczyński bd6ba89e96 removed ephemera usage from nym-api 2024-01-26 17:21:01 +00:00
257 changed files with 8004 additions and 6841 deletions
+21 -8
View File
@@ -9,10 +9,17 @@ on:
default: false
type: boolean
enable_wireguard:
description: 'Add --features wireguard'
description: "Add --features wireguard"
required: true
default: false
type: boolean
enable_deb:
description: "True to enable cargo-deb installation and .deb package building"
required: false
default: false
type: boolean
schedule:
- cron: "14 0 * * *"
pull_request:
paths:
- "clients/**"
@@ -28,6 +35,7 @@ on:
- "sdk/rust/nym-sdk/**"
- "service-providers/**"
- "tools/**"
- "nymvisor/**"
jobs:
publish-nym:
@@ -62,7 +70,9 @@ jobs:
- name: Set CARGO_FEATURES
run: |
echo 'CARGO_FEATURES=--features wireguard' >> $GITHUB_ENV
if: github.event_name == 'workflow_dispatch' && inputs.enable_wireguard == true
if: >
github.event_name == 'schedule' ||
(github.event_name == 'workflow_dispatch' && inputs.enable_wireguard == true)
- name: Install Rust stable
uses: actions-rs/toolchain@v1
@@ -80,12 +90,12 @@ jobs:
with:
command: install
args: cargo-deb
if: github.event_name == 'workflow_dispatch' && inputs.enable_deb == true
- name: Build deb packages
shell: bash
run: make deb
# If this was a manual workflow_dispatch, publish binaries.
if: github.event_name == 'workflow_dispatch' && inputs.enable_deb == true
- name: Upload Artifact
if: github.event_name == 'workflow_dispatch'
@@ -101,12 +111,13 @@ jobs:
target/release/nym-network-requester
target/release/nym-network-statistics
target/release/nym-cli
target/release/nymvisor
retention-days: 30
# If this was a pull_request, upload to build server
# If this was a pull_request or nightly, upload to build server
- name: Prepare build output
if: github.event_name == 'pull_request'
# if: github.event_name == 'schedule' || github.event_name == 'pull_request'
shell: bash
env:
OUTPUT_DIR: ci-builds/${{ github.ref_name }}
@@ -118,12 +129,14 @@ jobs:
cp target/release/nym-api $OUTPUT_DIR
cp target/release/nym-network-requester $OUTPUT_DIR
cp target/release/nym-network-statistics $OUTPUT_DIR
cp target/release/nymvisor $OUTPUT_DIR
cp target/release/nym-cli $OUTPUT_DIR
cp target/release/explorer-api $OUTPUT_DIR
cp target/debian/*.deb $OUTPUT_DIR
if [ ${{ github.event_name == 'workflow_dispatch' && inputs.enable_deb == true }} = true ]; then
cp target/debian/*.deb $OUTPUT_DIR
fi
- name: Deploy branch to CI www
if: github.event_name == 'pull_request'
continue-on-error: true
uses: easingthemes/ssh-deploy@main
env:
+5 -1
View File
@@ -1,4 +1,4 @@
name: Publish Nym binaries
name: publish-nym-binaries
on:
workflow_dispatch:
@@ -29,6 +29,7 @@ jobs:
client_hash: ${{ steps.binary-hashes.outputs.client_hash }}
mixnode_hash: ${{ steps.binary-hashes.outputs.mixnode_hash }}
gateway_hash: ${{ steps.binary-hashes.outputs.gateway_hash }}
nymvisor_hash: ${{ steps.binary-hashes.outputs.nymvisor_hash }}
socks5_hash: ${{ steps.binary-hashes.outputs.socks5_hash }}
netreq_hash: ${{ steps.binary-hashes.outputs.netreq_hash }}
cli_hash: ${{ steps.binary-hashes.outputs.cli_hash }}
@@ -36,6 +37,7 @@ jobs:
client_version: ${{ steps.binary-versions.outputs.client_version }}
mixnode_version: ${{ steps.binary-versions.outputs.mixnode_version }}
gateway_version: ${{ steps.binary-versions.outputs.gateway_version }}
nymvisor_version: ${{ steps.binary-versions.outputs.nymvisor_version }}
socks5_version: ${{ steps.binary-versions.outputs.socks5_version }}
netreq_version: ${{ steps.binary-versions.outputs.netreq_version }}
cli_version: ${{ steps.binary-versions.outputs.cli_version }}
@@ -78,6 +80,7 @@ jobs:
target/release/nym-network-requester
target/release/nym-network-statistics
target/release/nym-cli
target/release/nymvisor
retention-days: 30
- id: create-release
@@ -95,6 +98,7 @@ jobs:
target/release/nym-network-requester
target/release/nym-network-statistics
target/release/nym-cli
target/release/nymvisor
push-release-data-client:
if: ${{ (startsWith(github.ref, 'refs/tags/nym-binaries-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
@@ -1,4 +1,4 @@
name: Publish Nym Connect - desktop (MacOS)
name: publish-nym-connect-macos
on:
workflow_dispatch:
release:
@@ -1,4 +1,4 @@
name: Publish Nym Connect - desktop (Ubuntu)
name: publish-nym-connect-ubuntu
on:
workflow_dispatch:
release:
@@ -1,4 +1,4 @@
name: Publish Nym Connect - desktop (Windows 10)
name: publish-nym-connect-win10
on:
workflow_dispatch:
release:
+1 -1
View File
@@ -1,4 +1,4 @@
name: Build release of Nym smart contracts
name: publish-nym-contracts
on:
workflow_dispatch:
release:
@@ -1,4 +1,4 @@
name: Publish Nym Wallet (MacOS)
name: publish-nym-wallet-macos
on:
workflow_dispatch:
release:
@@ -1,4 +1,4 @@
name: Publish Nym Wallet (Ubuntu)
name: publish-nym-wallet-ubuntu
on:
workflow_dispatch:
release:
@@ -1,4 +1,4 @@
name: Publish Nym Wallet (Windows 10)
name: publish-nym-wallet-win10
on:
workflow_dispatch:
release:
+1 -1
View File
@@ -1,4 +1,4 @@
name: Publish Typescript SDK
name: publish-sdk-npm
on:
workflow_dispatch:
+1 -1
View File
@@ -1,4 +1,4 @@
name: Releases - calculate file hashes
name: release-calculate-hash
on:
workflow_call:
+17
View File
@@ -4,6 +4,23 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
## [Unreleased]
## [2024.1-marabou] (2024-02-15)
**New Features:**
- Introduced nymvisor support for nym-api, gateway, and mixnode binaries ([#4158])
- Revamped nym-api execution with the addition of init and run commands ([#4225])
**Enhancements:**
- Implemented internal improvements for gateways to optimize internal packet routing
- Improved routing score calculation
**Bug Fixes:**
- Resolved various bugs to enhance overall stability
[#4158]: https://github.com/nymtech/nym/pull/4158
[#4225]: https://github.com/nymtech/nym/pull/4225
## [2023.5-rolo] (2023-11-28)
- Gateway won't open websocket listener until embedded Network Requester becomes available ([#4166])
Generated
+804 -2183
View File
File diff suppressed because it is too large Load Diff
+8 -5
View File
@@ -32,7 +32,7 @@ members = [
"common/cosmwasm-smart-contracts/coconut-bandwidth-contract",
"common/cosmwasm-smart-contracts/coconut-dkg",
"common/cosmwasm-smart-contracts/contracts-common",
"common/cosmwasm-smart-contracts/ephemera",
# "common/cosmwasm-smart-contracts/ephemera",
"common/cosmwasm-smart-contracts/group-contract",
"common/cosmwasm-smart-contracts/mixnet-contract",
"common/cosmwasm-smart-contracts/multisig-contract",
@@ -56,6 +56,7 @@ members = [
"common/node-tester-utils",
"common/nonexhaustive-delayqueue",
"common/nymcoconut",
"common/nym-id",
"common/nymsphinx",
"common/nymsphinx/acknowledgements",
"common/nymsphinx/addressing",
@@ -104,8 +105,9 @@ members = [
"nym-outfox",
"nym-validator-rewarder",
"tools/internal/ssl-inject",
"tools/internal/sdk-version-bump",
# "tools/internal/sdk-version-bump",
"tools/nym-cli",
"tools/nym-id-cli",
"tools/nym-nr-query",
"tools/nymvisor",
"tools/ts-rs-cli",
@@ -143,6 +145,7 @@ anyhow = "1.0.71"
async-trait = "0.1.68"
axum = "0.6.20"
base64 = "0.21.4"
bs58 = "0.5.0"
bip39 = { version = "2.0.0", features = ["zeroize"] }
clap = "4.4.7"
cfg-if = "1.0.0"
@@ -158,7 +161,7 @@ log = "0.4"
once_cell = "1.7.2"
parking_lot = "0.12.1"
rand = "0.8.5"
reqwest = "0.11.22"
reqwest = { version = "0.11.22", default_features = false, features = ["rustls-tls"] }
schemars = "0.8.1"
serde = "1.0.152"
serde_json = "1.0.91"
@@ -168,9 +171,9 @@ time = "0.3.30"
thiserror = "1.0.48"
tokio = "1.33.0"
tokio-util = "0.7.10"
tokio-tungstenite = "0.20.1"
tokio-tungstenite = { version = "0.20.1", features = ["rustls"] }
tracing = "0.1.37"
tungstenite = { version = "0.20.1", default-features = false }
tungstenite = { version = "0.20.1", default-features = false, features = ["rustls"] }
ts-rs = "7.0.0"
utoipa = "3.5.0"
utoipa-swagger-ui = "3.1.5"
+5 -3
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-client"
version = "1.1.32"
version = "1.1.33"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
description = "Implementation of the Nym Client"
edition = "2021"
@@ -21,18 +21,19 @@ futures = { workspace = true } # bunch of futures stuff, however, now that I thi
# and the single instance of abortable we have should really be refactored anyway
url = { workspace = true }
bs58 = { workspace = true }
clap = { workspace = true, features = ["cargo", "derive"] }
dirs = "4.0"
lazy_static = "1.4.0"
log = { workspace = true } # self explanatory
pretty_env_logger = "0.4" # for formatting log messages
rand = { version = "0.7.3", features = ["wasm-bindgen"] } # rng-related traits + some rng implementation to use
serde = { workspace = true, features = ["derive"] } # for config serialization/deserialization
serde_json = { workspace = true }
thiserror = { workspace = true }
tap = "1.0.1"
time = { workspace = true }
tokio = { workspace = true, features = ["rt-multi-thread", "net", "signal"] } # async runtime
tokio-tungstenite = { workspace = true }
zeroize = { workspace = true }
## internal
nym-bandwidth-controller = { path = "../../common/bandwidth-controller" }
@@ -50,5 +51,6 @@ nym-task = { path = "../../common/task" }
nym-topology = { path = "../../common/topology" }
nym-validator-client = { path = "../../common/client-libs/validator-client", features = ["http-client"] }
nym-client-websocket-requests = { path = "websocket-requests" }
nym-id = { path = "../../common/nym-id" }
[dev-dependencies]
@@ -2160,9 +2160,9 @@
}
},
"node_modules/ip": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
"integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=",
"version": "1.1.9",
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.9.tgz",
"integrity": "sha512-cyRxvOEpNHNtchU3Ln9KC/auJgup87llfQpQ+t5ghoC/UhL16SWzbueiCsdTnWmqAWl7LadfuwhlqmtOaqMHdQ==",
"dev": true
},
"node_modules/ipaddr.js": {
@@ -6157,9 +6157,9 @@
"dev": true
},
"ip": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
"integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=",
"version": "1.1.9",
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.9.tgz",
"integrity": "sha512-cyRxvOEpNHNtchU3Ln9KC/auJgup87llfQpQ+t5ghoC/UhL16SWzbueiCsdTnWmqAWl7LadfuwhlqmtOaqMHdQ==",
"dev": true
},
"ipaddr.js": {
@@ -0,0 +1,54 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::commands::try_load_current_config;
use crate::error::ClientError;
use clap::ArgGroup;
use nym_id::import_credential;
use std::fs;
use std::path::PathBuf;
fn parse_encoded_credential_data(raw: &str) -> bs58::decode::Result<Vec<u8>> {
bs58::decode(raw).into_vec()
}
#[derive(clap::Args)]
#[clap(group(ArgGroup::new("cred_data").required(true)))]
pub(crate) struct Args {
/// Id of client that is going to import the credential
#[clap(long)]
pub id: String,
/// Explicitly provide the encoded credential data (as base58)
#[clap(long, group = "cred_data", value_parser = parse_encoded_credential_data)]
pub(crate) credential_data: Option<Vec<u8>>,
/// Specifies the path to file containing binary credential data
#[clap(long, group = "cred_data")]
pub(crate) credential_path: Option<PathBuf>,
// currently hidden as there exists only a single serialization standard
#[clap(long, hide = true)]
pub(crate) version: Option<u8>,
}
pub(crate) async fn execute(args: Args) -> Result<(), ClientError> {
let config = try_load_current_config(&args.id)?;
let credentials_store = nym_credential_storage::initialise_persistent_storage(
&config.storage_paths.common_paths.credentials_database,
)
.await;
let raw_credential = match args.credential_data {
Some(data) => data,
None => {
// SAFETY: one of those arguments must have been set
fs::read(args.credential_path.unwrap())?
}
};
import_credential(credentials_store, raw_credential, args.version).await?;
Ok(())
}
+8 -7
View File
@@ -8,7 +8,6 @@ use crate::client::config::{BaseClientConfig, Config};
use crate::error::ClientError;
use clap::CommandFactory;
use clap::{Parser, Subcommand};
use lazy_static::lazy_static;
use log::{error, info};
use nym_bin_common::bin_info;
use nym_bin_common::completions::{fig_generate, ArgShell};
@@ -21,18 +20,16 @@ use nym_client_core::error::ClientCoreError;
use nym_config::OptionalSet;
use std::error::Error;
use std::net::IpAddr;
use std::sync::OnceLock;
pub(crate) mod build_info;
pub(crate) mod import_credential;
pub(crate) mod init;
pub(crate) mod run;
lazy_static! {
pub static ref PRETTY_BUILD_INFORMATION: String = bin_info!().pretty_print();
}
// Helper for passing LONG_VERSION to clap
fn pretty_build_info_static() -> &'static str {
&PRETTY_BUILD_INFORMATION
static PRETTY_BUILD_INFORMATION: OnceLock<String> = OnceLock::new();
PRETTY_BUILD_INFORMATION.get_or_init(|| bin_info!().pretty_print())
}
#[derive(Parser)]
@@ -58,6 +55,9 @@ pub(crate) enum Commands {
/// Run the Nym client with provided configuration client optionally overriding set parameters
Run(run::Run),
/// Import a pre-generated credential
ImportCredential(import_credential::Args),
/// Show build information of this binary
BuildInfo(build_info::BuildInfo),
@@ -86,6 +86,7 @@ pub(crate) async fn execute(args: Cli) -> Result<(), Box<dyn Error + Send + Sync
match args.command {
Commands::Init(m) => init::execute(m).await?,
Commands::Run(m) => run::execute(m).await?,
Commands::ImportCredential(m) => import_credential::execute(m).await?,
Commands::BuildInfo(m) => build_info::execute(m),
Commands::Completions(s) => s.generate(&mut Cli::command(), bin_name),
Commands::GenerateFigSpec => fig_generate(&mut Cli::command(), bin_name),
+6 -1
View File
@@ -1,11 +1,13 @@
use nym_client_core::error::ClientCoreError;
use nym_id::NymIdError;
#[derive(thiserror::Error, Debug)]
pub enum ClientError {
#[error("I/O error: {0}")]
IoError(#[from] std::io::Error),
#[error("client-core error: {0}")]
#[error(transparent)]
ClientCoreError(#[from] ClientCoreError),
#[error("Failed to load config for: {0}")]
@@ -20,4 +22,7 @@ pub enum ClientError {
#[error("Attempted to start the client in invalid socket mode")]
InvalidSocketMode,
#[error(transparent)]
NymIdError(#[from] NymIdError),
}
+5 -3
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-socks5-client"
version = "1.1.32"
version = "1.1.33"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
edition = "2021"
@@ -8,17 +8,18 @@ rust-version = "1.56"
license.workspace = true
[dependencies]
bs58 = { workspace = true }
clap = { workspace = true, features = ["cargo", "derive"] }
lazy_static = "1.4.0"
log = { workspace = true }
pretty_env_logger = "0.4"
serde = { workspace = true, features = ["derive"] } # for config serialization/deserialization
serde_json = { workspace = true }
tap = "1.0.1"
thiserror = { workspace = true }
tokio = { version = "1.24.1", features = ["rt-multi-thread", "net", "signal"] }
rand = "0.7.3"
time = { workspace = true }
url = { workspace = true }
zeroize = { workspace = true }
# internal
nym-bin-common = { path = "../../common/bin-common", features = ["output_format"] }
@@ -34,6 +35,7 @@ nym-ordered-buffer = { path = "../../common/socks5/ordered-buffer" }
nym-pemstore = { path = "../../common/pemstore" }
nym-topology = { path = "../../common/topology" }
nym-socks5-client-core = { path = "../../common/socks5-client-core" }
nym-id = { path = "../../common/nym-id" }
[features]
default = []
@@ -0,0 +1,54 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::commands::try_load_current_config;
use crate::error::Socks5ClientError;
use clap::ArgGroup;
use nym_id::import_credential;
use std::fs;
use std::path::PathBuf;
fn parse_encoded_credential_data(raw: &str) -> bs58::decode::Result<Vec<u8>> {
bs58::decode(raw).into_vec()
}
#[derive(clap::Args)]
#[clap(group(ArgGroup::new("cred_data").required(true)))]
pub(crate) struct Args {
/// Id of client that is going to import the credential
#[clap(long)]
pub id: String,
/// Explicitly provide the encoded credential data (as base58)
#[clap(long, group = "cred_data", value_parser = parse_encoded_credential_data)]
pub(crate) credential_data: Option<Vec<u8>>,
/// Specifies the path to file containing binary credential data
#[clap(long, group = "cred_data")]
pub(crate) credential_path: Option<PathBuf>,
// currently hidden as there exists only a single serialization standard
#[clap(long, hide = true)]
pub(crate) version: Option<u8>,
}
pub(crate) async fn execute(args: Args) -> Result<(), Socks5ClientError> {
let config = try_load_current_config(&args.id)?;
let credentials_store = nym_credential_storage::initialise_persistent_storage(
&config.storage_paths.common_paths.credentials_database,
)
.await;
let raw_credential = match args.credential_data {
Some(data) => data,
None => {
// SAFETY: one of those arguments must have been set
fs::read(args.credential_path.unwrap())?
}
};
import_credential(credentials_store, raw_credential, args.version).await?;
Ok(())
}
+8 -7
View File
@@ -9,7 +9,6 @@ use crate::config::{BaseClientConfig, Config, SocksClientPaths};
use crate::error::Socks5ClientError;
use clap::CommandFactory;
use clap::{Parser, Subcommand};
use lazy_static::lazy_static;
use log::{error, info};
use nym_bin_common::bin_info;
use nym_bin_common::completions::{fig_generate, ArgShell};
@@ -24,18 +23,16 @@ use nym_config::OptionalSet;
use nym_sphinx::params::{PacketSize, PacketType};
use std::error::Error;
use std::net::IpAddr;
use std::sync::OnceLock;
pub(crate) mod build_info;
mod import_credential;
pub mod init;
pub(crate) mod run;
lazy_static! {
pub static ref PRETTY_BUILD_INFORMATION: String = bin_info!().pretty_print();
}
// Helper for passing LONG_VERSION to clap
fn pretty_build_info_static() -> &'static str {
&PRETTY_BUILD_INFORMATION
static PRETTY_BUILD_INFORMATION: OnceLock<String> = OnceLock::new();
PRETTY_BUILD_INFORMATION.get_or_init(|| bin_info!().pretty_print())
}
#[derive(Parser)]
@@ -61,6 +58,9 @@ pub(crate) enum Commands {
/// Run the Nym client with provided configuration client optionally overriding set parameters
Run(run::Run),
/// Import a pre-generated credential
ImportCredential(import_credential::Args),
/// Show build information of this binary
BuildInfo(build_info::BuildInfo),
@@ -92,6 +92,7 @@ pub(crate) async fn execute(args: Cli) -> Result<(), Box<dyn Error + Send + Sync
match args.command {
Commands::Init(m) => init::execute(m).await?,
Commands::Run(m) => run::execute(m).await?,
Commands::ImportCredential(m) => import_credential::execute(m).await?,
Commands::BuildInfo(m) => build_info::execute(m),
Commands::Completions(s) => s.generate(&mut Cli::command(), bin_name),
Commands::GenerateFigSpec => fig_generate(&mut Cli::command(), bin_name),
+6 -1
View File
@@ -1,5 +1,7 @@
use nym_client_core::error::ClientCoreError;
use nym_id::NymIdError;
#[derive(thiserror::Error, Debug)]
pub enum Socks5ClientError {
#[error("I/O error: {0}")]
@@ -18,6 +20,9 @@ pub enum Socks5ClientError {
#[error("Fail to bind address")]
FailToBindAddress,
#[error("client-core error: {0}")]
#[error(transparent)]
ClientCoreError(#[from] ClientCoreError),
#[error(transparent)]
NymIdError(#[from] NymIdError),
}
+3
View File
@@ -21,6 +21,9 @@ pub enum BandwidthControllerError {
#[error("There was a credential storage error - {0}")]
CredentialStorageError(Box<dyn std::error::Error + Send + Sync>),
#[error("the credential storage does not contain any usable credentials")]
NoCredentialsAvailable,
// this should really be fully incorporated into the above, but messing with coconut is the last thing I want to do now
#[error(transparent)]
StorageError(#[from] StorageError),
+66 -21
View File
@@ -3,10 +3,12 @@
use crate::error::BandwidthControllerError;
use crate::utils::stored_credential_to_issued_bandwidth;
use log::{error, warn};
use log::{debug, error, warn};
use nym_credential_storage::storage::Storage;
use nym_credentials::coconut::bandwidth::issued::BandwidthCredentialIssuedDataVariant;
use nym_credentials::coconut::bandwidth::CredentialSpendingData;
use nym_credentials::coconut::utils::obtain_aggregate_verification_key;
use nym_credentials::IssuedBandwidthCredential;
use nym_credentials_interface::VerificationKey;
use nym_validator_client::coconut::all_coconut_api_clients;
use nym_validator_client::nym_api::EpochId;
@@ -33,11 +35,67 @@ pub struct PreparedCredential {
pub credential_id: i64,
}
pub struct RetrievedCredential {
pub credential: IssuedBandwidthCredential,
pub credential_id: i64,
}
impl<C, St: Storage> BandwidthController<C, St> {
pub fn new(storage: St, client: C) -> Self {
BandwidthController { storage, client }
}
/// Tries to retrieve one of the stored, unused credentials that hasn't yet expired.
/// It marks any retrieved intermediate credentials as expired.
pub async fn get_next_usable_credential(
&self,
) -> Result<RetrievedCredential, BandwidthControllerError>
where
<St as Storage>::StorageError: Send + Sync + 'static,
{
loop {
let Some(maybe_next) = self
.storage
.get_next_unspent_credential()
.await
.map_err(|err| BandwidthControllerError::CredentialStorageError(Box::new(err)))?
else {
return Err(BandwidthControllerError::NoCredentialsAvailable);
};
let id = maybe_next.id;
// try to deserialize it
let valid_credential = match stored_credential_to_issued_bandwidth(maybe_next) {
// check if it has already expired
Ok(credential) => match credential.variant_data() {
BandwidthCredentialIssuedDataVariant::Voucher(_) => {
debug!("credential {id} is a bandwidth voucher");
credential
}
BandwidthCredentialIssuedDataVariant::FreePass(freepass_info) => {
debug!("credential {id} is a free pass");
if freepass_info.expired() {
warn!("the free pass (id: {id}) has already expired! The expiration was set to {}", freepass_info.expiry_date());
self.storage.mark_expired(id).await.map_err(|err| {
BandwidthControllerError::CredentialStorageError(Box::new(err))
})?;
continue;
}
credential
}
},
Err(err) => {
error!("failed to deserialize credential with id {id}: {err}. it may need to be manually removed from the storage");
return Err(err);
}
};
return Ok(RetrievedCredential {
credential: valid_credential,
credential_id: id,
});
}
}
pub fn storage(&self) -> &St {
&self.storage
}
@@ -61,29 +119,16 @@ impl<C, St: Storage> BandwidthController<C, St> {
C: DkgQueryClient + Sync + Send,
<St as Storage>::StorageError: Send + Sync + 'static,
{
let retrieved_credential = self
.storage
.get_next_unspent_credential()
.await
.map_err(|err| BandwidthControllerError::CredentialStorageError(Box::new(err)))?;
let retrieved_credential = self.get_next_usable_credential().await?;
let epoch_id = retrieved_credential.epoch_id as EpochId;
let credential_id = retrieved_credential.id;
let epoch_id = retrieved_credential.credential.epoch_id();
let credential_id = retrieved_credential.credential_id;
let issued_bandwidth = stored_credential_to_issued_bandwidth(retrieved_credential)?;
let verification_key = self.get_aggregate_verification_key(epoch_id).await?;
let verification_key = match self.get_aggregate_verification_key(epoch_id).await {
Ok(key) => key,
Err(err) => {
warn!("failed to obtain master verification key: {err}. Putting the credential back into the database");
// TODO: ERROR RECOVERY:
error!("unimplemented: putting the credential back into the database");
return Err(err);
}
};
let spend_request = issued_bandwidth.prepare_for_spending(&verification_key)?;
let spend_request = retrieved_credential
.credential
.prepare_for_spending(&verification_key)?;
Ok(PreparedCredential {
data: spend_request,
@@ -69,6 +69,35 @@ impl BinaryBuildInformation {
}
}
// Varient where we want to use the metadata generated by vergen in the consuming crate.
pub const fn new_with_local_vergen(
binary_name: &'static str,
build_timestamp: &'static str,
build_version: &'static str,
commit_sha: &'static str,
commit_timestamp: &'static str,
commit_branch: &'static str,
) -> Self {
let cargo_debug = env!("VERGEN_CARGO_DEBUG");
let cargo_profile = if const_str::equal!(cargo_debug, "true") {
"debug"
} else {
"release"
};
BinaryBuildInformation {
binary_name,
build_timestamp,
build_version,
commit_sha,
commit_timestamp,
commit_branch,
rustc_version: env!("VERGEN_RUSTC_SEMVER"),
rustc_channel: env!("VERGEN_RUSTC_CHANNEL"),
cargo_profile,
}
}
pub fn to_owned(&self) -> BinaryBuildInformationOwned {
BinaryBuildInformationOwned {
binary_name: self.binary_name.to_owned(),
@@ -187,3 +216,33 @@ macro_rules! bin_info_owned {
.to_owned()
};
}
// variant that picks up the vergen build information generated by the build.rs in the consumer
// crate.
#[macro_export]
macro_rules! bin_info_local_vergen {
() => {
$crate::build_information::BinaryBuildInformation::new_with_local_vergen(
env!("CARGO_PKG_NAME"),
env!("VERGEN_BUILD_TIMESTAMP"),
env!("CARGO_PKG_VERSION"),
env!("VERGEN_GIT_SHA"),
env!("VERGEN_GIT_COMMIT_TIMESTAMP"),
env!("VERGEN_GIT_BRANCH"),
)
};
}
#[macro_export]
macro_rules! bin_info_local_vergen_owned {
() => {
$crate::build_information::BinaryBuildInformation::new_with_local_vergen(
env!("CARGO_PKG_NAME"),
env!("VERGEN_BUILD_TIMESTAMP"),
env!("CARGO_PKG_VERSION"),
env!("VERGEN_GIT_SHA"),
env!("VERGEN_GIT_COMMIT_TIMESTAMP"),
env!("VERGEN_GIT_BRANCH"),
)
};
}
@@ -52,6 +52,7 @@ use nym_topology::provider_trait::TopologyProvider;
use nym_topology::HardcodedTopologyProvider;
use nym_validator_client::nyxd::contract_traits::DkgQueryClient;
use std::fmt::Debug;
use std::os::raw::c_int as RawFd;
use std::path::Path;
use std::sync::Arc;
use url::Url;
@@ -103,6 +104,12 @@ pub struct ClientState {
pub shared_lane_queue_lengths: LaneQueueLengths,
pub reply_controller_sender: ReplyControllerSender,
pub topology_accessor: TopologyAccessor,
pub gateway_connection: GatewayConnection,
}
#[derive(Clone, Copy, Debug)]
pub struct GatewayConnection {
pub gateway_ws_fd: Option<RawFd>,
}
pub enum ClientInputStatus {
@@ -666,6 +673,7 @@ where
shutdown.fork("gateway_transceiver"),
)
.await?;
let gateway_ws_fd = gateway_transceiver.ws_fd();
let reply_storage = Self::setup_persistent_reply_storage(
reply_storage_backend,
@@ -759,6 +767,7 @@ where
shared_lane_queue_lengths,
reply_controller_sender,
topology_accessor: shared_topology_accessor,
gateway_connection: GatewayConnection { gateway_ws_fd },
},
task_handle: shutdown,
})
@@ -8,6 +8,7 @@ use nym_gateway_client::GatewayClient;
pub use nym_gateway_client::{GatewayPacketRouter, PacketRouter};
use nym_sphinx::forwarding::packet::MixPacket;
use std::fmt::Debug;
use std::os::raw::c_int as RawFd;
use thiserror::Error;
#[cfg(not(target_arch = "wasm32"))]
@@ -25,6 +26,7 @@ fn erase_err<E: std::error::Error + Send + Sync + 'static>(err: E) -> ErasedGate
/// This combines combines the functionalities of being able to send and receive mix packets.
pub trait GatewayTransceiver: GatewaySender + GatewayReceiver {
fn gateway_identity(&self) -> identity::PublicKey;
fn ws_fd(&self) -> Option<RawFd>;
}
/// This trait defines the functionality of sending `MixPacket` into the mixnet,
@@ -66,6 +68,9 @@ impl<G: GatewayTransceiver + ?Sized + Send> GatewayTransceiver for Box<G> {
fn gateway_identity(&self) -> identity::PublicKey {
(**self).gateway_identity()
}
fn ws_fd(&self) -> Option<RawFd> {
(**self).ws_fd()
}
}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
@@ -112,6 +117,9 @@ where
fn gateway_identity(&self) -> identity::PublicKey {
self.gateway_client.gateway_identity()
}
fn ws_fd(&self) -> Option<RawFd> {
self.gateway_client.ws_fd()
}
}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
@@ -187,6 +195,9 @@ mod nonwasm_sealed {
fn gateway_identity(&self) -> identity::PublicKey {
self.local_identity
}
fn ws_fd(&self) -> Option<RawFd> {
None
}
}
#[async_trait]
@@ -259,4 +270,7 @@ impl GatewayTransceiver for MockGateway {
fn gateway_identity(&self) -> identity::PublicKey {
self.dummy_identity
}
fn ws_fd(&self) -> Option<RawFd> {
None
}
}
@@ -266,11 +266,11 @@ impl std::ops::Div<f64> for PacketRates {
impl PacketRates {
fn summary(&self) -> String {
format!(
"rx: {}/s (real: {}/s), tx: {}/s (real: {}/s)",
bibytes2(self.real_packets_received_size + self.cover_packets_received_size),
"down: {}/s, up: {}/s (cover down: {}/s, cover up: {}/s)",
bibytes2(self.real_packets_received_size),
bibytes2(self.real_packets_sent_size + self.cover_packets_sent_size),
bibytes2(self.real_packets_sent_size),
bibytes2(self.cover_packets_received_size),
bibytes2(self.cover_packets_sent_size),
)
}
@@ -288,6 +288,7 @@ impl PacketRates {
}
}
#[derive(Debug)]
pub(crate) enum PacketStatisticsEvent {
// The real packets sent. Recall that acks are sent by the gateway, so it's not included here.
RealPacketSent(usize),
@@ -443,7 +444,11 @@ impl PacketStatisticsControl {
// Check what the number of retransmissions was during the recording window
if let Some((_, start_stats)) = self.history.front() {
let delta = self.stats.clone() - start_stats.clone();
log::info!("retransmissions: {}", delta.retransmissions_queued,);
log::info!(
"mix packet retransmissions/real mix packets: {}/{}",
delta.retransmissions_queued,
delta.real_packets_queued,
);
} else {
log::warn!("Unable to check retransmissions during recording window");
}
+1 -1
View File
@@ -48,7 +48,7 @@ features = ["net", "sync", "time"]
workspace = true
# the choice of this particular tls feature was arbitrary;
# if you reckon a different one would be more appropriate, feel free to change it
features = ["native-tls"]
# features = ["native-tls"]
# wasm-only dependencies
[target."cfg(target_arch = \"wasm32\")".dependencies.wasm-bindgen]
@@ -6,7 +6,7 @@ use crate::packet_router::PacketRouter;
pub use crate::packet_router::{
AcknowledgementReceiver, AcknowledgementSender, MixnetMessageReceiver, MixnetMessageSender,
};
use crate::socket_state::{PartiallyDelegated, SocketState};
use crate::socket_state::{ws_fd, PartiallyDelegated, SocketState};
use crate::traits::GatewayPacketRouter;
use crate::{cleanup_socket_message, try_decrypt_binary_message};
use futures::{SinkExt, StreamExt};
@@ -20,7 +20,8 @@ use nym_gateway_requests::authentication::encrypted_address::EncryptedAddressByt
use nym_gateway_requests::iv::IV;
use nym_gateway_requests::registration::handshake::{client_handshake, SharedKeys};
use nym_gateway_requests::{
BinaryRequest, ClientControlRequest, ServerResponse, CURRENT_PROTOCOL_VERSION,
BinaryRequest, ClientControlRequest, ServerResponse, CREDENTIAL_UPDATE_V1_PROTOCOL_VERSION,
CURRENT_PROTOCOL_VERSION,
};
use nym_network_defaults::{REMAINING_BANDWIDTH_THRESHOLD, TOKENS_TO_BURN};
use nym_sphinx::forwarding::packet::MixPacket;
@@ -32,11 +33,15 @@ use std::sync::Arc;
use std::time::Duration;
use tungstenite::protocol::Message;
#[cfg(unix)]
use std::os::fd::RawFd;
#[cfg(not(target_arch = "wasm32"))]
use tokio::time::sleep;
#[cfg(not(target_arch = "wasm32"))]
use tokio_tungstenite::connect_async;
#[cfg(not(unix))]
use std::os::raw::c_int as RawFd;
#[cfg(target_arch = "wasm32")]
use wasm_utils::websocket::JSWebsocket;
#[cfg(target_arch = "wasm32")]
@@ -152,6 +157,14 @@ impl<C, St> GatewayClient<C, St> {
self.gateway_identity
}
pub fn ws_fd(&self) -> Option<RawFd> {
match &self.connection {
SocketState::Available(conn) => ws_fd(conn.as_ref()),
SocketState::PartiallyDelegated(conn) => conn.ws_fd(),
_ => None,
}
}
pub fn remaining_bandwidth(&self) -> i64 {
self.bandwidth_remaining
}
@@ -382,6 +395,8 @@ impl<C, St> GatewayClient<C, St> {
&self,
gateway_protocol: Option<u8>,
) -> Result<(), GatewayClientError> {
debug!("gateway protocol: {gateway_protocol:?}, ours: {CURRENT_PROTOCOL_VERSION}");
// right now there are no failure cases here, but this might change in the future
match gateway_protocol {
None => {
@@ -491,6 +506,7 @@ impl<C, St> GatewayClient<C, St> {
self.check_gateway_protocol(protocol_version)?;
self.authenticated = status;
self.bandwidth_remaining = bandwidth_remaining;
self.negotiated_protocol = protocol_version;
Ok(())
}
ServerResponse::Error { message } => Err(GatewayClientError::GatewayError(message)),
@@ -577,6 +593,18 @@ impl<C, St> GatewayClient<C, St> {
return self.try_claim_testnet_bandwidth().await;
}
let Some(gateway_protocol) = self.negotiated_protocol else {
return Err(GatewayClientError::OutdatedGatewayCredentialVersion {
negotiated_protocol: None,
});
};
if gateway_protocol < CREDENTIAL_UPDATE_V1_PROTOCOL_VERSION {
return Err(GatewayClientError::OutdatedGatewayCredentialVersion {
negotiated_protocol: Some(gateway_protocol),
});
}
let prepared_credential = self
.bandwidth_controller
.as_ref()
@@ -47,6 +47,9 @@ pub enum GatewayClientError {
#[error("Credential could not be serialized")]
SerializeCredential,
#[error("can not spend bandwidth credential with the gateway as it's using outdated protocol (version: {negotiated_protocol:?})")]
OutdatedGatewayCredentialVersion { negotiated_protocol: Option<u8> },
#[error("Client is not authenticated")]
NotAuthenticated,
@@ -11,9 +11,12 @@ use futures::{SinkExt, StreamExt};
use log::*;
use nym_gateway_requests::registration::handshake::SharedKeys;
use nym_task::TaskClient;
use std::os::raw::c_int as RawFd;
use std::sync::Arc;
use tungstenite::Message;
#[cfg(unix)]
use std::os::fd::AsRawFd;
#[cfg(not(target_arch = "wasm32"))]
use tokio::net::TcpStream;
#[cfg(not(target_arch = "wasm32"))]
@@ -37,9 +40,22 @@ type WsConn = JSWebsocket;
type SplitStreamReceiver = oneshot::Receiver<Result<SplitStream<WsConn>, GatewayClientError>>;
pub(crate) fn ws_fd(_conn: &WsConn) -> Option<RawFd> {
#[cfg(unix)]
match _conn.get_ref() {
MaybeTlsStream::Plain(stream) => Some(stream.as_raw_fd()),
&_ => unreachable!(
"If tls features are enabled, the inner stream needs to be unpacked into raw fd"
),
}
#[cfg(not(unix))]
None
}
pub(crate) struct PartiallyDelegated {
sink_half: SplitSink<WsConn, Message>,
delegated_stream: (SplitStreamReceiver, oneshot::Sender<()>),
ws_fd: Option<RawFd>,
}
impl PartiallyDelegated {
@@ -92,6 +108,8 @@ impl PartiallyDelegated {
let (notify_sender, notify_receiver) = oneshot::channel();
let (stream_sender, stream_receiver) = oneshot::channel();
let ws_fd = ws_fd(&conn);
let (sink, mut stream) = conn.split();
let mixnet_receiver_future = async move {
@@ -141,11 +159,16 @@ impl PartiallyDelegated {
tokio::spawn(mixnet_receiver_future);
PartiallyDelegated {
ws_fd,
sink_half: sink,
delegated_stream: (stream_receiver, notify_sender),
}
}
pub(crate) fn ws_fd(&self) -> Option<RawFd> {
self.ws_fd
}
// if we want to send a message and don't care about response, we can don't need to reunite the split,
// the sink itself is enough
pub(crate) async fn send_without_response(
@@ -31,7 +31,6 @@ log = { workspace = true }
url = { workspace = true, features = ["serde"] }
tokio = { workspace = true, features = ["sync", "time"] }
futures = { workspace = true }
openssl = { version = "^0.10.55", features = ["vendored"], optional = true }
nym-coconut = { path = "../../nymcoconut" }
nym-network-defaults = { path = "../../network-defaults" }
@@ -90,7 +89,7 @@ required-features = ["http-client"]
[features]
default = ["http-client"]
http-client = ["cosmrs/rpc", "openssl"]
http-client = ["cosmrs/rpc"]
generate-ts = []
contract-testing = ["nym-mixnet-contract-common/contract-testing"]
@@ -397,7 +397,7 @@ pub trait NymApiClientExt: ApiClient {
routes::API_VERSION,
routes::COCONUT_ROUTES,
routes::BANDWIDTH,
routes::COCONUT_FREE_PASS_NONCE,
routes::COCONUT_FREE_PASS,
],
NO_PARAMS,
request,
@@ -7,19 +7,20 @@ use crate::nyxd::error::NyxdError;
use crate::nyxd::CosmWasmClient;
use async_trait::async_trait;
use cosmrs::AccountId;
use nym_coconut_dkg_common::types::ChunkIndex;
use cosmwasm_std::Addr;
use log::trace;
use nym_coconut_dkg_common::types::{ChunkIndex, NodeIndex, StateAdvanceResponse};
use serde::Deserialize;
use nym_coconut_dkg_common::dealer::RegisteredDealerDetails;
pub use nym_coconut_dkg_common::{
dealer::{DealerDetailsResponse, PagedDealerResponse},
dealer::{DealerDetailsResponse, PagedDealerIndexResponse, PagedDealerResponse},
dealing::{
DealerDealingsStatusResponse, DealingChunkResponse, DealingChunkStatusResponse,
DealingMetadataResponse, DealingStatusResponse,
},
msg::QueryMsg as DkgQueryMsg,
types::{
DealerDetails, DealingIndex, Epoch, EpochId, EpochState, InitialReplacementData, State,
},
types::{DealerDetails, DealingIndex, Epoch, EpochId, EpochState, State},
verification_key::{ContractVKShare, PagedVKSharesResponse, VkShareResponse},
};
@@ -40,13 +41,25 @@ pub trait DkgQueryClient {
self.query_dkg_contract(request).await
}
async fn can_advance_state(&self) -> Result<StateAdvanceResponse, NyxdError> {
let request = DkgQueryMsg::CanAdvanceState {};
self.query_dkg_contract(request).await
}
async fn get_current_epoch_threshold(&self) -> Result<Option<u64>, NyxdError> {
let request = DkgQueryMsg::GetCurrentEpochThreshold {};
self.query_dkg_contract(request).await
}
async fn get_initial_dealers(&self) -> Result<Option<InitialReplacementData>, NyxdError> {
let request = DkgQueryMsg::GetInitialDealers {};
async fn get_registered_dealer_details(
&self,
address: &AccountId,
epoch_id: Option<EpochId>,
) -> Result<RegisteredDealerDetails, NyxdError> {
let request = DkgQueryMsg::GetRegisteredDealer {
dealer_address: address.to_string(),
epoch_id,
};
self.query_dkg_contract(request).await
}
@@ -69,12 +82,12 @@ pub trait DkgQueryClient {
self.query_dkg_contract(request).await
}
async fn get_past_dealers_paged(
async fn get_dealer_indices_paged(
&self,
start_after: Option<String>,
limit: Option<u32>,
) -> Result<PagedDealerResponse, NyxdError> {
let request = DkgQueryMsg::GetPastDealers { start_after, limit };
) -> Result<PagedDealerIndexResponse, NyxdError> {
let request = DkgQueryMsg::GetDealerIndices { start_after, limit };
self.query_dkg_contract(request).await
}
@@ -190,8 +203,8 @@ pub trait PagedDkgQueryClient: DkgQueryClient {
collect_paged!(self, get_current_dealers_paged, dealers)
}
async fn get_all_past_dealers(&self) -> Result<Vec<DealerDetails>, NyxdError> {
collect_paged!(self, get_past_dealers_paged, dealers)
async fn get_all_dealer_indices(&self) -> Result<Vec<(Addr, NodeIndex)>, NyxdError> {
collect_paged!(self, get_dealer_indices_paged, indices)
}
async fn get_all_verification_key_shares(
@@ -218,6 +231,7 @@ where
let dkg_contract_address = &self
.dkg_contract_address()
.ok_or_else(|| NyxdError::unavailable_contract_address("dkg contract"))?;
trace!("using the following dkg contract: {dkg_contract_address}");
self.query_contract_smart(dkg_contract_address, &query)
.await
}
@@ -238,18 +252,24 @@ mod tests {
match msg {
DkgQueryMsg::GetState {} => client.get_state().ignore(),
DkgQueryMsg::GetCurrentEpochState {} => client.get_current_epoch().ignore(),
DkgQueryMsg::CanAdvanceState {} => client.can_advance_state().ignore(),
DkgQueryMsg::GetCurrentEpochThreshold {} => {
client.get_current_epoch_threshold().ignore()
}
DkgQueryMsg::GetInitialDealers {} => client.get_initial_dealers().ignore(),
DkgQueryMsg::GetRegisteredDealer {
dealer_address,
epoch_id,
} => client
.get_registered_dealer_details(&dealer_address.parse().unwrap(), epoch_id)
.ignore(),
DkgQueryMsg::GetDealerDetails { dealer_address } => client
.get_dealer_details(&dealer_address.parse().unwrap())
.ignore(),
DkgQueryMsg::GetCurrentDealers { limit, start_after } => client
.get_current_dealers_paged(start_after, limit)
.ignore(),
DkgQueryMsg::GetPastDealers { limit, start_after } => {
client.get_past_dealers_paged(start_after, limit).ignore()
DkgQueryMsg::GetDealerIndices { limit, start_after } => {
client.get_dealer_indices_paged(start_after, limit).ignore()
}
DkgQueryMsg::GetDealingStatus {
epoch_id,
@@ -39,13 +39,6 @@ pub trait DkgSigningClient {
.await
}
async fn surpass_threshold(&self, fee: Option<Fee>) -> Result<ExecuteResult, NyxdError> {
let req = DkgExecuteMsg::SurpassedThreshold {};
self.execute_dkg_contract(fee, req, "surpass DKG threshold".to_string(), vec![])
.await
}
async fn register_dealer(
&self,
bte_key: EncodedBTEPublicKeyWithProof,
@@ -85,10 +78,9 @@ pub trait DkgSigningClient {
async fn submit_dealing_chunk(
&self,
chunk: PartialContractDealing,
resharing: bool,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
let req = DkgExecuteMsg::CommitDealingsChunk { chunk, resharing };
let req = DkgExecuteMsg::CommitDealingsChunk { chunk };
self.execute_dkg_contract(fee, req, "dealing chunk commitment".to_string(), vec![])
.await
@@ -130,6 +122,20 @@ pub trait DkgSigningClient {
)
.await
}
async fn trigger_dkg_reset(&self, fee: Option<Fee>) -> Result<ExecuteResult, NyxdError> {
let req = DkgExecuteMsg::TriggerReset {};
self.execute_dkg_contract(fee, req, "trigger DKG reset".to_string(), vec![])
.await
}
async fn trigger_dkg_resharing(&self, fee: Option<Fee>) -> Result<ExecuteResult, NyxdError> {
let req = DkgExecuteMsg::TriggerResharing {};
self.execute_dkg_contract(fee, req, "trigger DKG resharing".to_string(), vec![])
.await
}
}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
@@ -192,8 +198,8 @@ mod tests {
} => client
.submit_dealing_metadata(dealing_index, chunks, resharing, None)
.ignore(),
DkgExecuteMsg::CommitDealingsChunk { chunk, resharing } => {
client.submit_dealing_chunk(chunk, resharing, None).ignore()
DkgExecuteMsg::CommitDealingsChunk { chunk } => {
client.submit_dealing_chunk(chunk, None).ignore()
}
DkgExecuteMsg::CommitVerificationKeyShare { share, resharing } => client
.submit_verification_key_share(share, resharing, None)
@@ -201,8 +207,9 @@ mod tests {
DkgExecuteMsg::VerifyVerificationKeyShare { owner, resharing } => client
.verify_verification_key_share(&owner.parse().unwrap(), resharing, None)
.ignore(),
DkgExecuteMsg::SurpassedThreshold {} => client.surpass_threshold(None).ignore(),
DkgExecuteMsg::AdvanceEpochState {} => client.advance_dkg_epoch_state(None).ignore(),
DkgExecuteMsg::TriggerReset {} => client.trigger_dkg_reset(None).ignore(),
DkgExecuteMsg::TriggerResharing {} => client.trigger_dkg_resharing(None).ignore(),
};
}
}
@@ -16,7 +16,6 @@ use crate::signing::tx_signer::TxSigner;
use crate::signing::AccountData;
use crate::{DirectSigningReqwestRpcNyxdClient, QueryReqwestRpcNyxdClient, ReqwestRpcClient};
use async_trait::async_trait;
use cosmrs::cosmwasm;
use cosmrs::tendermint::{abci, evidence::Evidence, Genesis};
use cosmrs::tx::{Raw, SignDoc};
use cosmwasm_std::Addr;
@@ -40,7 +39,7 @@ pub use crate::rpc::TendermintRpcClient;
pub use coin::Coin;
pub use cosmrs::{
bank::MsgSend,
bip32,
bip32, cosmwasm,
crypto::PublicKey,
query::{PageRequest, PageResponse},
tendermint::{
+2 -1
View File
@@ -9,7 +9,7 @@ license.workspace = true
anyhow = { workspace = true }
base64 = "0.13.0"
bip39 = { workspace = true }
bs58 = "0.4"
bs58 = { workspace = true }
comfy-table = "6.0.0"
cfg-if = "1.0.0"
clap = { workspace = true, features = ["derive"] }
@@ -55,6 +55,7 @@ nym-credentials = { path = "../../common/credentials" }
nym-credentials-interface = { path = "../../common/credentials-interface" }
nym-credential-storage = { path = "../../common/credential-storage" }
nym-credential-utils = { path = "../../common/credential-utils" }
nym-id = { path = "../nym-id" }
nym-pemstore = { path = "../../common/pemstore", version = "0.3.0" }
nym-types = { path = "../../common/types" }
@@ -35,10 +35,12 @@ fn parse_rfc3339_expiration_date(raw: &str) -> Result<OffsetDateTime, time::erro
#[clap(group(ArgGroup::new("expiration").required(true)))]
pub struct Args {
/// Specifies the expiration date of the free pass(es)
/// Requires
/// Can't be set to more than a week into the future.
#[clap(long, group = "expiration", value_parser = parse_rfc3339_expiration_date)]
pub(crate) expiration_date: Option<OffsetDateTime>,
/// The expiration of the free pass(es) expresses as unix timestamp.
/// Can't be set to more than a week into the future.
#[clap(long, group = "expiration")]
pub(crate) expiration_timestamp: Option<i64>,
@@ -0,0 +1,64 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::utils::CommonConfigsWrapper;
use anyhow::bail;
use clap::ArgGroup;
use clap::Parser;
use nym_credential_storage::initialise_persistent_storage;
use nym_id::import_credential;
use std::fs;
use std::path::PathBuf;
fn parse_encoded_credential_data(raw: &str) -> bs58::decode::Result<Vec<u8>> {
bs58::decode(raw).into_vec()
}
#[derive(Debug, Parser)]
#[clap(group(ArgGroup::new("cred_data").required(true)))]
pub struct Args {
/// Config file of the client that is supposed to use the credential.
#[clap(long)]
pub(crate) client_config: PathBuf,
/// Explicitly provide the encoded credential data (as base58)
#[clap(long, group = "cred_data", value_parser = parse_encoded_credential_data)]
pub(crate) credential_data: Option<Vec<u8>>,
/// Specifies the path to file containing binary credential data
#[clap(long, group = "cred_data")]
pub(crate) credential_path: Option<PathBuf>,
// currently hidden as there exists only a single serialization standard
#[clap(long, hide = true)]
pub(crate) version: Option<u8>,
}
pub async fn execute(args: Args) -> anyhow::Result<()> {
let loaded = CommonConfigsWrapper::try_load(args.client_config)?;
if let Ok(id) = loaded.try_get_id() {
println!("loaded config file for client '{id}'");
}
let Ok(credentials_store) = loaded.try_get_credentials_store() else {
bail!("the loaded config does not have a credentials store information")
};
println!(
"using credentials store at '{}'",
credentials_store.display()
);
let credentials_store = initialise_persistent_storage(credentials_store).await;
let raw_credential = match args.credential_data {
Some(data) => data,
None => {
// SAFETY: one of those arguments must have been set
fs::read(args.credential_path.unwrap())?
}
};
import_credential(credentials_store, raw_credential, args.version).await?;
Ok(())
}
+2
View File
@@ -4,6 +4,7 @@
use clap::{Args, Subcommand};
pub mod generate_freepass;
pub mod import_credential;
pub mod issue_credentials;
pub mod recover_credentials;
@@ -19,4 +20,5 @@ pub enum CoconutCommands {
GenerateFreepass(generate_freepass::Args),
IssueCredentials(issue_credentials::Args),
RecoverCredentials(recover_credentials::Args),
ImportCredential(import_credential::Args),
}
@@ -14,20 +14,32 @@ pub struct DealerDetails {
pub assigned_index: NodeIndex,
}
#[cw_serde]
pub struct DealerRegistrationDetails {
pub bte_public_key_with_proof: EncodedBTEPublicKeyWithProof,
pub ed25519_identity: String,
pub announce_address: String,
}
#[cw_serde]
#[derive(Copy)]
pub enum DealerType {
Current,
Past,
Current { assigned_index: NodeIndex },
Past { assigned_index: NodeIndex },
Unknown,
}
impl DealerType {
pub fn is_current(&self) -> bool {
matches!(&self, DealerType::Current)
matches!(&self, DealerType::Current { .. })
}
}
#[cw_serde]
pub struct RegisteredDealerDetails {
pub details: Option<DealerRegistrationDetails>,
}
#[cw_serde]
pub struct DealerDetailsResponse {
pub details: Option<DealerDetails>,
@@ -65,3 +77,20 @@ impl PagedDealerResponse {
}
}
}
#[cw_serde]
pub struct PagedDealerIndexResponse {
pub indices: Vec<(Addr, NodeIndex)>,
/// Field indicating paging information for the following queries if the caller wishes to get further entries.
pub start_next_after: Option<Addr>,
}
impl PagedDealerIndexResponse {
pub fn new(indices: Vec<(Addr, NodeIndex)>, start_next_after: Option<Addr>) -> Self {
PagedDealerIndexResponse {
indices,
start_next_after,
}
}
}
@@ -11,12 +11,15 @@ use cosmwasm_schema::cw_serde;
#[cfg(feature = "schema")]
use crate::{
dealer::{DealerDetailsResponse, PagedDealerResponse},
dealer::{
DealerDetailsResponse, PagedDealerIndexResponse, PagedDealerResponse,
RegisteredDealerDetails,
},
dealing::{
DealerDealingsStatusResponse, DealingChunkResponse, DealingChunkStatusResponse,
DealingMetadataResponse, DealingStatusResponse,
},
types::{Epoch, InitialReplacementData, State},
types::{Epoch, State, StateAdvanceResponse},
verification_key::{PagedVKSharesResponse, VkShareResponse},
};
#[cfg(feature = "schema")]
@@ -53,7 +56,6 @@ pub enum ExecuteMsg {
CommitDealingsChunk {
chunk: PartialContractDealing,
resharing: bool,
},
CommitVerificationKeyShare {
@@ -66,9 +68,11 @@ pub enum ExecuteMsg {
resharing: bool,
},
SurpassedThreshold {},
AdvanceEpochState {},
TriggerReset {},
TriggerResharing {},
}
#[cw_serde]
@@ -83,8 +87,14 @@ pub enum QueryMsg {
#[cfg_attr(feature = "schema", returns(u64))]
GetCurrentEpochThreshold {},
#[cfg_attr(feature = "schema", returns(Option<InitialReplacementData>))]
GetInitialDealers {},
#[cfg_attr(feature = "schema", returns(StateAdvanceResponse))]
CanAdvanceState {},
#[cfg_attr(feature = "schema", returns(RegisteredDealerDetails))]
GetRegisteredDealer {
dealer_address: String,
epoch_id: Option<EpochId>,
},
#[cfg_attr(feature = "schema", returns(DealerDetailsResponse))]
GetDealerDetails { dealer_address: String },
@@ -95,8 +105,8 @@ pub enum QueryMsg {
start_after: Option<String>,
},
#[cfg_attr(feature = "schema", returns(PagedDealerResponse))]
GetPastDealers {
#[cfg_attr(feature = "schema", returns(PagedDealerIndexResponse))]
GetDealerIndices {
limit: Option<u32>,
start_after: Option<String>,
},
@@ -5,7 +5,7 @@ use cosmwasm_schema::cw_serde;
use std::fmt::{Display, Formatter};
use std::str::FromStr;
pub use crate::dealer::{DealerDetails, PagedDealerResponse};
pub use crate::dealer::{DealerDetails, DealerRegistrationDetails, PagedDealerResponse};
pub use contracts_common::dealings::ContractSafeBytes;
pub use cosmwasm_std::{Addr, Coin, Timestamp};
pub use cw4::Cw4Contract;
@@ -22,9 +22,19 @@ pub type ChunkIndex = u16;
pub type PartialContractDealingData = ContractSafeBytes;
#[cw_serde]
pub struct InitialReplacementData {
pub initial_dealers: Vec<Addr>,
pub initial_height: u64,
#[derive(Copy, Default)]
pub struct StateAdvanceResponse {
pub current_state: EpochState,
pub progress: StateProgress,
pub deadline: Option<Timestamp>,
pub reached_deadline: bool,
pub is_complete: bool,
}
impl StateAdvanceResponse {
pub fn can_advance(&self) -> bool {
self.reached_deadline || self.is_complete
}
}
#[cw_serde]
@@ -40,6 +50,26 @@ pub struct TimeConfiguration {
pub in_progress_time_secs: u64,
}
impl TimeConfiguration {
pub fn state_duration(&self, state: EpochState) -> Option<u64> {
match state {
EpochState::WaitingInitialisation => None,
EpochState::PublicKeySubmission { .. } => Some(self.public_key_submission_time_secs),
EpochState::DealingExchange { .. } => Some(self.dealing_exchange_time_secs),
EpochState::VerificationKeySubmission { .. } => {
Some(self.verification_key_submission_time_secs)
}
EpochState::VerificationKeyValidation { .. } => {
Some(self.verification_key_validation_time_secs)
}
EpochState::VerificationKeyFinalization { .. } => {
Some(self.verification_key_finalization_time_secs)
}
EpochState::InProgress => Some(self.in_progress_time_secs),
}
}
}
impl FromStr for TimeConfiguration {
type Err = String;
@@ -87,13 +117,41 @@ pub struct State {
pub key_size: u32,
}
#[cw_serde]
#[derive(Copy, Default)]
pub struct StateProgress {
/// Counts the number of dealers that have registered in this epoch.
// ideally we want to have here all group members
pub registered_dealers: u32,
/// Counts the number of resharing dealers that have registered in this epoch.
/// This field is only populated during a resharing exchange.
/// It is always <= registered_dealers.
pub registered_resharing_dealers: u32,
/// Counts the number of fully received dealings (i.e. full chunks) from all the allowed dealers.
// we expect registered_dealers * state.key_size number of dealings here (each dealer has to submit key_size number of dealings)
pub submitted_dealings: u32,
/// Counts the number of submitted verification key shared from the dealers.
// we expect registered_dealers number of keys here
pub submitted_key_shares: u32,
/// Counts the number of verified key shares.
// we expect submitted_key_shares number of verified keys here
pub verified_keys: u32,
}
#[cw_serde]
#[derive(Copy, Default)]
pub struct Epoch {
pub state: EpochState,
pub epoch_id: EpochId,
pub state_progress: StateProgress,
pub time_configuration: TimeConfiguration,
pub finish_timestamp: Option<Timestamp>,
#[serde(alias = "finish_timestamp")]
pub deadline: Option<Timestamp>,
}
impl Epoch {
@@ -103,35 +161,45 @@ impl Epoch {
time_configuration: TimeConfiguration,
current_timestamp: Timestamp,
) -> Self {
let duration = match state {
EpochState::WaitingInitialisation => None,
EpochState::PublicKeySubmission { .. } => {
Some(time_configuration.public_key_submission_time_secs)
}
EpochState::DealingExchange { .. } => {
Some(time_configuration.dealing_exchange_time_secs)
}
EpochState::VerificationKeySubmission { .. } => {
Some(time_configuration.verification_key_submission_time_secs)
}
EpochState::VerificationKeyValidation { .. } => {
Some(time_configuration.verification_key_validation_time_secs)
}
EpochState::VerificationKeyFinalization { .. } => {
Some(time_configuration.verification_key_finalization_time_secs)
}
EpochState::InProgress => Some(time_configuration.in_progress_time_secs),
};
let duration = time_configuration.state_duration(state);
Epoch {
state,
epoch_id,
state_progress: Default::default(),
time_configuration,
finish_timestamp: duration.map(|d| current_timestamp.plus_seconds(d)),
deadline: duration.map(|d| current_timestamp.plus_seconds(d)),
}
}
pub fn update(mut self, next_state: EpochState, current_timestamp: Timestamp) -> Self {
self.state = next_state;
let duration = self.time_configuration.state_duration(next_state);
self.deadline = duration.map(|d| current_timestamp.plus_seconds(d));
self
}
pub fn next_reset(self, current_timestamp: Timestamp) -> Self {
Epoch::new(
EpochState::PublicKeySubmission { resharing: false },
self.epoch_id + 1,
self.time_configuration,
current_timestamp,
)
}
pub fn next_resharing(self, current_timestamp: Timestamp) -> Self {
Epoch::new(
EpochState::PublicKeySubmission { resharing: true },
self.epoch_id + 1,
self.time_configuration,
current_timestamp,
)
}
pub fn final_timestamp_secs(&self) -> Option<u64> {
let mut finish = self.finish_timestamp?.seconds();
let mut finish = self.deadline?.seconds();
let time_configuration = self.time_configuration;
let mut curr_epoch_state = self.state;
while let Some(state) = curr_epoch_state.next() {
@@ -256,4 +324,8 @@ impl EpochState {
pub fn is_in_progress(&self) -> bool {
matches!(self, EpochState::InProgress)
}
pub fn is_dealing_exchange(&self) -> bool {
matches!(self, EpochState::DealingExchange { .. })
}
}
@@ -8,7 +8,7 @@ license = { workspace = true }
repository = { workspace = true }
[dependencies]
bs58 = "0.4.0"
bs58 = { workspace = true }
cosmwasm-std = { workspace = true }
cosmwasm-schema = { workspace = true }
schemars = "0.8"
@@ -13,5 +13,6 @@ CREATE TABLE coconut_credentials
credential_type TEXT NOT NULL,
credential_data BLOB NOT NULL,
epoch_id INTEGER NOT NULL,
consumed BOOLEAN NOT NULL
consumed BOOLEAN NOT NULL,
expired BOOLEAN NOT NULL
);
@@ -48,13 +48,18 @@ impl CoconutCredentialManager {
credential_type,
epoch_id,
consumed: false,
expired: false,
})
}
/// Tries to retrieve one of the stored, unused credentials.
pub async fn get_next_unspent_credential(&self) -> Option<StoredIssuedCredential> {
let creds = self.inner.read().await;
creds.data.iter().find(|c| !c.consumed).cloned()
creds
.data
.iter()
.find(|c| !c.consumed && !c.expired)
.cloned()
}
/// Consumes in the database the specified credential.
@@ -68,4 +73,16 @@ impl CoconutCredentialManager {
cred.consumed = true;
}
}
/// Marks the specified credential as expired
///
/// # Arguments
///
/// * `id`: Id of the credential to mark as expired.
pub async fn mark_expired(&self, id: i64) {
let mut creds = self.inner.write().await;
if let Some(cred) = creds.data.get_mut(id as usize) {
cred.expired = true;
}
}
}
@@ -27,8 +27,8 @@ impl CoconutCredentialManager {
) -> Result<(), sqlx::Error> {
sqlx::query!(
r#"
INSERT INTO coconut_credentials(serialization_revision, credential_type, credential_data, epoch_id, consumed)
VALUES (?, ?, ?, ?, false)
INSERT INTO coconut_credentials(serialization_revision, credential_type, credential_data, epoch_id, consumed, expired)
VALUES (?, ?, ?, ?, false, false)
"#,
serialization_revision, credential_type, credential_data, epoch_id
).execute(&self.connection_pool).await?;
@@ -38,9 +38,11 @@ impl CoconutCredentialManager {
pub async fn get_next_unspent_credential(
&self,
) -> Result<Option<StoredIssuedCredential>, sqlx::Error> {
sqlx::query_as("SELECT * FROM coconut_credentials WHERE NOT consumed LIMIT 1")
.fetch_optional(&self.connection_pool)
.await
sqlx::query_as(
"SELECT * FROM coconut_credentials WHERE NOT consumed AND NOT expired LIMIT 1",
)
.fetch_optional(&self.connection_pool)
.await
}
/// Consumes in the database the specified credential.
@@ -57,4 +59,19 @@ impl CoconutCredentialManager {
.await?;
Ok(())
}
/// Marks the specified credential as expired
///
/// # Arguments
///
/// * `id`: Id of the credential to mark as expired.
pub async fn mark_expired(&self, id: i64) -> Result<(), sqlx::Error> {
sqlx::query!(
"UPDATE coconut_credentials SET expired = TRUE WHERE id = ?",
id
)
.execute(&self.connection_pool)
.await?;
Ok(())
}
}
@@ -44,14 +44,11 @@ impl Storage for EphemeralStorage {
async fn get_next_unspent_credential(
&self,
) -> Result<StoredIssuedCredential, Self::StorageError> {
let credential = self
) -> Result<Option<StoredIssuedCredential>, Self::StorageError> {
Ok(self
.coconut_credential_manager
.get_next_unspent_credential()
.await
.ok_or(StorageError::NoCredential)?;
Ok(credential)
.await)
}
async fn consume_coconut_credential(&self, id: i64) -> Result<(), StorageError> {
@@ -61,4 +58,10 @@ impl Storage for EphemeralStorage {
Ok(())
}
async fn mark_expired(&self, id: i64) -> Result<(), Self::StorageError> {
self.coconut_credential_manager.mark_expired(id).await;
Ok(())
}
}
+1
View File
@@ -27,6 +27,7 @@ pub struct StoredIssuedCredential {
pub epoch_id: u32,
pub consumed: bool,
pub expired: bool,
}
pub struct StorableIssuedCredential<'a> {
@@ -76,14 +76,11 @@ impl Storage for PersistentStorage {
async fn get_next_unspent_credential(
&self,
) -> Result<StoredIssuedCredential, Self::StorageError> {
let credential = self
) -> Result<Option<StoredIssuedCredential>, Self::StorageError> {
Ok(self
.coconut_credential_manager
.get_next_unspent_credential()
.await?
.ok_or(StorageError::NoCredential)?;
Ok(credential)
.await?)
}
async fn consume_coconut_credential(&self, id: i64) -> Result<(), StorageError> {
@@ -93,4 +90,10 @@ impl Storage for PersistentStorage {
Ok(())
}
async fn mark_expired(&self, id: i64) -> Result<(), Self::StorageError> {
self.coconut_credential_manager.mark_expired(id).await?;
Ok(())
}
}
+10 -2
View File
@@ -14,10 +14,11 @@ pub trait Storage: Send + Sync {
bandwidth_credential: StorableIssuedCredential<'a>,
) -> Result<(), Self::StorageError>;
/// Tries to retrieve one of the stored, unused credentials.
/// Tries to retrieve one of the stored, unused credentials,
/// that is also not marked as expired
async fn get_next_unspent_credential(
&self,
) -> Result<StoredIssuedCredential, Self::StorageError>;
) -> Result<Option<StoredIssuedCredential>, Self::StorageError>;
/// Marks as consumed in the database the specified credential.
///
@@ -25,4 +26,11 @@ pub trait Storage: Send + Sync {
///
/// * `id`: Id of the credential to be consumed.
async fn consume_coconut_credential(&self, id: i64) -> Result<(), Self::StorageError>;
/// Marks the specified credential as expired
///
/// # Arguments
///
/// * `id`: Id of the credential to mark as expired.
async fn mark_expired(&self, id: i64) -> Result<(), Self::StorageError>;
}
@@ -53,7 +53,7 @@ impl RecoveryStorage {
pub fn voucher_filename(voucher: &IssuanceBandwidthCredential) -> String {
let prefix = voucher.typ().to_string();
let suffix = voucher.blinded_g1_serial_number_bs58();
let suffix = voucher.blinded_serial_number_bs58();
format!("{prefix}-{suffix}.{DUMPED_VOUCHER_EXTENSION}")
}
+1 -1
View File
@@ -92,7 +92,7 @@ where
.as_secs();
if epoch.state.is_final() {
if let Some(finish_timestamp) = epoch.finish_timestamp {
if let Some(finish_timestamp) = epoch.deadline {
if current_timestamp_secs + SAFETY_BUFFER_SECS >= finish_timestamp.seconds() {
info!("In the next {} minute(s), a transition will take place in the coconut system. Deposits should be halted in this time for safety reasons.", SAFETY_BUFFER_SECS / 60);
exit(0);
+7 -3
View File
@@ -10,9 +10,9 @@ use thiserror::Error;
pub use nym_coconut::{
aggregate_signature_shares, aggregate_verification_keys, blind_sign, hash_to_scalar, keygen,
prepare_blind_sign, prove_bandwidth_credential, verify_credential, Attribute, Base58,
BlindSignRequest, BlindedSignature, Bytable, CoconutError, KeyPair, Parameters,
PrivateAttribute, PublicAttribute, SecretKey, Signature, SignatureShare, VerificationKey,
VerifyCredentialRequest,
BlindSignRequest, BlindedSerialNumber, BlindedSignature, Bytable, CoconutError, KeyPair,
Parameters, PrivateAttribute, PublicAttribute, SecretKey, Signature, SignatureShare,
VerificationKey, VerifyCredentialRequest,
};
pub const VOUCHER_INFO_TYPE: &str = "BandwidthVoucher";
@@ -129,4 +129,8 @@ impl CredentialSpendingData {
// the first attribute is variant specific bandwidth encoding, the second one should be the type
self.public_attributes_plain.first()
}
pub fn blinded_serial_number(&self) -> BlindedSerialNumber {
self.verify_credential_request.blinded_serial_number()
}
}
@@ -14,7 +14,7 @@ use zeroize::{Zeroize, ZeroizeOnDrop};
pub const MAX_FREE_PASS_VALIDITY: Duration = Duration::WEEK; // 1 week
#[derive(Zeroize, ZeroizeOnDrop, Serialize, Deserialize)]
#[derive(Debug, Zeroize, ZeroizeOnDrop, Serialize, Deserialize)]
pub struct FreePassIssuedData {
/// the plain validity value of this credential expressed as unix timestamp
#[zeroize(skip)]
@@ -30,6 +30,14 @@ impl<'a> From<&'a FreePassIssuanceData> for FreePassIssuedData {
}
impl FreePassIssuedData {
pub fn expired(&self) -> bool {
self.expiry_date <= OffsetDateTime::now_utc()
}
pub fn expiry_date(&self) -> OffsetDateTime {
self.expiry_date
}
pub fn expiry_date_plain(&self) -> String {
self.expiry_date.unix_timestamp().to_string()
}
@@ -85,7 +93,7 @@ impl FreePassIssuanceData {
pub async fn obtain_free_pass_nonce(
&self,
client: &nym_validator_client::client::NymApiClient,
) -> Result<u32, Error> {
) -> Result<[u8; 16], Error> {
let server_response = client.free_pass_nonce().await?;
Ok(server_response.current_nonce)
}
@@ -94,12 +102,11 @@ impl FreePassIssuanceData {
&self,
signing_request: &CredentialSigningData,
account_data: &AccountData,
issuer_nonce: u32,
issuer_nonce: [u8; 16],
) -> Result<FreePassRequest, Error> {
let plaintext = issuer_nonce.to_be_bytes();
let nonce_signature = account_data
.private_key()
.sign(&plaintext)
.sign(&issuer_nonce)
.map_err(|_| Error::Secp256k1SignFailure)?;
Ok(FreePassRequest {
@@ -9,10 +9,10 @@ use crate::coconut::bandwidth::{
};
use crate::coconut::utils::scalar_serde_helper;
use crate::error::Error;
use bls12_381::G1Projective;
use nym_credentials_interface::{
aggregate_signature_shares, hash_to_scalar, prepare_blind_sign, Attribute, BlindedSignature,
Parameters, PrivateAttribute, PublicAttribute, Signature, SignatureShare, VerificationKey,
aggregate_signature_shares, hash_to_scalar, prepare_blind_sign, Attribute, BlindedSerialNumber,
BlindedSignature, Parameters, PrivateAttribute, PublicAttribute, Signature, SignatureShare,
VerificationKey,
};
use nym_crypto::asymmetric::{encryption, identity};
use nym_validator_client::nym_api::EpochId;
@@ -140,15 +140,14 @@ impl IssuanceBandwidthCredential {
Self::new(FreePassIssuanceData::new(expiry_date))
}
pub fn blind_serial_number_in_g1subgroup(&self) -> G1Projective {
bandwidth_credential_params().gen1() * self.serial_number
pub fn blind_serial_number(&self) -> BlindedSerialNumber {
(bandwidth_credential_params().gen2() * self.serial_number).into()
}
// NOT TO BE CONFUSED WITH BLINDED SERIAL NUMBER IN CREDENTIAL ITSELF
pub fn blinded_g1_serial_number_bs58(&self) -> String {
pub fn blinded_serial_number_bs58(&self) -> String {
use nym_credentials_interface::Base58;
self.blind_serial_number_in_g1subgroup().to_bs58()
self.blind_serial_number().to_bs58()
}
pub fn typ(&self) -> CredentialType {
@@ -316,9 +315,12 @@ impl IssuanceBandwidthCredential {
}
// TODO: is that actually needed?
// idea: make it consistent with the issued credential and its vX serde
pub fn try_from_recovered_bytes(bytes: &[u8]) -> Result<Self, Error> {
use bincode::Options;
Ok(make_recovery_bincode_serializer().deserialize(bytes)?)
make_recovery_bincode_serializer()
.deserialize(bytes)
.map_err(|source| Error::RecoveryCredentialDeserializationFailure { source })
}
}
@@ -328,3 +330,18 @@ fn make_recovery_bincode_serializer() -> impl bincode::Options {
.with_big_endian()
.with_varint_encoding()
}
#[cfg(test)]
mod tests {
use super::*;
fn assert_zeroize_on_drop<T: ZeroizeOnDrop>() {}
fn assert_zeroize<T: Zeroize>() {}
#[test]
fn credential_is_zeroized() {
assert_zeroize::<IssuanceBandwidthCredential>();
assert_zeroize_on_drop::<IssuanceBandwidthCredential>();
}
}
@@ -20,7 +20,7 @@ use zeroize::{Zeroize, ZeroizeOnDrop};
pub const CURRENT_SERIALIZATION_REVISION: u8 = 1;
#[derive(Zeroize, Serialize, Deserialize)]
#[derive(Debug, Zeroize, Serialize, Deserialize)]
pub enum BandwidthCredentialIssuedDataVariant {
Voucher(BandwidthVoucherIssuedData),
FreePass(FreePassIssuedData),
@@ -116,6 +116,23 @@ impl IssuedBandwidthCredential {
}
}
pub fn try_unpack(bytes: &[u8], revision: impl Into<Option<u8>>) -> Result<Self, Error> {
let revision = revision.into().unwrap_or(CURRENT_SERIALIZATION_REVISION);
match revision {
1 => Self::unpack_v1(bytes),
_ => Err(Error::UnknownSerializationRevision { revision }),
}
}
pub fn epoch_id(&self) -> EpochId {
self.epoch_id
}
pub fn variant_data(&self) -> &BandwidthCredentialIssuedDataVariant {
&self.variant_data
}
pub fn current_serialization_revision(&self) -> u8 {
CURRENT_SERIALIZATION_REVISION
}
@@ -130,7 +147,12 @@ impl IssuedBandwidthCredential {
/// Unpack (deserialize) the credential data from the given bytes using v1 serializer.
pub fn unpack_v1(bytes: &[u8]) -> Result<Self, Error> {
use bincode::Options;
Ok(make_storable_bincode_serializer().deserialize(bytes)?)
make_storable_bincode_serializer()
.deserialize(bytes)
.map_err(|source| Error::SerializationFailure {
source,
revision: 1,
})
}
pub fn randomise_signature(&mut self) {
@@ -183,3 +205,18 @@ fn make_storable_bincode_serializer() -> impl bincode::Options {
.with_big_endian()
.with_varint_encoding()
}
#[cfg(test)]
mod tests {
use super::*;
fn assert_zeroize_on_drop<T: ZeroizeOnDrop>() {}
fn assert_zeroize<T: Zeroize>() {}
#[test]
fn credential_is_zeroized() {
assert_zeroize::<IssuedBandwidthCredential>();
assert_zeroize_on_drop::<IssuedBandwidthCredential>();
}
}
@@ -13,7 +13,7 @@ use nym_validator_client::nyxd::{Coin, Hash};
use serde::{Deserialize, Serialize};
use zeroize::{Zeroize, ZeroizeOnDrop};
#[derive(Zeroize, ZeroizeOnDrop, Serialize, Deserialize)]
#[derive(Debug, Zeroize, ZeroizeOnDrop, Serialize, Deserialize)]
pub struct BandwidthVoucherIssuedData {
/// the plain value (e.g., bandwidth) encoded in this voucher
// note: for legacy reasons we're only using the value of the coin and ignoring the denom
@@ -30,6 +30,10 @@ impl<'a> From<&'a BandwidthVoucherIssuanceData> for BandwidthVoucherIssuedData {
}
impl BandwidthVoucherIssuedData {
pub fn value(&self) -> &Coin {
&self.value
}
pub fn value_plain(&self) -> String {
self.value.amount.to_string()
}
+13 -2
View File
@@ -5,6 +5,7 @@ use nym_credentials_interface::CoconutError;
use nym_crypto::asymmetric::encryption::KeyRecoveryError;
use nym_validator_client::ValidatorClientError;
use crate::coconut::bandwidth::issued::CURRENT_SERIALIZATION_REVISION;
use thiserror::Error;
#[derive(Debug, Error)]
@@ -12,8 +13,18 @@ pub enum Error {
#[error("IO error")]
IOError(#[from] std::io::Error),
#[error("failed to (de)serialize credential structure: {0}")]
SerializationFailure(#[from] bincode::Error),
#[error("failed to deserialize a recovery credential: {source}")]
RecoveryCredentialDeserializationFailure { source: bincode::Error },
#[error("failed to (de)serialize provided credential using revision {revision}: {source}")]
SerializationFailure {
#[source]
source: bincode::Error,
revision: u8,
},
#[error("unknown credential serializatio revision {revision}. the current (and max supported) version is {CURRENT_SERIALIZATION_REVISION}")]
UnknownSerializationRevision { revision: u8 },
#[error("The detailed description is yet to be determined")]
BandwidthCredentialError,
+1
View File
@@ -9,3 +9,4 @@ pub use coconut::bandwidth::{
IssuedBandwidthCredential,
};
pub use coconut::utils::{obtain_aggregate_signature, obtain_aggregate_verification_key};
pub use error::Error;
+1 -1
View File
@@ -9,7 +9,7 @@ repository = { workspace = true }
[dependencies]
aes = { version = "0.8.1", optional = true }
bs58 = "0.4.0"
bs58 = { workspace = true }
blake3 = { version = "1.3.1", features = ["traits-preview"], optional = true }
ctr = { version = "0.9.1", optional = true }
digest = { version = "0.10.3", optional = true }
+1 -1
View File
@@ -14,7 +14,7 @@ bitvec = "1.0.0"
# as we need to be able to serialize Gt so that we could create the lookup table for baby-step-giant-step algorithm
bls12_381 = { workspace = true, default-features = false, features = ["alloc", "pairings", "experimental", "zeroize"] }
nym-contracts-common = { path = "../cosmwasm-smart-contracts/contracts-common", optional = true }
bs58 = "0.4"
bs58 = { workspace = true }
lazy_static = "1.4.0"
+1
View File
@@ -11,6 +11,7 @@ license.workspace = true
[dependencies]
bincode = "1.3.3"
bytes = "1.5.0"
nym-bin-common = { path = "../bin-common" }
nym-sphinx = { path = "../nymsphinx" }
rand = "0.8.5"
serde = { workspace = true, features = ["derive"] }
+25 -1
View File
@@ -1,8 +1,32 @@
use serde::{Deserialize, Serialize};
use std::fmt::{Display, Formatter};
use std::net::{Ipv4Addr, Ipv6Addr};
pub mod codec;
pub mod request;
pub mod response;
pub const CURRENT_VERSION: u8 = 2;
// version 3: initial version
// version 4: IPv6 support
pub const CURRENT_VERSION: u8 = 4;
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct IpPair {
pub ipv4: Ipv4Addr,
pub ipv6: Ipv6Addr,
}
impl IpPair {
pub fn new(ipv4: Ipv4Addr, ipv6: Ipv6Addr) -> Self {
IpPair { ipv4, ipv6 }
}
}
impl Display for IpPair {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
writeln!(f, "IPv4: {}, IPV6: {}", self.ipv4, self.ipv6)
}
}
fn make_bincode_serializer() -> impl bincode::Options {
use bincode::Options;
+63 -15
View File
@@ -1,9 +1,7 @@
use std::net::IpAddr;
use nym_sphinx::addressing::clients::Recipient;
use serde::{Deserialize, Serialize};
use crate::{make_bincode_serializer, CURRENT_VERSION};
use crate::{make_bincode_serializer, IpPair, CURRENT_VERSION};
fn generate_random() -> u64 {
use rand::RngCore;
@@ -19,10 +17,11 @@ pub struct IpPacketRequest {
impl IpPacketRequest {
pub fn new_static_connect_request(
ip: IpAddr,
ips: IpPair,
reply_to: Recipient,
reply_to_hops: Option<u8>,
reply_to_avg_mix_delays: Option<f64>,
buffer_timeout: Option<u64>,
) -> (Self, u64) {
let request_id = generate_random();
(
@@ -30,10 +29,11 @@ impl IpPacketRequest {
version: CURRENT_VERSION,
data: IpPacketRequestData::StaticConnect(StaticConnectRequest {
request_id,
ip,
ips,
reply_to,
reply_to_hops,
reply_to_avg_mix_delays,
buffer_timeout,
}),
},
request_id,
@@ -44,6 +44,7 @@ impl IpPacketRequest {
reply_to: Recipient,
reply_to_hops: Option<u8>,
reply_to_avg_mix_delays: Option<f64>,
buffer_timeout: Option<u64>,
) -> (Self, u64) {
let request_id = generate_random();
(
@@ -54,6 +55,7 @@ impl IpPacketRequest {
reply_to,
reply_to_hops,
reply_to_avg_mix_delays,
buffer_timeout,
}),
},
request_id,
@@ -74,10 +76,10 @@ impl IpPacketRequest {
)
}
pub fn new_ip_packet(ip_packet: bytes::Bytes) -> Self {
pub fn new_data_request(ip_packets: bytes::Bytes) -> Self {
Self {
version: CURRENT_VERSION,
data: IpPacketRequestData::Data(DataRequest { ip_packet }),
data: IpPacketRequestData::Data(DataRequest { ip_packets }),
}
}
@@ -87,6 +89,8 @@ impl IpPacketRequest {
IpPacketRequestData::DynamicConnect(request) => Some(request.request_id),
IpPacketRequestData::Disconnect(request) => Some(request.request_id),
IpPacketRequestData::Data(_) => None,
IpPacketRequestData::Ping(request) => Some(request.request_id),
IpPacketRequestData::Health(request) => Some(request.request_id),
}
}
@@ -96,6 +100,8 @@ impl IpPacketRequest {
IpPacketRequestData::DynamicConnect(request) => Some(&request.reply_to),
IpPacketRequestData::Disconnect(request) => Some(&request.reply_to),
IpPacketRequestData::Data(_) => None,
IpPacketRequestData::Ping(request) => Some(&request.reply_to),
IpPacketRequestData::Health(request) => Some(&request.reply_to),
}
}
@@ -119,35 +125,58 @@ pub enum IpPacketRequestData {
DynamicConnect(DynamicConnectRequest),
Disconnect(DisconnectRequest),
Data(DataRequest),
Ping(PingRequest),
Health(HealthRequest),
}
// A static connect request is when the client provides the internal IP address it will use on the
// ip packet router.
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct StaticConnectRequest {
pub request_id: u64,
pub ip: IpAddr,
pub ips: IpPair,
// The nym-address the response should be sent back to
pub reply_to: Recipient,
// The number of mix node hops that responses should take, in addition to the entry and exit
// node. Zero means only client -> entry -> exit -> client.
pub reply_to_hops: Option<u8>,
// The average delay at each mix node, in milliseconds. Currently this is not supported by the
// ip packet router.
pub reply_to_avg_mix_delays: Option<f64>,
// The maximum time in milliseconds the IPR should wait when filling up a mix packet
// with ip packets.
pub buffer_timeout: Option<u64>,
}
// A dynamic connect request is when the client does not provide the internal IP address it will use
// on the ip packet router, and instead requests one to be assigned to it.
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct DynamicConnectRequest {
pub request_id: u64,
// The nym-address the response should be sent back to
pub reply_to: Recipient,
// The number of mix node hops that responses should take, in addition to the entry and exit
// node. Zero means only client -> entry -> exit -> client.
pub reply_to_hops: Option<u8>,
// The average delay at each mix node, in milliseconds. Currently this is not supported by the
// ip packet router.
pub reply_to_avg_mix_delays: Option<f64>,
// The maximum time in milliseconds the IPR should wait when filling up a mix packet
// with ip packets.
pub buffer_timeout: Option<u64>,
}
// A disconnect request is when the client wants to disconnect from the ip packet router and free
// up the allocated IP address.
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct DisconnectRequest {
pub request_id: u64,
@@ -155,14 +184,32 @@ pub struct DisconnectRequest {
pub reply_to: Recipient,
}
// A data request is when the client wants to send an IP packet to a destination.
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct DataRequest {
pub ip_packet: bytes::Bytes,
pub ip_packets: bytes::Bytes,
}
// A ping request is when the client wants to check if the ip packet router is still alive.
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct PingRequest {
pub request_id: u64,
// The nym-address the response should be sent back to
pub reply_to: Recipient,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct HealthRequest {
pub request_id: u64,
// The nym-address the response should be sent back to
pub reply_to: Recipient,
}
#[cfg(test)]
mod tests {
use super::*;
use std::net::{Ipv4Addr, Ipv6Addr};
use std::str::FromStr;
#[test]
fn check_size_of_request() {
@@ -171,14 +218,15 @@ mod tests {
data: IpPacketRequestData::StaticConnect(
StaticConnectRequest {
request_id: 123,
ip: IpAddr::from([10, 0, 0, 1]),
ips: IpPair::new(Ipv4Addr::from_str("10.0.0.1").unwrap(), Ipv6Addr::from_str("2001:db8:a160::1").unwrap()),
reply_to: Recipient::try_from_base58_string("D1rrpsysCGCYXy9saP8y3kmNpGtJZUXN9SvFoUcqAsM9.9Ssso1ea5NfkbMASdiseDSjTN1fSWda5SgEVjdSN4CvV@GJqd3ZxpXWSNxTfx7B1pPtswpetH4LnJdFeLeuY5KUuN").unwrap(),
reply_to_hops: None,
reply_to_avg_mix_delays: None,
buffer_timeout: None,
},
)
),
};
assert_eq!(connect.to_bytes().unwrap().len(), 107);
assert_eq!(connect.to_bytes().unwrap().len(), 123);
}
#[test]
@@ -186,7 +234,7 @@ mod tests {
let data = IpPacketRequest {
version: 4,
data: IpPacketRequestData::Data(DataRequest {
ip_packet: bytes::Bytes::from(vec![1u8; 32]),
ip_packets: bytes::Bytes::from(vec![1u8; 32]),
}),
};
assert_eq!(data.to_bytes().unwrap().len(), 35);
@@ -197,7 +245,7 @@ mod tests {
let data = IpPacketRequest {
version: 4,
data: IpPacketRequestData::Data(DataRequest {
ip_packet: bytes::Bytes::from(vec![1, 2, 4, 2, 5]),
ip_packets: bytes::Bytes::from(vec![1, 2, 4, 2, 5]),
}),
};
@@ -214,7 +262,7 @@ mod tests {
assert_eq!(
deserialized.data,
IpPacketRequestData::Data(DataRequest {
ip_packet: bytes::Bytes::from(vec![1, 2, 4, 2, 5]),
ip_packets: bytes::Bytes::from(vec![1, 2, 4, 2, 5]),
})
);
}
+104 -6
View File
@@ -1,9 +1,7 @@
use std::net::IpAddr;
use nym_sphinx::addressing::clients::Recipient;
use serde::{Deserialize, Serialize};
use crate::{make_bincode_serializer, CURRENT_VERSION};
use crate::{make_bincode_serializer, IpPair, CURRENT_VERSION};
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct IpPacketResponse {
@@ -38,13 +36,13 @@ impl IpPacketResponse {
}
}
pub fn new_dynamic_connect_success(request_id: u64, reply_to: Recipient, ip: IpAddr) -> Self {
pub fn new_dynamic_connect_success(request_id: u64, reply_to: Recipient, ips: IpPair) -> Self {
Self {
version: CURRENT_VERSION,
data: IpPacketResponseData::DynamicConnect(DynamicConnectResponse {
request_id,
reply_to,
reply: DynamicConnectResponseReply::Success(DynamicConnectSuccess { ip }),
reply: DynamicConnectResponseReply::Success(DynamicConnectSuccess { ips }),
}),
}
}
@@ -64,6 +62,45 @@ impl IpPacketResponse {
}
}
pub fn new_disconnect_success(request_id: u64, reply_to: Recipient) -> Self {
Self {
version: CURRENT_VERSION,
data: IpPacketResponseData::Disconnect(DisconnectResponse {
request_id,
reply_to,
reply: DisconnectResponseReply::Success,
}),
}
}
pub fn new_disconnect_failure(
request_id: u64,
reply_to: Recipient,
reason: DisconnectFailureReason,
) -> Self {
Self {
version: CURRENT_VERSION,
data: IpPacketResponseData::Disconnect(DisconnectResponse {
request_id,
reply_to,
reply: DisconnectResponseReply::Failure(reason),
}),
}
}
pub fn new_unrequested_disconnect(
reply_to: Recipient,
reason: UnrequestedDisconnectReason,
) -> Self {
Self {
version: CURRENT_VERSION,
data: IpPacketResponseData::UnrequestedDisconnect(UnrequestedDisconnect {
reply_to,
reason,
}),
}
}
pub fn new_ip_packet(ip_packet: bytes::Bytes) -> Self {
Self {
version: CURRENT_VERSION,
@@ -106,7 +143,10 @@ impl IpPacketResponse {
IpPacketResponseData::StaticConnect(response) => Some(response.request_id),
IpPacketResponseData::DynamicConnect(response) => Some(response.request_id),
IpPacketResponseData::Disconnect(response) => Some(response.request_id),
IpPacketResponseData::UnrequestedDisconnect(_) => None,
IpPacketResponseData::Data(_) => None,
IpPacketResponseData::Pong(response) => Some(response.request_id),
IpPacketResponseData::Health(response) => Some(response.request_id),
IpPacketResponseData::Error(response) => Some(response.request_id),
}
}
@@ -116,7 +156,10 @@ impl IpPacketResponse {
IpPacketResponseData::StaticConnect(response) => Some(&response.reply_to),
IpPacketResponseData::DynamicConnect(response) => Some(&response.reply_to),
IpPacketResponseData::Disconnect(response) => Some(&response.reply_to),
IpPacketResponseData::UnrequestedDisconnect(response) => Some(&response.reply_to),
IpPacketResponseData::Data(_) => None,
IpPacketResponseData::Pong(response) => Some(&response.reply_to),
IpPacketResponseData::Health(response) => Some(&response.reply_to),
IpPacketResponseData::Error(response) => Some(&response.reply_to),
}
}
@@ -137,10 +180,28 @@ impl IpPacketResponse {
#[allow(clippy::large_enum_variant)]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum IpPacketResponseData {
// Response for a static connect request
StaticConnect(StaticConnectResponse),
// Response for a dynamic connect request
DynamicConnect(DynamicConnectResponse),
// Response for a disconnect initiqated by the client
Disconnect(DisconnectResponse),
// Message from the server that the client got disconnected without the client initiating it
UnrequestedDisconnect(UnrequestedDisconnect),
// Response to a data request
Data(DataResponse),
// Response to ping request
Pong(PongResponse),
// Response for a health request
Health(HealthResponse),
// Error response
Error(ErrorResponse),
}
@@ -200,7 +261,7 @@ impl DynamicConnectResponseReply {
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct DynamicConnectSuccess {
pub ip: IpAddr,
pub ips: IpPair,
}
#[derive(Clone, Debug, Serialize, Deserialize, thiserror::Error)]
@@ -234,11 +295,48 @@ pub enum DisconnectFailureReason {
Other(String),
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct UnrequestedDisconnect {
pub reply_to: Recipient,
pub reason: UnrequestedDisconnectReason,
}
#[derive(Clone, Debug, Serialize, Deserialize, thiserror::Error)]
pub enum UnrequestedDisconnectReason {
#[error("client mixnet traffic timeout")]
ClientMixnetTrafficTimeout,
#[error("client tun traffic timeout")]
ClientTunTrafficTimeout,
#[error("{0}")]
Other(String),
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct DataResponse {
pub ip_packet: bytes::Bytes,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct PongResponse {
pub request_id: u64,
pub reply_to: Recipient,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct HealthResponse {
pub request_id: u64,
pub reply_to: Recipient,
pub reply: HealthResponseReply,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct HealthResponseReply {
// Return the binary build information of the IPR
pub build_info: nym_bin_common::build_information::BinaryBuildInformationOwned,
// Return if the IPR has performed a successful routing test.
pub routable: Option<bool>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ErrorResponse {
pub request_id: u64,
+20
View File
@@ -0,0 +1,20 @@
[package]
name = "nym-id"
version = "0.1.0"
authors.workspace = true
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
edition.workspace = true
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
thiserror.workspace = true
time.workspace = true
tracing.workspace = true
zeroize.workspace = true
nym-credential-storage = { path = "../credential-storage" }
nym-credentials = { path = "../credentials" }
+20
View File
@@ -0,0 +1,20 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::error::Error;
use thiserror::Error;
use time::OffsetDateTime;
#[derive(Debug, Error)]
pub enum NymIdError {
#[error("failed to deserialize provided credential: {source}")]
CredentialDeserializationFailure { source: nym_credentials::Error },
#[error("attempted to import an expired credential (it expired on {expiration})")]
ExpiredCredentialImport { expiration: OffsetDateTime },
#[error("failed to store credential in the provided store: {source}")]
StorageError {
source: Box<dyn Error + Send + Sync>,
},
}
+71
View File
@@ -0,0 +1,71 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::NymIdError;
use nym_credential_storage::models::StorableIssuedCredential;
use nym_credential_storage::storage::Storage;
use nym_credentials::coconut::bandwidth::issued::BandwidthCredentialIssuedDataVariant;
use nym_credentials::IssuedBandwidthCredential;
use tracing::{debug, warn};
use zeroize::Zeroizing;
pub async fn import_credential<S>(
credentials_store: S,
raw_credential: Vec<u8>,
credential_version: impl Into<Option<u8>>,
) -> Result<(), NymIdError>
where
S: Storage,
<S as Storage>::StorageError: Send + Sync + 'static,
{
let raw_credential = Zeroizing::new(raw_credential);
// note: the type itself implements ZeroizeOnDrop
let credential = IssuedBandwidthCredential::try_unpack(&raw_credential, credential_version)
.map_err(|source| NymIdError::CredentialDeserializationFailure { source })?;
debug!(
"attempting to import credential of type {}",
credential.typ()
);
match credential.variant_data() {
BandwidthCredentialIssuedDataVariant::Voucher(voucher_info) => {
debug!("with value of {}", voucher_info.value())
}
BandwidthCredentialIssuedDataVariant::FreePass(freepass_info) => {
debug!("with expiry at {}", freepass_info.expiry_date());
if freepass_info.expired() {
warn!("the free pass has already expired!");
// technically we can import it, but the gateway will just reject it so what's the point
return Err(NymIdError::ExpiredCredentialImport {
expiration: freepass_info.expiry_date(),
});
}
}
}
// SAFETY:
// for the epoch to run over u32::MAX, we'd have to advance it for few centuries every block...
// the alternative is a very particularly malformed serialized data, but at that point blowing up is the right call
// because we can't rely on it anyway
#[allow(clippy::expect_used)]
let storable = StorableIssuedCredential {
serialization_revision: credential.current_serialization_revision(),
credential_data: &raw_credential,
credential_type: credential.typ().to_string(),
epoch_id: credential
.epoch_id()
.try_into()
.expect("our epoch is has run over u32::MAX!"),
};
credentials_store
.insert_issued_credential(storable)
.await
.map_err(|source| NymIdError::StorageError {
source: Box::new(source),
})?;
Ok(())
}
+11
View File
@@ -0,0 +1,11 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
#![warn(clippy::expect_used)]
#![warn(clippy::unwrap_used)]
pub mod error;
pub mod import_credential;
pub use error::NymIdError;
pub use import_credential::import_credential;
+1 -1
View File
@@ -15,7 +15,7 @@ rand = "0.8"
thiserror = { workspace = true }
serde = { workspace = true }
serde_derive = "1.0"
bs58 = "0.4.0"
bs58 = { workspace = true }
sha2 = "0.9"
zeroize = { workspace = true, optional = true }
+1
View File
@@ -24,6 +24,7 @@ pub use scheme::setup::Parameters;
pub use scheme::verification::check_vk_pairing;
pub use scheme::verification::prove_bandwidth_credential;
pub use scheme::verification::verify_credential;
pub use scheme::verification::BlindedSerialNumber;
pub use scheme::verification::VerifyCredentialRequest;
pub use scheme::BlindedSignature;
pub use scheme::Signature;
+39 -10
View File
@@ -1,17 +1,46 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2022-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use bls12_381::G2Projective;
use group::Curve;
use std::convert::TryFrom;
use std::convert::TryInto;
use crate::error::{CoconutError, Result};
use crate::traits::{Base58, Bytable};
use crate::utils::try_deserialize_g2_projective;
use bls12_381::{G2Affine, G2Projective};
use group::Curve;
use std::convert::TryFrom;
use std::convert::TryInto;
use std::fmt::{Debug, Formatter};
use std::ops::Deref;
pub struct BlindedSerialNumber {
pub(crate) inner: G2Projective,
#[derive(PartialEq, Eq, Clone, Copy)]
pub struct BlindedSerialNumber(G2Projective);
// use custom Debug implementation to show base58 encoding (rather than raw curve elements)
impl Debug for BlindedSerialNumber {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("BlindedSerialNumber")
.field(&self.to_bs58())
.finish()
}
}
impl From<G2Projective> for BlindedSerialNumber {
fn from(value: G2Projective) -> Self {
BlindedSerialNumber(value)
}
}
impl From<G2Affine> for BlindedSerialNumber {
fn from(value: G2Affine) -> Self {
BlindedSerialNumber(value.into())
}
}
impl Deref for BlindedSerialNumber {
type Target = G2Projective;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl TryFrom<&[u8]> for BlindedSerialNumber {
@@ -34,13 +63,13 @@ impl TryFrom<&[u8]> for BlindedSerialNumber {
),
)?;
Ok(BlindedSerialNumber { inner })
Ok(BlindedSerialNumber(inner))
}
}
impl Bytable for BlindedSerialNumber {
fn to_byte_vec(&self) -> Vec<u8> {
self.inner.to_affine().to_compressed().to_vec()
self.0.to_affine().to_compressed().to_vec()
}
fn try_from_byte_slice(slice: &[u8]) -> Result<Self> {
+20 -25
View File
@@ -1,22 +1,21 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2021-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use core::ops::Neg;
use std::convert::TryFrom;
use std::convert::TryInto;
use bls12_381::{multi_miller_loop, G1Affine, G2Prepared, G2Projective, Scalar};
use group::{Curve, Group};
use crate::error::{CoconutError, Result};
use crate::proofs::ProofKappaZeta;
use crate::scheme::double_use::BlindedSerialNumber;
use crate::scheme::setup::Parameters;
use crate::scheme::Signature;
use crate::scheme::VerificationKey;
use crate::traits::{Base58, Bytable};
use crate::utils::try_deserialize_g2_projective;
use crate::Attribute;
use bls12_381::{multi_miller_loop, G1Affine, G2Prepared, G2Projective, Scalar};
use core::ops::Neg;
use group::{Curve, Group};
use std::convert::TryFrom;
use std::convert::TryInto;
pub use crate::scheme::double_use::BlindedSerialNumber;
// TODO NAMING: this whole thing
// Theta
@@ -25,7 +24,7 @@ pub struct VerifyCredentialRequest {
// blinded_message (kappa)
pub blinded_message: G2Projective,
// blinded serial number (zeta)
pub blinded_serial_number: G2Projective,
pub blinded_serial_number: BlindedSerialNumber,
// sigma
pub credential: Signature,
// pi_v
@@ -53,15 +52,10 @@ impl TryFrom<&[u8]> for VerifyCredentialRequest {
),
)?;
// safety: we just checked for the length so the unwraps are fine
#[allow(clippy::unwrap_used)]
let blinded_serial_number_bytes = bytes[96..192].try_into().unwrap();
let blinded_serial_number = try_deserialize_g2_projective(
&blinded_serial_number_bytes,
CoconutError::Deserialization(
"failed to deserialize the blinded serial number (zeta)".to_string(),
),
)?;
let blinded_serial_number_bytes = &bytes[96..192];
let blinded_serial_number =
BlindedSerialNumber::try_from_byte_slice(blinded_serial_number_bytes)?;
let credential = Signature::try_from(&bytes[192..288])?;
let pi_v = ProofKappaZeta::from_bytes(&bytes[288..])?;
@@ -87,7 +81,7 @@ impl VerifyCredentialRequest {
pub fn has_blinded_serial_number(&self, blinded_serial_number_bs58: &str) -> Result<bool> {
let blinded_serial_number = BlindedSerialNumber::try_from_bs58(blinded_serial_number_bs58)?;
let ret = self.blinded_serial_number.eq(&blinded_serial_number.inner);
let ret = self.blinded_serial_number.eq(&blinded_serial_number);
Ok(ret)
}
@@ -111,11 +105,12 @@ impl VerifyCredentialRequest {
VerifyCredentialRequest::try_from(bytes)
}
pub fn blinded_serial_number(&self) -> BlindedSerialNumber {
self.blinded_serial_number
}
pub fn blinded_serial_number_bs58(&self) -> String {
let blinded_serial_nuumber = BlindedSerialNumber {
inner: self.blinded_serial_number,
};
blinded_serial_nuumber.to_bs58()
self.blinded_serial_number.to_bs58()
}
}
@@ -198,7 +193,7 @@ pub fn prove_bandwidth_credential(
Ok(VerifyCredentialRequest {
blinded_message,
blinded_serial_number,
blinded_serial_number: blinded_serial_number.into(),
credential: signature_prime,
pi_v,
})
@@ -9,7 +9,7 @@ repository = { workspace = true }
[dependencies]
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
bs58 = "0.4"
bs58 = { workspace = true }
serde = { workspace = true }
thiserror = { workspace = true }
+1
View File
@@ -21,6 +21,7 @@ sqlx = { workspace = true, features = ["runtime-tokio-rustls", "sqlite", "macros
tendermint.workspace = true
tendermint-rpc = { workspace = true, features = ["websocket-client", "http-client"] }
thiserror.workspace = true
time = { workspace = true }
tokio = { workspace = true, features = ["full"] }
tokio-stream = "0.1.14"
tokio-util = { version = "0.7.10", features = ["rt"]}
+1 -1
View File
@@ -84,7 +84,7 @@ pub enum ScraperError {
EmptyBlockData { query: String },
#[error("reached maximum number of allowed errors for subscription events")]
MaximumSubscriptionFailures,
MaximumWebSocketFailures,
#[error("failed to begin storage tx: {source}")]
StorageTxBeginFailure {
+17 -22
View File
@@ -6,11 +6,10 @@ use crate::block_requester::BlockRequester;
use crate::error::ScraperError;
use crate::modules::{BlockModule, MsgModule, TxModule};
use crate::rpc_client::RpcClient;
use crate::scraper::subscriber::{run_websocket_driver, ChainSubscriber};
use crate::scraper::subscriber::ChainSubscriber;
use crate::storage::ScraperStorage;
use std::path::PathBuf;
use std::sync::Arc;
use tendermint_rpc::WebSocketClientDriver;
use tokio::sync::mpsc::{channel, unbounded_channel};
use tokio::sync::Notify;
use tokio_util::sync::CancellationToken;
@@ -67,20 +66,15 @@ impl NyxdScraperBuilder {
block_processor.set_tx_modules(self.tx_modules);
block_processor.set_msg_modules(self.msg_modules);
let mut chain_subscriber = ChainSubscriber::new(
let chain_subscriber = ChainSubscriber::new(
&scraper.config.websocket_url,
scraper.cancel_token.clone(),
scraper.task_tracker.clone(),
processing_tx,
)
.await?;
let ws_driver = chain_subscriber.ws_driver();
scraper.start_tasks(
block_requester,
block_processor,
chain_subscriber,
ws_driver,
);
scraper.start_tasks(block_requester, block_processor, chain_subscriber);
Ok(scraper)
}
@@ -141,7 +135,6 @@ impl NyxdScraper {
mut block_requester: BlockRequester,
mut block_processor: BlockProcessor,
mut chain_subscriber: ChainSubscriber,
ws_driver: WebSocketClientDriver,
) {
self.task_tracker
.spawn(async move { block_requester.run().await });
@@ -149,8 +142,7 @@ impl NyxdScraper {
.spawn(async move { block_processor.run().await });
self.task_tracker
.spawn(async move { chain_subscriber.run().await });
self.task_tracker
.spawn(run_websocket_driver(ws_driver, self.cancel_token.clone()));
self.task_tracker.close();
}
@@ -176,21 +168,16 @@ impl NyxdScraper {
rpc_client,
)
.await?;
let mut chain_subscriber = ChainSubscriber::new(
let chain_subscriber = ChainSubscriber::new(
&self.config.websocket_url,
self.cancel_token.clone(),
self.task_tracker.clone(),
processing_tx,
)
.await?;
let ws_driver = chain_subscriber.ws_driver();
// spawn them
self.start_tasks(
block_requester,
block_processor,
chain_subscriber,
ws_driver,
);
self.start_tasks(block_requester, block_processor, chain_subscriber);
Ok(())
}
@@ -201,10 +188,18 @@ impl NyxdScraper {
}
pub async fn stop(self) {
info!("stopping the chain scrapper");
info!("stopping the chain scraper");
assert!(self.task_tracker.is_closed());
self.cancel_token.cancel();
self.task_tracker.wait().await
}
pub fn cancel_token(&self) -> CancellationToken {
self.cancel_token.clone()
}
pub fn is_cancelled(&self) -> bool {
self.cancel_token.is_cancelled()
}
}
+129 -17
View File
@@ -6,18 +6,25 @@ use crate::error::ScraperError;
use tendermint_rpc::event::Event;
use tendermint_rpc::query::EventType;
use tendermint_rpc::{SubscriptionClient, WebSocketClient, WebSocketClientDriver};
use time::{Duration, OffsetDateTime};
use tokio::sync::mpsc::UnboundedSender;
use tokio_stream::StreamExt;
use tokio_util::sync::CancellationToken;
use tokio_util::task::TaskTracker;
use tracing::{error, info, warn};
use url::Url;
const MAX_FAILURES: usize = 10;
const MAX_RECONNECTION_ATTEMPTS: usize = 8;
const SOCKET_FAILURE_RESET: Duration = Duration::hours(2);
pub struct ChainSubscriber {
cancel: CancellationToken,
task_tracker: TaskTracker,
block_sender: UnboundedSender<BlockToProcess>,
websocket_endpoint: Url,
websocket_client: WebSocketClient,
websocket_driver: Option<WebSocketClientDriver>,
}
@@ -26,6 +33,7 @@ impl ChainSubscriber {
pub async fn new(
websocket_endpoint: &Url,
cancel: CancellationToken,
task_tracker: TaskTracker,
block_sender: UnboundedSender<BlockToProcess>,
) -> Result<Self, ScraperError> {
// sure, we could have just used websocket client entirely, but let's keep the logic for
@@ -39,7 +47,9 @@ impl ChainSubscriber {
Ok(ChainSubscriber {
cancel,
task_tracker,
block_sender,
websocket_endpoint: websocket_endpoint.clone(),
websocket_client: client,
websocket_driver: Some(driver),
})
@@ -53,8 +63,48 @@ impl ChainSubscriber {
Ok(())
}
pub(crate) async fn run(&mut self) -> Result<(), ScraperError> {
let _drop_guard = self.cancel.clone().drop_guard();
async fn remake_connection(&mut self) -> Result<(), ScraperError> {
info!(
"attempting to reestablish connection to {}",
self.websocket_endpoint
);
let (client, driver) = WebSocketClient::new(self.websocket_endpoint.as_str())
.await
.map_err(|source| ScraperError::WebSocketConnectionFailure {
url: self.websocket_endpoint.to_string(),
source,
})?;
self.websocket_client = client;
self.websocket_driver = Some(driver);
info!(
"managed to reestablish the websocket connection to {}",
self.websocket_endpoint
);
Ok(())
}
/// Returns whether the method exited due to the cancellation
async fn run_chain_subscription(&mut self) -> Result<bool, ScraperError> {
let Some(ws_driver) = self.websocket_driver.take() else {
error!("the websocket driver hasn't been created - we probably failed to establish the connection");
return Ok(false);
};
let driver_cancel = CancellationToken::new();
let _driver_guard = driver_cancel.clone().drop_guard();
// spawn the websocket driver task
let driver_handle = {
self.task_tracker.reopen();
let handle = self
.task_tracker
.spawn(run_websocket_driver(ws_driver, driver_cancel));
self.task_tracker.close();
handle
};
tokio::pin!(driver_handle);
info!("creating chain subscription");
let mut subs = self
@@ -70,12 +120,17 @@ impl ChainSubscriber {
tokio::select! {
_ = self.cancel.cancelled() => {
info!("received cancellation token");
break
// note: `_driver_guard` will get dropped here thus causing cancellation of the driver task
return Ok(true)
}
_ = &mut driver_handle => {
error!("our websocket driver has finished execution");
return Ok(self.cancel.is_cancelled())
}
maybe_event = subs.next() => {
let Some(maybe_event) = maybe_event else {
warn!("stopped receiving new events");
break;
return Ok(false)
};
match maybe_event {
Ok(event) => {
@@ -92,38 +147,95 @@ impl ChainSubscriber {
}
}
if failures >= MAX_FAILURES {
// note: the drop_guard will get dropped and thus cause a shutdown
return Err(ScraperError::MaximumSubscriptionFailures);
return Ok(false)
}
}
}
}
Ok(())
}
pub(crate) fn ws_driver(&mut self) -> WebSocketClientDriver {
#[allow(clippy::expect_used)]
self.websocket_driver
.take()
.expect("websocket driver has already been started!")
async fn websocket_backoff(&mut self, failure_count: usize) -> bool {
const MINIMUM_WAIT_MS: u64 = 10_000;
const INCREMENTAL_WAIT_MS: u64 = 30_000;
let backoff_duration_ms = MINIMUM_WAIT_MS + INCREMENTAL_WAIT_MS * failure_count as u64;
info!("going to wait {backoff_duration_ms} ms before re-attempting the reconnection");
tokio::select! {
_ = self.cancel.cancelled() => {
info!("received cancellation token");
true
}
_ = tokio::time::sleep(std::time::Duration::from_millis(backoff_duration_ms)) => false,
}
}
pub(crate) async fn run(&mut self) -> Result<(), ScraperError> {
let _drop_guard = self.cancel.clone().drop_guard();
let mut socket_failures = 0;
let mut last_failure = OffsetDateTime::now_utc();
loop {
if self.cancel.is_cancelled() {
return Ok(());
}
match self.run_chain_subscription().await {
Ok(cancelled) => {
if cancelled {
// we're in the middle of a shutdown
return Ok(());
}
socket_failures += 1;
}
Err(err) => {
error!("failed to create chain subscription: {err}");
socket_failures += 1;
}
}
warn!("current socket failure count: {socket_failures}. the last failure was at {last_failure}");
let now = OffsetDateTime::now_utc();
// if it's been a while since the last failure, reset the count
if now - last_failure > SOCKET_FAILURE_RESET {
warn!("resetting the failure count to 1");
socket_failures = 1;
}
last_failure = now;
if socket_failures >= MAX_RECONNECTION_ATTEMPTS {
error!("reached the maximum allowed failure count");
return Err(ScraperError::MaximumWebSocketFailures);
}
// BACKOFF
let cancelled = self.websocket_backoff(socket_failures).await;
if cancelled {
return Ok(());
}
if let Err(err) = self.remake_connection().await {
error!("failed to re-establish the websocket connection: {err}");
}
}
}
}
pub async fn run_websocket_driver(driver: WebSocketClientDriver, cancel: CancellationToken) {
pub async fn run_websocket_driver(driver: WebSocketClientDriver, driver_cancel: CancellationToken) {
info!("starting websocket driver");
tokio::select! {
_ = cancel.cancelled() => {
_ = driver_cancel.cancelled() => {
info!("received cancellation token")
}
res = driver.run() => {
match res {
Ok(_) => info!("our websocket driver has finished execution"),
Err(err) => {
// TODO: in the future just attempt to reconnect
error!("our websocket driver has errored out: {err}")
error!("our websocket driver has errored out: {err}");
}
}
cancel.cancel()
}
}
}
+1 -1
View File
@@ -12,7 +12,7 @@ documentation = { workspace = true }
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
bs58 = "0.4"
bs58 = { workspace = true }
log = { workspace = true }
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
thiserror = { workspace = true }
+38 -14
View File
@@ -1,3 +1,4 @@
use std::net::Ipv6Addr;
use std::{
collections::HashMap,
net::{IpAddr, Ipv4Addr},
@@ -19,6 +20,12 @@ const TUN_WRITE_TIMEOUT_MS: u64 = 1000;
#[derive(thiserror::Error, Debug)]
pub enum TunDeviceError {
#[error("{0}")]
IO(#[from] std::io::Error),
#[error("{0}")]
TokioTun(#[from] tokio_tun::Error),
#[error("timeout writing to tun device, dropping packet")]
TunWriteTimeout,
@@ -44,14 +51,18 @@ pub enum TunDeviceError {
FailedToLockPeer,
}
fn setup_tokio_tun_device(name: &str, address: Ipv4Addr, netmask: Ipv4Addr) -> tokio_tun::Tun {
fn setup_tokio_tun_device(
name: &str,
address: Ipv4Addr,
netmask: Ipv4Addr,
) -> Result<tokio_tun::Tun, TunDeviceError> {
log::info!("Creating TUN device with: address={address}, netmask={netmask}");
// Read MTU size from env variable NYM_MTU_SIZE, else default to 1420.
let mtu = std::env::var("NYM_MTU_SIZE")
.map(|mtu| mtu.parse().expect("NYM_MTU_SIZE must be a valid integer"))
.unwrap_or(1420);
log::info!("Using MTU size: {mtu}");
tokio_tun::Tun::builder()
Ok(tokio_tun::Tun::builder()
.name(name)
.tap(false)
.packet_info(false)
@@ -59,8 +70,7 @@ fn setup_tokio_tun_device(name: &str, address: Ipv4Addr, netmask: Ipv4Addr) -> t
.up()
.address(address)
.netmask(netmask)
.try_build()
.expect("Failed to setup tun device, do you have permission?")
.try_build()?)
}
pub struct TunDevice {
@@ -103,16 +113,18 @@ pub struct NatInner {
pub struct TunDeviceConfig {
pub base_name: String,
pub ip: Ipv4Addr,
pub netmask: Ipv4Addr,
pub ipv4: Ipv4Addr,
pub netmaskv4: Ipv4Addr,
pub ipv6: Ipv6Addr,
pub netmaskv6: String,
}
impl TunDevice {
pub fn new(
routing_mode: RoutingMode,
config: TunDeviceConfig,
) -> (Self, TunTaskTx, TunTaskResponseRx) {
let tun = Self::new_device_only(config);
) -> Result<(Self, TunTaskTx, TunTaskResponseRx), TunDeviceError> {
let tun = Self::new_device_only(config)?;
// Channels to communicate with the other tasks
let (tun_task_tx, tun_task_rx) = tun_task_channel();
@@ -125,20 +137,32 @@ impl TunDevice {
routing_mode,
};
(tun_device, tun_task_tx, tun_task_response_rx)
Ok((tun_device, tun_task_tx, tun_task_response_rx))
}
pub fn new_device_only(config: TunDeviceConfig) -> tokio_tun::Tun {
pub fn new_device_only(config: TunDeviceConfig) -> Result<tokio_tun::Tun, TunDeviceError> {
let TunDeviceConfig {
base_name,
ip,
netmask,
ipv4,
netmaskv4,
ipv6,
netmaskv6,
} = config;
let name = format!("{base_name}%d");
let tun = setup_tokio_tun_device(&name, ip, netmask);
let tun = setup_tokio_tun_device(&name, ipv4, netmaskv4)?;
log::info!("Created TUN device: {}", tun.name());
tun
std::process::Command::new("ip")
.args([
"-6",
"addr",
"add",
&format!("{}/{}", ipv6, netmaskv6),
"dev",
&tun.name(),
])
.output()?;
Ok(tun)
}
// Send outbound packets out on the wild internet
+34 -23
View File
@@ -116,6 +116,15 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3"
[[package]]
name = "bs58"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5353f36341f7451062466f0b755b96ac3a9547e4d7f6b70d603fc721a7d7896"
dependencies = [
"tinyvec",
]
[[package]]
name = "bumpalo"
version = "3.12.1"
@@ -178,6 +187,7 @@ dependencies = [
name = "coconut-test"
version = "0.1.0"
dependencies = [
"bs58 0.4.0",
"cosmwasm-std",
"cosmwasm-storage",
"cw-controllers",
@@ -194,8 +204,10 @@ dependencies = [
"nym-coconut-dkg-common",
"nym-group-contract-common",
"nym-multisig-contract-common",
"rand_chacha 0.2.2",
"schemars",
"serde",
"subtle-encoding",
"thiserror",
]
@@ -819,7 +831,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.32",
"syn 2.0.49",
]
[[package]]
@@ -1225,7 +1237,6 @@ dependencies = [
"cw4-group",
"nym-coconut-dkg-common",
"nym-group-contract-common",
"rusty-fork",
"semver",
"serde",
"thiserror",
@@ -1248,7 +1259,7 @@ dependencies = [
name = "nym-contracts-common"
version = "0.5.0"
dependencies = [
"bs58",
"bs58 0.5.0",
"cosmwasm-schema",
"cosmwasm-std",
"schemars",
@@ -1260,7 +1271,7 @@ dependencies = [
name = "nym-crypto"
version = "0.4.0"
dependencies = [
"bs58",
"bs58 0.5.0",
"ed25519-dalek",
"nym-pemstore",
"nym-sphinx-types",
@@ -1316,7 +1327,7 @@ dependencies = [
name = "nym-mixnet-contract"
version = "1.5.1"
dependencies = [
"bs58",
"bs58 0.4.0",
"cosmwasm-derive",
"cosmwasm-schema",
"cosmwasm-std",
@@ -1339,7 +1350,7 @@ dependencies = [
name = "nym-mixnet-contract-common"
version = "0.6.0"
dependencies = [
"bs58",
"bs58 0.4.0",
"cosmwasm-schema",
"cosmwasm-std",
"cw2",
@@ -1374,7 +1385,7 @@ name = "nym-name-service"
version = "0.1.0"
dependencies = [
"anyhow",
"bs58",
"bs58 0.4.0",
"cosmwasm-schema",
"cosmwasm-std",
"cw-controllers",
@@ -1421,7 +1432,7 @@ name = "nym-service-provider-directory"
version = "0.1.0"
dependencies = [
"anyhow",
"bs58",
"bs58 0.4.0",
"cosmwasm-schema",
"cosmwasm-std",
"cw-controllers",
@@ -1608,9 +1619,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.63"
version = "1.0.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb"
checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
dependencies = [
"unicode-ident",
]
@@ -1646,9 +1657,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
[[package]]
name = "quote"
version = "1.0.30"
version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5907a1b7c277254a8b15170f6e7c97cfa60ee7872a3217663bb81151e48184bb"
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
dependencies = [
"proc-macro2",
]
@@ -1888,9 +1899,9 @@ checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0"
[[package]]
name = "serde"
version = "1.0.190"
version = "1.0.196"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91d3c334ca1ee894a2c6f6ad698fe8c435b76d504b13d436f0685d648d6d96f7"
checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32"
dependencies = [
"serde_derive",
]
@@ -1906,13 +1917,13 @@ dependencies = [
[[package]]
name = "serde_derive"
version = "1.0.190"
version = "1.0.196"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3"
checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.32",
"syn 2.0.49",
]
[[package]]
@@ -1945,7 +1956,7 @@ checksum = "bcec881020c684085e55a25f7fd888954d56609ef363479dc5a1305eb0d40cab"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.32",
"syn 2.0.49",
]
[[package]]
@@ -2000,7 +2011,7 @@ dependencies = [
"aes",
"arrayref",
"blake2",
"bs58",
"bs58 0.4.0",
"byteorder",
"chacha",
"curve25519-dalek",
@@ -2059,9 +2070,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.32"
version = "2.0.49"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "239814284fd6f1a4ffe4ca893952cdd93c224b6a1571c9a9eadd670295c0c9e2"
checksum = "915aea9e586f80826ee59f8453c1101f9d1c4b3964cd2460185ee8e299ada496"
dependencies = [
"proc-macro2",
"quote",
@@ -2098,7 +2109,7 @@ checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.32",
"syn 2.0.49",
]
[[package]]
@@ -2447,5 +2458,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.32",
"syn 2.0.49",
]
+2
View File
@@ -34,6 +34,7 @@ incremental = false
overflow-checks = true
[workspace.dependencies]
bs58 = "0.4.0"
cosmwasm-crypto = "=1.3.0"
cosmwasm-derive = "=1.3.0"
cosmwasm-schema = "=1.3.0"
@@ -49,5 +50,6 @@ cw3-fixed-multisig = "=1.1.0"
cw4 = "=1.1.0"
cw20 = "=1.1.0"
semver = "1.0.21"
serde = "1.0.196"
thiserror = "1.0.48"
-1
View File
@@ -30,7 +30,6 @@ thiserror = { workspace = true }
cw-multi-test = { workspace = true }
cw4-group = { path = "../multisig/cw4-group" }
nym-group-contract-common = { path = "../../common/cosmwasm-smart-contracts/group-contract" }
rusty-fork = "0.3"
[features]
schema-gen = ["nym-coconut-dkg-common/schema", "cosmwasm-schema"]
+417 -98
View File
@@ -180,15 +180,11 @@
"commit_dealings_chunk": {
"type": "object",
"required": [
"chunk",
"resharing"
"chunk"
],
"properties": {
"chunk": {
"$ref": "#/definitions/PartialContractDealing"
},
"resharing": {
"type": "boolean"
}
},
"additionalProperties": false
@@ -249,10 +245,10 @@
{
"type": "object",
"required": [
"surpassed_threshold"
"advance_epoch_state"
],
"properties": {
"surpassed_threshold": {
"advance_epoch_state": {
"type": "object",
"additionalProperties": false
}
@@ -262,10 +258,23 @@
{
"type": "object",
"required": [
"advance_epoch_state"
"trigger_reset"
],
"properties": {
"advance_epoch_state": {
"trigger_reset": {
"type": "object",
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"trigger_resharing"
],
"properties": {
"trigger_resharing": {
"type": "object",
"additionalProperties": false
}
@@ -368,16 +377,45 @@
{
"type": "object",
"required": [
"get_initial_dealers"
"can_advance_state"
],
"properties": {
"get_initial_dealers": {
"can_advance_state": {
"type": "object",
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"get_registered_dealer"
],
"properties": {
"get_registered_dealer": {
"type": "object",
"required": [
"dealer_address"
],
"properties": {
"dealer_address": {
"type": "string"
},
"epoch_id": {
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 0.0
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
@@ -431,10 +469,10 @@
{
"type": "object",
"required": [
"get_past_dealers"
"get_dealer_indices"
],
"properties": {
"get_past_dealers": {
"get_dealer_indices": {
"type": "object",
"properties": {
"limit": {
@@ -716,6 +754,215 @@
},
"sudo": null,
"responses": {
"can_advance_state": {
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "StateAdvanceResponse",
"type": "object",
"required": [
"current_state",
"is_complete",
"progress",
"reached_deadline"
],
"properties": {
"current_state": {
"$ref": "#/definitions/EpochState"
},
"deadline": {
"anyOf": [
{
"$ref": "#/definitions/Timestamp"
},
{
"type": "null"
}
]
},
"is_complete": {
"type": "boolean"
},
"progress": {
"$ref": "#/definitions/StateProgress"
},
"reached_deadline": {
"type": "boolean"
}
},
"additionalProperties": false,
"definitions": {
"EpochState": {
"oneOf": [
{
"type": "string",
"enum": [
"waiting_initialisation",
"in_progress"
]
},
{
"type": "object",
"required": [
"public_key_submission"
],
"properties": {
"public_key_submission": {
"type": "object",
"required": [
"resharing"
],
"properties": {
"resharing": {
"type": "boolean"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"dealing_exchange"
],
"properties": {
"dealing_exchange": {
"type": "object",
"required": [
"resharing"
],
"properties": {
"resharing": {
"type": "boolean"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"verification_key_submission"
],
"properties": {
"verification_key_submission": {
"type": "object",
"required": [
"resharing"
],
"properties": {
"resharing": {
"type": "boolean"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"verification_key_validation"
],
"properties": {
"verification_key_validation": {
"type": "object",
"required": [
"resharing"
],
"properties": {
"resharing": {
"type": "boolean"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"verification_key_finalization"
],
"properties": {
"verification_key_finalization": {
"type": "object",
"required": [
"resharing"
],
"properties": {
"resharing": {
"type": "boolean"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
}
]
},
"StateProgress": {
"type": "object",
"required": [
"registered_dealers",
"registered_resharing_dealers",
"submitted_dealings",
"submitted_key_shares",
"verified_keys"
],
"properties": {
"registered_dealers": {
"description": "Counts the number of dealers that have registered in this epoch.",
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"registered_resharing_dealers": {
"description": "Counts the number of resharing dealers that have registered in this epoch. This field is only populated during a resharing exchange. It is always <= registered_dealers.",
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"submitted_dealings": {
"description": "Counts the number of fully received dealings (i.e. full chunks) from all the allowed dealers.",
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"submitted_key_shares": {
"description": "Counts the number of submitted verification key shared from the dealers.",
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"verified_keys": {
"description": "Counts the number of verified key shares.",
"type": "integer",
"format": "uint32",
"minimum": 0.0
}
},
"additionalProperties": false
},
"Timestamp": {
"description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```",
"allOf": [
{
"$ref": "#/definitions/Uint64"
}
]
},
"Uint64": {
"description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```",
"type": "string"
}
}
},
"get_c_w2_contract_version": {
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "ContractVersion",
@@ -813,15 +1060,11 @@
"required": [
"epoch_id",
"state",
"state_progress",
"time_configuration"
],
"properties": {
"epoch_id": {
"type": "integer",
"format": "uint64",
"minimum": 0.0
},
"finish_timestamp": {
"deadline": {
"anyOf": [
{
"$ref": "#/definitions/Timestamp"
@@ -831,9 +1074,17 @@
}
]
},
"epoch_id": {
"type": "integer",
"format": "uint64",
"minimum": 0.0
},
"state": {
"$ref": "#/definitions/EpochState"
},
"state_progress": {
"$ref": "#/definitions/StateProgress"
},
"time_configuration": {
"$ref": "#/definitions/TimeConfiguration"
}
@@ -956,6 +1207,49 @@
}
]
},
"StateProgress": {
"type": "object",
"required": [
"registered_dealers",
"registered_resharing_dealers",
"submitted_dealings",
"submitted_key_shares",
"verified_keys"
],
"properties": {
"registered_dealers": {
"description": "Counts the number of dealers that have registered in this epoch.",
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"registered_resharing_dealers": {
"description": "Counts the number of resharing dealers that have registered in this epoch. This field is only populated during a resharing exchange. It is always <= registered_dealers.",
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"submitted_dealings": {
"description": "Counts the number of fully received dealings (i.e. full chunks) from all the allowed dealers.",
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"submitted_key_shares": {
"description": "Counts the number of submitted verification key shared from the dealers.",
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"verified_keys": {
"description": "Counts the number of verified key shares.",
"type": "integer",
"format": "uint32",
"minimum": 0.0
}
},
"additionalProperties": false
},
"TimeConfiguration": {
"type": "object",
"required": [
@@ -1154,15 +1448,109 @@
"additionalProperties": false
},
"DealerType": {
"type": "string",
"enum": [
"current",
"past",
"unknown"
"oneOf": [
{
"type": "string",
"enum": [
"unknown"
]
},
{
"type": "object",
"required": [
"current"
],
"properties": {
"current": {
"type": "object",
"required": [
"assigned_index"
],
"properties": {
"assigned_index": {
"type": "integer",
"format": "uint64",
"minimum": 0.0
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"past"
],
"properties": {
"past": {
"type": "object",
"required": [
"assigned_index"
],
"properties": {
"assigned_index": {
"type": "integer",
"format": "uint64",
"minimum": 0.0
}
},
"additionalProperties": false
}
},
"additionalProperties": false
}
]
}
}
},
"get_dealer_indices": {
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "PagedDealerIndexResponse",
"type": "object",
"required": [
"indices"
],
"properties": {
"indices": {
"type": "array",
"items": {
"type": "array",
"items": [
{
"$ref": "#/definitions/Addr"
},
{
"type": "integer",
"format": "uint64",
"minimum": 0.0
}
],
"maxItems": 2,
"minItems": 2
}
},
"start_next_after": {
"description": "Field indicating paging information for the following queries if the caller wishes to get further entries.",
"anyOf": [
{
"$ref": "#/definitions/Addr"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false,
"definitions": {
"Addr": {
"description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
"type": "string"
}
}
},
"get_dealing_chunk": {
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "DealingChunkResponse",
@@ -1455,70 +1843,15 @@
}
}
},
"get_initial_dealers": {
"get_registered_dealer": {
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Nullable_InitialReplacementData",
"anyOf": [
{
"$ref": "#/definitions/InitialReplacementData"
},
{
"type": "null"
}
],
"definitions": {
"Addr": {
"description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
"type": "string"
},
"InitialReplacementData": {
"type": "object",
"required": [
"initial_dealers",
"initial_height"
],
"properties": {
"initial_dealers": {
"type": "array",
"items": {
"$ref": "#/definitions/Addr"
}
},
"initial_height": {
"type": "integer",
"format": "uint64",
"minimum": 0.0
}
},
"additionalProperties": false
}
}
},
"get_past_dealers": {
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "PagedDealerResponse",
"title": "RegisteredDealerDetails",
"type": "object",
"required": [
"dealers",
"per_page"
],
"properties": {
"dealers": {
"type": "array",
"items": {
"$ref": "#/definitions/DealerDetails"
}
},
"per_page": {
"type": "integer",
"format": "uint",
"minimum": 0.0
},
"start_next_after": {
"description": "Field indicating paging information for the following queries if the caller wishes to get further entries.",
"details": {
"anyOf": [
{
"$ref": "#/definitions/Addr"
"$ref": "#/definitions/DealerRegistrationDetails"
},
{
"type": "null"
@@ -1528,31 +1861,17 @@
},
"additionalProperties": false,
"definitions": {
"Addr": {
"description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
"type": "string"
},
"DealerDetails": {
"DealerRegistrationDetails": {
"type": "object",
"required": [
"address",
"announce_address",
"assigned_index",
"bte_public_key_with_proof",
"ed25519_identity"
],
"properties": {
"address": {
"$ref": "#/definitions/Addr"
},
"announce_address": {
"type": "string"
},
"assigned_index": {
"type": "integer",
"format": "uint64",
"minimum": 0.0
},
"bte_public_key_with_proof": {
"type": "string"
},
+18 -9
View File
@@ -91,15 +91,11 @@
"commit_dealings_chunk": {
"type": "object",
"required": [
"chunk",
"resharing"
"chunk"
],
"properties": {
"chunk": {
"$ref": "#/definitions/PartialContractDealing"
},
"resharing": {
"type": "boolean"
}
},
"additionalProperties": false
@@ -160,10 +156,10 @@
{
"type": "object",
"required": [
"surpassed_threshold"
"advance_epoch_state"
],
"properties": {
"surpassed_threshold": {
"advance_epoch_state": {
"type": "object",
"additionalProperties": false
}
@@ -173,10 +169,23 @@
{
"type": "object",
"required": [
"advance_epoch_state"
"trigger_reset"
],
"properties": {
"advance_epoch_state": {
"trigger_reset": {
"type": "object",
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"trigger_resharing"
],
"properties": {
"trigger_resharing": {
"type": "object",
"additionalProperties": false
}
+33 -4
View File
@@ -44,16 +44,45 @@
{
"type": "object",
"required": [
"get_initial_dealers"
"can_advance_state"
],
"properties": {
"get_initial_dealers": {
"can_advance_state": {
"type": "object",
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"get_registered_dealer"
],
"properties": {
"get_registered_dealer": {
"type": "object",
"required": [
"dealer_address"
],
"properties": {
"dealer_address": {
"type": "string"
},
"epoch_id": {
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 0.0
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
@@ -107,10 +136,10 @@
{
"type": "object",
"required": [
"get_past_dealers"
"get_dealer_indices"
],
"properties": {
"get_past_dealers": {
"get_dealer_indices": {
"type": "object",
"properties": {
"limit": {
@@ -0,0 +1,209 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "StateAdvanceResponse",
"type": "object",
"required": [
"current_state",
"is_complete",
"progress",
"reached_deadline"
],
"properties": {
"current_state": {
"$ref": "#/definitions/EpochState"
},
"deadline": {
"anyOf": [
{
"$ref": "#/definitions/Timestamp"
},
{
"type": "null"
}
]
},
"is_complete": {
"type": "boolean"
},
"progress": {
"$ref": "#/definitions/StateProgress"
},
"reached_deadline": {
"type": "boolean"
}
},
"additionalProperties": false,
"definitions": {
"EpochState": {
"oneOf": [
{
"type": "string",
"enum": [
"waiting_initialisation",
"in_progress"
]
},
{
"type": "object",
"required": [
"public_key_submission"
],
"properties": {
"public_key_submission": {
"type": "object",
"required": [
"resharing"
],
"properties": {
"resharing": {
"type": "boolean"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"dealing_exchange"
],
"properties": {
"dealing_exchange": {
"type": "object",
"required": [
"resharing"
],
"properties": {
"resharing": {
"type": "boolean"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"verification_key_submission"
],
"properties": {
"verification_key_submission": {
"type": "object",
"required": [
"resharing"
],
"properties": {
"resharing": {
"type": "boolean"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"verification_key_validation"
],
"properties": {
"verification_key_validation": {
"type": "object",
"required": [
"resharing"
],
"properties": {
"resharing": {
"type": "boolean"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"verification_key_finalization"
],
"properties": {
"verification_key_finalization": {
"type": "object",
"required": [
"resharing"
],
"properties": {
"resharing": {
"type": "boolean"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
}
]
},
"StateProgress": {
"type": "object",
"required": [
"registered_dealers",
"registered_resharing_dealers",
"submitted_dealings",
"submitted_key_shares",
"verified_keys"
],
"properties": {
"registered_dealers": {
"description": "Counts the number of dealers that have registered in this epoch.",
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"registered_resharing_dealers": {
"description": "Counts the number of resharing dealers that have registered in this epoch. This field is only populated during a resharing exchange. It is always <= registered_dealers.",
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"submitted_dealings": {
"description": "Counts the number of fully received dealings (i.e. full chunks) from all the allowed dealers.",
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"submitted_key_shares": {
"description": "Counts the number of submitted verification key shared from the dealers.",
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"verified_keys": {
"description": "Counts the number of verified key shares.",
"type": "integer",
"format": "uint32",
"minimum": 0.0
}
},
"additionalProperties": false
},
"Timestamp": {
"description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```",
"allOf": [
{
"$ref": "#/definitions/Uint64"
}
]
},
"Uint64": {
"description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```",
"type": "string"
}
}
}
@@ -5,15 +5,11 @@
"required": [
"epoch_id",
"state",
"state_progress",
"time_configuration"
],
"properties": {
"epoch_id": {
"type": "integer",
"format": "uint64",
"minimum": 0.0
},
"finish_timestamp": {
"deadline": {
"anyOf": [
{
"$ref": "#/definitions/Timestamp"
@@ -23,9 +19,17 @@
}
]
},
"epoch_id": {
"type": "integer",
"format": "uint64",
"minimum": 0.0
},
"state": {
"$ref": "#/definitions/EpochState"
},
"state_progress": {
"$ref": "#/definitions/StateProgress"
},
"time_configuration": {
"$ref": "#/definitions/TimeConfiguration"
}
@@ -148,6 +152,49 @@
}
]
},
"StateProgress": {
"type": "object",
"required": [
"registered_dealers",
"registered_resharing_dealers",
"submitted_dealings",
"submitted_key_shares",
"verified_keys"
],
"properties": {
"registered_dealers": {
"description": "Counts the number of dealers that have registered in this epoch.",
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"registered_resharing_dealers": {
"description": "Counts the number of resharing dealers that have registered in this epoch. This field is only populated during a resharing exchange. It is always <= registered_dealers.",
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"submitted_dealings": {
"description": "Counts the number of fully received dealings (i.e. full chunks) from all the allowed dealers.",
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"submitted_key_shares": {
"description": "Counts the number of submitted verification key shared from the dealers.",
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"verified_keys": {
"description": "Counts the number of verified key shares.",
"type": "integer",
"format": "uint32",
"minimum": 0.0
}
},
"additionalProperties": false
},
"TimeConfiguration": {
"type": "object",
"required": [
@@ -57,11 +57,59 @@
"additionalProperties": false
},
"DealerType": {
"type": "string",
"enum": [
"current",
"past",
"unknown"
"oneOf": [
{
"type": "string",
"enum": [
"unknown"
]
},
{
"type": "object",
"required": [
"current"
],
"properties": {
"current": {
"type": "object",
"required": [
"assigned_index"
],
"properties": {
"assigned_index": {
"type": "integer",
"format": "uint64",
"minimum": 0.0
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"past"
],
"properties": {
"past": {
"type": "object",
"required": [
"assigned_index"
],
"properties": {
"assigned_index": {
"type": "integer",
"format": "uint64",
"minimum": 0.0
}
},
"additionalProperties": false
}
},
"additionalProperties": false
}
]
}
}
@@ -0,0 +1,46 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "PagedDealerIndexResponse",
"type": "object",
"required": [
"indices"
],
"properties": {
"indices": {
"type": "array",
"items": {
"type": "array",
"items": [
{
"$ref": "#/definitions/Addr"
},
{
"type": "integer",
"format": "uint64",
"minimum": 0.0
}
],
"maxItems": 2,
"minItems": 2
}
},
"start_next_after": {
"description": "Field indicating paging information for the following queries if the caller wishes to get further entries.",
"anyOf": [
{
"$ref": "#/definitions/Addr"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false,
"definitions": {
"Addr": {
"description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
"type": "string"
}
}
}
@@ -0,0 +1,40 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "RegisteredDealerDetails",
"type": "object",
"properties": {
"details": {
"anyOf": [
{
"$ref": "#/definitions/DealerRegistrationDetails"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false,
"definitions": {
"DealerRegistrationDetails": {
"type": "object",
"required": [
"announce_address",
"bte_public_key_with_proof",
"ed25519_identity"
],
"properties": {
"announce_address": {
"type": "string"
},
"bte_public_key_with_proof": {
"type": "string"
},
"ed25519_identity": {
"type": "string"
}
},
"additionalProperties": false
}
}
}
+21 -11
View File
@@ -2,7 +2,8 @@
// SPDX-License-Identifier: Apache-2.0
use crate::dealers::queries::{
query_current_dealers_paged, query_dealer_details, query_past_dealers_paged,
query_current_dealers_paged, query_dealer_details, query_dealers_indices_paged,
query_registered_dealer_details,
};
use crate::dealers::transactions::try_add_dealer;
use crate::dealings::queries::{
@@ -11,11 +12,11 @@ use crate::dealings::queries::{
};
use crate::dealings::transactions::{try_commit_dealings_chunk, try_submit_dealings_metadata};
use crate::epoch_state::queries::{
query_current_epoch, query_current_epoch_threshold, query_initial_dealers,
query_can_advance_state, query_current_epoch, query_current_epoch_threshold,
};
use crate::epoch_state::storage::CURRENT_EPOCH;
use crate::epoch_state::transactions::{
advance_epoch_state, try_initiate_dkg, try_surpassed_threshold,
try_advance_epoch_state, try_initiate_dkg, try_trigger_reset, try_trigger_resharing,
};
use crate::error::ContractError;
use crate::state::queries::query_state;
@@ -108,8 +109,8 @@ pub fn execute(
chunks,
resharing,
} => try_submit_dealings_metadata(deps, info, dealing_index, chunks, resharing),
ExecuteMsg::CommitDealingsChunk { chunk, resharing } => {
try_commit_dealings_chunk(deps, env, info, chunk, resharing)
ExecuteMsg::CommitDealingsChunk { chunk } => {
try_commit_dealings_chunk(deps, env, info, chunk)
}
ExecuteMsg::CommitVerificationKeyShare { share, resharing } => {
try_commit_verification_key_share(deps, env, info, share, resharing)
@@ -117,28 +118,37 @@ pub fn execute(
ExecuteMsg::VerifyVerificationKeyShare { owner, resharing } => {
try_verify_verification_key_share(deps, info, owner, resharing)
}
ExecuteMsg::SurpassedThreshold {} => try_surpassed_threshold(deps, env),
ExecuteMsg::AdvanceEpochState {} => advance_epoch_state(deps, env),
ExecuteMsg::AdvanceEpochState {} => try_advance_epoch_state(deps, env),
ExecuteMsg::TriggerReset {} => try_trigger_reset(deps, env, info),
ExecuteMsg::TriggerResharing {} => try_trigger_resharing(deps, env, info),
}
}
#[entry_point]
pub fn query(deps: Deps<'_>, _env: Env, msg: QueryMsg) -> Result<QueryResponse, ContractError> {
pub fn query(deps: Deps<'_>, env: Env, msg: QueryMsg) -> Result<QueryResponse, ContractError> {
let response = match msg {
QueryMsg::GetState {} => to_binary(&query_state(deps.storage)?)?,
QueryMsg::GetCurrentEpochState {} => to_binary(&query_current_epoch(deps.storage)?)?,
QueryMsg::CanAdvanceState {} => to_binary(&query_can_advance_state(deps.storage, env)?)?,
QueryMsg::GetCurrentEpochThreshold {} => {
to_binary(&query_current_epoch_threshold(deps.storage)?)?
}
QueryMsg::GetInitialDealers {} => to_binary(&query_initial_dealers(deps.storage)?)?,
QueryMsg::GetRegisteredDealer {
dealer_address,
epoch_id,
} => to_binary(&query_registered_dealer_details(
deps,
dealer_address,
epoch_id,
)?)?,
QueryMsg::GetDealerDetails { dealer_address } => {
to_binary(&query_dealer_details(deps, dealer_address)?)?
}
QueryMsg::GetCurrentDealers { limit, start_after } => {
to_binary(&query_current_dealers_paged(deps, start_after, limit)?)?
}
QueryMsg::GetPastDealers { limit, start_after } => {
to_binary(&query_past_dealers_paged(deps, start_after, limit)?)?
QueryMsg::GetDealerIndices { limit, start_after } => {
to_binary(&query_dealers_indices_paged(deps, start_after, limit)?)?
}
QueryMsg::GetDealingsMetadata {
epoch_id,
+166 -127
View File
@@ -1,36 +1,34 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2022-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::dealers::storage::{self, IndexedDealersMap};
use crate::dealers::storage::{
self, get_dealer_details, get_dealer_index, get_registration_details, DEALERS_INDICES,
EPOCH_DEALERS_MAP,
};
use crate::epoch_state::storage::CURRENT_EPOCH;
use cosmwasm_std::{Deps, Order, StdResult};
use cw_storage_plus::Bound;
use nym_coconut_dkg_common::dealer::{DealerDetailsResponse, DealerType, PagedDealerResponse};
use nym_coconut_dkg_common::dealer::{
DealerDetailsResponse, DealerType, PagedDealerIndexResponse, PagedDealerResponse,
RegisteredDealerDetails,
};
use nym_coconut_dkg_common::types::{DealerDetails, EpochId};
fn query_dealers(
pub fn query_registered_dealer_details(
deps: Deps<'_>,
start_after: Option<String>,
limit: Option<u32>,
underlying_map: &IndexedDealersMap<'_>,
) -> StdResult<PagedDealerResponse> {
let limit = limit
.unwrap_or(storage::DEALERS_PAGE_DEFAULT_LIMIT)
.min(storage::DEALERS_PAGE_MAX_LIMIT) as usize;
dealer_address: String,
epoch_id: Option<EpochId>,
) -> StdResult<RegisteredDealerDetails> {
let addr = deps.api.addr_validate(&dealer_address)?;
let addr = start_after
.map(|addr| deps.api.addr_validate(&addr))
.transpose()?;
let epoch_id = match epoch_id {
Some(epoch_id) => epoch_id,
None => CURRENT_EPOCH.load(deps.storage)?.epoch_id,
};
let start = addr.as_ref().map(Bound::exclusive);
let dealers = underlying_map
.range(deps.storage, start, None, Order::Ascending)
.take(limit)
.map(|res| res.map(|item| item.1))
.collect::<StdResult<Vec<_>>>()?;
let start_next_after = dealers.last().map(|dealer| dealer.address.clone());
Ok(PagedDealerResponse::new(dealers, limit, start_next_after))
Ok(RegisteredDealerDetails {
details: get_registration_details(deps.storage, &addr, epoch_id).ok(),
})
}
pub fn query_dealer_details(
@@ -38,32 +36,93 @@ pub fn query_dealer_details(
dealer_address: String,
) -> StdResult<DealerDetailsResponse> {
let addr = deps.api.addr_validate(&dealer_address)?;
if let Some(current) = storage::current_dealers().may_load(deps.storage, &addr)? {
let current_epoch_id = CURRENT_EPOCH.load(deps.storage)?.epoch_id;
// if the address has registration data for the current epoch, it means it's an active dealer
if let Ok(dealer_details) = get_dealer_details(deps.storage, &addr, current_epoch_id) {
let assigned_index = dealer_details.assigned_index;
return Ok(DealerDetailsResponse::new(
Some(current),
DealerType::Current,
Some(dealer_details),
DealerType::Current { assigned_index },
));
}
if let Some(past) = storage::past_dealers().may_load(deps.storage, &addr)? {
return Ok(DealerDetailsResponse::new(Some(past), DealerType::Past));
// and if has had an assigned index it must have been a dealer at some point in the past
if let Ok(assigned_index) = get_dealer_index(deps.storage, &addr, current_epoch_id) {
return Ok(DealerDetailsResponse::new(
None,
DealerType::Past { assigned_index },
));
}
Ok(DealerDetailsResponse::new(None, DealerType::Unknown))
}
pub fn query_dealers_indices_paged(
deps: Deps<'_>,
start_after: Option<String>,
limit: Option<u32>,
) -> StdResult<PagedDealerIndexResponse> {
let limit = limit
.unwrap_or(storage::DEALER_INDICES_PAGE_DEFAULT_LIMIT)
.min(storage::DEALER_INDICES_PAGE_MAX_LIMIT) as usize;
let addr = start_after
.map(|addr| deps.api.addr_validate(&addr))
.transpose()?;
let start = addr.as_ref().map(Bound::exclusive);
let dealers = DEALERS_INDICES
.range(deps.storage, start, None, Order::Ascending)
.take(limit)
.collect::<StdResult<Vec<_>>>()?;
let start_next_after = dealers.last().map(|dealer| dealer.0.clone());
Ok(PagedDealerIndexResponse::new(dealers, start_next_after))
}
pub fn query_current_dealers_paged(
deps: Deps<'_>,
start_after: Option<String>,
limit: Option<u32>,
) -> StdResult<PagedDealerResponse> {
query_dealers(deps, start_after, limit, &storage::current_dealers())
}
let limit = limit
.unwrap_or(storage::DEALERS_PAGE_DEFAULT_LIMIT)
.min(storage::DEALERS_PAGE_MAX_LIMIT) as usize;
let addr = start_after
.map(|addr| deps.api.addr_validate(&addr))
.transpose()?;
pub fn query_past_dealers_paged(
deps: Deps<'_>,
start_after: Option<String>,
limit: Option<u32>,
) -> StdResult<PagedDealerResponse> {
query_dealers(deps, start_after, limit, &storage::past_dealers())
let start = addr.as_ref().map(Bound::exclusive);
let current_epoch_id = CURRENT_EPOCH.load(deps.storage)?.epoch_id;
let dealers = EPOCH_DEALERS_MAP
.prefix(current_epoch_id)
.range(deps.storage, start, None, Order::Ascending)
.take(limit)
.map(|res| {
res.map(|(address, details)| {
// SAFETY: if we have DealerRegistrationDetails saved, it means we MUST also have its node index
// otherwise some serious invariants have been broken in the contract, and we're in trouble
#[allow(clippy::expect_used)]
let assigned_index = get_dealer_index(deps.storage, &address, current_epoch_id)
.expect("could not retrieve dealer index for a registered dealer");
DealerDetails {
address,
bte_public_key_with_proof: details.bte_public_key_with_proof,
ed25519_identity: details.ed25519_identity,
announce_address: details.announce_address,
assigned_index,
}
})
})
.collect::<StdResult<Vec<_>>>()?;
let start_next_after = dealers.last().map(|dealer| dealer.address.clone());
Ok(PagedDealerResponse::new(dealers, limit, start_next_after))
}
#[cfg(test)]
@@ -71,24 +130,22 @@ pub(crate) mod tests {
use super::*;
use crate::dealers::storage::{DEALERS_PAGE_DEFAULT_LIMIT, DEALERS_PAGE_MAX_LIMIT};
use crate::support::tests::fixtures::dealer_details_fixture;
use crate::support::tests::helpers::init_contract;
use crate::support::tests::helpers::{init_contract, insert_dealer};
use cosmwasm_std::DepsMut;
fn fill_dealers(deps: DepsMut<'_>, mapping: &IndexedDealersMap<'_>, size: usize) {
for n in 0..size {
let dealer_details = dealer_details_fixture(n as u64);
mapping
.save(deps.storage, &dealer_details.address, &dealer_details)
.unwrap();
fn fill_dealers(mut deps: DepsMut<'_>, epoch_id: EpochId, size: usize) {
for assigned_index in 0..size {
let dealer_details = dealer_details_fixture(assigned_index as u64);
insert_dealer(deps.branch(), epoch_id, &dealer_details);
}
}
fn remove_dealers(deps: DepsMut<'_>, mapping: &IndexedDealersMap<'_>, size: usize) {
for n in 0..size {
let dealer_details = dealer_details_fixture(n as u64);
mapping
.remove(deps.storage, &dealer_details.address)
.unwrap();
fn remove_dealers(deps: DepsMut<'_>, epoch_id: EpochId, size: usize) {
for assigned_index in 0..size {
let dealer_details = dealer_details_fixture(assigned_index as u64);
DEALERS_INDICES.remove(deps.storage, &dealer_details.address);
EPOCH_DEALERS_MAP.remove(deps.storage, (epoch_id, &dealer_details.address));
}
}
@@ -96,10 +153,8 @@ pub(crate) mod tests {
fn dealers_empty_on_init() {
let deps = init_contract();
for mapping in [storage::current_dealers(), storage::past_dealers()] {
let page1 = query_dealers(deps.as_ref(), None, None, &mapping).unwrap();
assert_eq!(0, page1.dealers.len() as u32);
}
let page1 = query_current_dealers_paged(deps.as_ref(), None, None).unwrap();
assert_eq!(0, page1.dealers.len() as u32);
}
#[test]
@@ -107,30 +162,26 @@ pub(crate) mod tests {
let mut deps = init_contract();
let limit = 2;
for mapping in [storage::current_dealers(), storage::past_dealers()] {
fill_dealers(deps.as_mut(), &mapping, 1000);
fill_dealers(deps.as_mut(), 0, 1000);
let page1 = query_dealers(deps.as_ref(), None, Option::from(limit), &mapping).unwrap();
assert_eq!(limit, page1.dealers.len() as u32);
let page1 = query_current_dealers_paged(deps.as_ref(), None, Option::from(limit)).unwrap();
assert_eq!(limit, page1.dealers.len() as u32);
remove_dealers(deps.as_mut(), &mapping, 1000);
}
remove_dealers(deps.as_mut(), 0, 1000);
}
#[test]
fn dealers_paged_retrieval_has_default_limit() {
let mut deps = init_contract();
for mapping in [storage::current_dealers(), storage::past_dealers()] {
fill_dealers(deps.as_mut(), &mapping, 1000);
fill_dealers(deps.as_mut(), 0, 1000);
// query without explicitly setting a limit
let page1 = query_dealers(deps.as_ref(), None, None, &mapping).unwrap();
// query without explicitly setting a limit
let page1 = query_current_dealers_paged(deps.as_ref(), None, None).unwrap();
assert_eq!(DEALERS_PAGE_DEFAULT_LIMIT, page1.dealers.len() as u32);
assert_eq!(DEALERS_PAGE_DEFAULT_LIMIT, page1.dealers.len() as u32);
remove_dealers(deps.as_mut(), &mapping, 1000);
}
remove_dealers(deps.as_mut(), 0, 1000);
}
#[test]
@@ -140,18 +191,16 @@ pub(crate) mod tests {
// query with a crazily high limit in an attempt to use too many resources
let crazy_limit = 1000 * DEALERS_PAGE_MAX_LIMIT;
for mapping in [storage::current_dealers(), storage::past_dealers()] {
fill_dealers(deps.as_mut(), &mapping, 1000);
fill_dealers(deps.as_mut(), 0, 1000);
let page1 =
query_dealers(deps.as_ref(), None, Option::from(crazy_limit), &mapping).unwrap();
let page1 =
query_current_dealers_paged(deps.as_ref(), None, Option::from(crazy_limit)).unwrap();
// we default to a decent sized upper bound instead
let expected_limit = DEALERS_PAGE_MAX_LIMIT;
assert_eq!(expected_limit, page1.dealers.len() as u32);
// we default to a decent sized upper bound instead
let expected_limit = DEALERS_PAGE_MAX_LIMIT;
assert_eq!(expected_limit, page1.dealers.len() as u32);
remove_dealers(deps.as_mut(), &mapping, 1000);
}
remove_dealers(deps.as_mut(), 0, 1000);
}
#[test]
@@ -160,62 +209,52 @@ pub(crate) mod tests {
let per_page = 2;
for mapping in [storage::current_dealers(), storage::past_dealers()] {
fill_dealers(deps.as_mut(), &mapping, 1);
let page1 =
query_dealers(deps.as_ref(), None, Option::from(per_page), &mapping).unwrap();
fill_dealers(deps.as_mut(), 0, 1);
let page1 =
query_current_dealers_paged(deps.as_ref(), None, Option::from(per_page)).unwrap();
// page should have 1 result on it
assert_eq!(1, page1.dealers.len());
remove_dealers(deps.as_mut(), &mapping, 1);
}
// page should have 1 result on it
assert_eq!(1, page1.dealers.len());
remove_dealers(deps.as_mut(), 0, 1);
for mapping in [storage::current_dealers(), storage::past_dealers()] {
fill_dealers(deps.as_mut(), &mapping, 2);
// page1 should have 2 results on it
let page1 =
query_dealers(deps.as_ref(), None, Option::from(per_page), &mapping).unwrap();
assert_eq!(2, page1.dealers.len());
remove_dealers(deps.as_mut(), &mapping, 2);
}
fill_dealers(deps.as_mut(), 0, 2);
// page1 should have 2 results on it
let page1 =
query_current_dealers_paged(deps.as_ref(), None, Option::from(per_page)).unwrap();
assert_eq!(2, page1.dealers.len());
remove_dealers(deps.as_mut(), 0, 2);
for mapping in [storage::current_dealers(), storage::past_dealers()] {
fill_dealers(deps.as_mut(), &mapping, 3);
// page1 still has 2 results
let page1 =
query_dealers(deps.as_ref(), None, Option::from(per_page), &mapping).unwrap();
assert_eq!(2, page1.dealers.len());
fill_dealers(deps.as_mut(), 0, 3);
// page1 still has 2 results
let page1 =
query_current_dealers_paged(deps.as_ref(), None, Option::from(per_page)).unwrap();
assert_eq!(2, page1.dealers.len());
// retrieving the next page should start after the last key on this page
let start_after = page1.start_next_after.unwrap();
let page2 = query_dealers(
deps.as_ref(),
Option::from(start_after.to_string()),
Option::from(per_page),
&mapping,
)
.unwrap();
// retrieving the next page should start after the last key on this page
let start_after = page1.start_next_after.unwrap();
let page2 = query_current_dealers_paged(
deps.as_ref(),
Option::from(start_after.to_string()),
Option::from(per_page),
)
.unwrap();
assert_eq!(1, page2.dealers.len());
remove_dealers(deps.as_mut(), &mapping, 3);
}
assert_eq!(1, page2.dealers.len());
remove_dealers(deps.as_mut(), 0, 3);
for mapping in [storage::current_dealers(), storage::past_dealers()] {
fill_dealers(deps.as_mut(), &mapping, 4);
let page1 =
query_dealers(deps.as_ref(), None, Option::from(per_page), &mapping).unwrap();
let start_after = page1.start_next_after.unwrap();
let page2 = query_dealers(
deps.as_ref(),
Option::from(start_after.to_string()),
Option::from(per_page),
&mapping,
)
.unwrap();
fill_dealers(deps.as_mut(), 0, 4);
let page1 =
query_current_dealers_paged(deps.as_ref(), None, Option::from(per_page)).unwrap();
let start_after = page1.start_next_after.unwrap();
let page2 = query_current_dealers_paged(
deps.as_ref(),
Option::from(start_after.to_string()),
Option::from(per_page),
)
.unwrap();
// now we have 2 pages, with 2 results on the second page
assert_eq!(2, page2.dealers.len());
remove_dealers(deps.as_mut(), &mapping, 4);
}
// now we have 2 pages, with 2 results on the second page
assert_eq!(2, page2.dealers.len());
remove_dealers(deps.as_mut(), 0, 4);
}
}
+84 -26
View File
@@ -1,44 +1,102 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use cosmwasm_std::{Addr, StdResult, Storage};
use cw_storage_plus::{Index, IndexList, IndexedMap, Item, UniqueIndex};
use nym_coconut_dkg_common::types::{DealerDetails, NodeIndex};
use crate::error::ContractError;
use crate::Dealer;
use cosmwasm_std::{StdResult, Storage};
use cw_storage_plus::{Item, Map};
use nym_coconut_dkg_common::types::{DealerDetails, DealerRegistrationDetails, EpochId, NodeIndex};
const CURRENT_DEALERS_PK: &str = "crd";
const PAST_DEALERS_PK: &str = "ptd";
const DEALERS_NODE_INDEX_IDX_NAMESPACE: &str = "dni";
pub(crate) const DEALER_INDICES_PAGE_MAX_LIMIT: u32 = 80;
pub(crate) const DEALER_INDICES_PAGE_DEFAULT_LIMIT: u32 = 40;
pub(crate) const DEALERS_PAGE_MAX_LIMIT: u32 = 75;
pub(crate) const DEALERS_PAGE_DEFAULT_LIMIT: u32 = 50;
pub(crate) const DEALERS_PAGE_MAX_LIMIT: u32 = 25;
pub(crate) const DEALERS_PAGE_DEFAULT_LIMIT: u32 = 10;
pub(crate) const NODE_INDEX_COUNTER: Item<NodeIndex> = Item::new("node_index_counter");
pub(crate) type IndexedDealersMap<'a> = IndexedMap<'a, &'a Addr, DealerDetails, DealersIndex<'a>>;
pub(crate) const DEALERS_INDICES: Map<Dealer, NodeIndex> = Map::new("dealer_index");
pub(crate) struct DealersIndex<'a> {
pub(crate) node_index: UniqueIndex<'a, NodeIndex, DealerDetails>,
}
pub(crate) const EPOCH_DEALERS_MAP: Map<(EpochId, Dealer), DealerRegistrationDetails> =
Map::new("epoch_dealers");
impl<'a> IndexList<DealerDetails> for DealersIndex<'a> {
fn get_indexes(&'_ self) -> Box<dyn Iterator<Item = &'_ dyn Index<DealerDetails>> + '_> {
let v: Vec<&dyn Index<DealerDetails>> = vec![&self.node_index];
Box::new(v.into_iter())
/// Attempts to retrieve a pre-assign node index associated with given dealer.
/// If one doesn't exist, a new one is assigned.
pub(crate) fn get_or_assign_index(
storage: &mut dyn Storage,
dealer: Dealer,
) -> StdResult<NodeIndex> {
if let Some(index) = DEALERS_INDICES.may_load(storage, dealer)? {
return Ok(index);
}
let index = next_node_index(storage)?;
DEALERS_INDICES.save(storage, dealer, &index)?;
Ok(index)
}
pub(crate) fn current_dealers<'a>() -> IndexedDealersMap<'a> {
let indexes = DealersIndex {
node_index: UniqueIndex::new(|d| d.assigned_index, DEALERS_NODE_INDEX_IDX_NAMESPACE),
};
IndexedMap::new(CURRENT_DEALERS_PK, indexes)
pub(crate) fn save_dealer_details_if_not_a_dealer(
storage: &mut dyn Storage,
dealer: Dealer,
epoch_id: EpochId,
details: DealerRegistrationDetails,
) -> Result<(), ContractError> {
if EPOCH_DEALERS_MAP.has(storage, (epoch_id, dealer)) {
return Err(ContractError::AlreadyADealer);
}
EPOCH_DEALERS_MAP.save(storage, (epoch_id, dealer), &details)?;
Ok(())
}
pub(crate) fn past_dealers<'a>() -> IndexedDealersMap<'a> {
let indexes = DealersIndex {
node_index: UniqueIndex::new(|d| d.assigned_index, DEALERS_NODE_INDEX_IDX_NAMESPACE),
};
IndexedMap::new(PAST_DEALERS_PK, indexes)
pub(crate) fn ensure_dealer(
storage: &dyn Storage,
dealer: Dealer,
epoch_id: EpochId,
) -> Result<(), ContractError> {
if !is_dealer(storage, dealer, epoch_id) {
return Err(ContractError::NotADealer { epoch_id });
}
Ok(())
}
pub(crate) fn is_dealer(storage: &dyn Storage, dealer: Dealer, epoch_id: EpochId) -> bool {
EPOCH_DEALERS_MAP.has(storage, (epoch_id, dealer))
}
// note: `epoch_id` is provided purely for the error message. it has nothing to do with storage retrieval
pub(crate) fn get_dealer_index(
storage: &dyn Storage,
dealer: Dealer,
epoch_id: EpochId,
) -> Result<NodeIndex, ContractError> {
DEALERS_INDICES
.may_load(storage, dealer)?
.ok_or(ContractError::NotADealer { epoch_id })
}
pub(crate) fn get_registration_details(
storage: &dyn Storage,
dealer: Dealer,
epoch_id: EpochId,
) -> Result<DealerRegistrationDetails, ContractError> {
EPOCH_DEALERS_MAP
.may_load(storage, (epoch_id, dealer))?
.ok_or(ContractError::NotADealer { epoch_id })
}
pub(crate) fn get_dealer_details(
storage: &dyn Storage,
dealer: Dealer,
epoch_id: EpochId,
) -> Result<DealerDetails, ContractError> {
let registration_details = get_registration_details(storage, dealer, epoch_id)?;
let assigned_index = get_dealer_index(storage, dealer, epoch_id)?;
Ok(DealerDetails {
address: dealer.to_owned(),
bte_public_key_with_proof: registration_details.bte_public_key_with_proof,
ed25519_identity: registration_details.ed25519_identity,
announce_address: registration_details.announce_address,
assigned_index,
})
}
pub(crate) fn next_node_index(store: &mut dyn Storage) -> StdResult<NodeIndex> {
@@ -1,34 +1,24 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2022-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::dealers::storage as dealers_storage;
use crate::epoch_state::storage::INITIAL_REPLACEMENT_DATA;
use crate::dealers::storage::{
get_or_assign_index, is_dealer, save_dealer_details_if_not_a_dealer,
};
use crate::epoch_state::storage::CURRENT_EPOCH;
use crate::epoch_state::utils::check_epoch_state;
use crate::error::ContractError;
use crate::state::storage::STATE;
use cosmwasm_std::{Addr, DepsMut, MessageInfo, Response};
use nym_coconut_dkg_common::types::{DealerDetails, EncodedBTEPublicKeyWithProof, EpochState};
use crate::Dealer;
use cosmwasm_std::{Deps, DepsMut, MessageInfo, Response, StdResult};
use nym_coconut_dkg_common::dealer::DealerRegistrationDetails;
use nym_coconut_dkg_common::types::{EncodedBTEPublicKeyWithProof, EpochState};
// currently we only require that
// a) it's part of the signer group
// b) it isn't already a dealer
fn verify_dealer(deps: DepsMut<'_>, dealer: &Addr, resharing: bool) -> Result<(), ContractError> {
if dealers_storage::current_dealers()
.may_load(deps.storage, dealer)?
.is_some()
{
return Err(ContractError::AlreadyADealer);
}
fn ensure_group_member(deps: Deps, dealer: Dealer) -> Result<(), ContractError> {
let state = STATE.load(deps.storage)?;
let height = if resharing {
Some(INITIAL_REPLACEMENT_DATA.load(deps.storage)?.initial_height)
} else {
None
};
state
.group_addr
.is_voting_member(&deps.querier, dealer, height)?
.is_voting_member(&deps.querier, dealer, None)?
.ok_or(ContractError::Unauthorized {})?;
Ok(())
@@ -37,42 +27,57 @@ fn verify_dealer(deps: DepsMut<'_>, dealer: &Addr, resharing: bool) -> Result<()
// future optimisation:
// for a recurring dealer just let it refresh the keys without having to do all the storage operations
pub fn try_add_dealer(
mut deps: DepsMut<'_>,
deps: DepsMut<'_>,
info: MessageInfo,
bte_key_with_proof: EncodedBTEPublicKeyWithProof,
identity_key: String,
announce_address: String,
resharing: bool,
) -> Result<Response, ContractError> {
let epoch = CURRENT_EPOCH.load(deps.storage)?;
check_epoch_state(deps.storage, EpochState::PublicKeySubmission { resharing })?;
verify_dealer(deps.branch(), &info.sender, resharing)?;
// make sure this potential dealer actually belong to the group
ensure_group_member(deps.as_ref(), &info.sender)?;
// if it was already a dealer in the past, assign the same node index
let node_index = if let Some(prior_details) =
dealers_storage::past_dealers().may_load(deps.storage, &info.sender)?
{
// since this dealer is going to become active now, remove it from the past dealers
dealers_storage::past_dealers().replace(
deps.storage,
&info.sender,
None,
Some(&prior_details),
)?;
prior_details.assigned_index
} else {
dealers_storage::next_node_index(deps.storage)?
};
let node_index = get_or_assign_index(deps.storage, &info.sender)?;
// save the dealer into the storage
let dealer_details = DealerDetails {
address: info.sender.clone(),
// save the dealer into the storage (if it hasn't already been saved)
let dealer_details = DealerRegistrationDetails {
bte_public_key_with_proof: bte_key_with_proof,
ed25519_identity: identity_key,
announce_address,
assigned_index: node_index,
};
dealers_storage::current_dealers().save(deps.storage, &info.sender, &dealer_details)?;
save_dealer_details_if_not_a_dealer(
deps.storage,
&info.sender,
epoch.epoch_id,
dealer_details,
)?;
// check if it's a resharing dealer
let is_resharing_dealer = resharing
&& is_dealer(
deps.storage,
&info.sender,
epoch
.epoch_id
.checked_sub(1)
.expect("epoch invariant broken: resharing during 0th epoch"),
);
// increment the number of registered dealers
CURRENT_EPOCH.update(deps.storage, |epoch| -> StdResult<_> {
let mut updated_epoch = epoch;
updated_epoch.state_progress.registered_dealers += 1;
if is_resharing_dealer {
updated_epoch.state_progress.registered_resharing_dealers += 1;
}
Ok(updated_epoch)
})?;
Ok(Response::new().add_attribute("node_index", node_index.to_string()))
}
@@ -80,63 +85,12 @@ pub fn try_add_dealer(
#[cfg(test)]
pub(crate) mod tests {
use super::*;
use crate::dealers::storage::current_dealers;
use crate::epoch_state::transactions::{advance_epoch_state, try_initiate_dkg};
use crate::support::tests::fixtures::dealer_details_fixture;
use crate::epoch_state::transactions::{try_advance_epoch_state, try_initiate_dkg};
use crate::support::tests::helpers;
use crate::support::tests::helpers::{add_fixture_dealer, ADMIN_ADDRESS, GROUP_MEMBERS};
use crate::support::tests::helpers::{add_fixture_dealer, ADMIN_ADDRESS};
use cosmwasm_std::testing::{mock_env, mock_info};
use cw4::Member;
use nym_coconut_dkg_common::types::{InitialReplacementData, TimeConfiguration};
use rusty_fork::rusty_fork_test;
rusty_fork_test! {
#[test]
fn verification() {
let mut deps = helpers::init_contract();
let new_dealer = Addr::unchecked("new_dealer");
let details1 = dealer_details_fixture(1);
let details2 = dealer_details_fixture(2);
let details3 = dealer_details_fixture(3);
current_dealers()
.save(deps.as_mut().storage, &details1.address, &details1)
.unwrap();
let err = verify_dealer(deps.as_mut(), &details1.address, false).unwrap_err();
assert_eq!(err, ContractError::AlreadyADealer);
INITIAL_REPLACEMENT_DATA
.save(
deps.as_mut().storage,
&InitialReplacementData {
initial_dealers: vec![details1.address, details2.address, details3.address],
initial_height: 1,
},
)
.unwrap();
let err = verify_dealer(deps.as_mut(), &new_dealer, false).unwrap_err();
assert_eq!(err, ContractError::Unauthorized);
GROUP_MEMBERS.lock().unwrap().push((
Member {
addr: new_dealer.to_string(),
weight: 10,
},
2,
));
verify_dealer(deps.as_mut(), &new_dealer, false).unwrap();
let err = verify_dealer(deps.as_mut(), &new_dealer, true).unwrap_err();
assert_eq!(err, ContractError::Unauthorized);
INITIAL_REPLACEMENT_DATA
.update::<_, ContractError>(deps.as_mut().storage, |mut data| {
data.initial_height = 2;
Ok(data)
})
.unwrap();
verify_dealer(deps.as_mut(), &new_dealer, true).unwrap();
}
}
use cosmwasm_std::Addr;
use nym_coconut_dkg_common::types::TimeConfiguration;
#[test]
fn invalid_state() {
@@ -156,7 +110,7 @@ pub(crate) mod tests {
.plus_seconds(TimeConfiguration::default().public_key_submission_time_secs);
add_fixture_dealer(deps.as_mut());
advance_epoch_state(deps.as_mut(), env).unwrap();
try_advance_epoch_state(deps.as_mut(), env).unwrap();
let ret = try_add_dealer(
deps.as_mut(),
@@ -2,15 +2,14 @@
// SPDX-License-Identifier: Apache-2.0
use crate::error::ContractError;
use cosmwasm_std::{Addr, Storage};
use crate::Dealer;
use cosmwasm_std::Storage;
use cw_storage_plus::{Key, Map, Path, PrimaryKey};
use nym_coconut_dkg_common::dealing::{DealingMetadata, PartialContractDealing};
use nym_coconut_dkg_common::types::{
ChunkIndex, ContractSafeBytes, DealingIndex, EpochId, PartialContractDealingData,
};
type Dealer<'a> = &'a Addr;
/// Metadata for a dealing for given `EpochId`, submitted by particular `Dealer` for given `DealingIndex`.
pub(crate) const DEALINGS_METADATA: Map<(EpochId, Dealer, DealingIndex), DealingMetadata> =
Map::new("dealings_metadata");
@@ -180,7 +179,7 @@ impl StoredDealing {
pub(crate) fn unchecked_all_entries(
storage: &dyn Storage,
) -> Vec<(
(EpochId, Addr, (DealingIndex, ChunkIndex)),
(EpochId, cosmwasm_std::Addr, (DealingIndex, ChunkIndex)),
PartialContractDealingData,
)> {
use cw_storage_plus::KeyDeserialize;
@@ -210,6 +209,7 @@ impl StoredDealing {
mod tests {
use super::*;
use crate::support::tests::helpers::init_contract;
use cosmwasm_std::Addr;
use cw_storage_plus::Bound;
use std::collections::HashMap;
@@ -1,11 +1,11 @@
// Copyright 2022-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::dealers::storage as dealers_storage;
use crate::dealers::storage::ensure_dealer;
use crate::dealings::storage::{
metadata_exists, must_read_metadata, store_metadata, StoredDealing,
};
use crate::epoch_state::storage::{CURRENT_EPOCH, INITIAL_REPLACEMENT_DATA};
use crate::epoch_state::storage::CURRENT_EPOCH;
use crate::epoch_state::utils::check_epoch_state;
use crate::error::ContractError;
use crate::state::storage::STATE;
@@ -13,31 +13,25 @@ use cosmwasm_std::{Addr, DepsMut, Env, MessageInfo, Response, Storage};
use nym_coconut_dkg_common::dealing::{
DealingChunkInfo, DealingMetadata, PartialContractDealing, MAX_DEALING_CHUNKS,
};
use nym_coconut_dkg_common::types::{ChunkIndex, DealingIndex, EpochState};
use nym_coconut_dkg_common::types::{ChunkIndex, DealingIndex, EpochId, EpochState};
// make sure the epoch is in the dealing exchange and the message sender is a valid dealer for this epoch
fn ensure_permission(
storage: &dyn Storage,
sender: &Addr,
current_epoch_id: EpochId,
resharing: bool,
) -> Result<(), ContractError> {
check_epoch_state(storage, EpochState::DealingExchange { resharing })?;
// ensure the sender is a dealer
if dealers_storage::current_dealers()
.may_load(storage, sender)?
.is_none()
{
return Err(ContractError::NotADealer);
}
if resharing
&& !INITIAL_REPLACEMENT_DATA
.load(storage)?
.initial_dealers
.contains(sender)
{
return Err(ContractError::NotAnInitialDealer);
// ensure the sender is a dealer for this epoch
ensure_dealer(storage, sender, current_epoch_id)?;
// if we're in resharing, make sure this sender has also been a dealer in the previous epoch
if resharing {
ensure_dealer(storage, sender, current_epoch_id.saturating_sub(1))?;
}
Ok(())
}
@@ -48,10 +42,10 @@ pub fn try_submit_dealings_metadata(
chunks: Vec<DealingChunkInfo>,
resharing: bool,
) -> Result<Response, ContractError> {
ensure_permission(deps.storage, &info.sender, resharing)?;
let state = STATE.load(deps.storage)?;
let epoch = CURRENT_EPOCH.load(deps.storage)?;
let state = STATE.load(deps.storage)?;
ensure_permission(deps.storage, &info.sender, epoch.epoch_id, resharing)?;
// don't allow overwriting existing metadata
if metadata_exists(deps.storage, epoch.epoch_id, &info.sender, dealing_index) {
@@ -139,11 +133,11 @@ pub fn try_commit_dealings_chunk(
env: Env,
info: MessageInfo,
chunk: PartialContractDealing,
resharing: bool,
) -> Result<Response, ContractError> {
ensure_permission(deps.storage, &info.sender, resharing)?;
// note: checking permissions is implicit as if the metadata exists,
// the sender must have been allowed to submit it
let epoch = CURRENT_EPOCH.load(deps.storage)?;
let mut epoch = CURRENT_EPOCH.load(deps.storage)?;
// read meta
let mut metadata = must_read_metadata(
@@ -199,6 +193,13 @@ pub fn try_commit_dealings_chunk(
// store the dealing
StoredDealing::save(deps.storage, epoch.epoch_id, &info.sender, chunk);
// this is less than ideal since we have to iterate through all the chunks, but realistically,
// there won't be a lot of them
if metadata.is_complete() {
epoch.state_progress.submitted_dealings += 1;
CURRENT_EPOCH.save(deps.storage, &epoch)?;
}
Ok(Response::new())
}
@@ -206,18 +207,14 @@ pub fn try_commit_dealings_chunk(
pub(crate) mod tests {
use super::*;
use crate::epoch_state::storage::CURRENT_EPOCH;
use crate::epoch_state::transactions::{advance_epoch_state, try_initiate_dkg};
use crate::support::tests::fixtures::{
dealer_details_fixture, dealing_metadata_fixture, partial_dealing_fixture,
};
use crate::epoch_state::transactions::{try_advance_epoch_state, try_initiate_dkg};
use crate::support::tests::fixtures::{dealing_metadata_fixture, partial_dealing_fixture};
use crate::support::tests::helpers;
use crate::support::tests::helpers::{add_fixture_dealer, ADMIN_ADDRESS};
use crate::support::tests::helpers::{add_current_dealer, re_register_dealer, ADMIN_ADDRESS};
use cosmwasm_std::testing::{mock_env, mock_info};
use cosmwasm_std::Addr;
use nym_coconut_dkg_common::dealer::DealerDetails;
use nym_coconut_dkg_common::types::{
ContractSafeBytes, InitialReplacementData, TimeConfiguration,
};
use nym_coconut_dkg_common::types::{ContractSafeBytes, TimeConfiguration};
#[test]
fn invalid_commit_dealing_chunk() {
@@ -227,41 +224,27 @@ pub(crate) mod tests {
let owner = Addr::unchecked("owner1");
let info = mock_info(owner.as_str(), &[]);
let dealing = partial_dealing_fixture();
let chunk = partial_dealing_fixture();
let ret = try_commit_dealings_chunk(
deps.as_mut(),
env.clone(),
info.clone(),
dealing.clone(),
false,
)
.unwrap_err();
// no dealing metadata
let ret =
try_commit_dealings_chunk(deps.as_mut(), env.clone(), info.clone(), chunk.clone())
.unwrap_err();
assert_eq!(
ret,
ContractError::IncorrectEpochState {
current_state: EpochState::PublicKeySubmission { resharing: false }.to_string(),
expected_state: EpochState::DealingExchange { resharing: false }.to_string()
ContractError::UnavailableDealingMetadata {
epoch_id: 0,
dealer: info.sender.clone(),
dealing_index: chunk.dealing_index,
}
);
// add dealing metadata
env.block.time = env
.block
.time
.plus_seconds(TimeConfiguration::default().public_key_submission_time_secs);
add_fixture_dealer(deps.as_mut());
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
let ret = try_commit_dealings_chunk(
deps.as_mut(),
env.clone(),
info.clone(),
dealing.clone(),
false,
)
.unwrap_err();
assert_eq!(ret, ContractError::NotADealer);
try_advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
let dealer_details = DealerDetails {
address: owner.clone(),
bte_public_key_with_proof: String::new(),
@@ -269,60 +252,12 @@ pub(crate) mod tests {
announce_address: String::new(),
assigned_index: 1,
};
dealers_storage::current_dealers()
.save(deps.as_mut().storage, &owner, &dealer_details)
.unwrap();
add_current_dealer(deps.as_mut(), &dealer_details);
// assume we're in resharing mode
CURRENT_EPOCH
.update::<_, ContractError>(deps.as_mut().storage, |mut epoch| {
epoch.state = EpochState::DealingExchange { resharing: true };
Ok(epoch)
})
.unwrap();
INITIAL_REPLACEMENT_DATA
.save(
deps.as_mut().storage,
&InitialReplacementData {
initial_dealers: vec![],
initial_height: 1,
},
)
.unwrap();
let ret = try_commit_dealings_chunk(
deps.as_mut(),
env.clone(),
info.clone(),
dealing.clone(),
true,
)
.unwrap_err();
assert_eq!(ret, ContractError::NotAnInitialDealer);
INITIAL_REPLACEMENT_DATA
.update::<_, ContractError>(deps.as_mut().storage, |mut data| {
data.initial_dealers = vec![dealer_details_fixture(1).address];
Ok(data)
})
.unwrap();
// back to 'normal' mode
CURRENT_EPOCH
.update::<_, ContractError>(deps.as_mut().storage, |mut epoch| {
epoch.state = EpochState::DealingExchange { resharing: false };
Ok(epoch)
})
.unwrap();
// TODO: test case: no metadata
//
//
// add dealing metadata
try_submit_dealings_metadata(
deps.as_mut(),
info.clone(),
0,
chunk.dealing_index,
dealing_metadata_fixture(),
false,
)
@@ -338,7 +273,6 @@ pub(crate) mod tests {
chunk_index: 42,
data: ContractSafeBytes(vec![1, 2, 3]),
},
false,
)
.unwrap_err();
assert_eq!(
@@ -352,24 +286,14 @@ pub(crate) mod tests {
);
// 'good' dealing
let ret = try_commit_dealings_chunk(
deps.as_mut(),
env.clone(),
info.clone(),
dealing.clone(),
false,
);
let ret =
try_commit_dealings_chunk(deps.as_mut(), env.clone(), info.clone(), chunk.clone());
assert!(ret.is_ok());
// duplicate dealing
let ret = try_commit_dealings_chunk(
deps.as_mut(),
env.clone(),
info.clone(),
dealing.clone(),
false,
)
.unwrap_err();
let ret =
try_commit_dealings_chunk(deps.as_mut(), env.clone(), info.clone(), chunk.clone())
.unwrap_err();
assert_eq!(
ret,
ContractError::DealingChunkAlreadyCommitted {
@@ -388,6 +312,7 @@ pub(crate) mod tests {
Ok(epoch)
})
.unwrap();
re_register_dealer(deps.as_mut(), &info.sender);
try_submit_dealings_metadata(
deps.as_mut(),
@@ -398,7 +323,7 @@ pub(crate) mod tests {
)
.unwrap();
let ret = try_commit_dealings_chunk(deps.as_mut(), env, info, dealing.clone(), false);
let ret = try_commit_dealings_chunk(deps.as_mut(), env, info, chunk.clone());
assert!(ret.is_ok());
}
}

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