Compare commits

...

116 Commits

Author SHA1 Message Date
Tommy Verrall 8d8d436bf0 Update sandbox.env 2024-01-16 12:24:28 +01:00
Jędrzej Stuczyński f4e42d74c4 Merge pull request #4326 from nymtech/bugfix/nymvisor-windows-ci
Bugfix/nymvisor windows ci
2024-01-16 11:36:47 +01:00
Pierre Dommerc 9de1e6e844 chore: remove nym-vpn projects (#4327) 2024-01-16 11:10:26 +01:00
Tommy Verrall 713df39106 Merge pull request #4324 from nymtech/feature/bity-sell
Update text on buy page to Buy/Sell
2024-01-15 16:55:18 +01:00
Jędrzej Stuczyński 5ec4674f9b even more target locking 2024-01-15 15:45:19 +00:00
Jędrzej Stuczyński 58c5092e80 added a dummy main for non unix targets 2024-01-15 15:18:52 +00:00
Jędrzej Stuczyński 677ad54a7f used global unix cfg 2024-01-15 14:54:49 +00:00
fmtabbara 5e17c3199f update text on buy page to buy/sell 2024-01-15 14:32:39 +00:00
Tommy Verrall 3c3a34ec0f Merge pull request #4322 from nymtech/update-localnet-script
Check that localnet is running fromt the scripts dir
2024-01-15 13:43:27 +01:00
Tommy Verrall e9b442e634 Merge pull request #4303 from nymtech/dependabot/npm_and_yarn/follow-redirects-1.15.4
Bump follow-redirects from 1.15.3 to 1.15.4
2024-01-15 13:42:55 +01:00
Tommy Verrall ca02e2bce1 Merge pull request #4310 from nymtech/dependabot/npm_and_yarn/wasm/node-tester/internal-dev/follow-redirects-1.15.4
Bump follow-redirects from 1.15.2 to 1.15.4 in /wasm/node-tester/internal-dev
2024-01-15 13:42:36 +01:00
Tommy Verrall 985ab43fe9 Merge pull request #4306 from nymtech/dependabot/npm_and_yarn/clients/native/examples/js-examples/websocket/follow-redirects-1.15.4
Bump follow-redirects from 1.14.9 to 1.15.4 in /clients/native/examples/js-examples/websocket
2024-01-15 13:42:12 +01:00
dependabot[bot] f4dad37b14 Bump follow-redirects from 1.15.2 to 1.15.4 in /wasm/client/internal-dev (#4311)
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.2 to 1.15.4.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.2...v1.15.4)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-15 11:52:56 +01:00
durch 08a57fa8df Check that we are running fromt the scripts dir 2024-01-15 10:25:51 +01:00
Tommy Verrall 98e88e2f11 Merge pull request #4253 from nymtech/feature/validator-rewarder
Feature/validator rewarder
2024-01-15 09:25:26 +01:00
Tommy Verrall 2a589b049c Merge pull request #4316 from nymtech/feature/bump_vergen
Bump vergen version to latest
2024-01-12 14:44:09 +00:00
Tommy Verrall 9bcd56e254 Merge pull request #4312 from nymtech/dependabot/npm_and_yarn/wasm/mix-fetch/internal-dev/follow-redirects-1.15.4
Bump follow-redirects from 1.15.2 to 1.15.4 in /wasm/mix-fetch/internal-dev
2024-01-12 12:58:55 +00:00
Bogdan-Ștefan Neacşu 025ba2ec5f Bump vergen version to latest 2024-01-12 14:46:37 +02:00
Tommy Verrall 1a1d11c447 Merge pull request #4247 from nymtech/fix/test-route-construction
Only use good nodes for test route construction
2024-01-12 12:28:21 +00:00
serinko 958bc2ae9a [DOCs]: NymVPN alpha - pages for events (#4309)
* initialise new nymvpn guide pages

* docs: nymvpn guide, testing, troubleshooting and faq

* add faq

* remove todo points

* resolve review comments

* change landing page order

* incorporate huxis user feedback

* add binaries link

* change menu naming -> upper case

* final version for cryptotalk demo

* change naming convention client -> cli
2024-01-11 16:34:25 +00:00
Jon Häggblad d3d5cc3424 Don't build wg deps where it's not supported (#4305)
* Don't build wg deps where it's not supported

* Fix compilation

* Another fix

* More fixes

* another fix
2024-01-11 13:53:57 +01:00
Pierre Dommerc a834bb17f8 chore(vpn-desktop): bump version 0.0.2 (#4314)
* bump version

* add nym-vpn html icon
2024-01-11 12:18:33 +01:00
Zane Schepke cee6d8c308 Merge pull request #4298 from nymtech/fix/vpndesktop_ui_divergences
fix(vpn-desktop-ui): fix design divergences
2024-01-11 06:03:54 -05:00
Zane Schepke 142eaf533b fix macos artifact 2024-01-11 06:01:37 -05:00
Jon Häggblad 42365769f8 Update Cargo.lock for nym-vpn-ui (#4313) 2024-01-11 09:52:44 +01:00
pierre 9fc822298f fix overflow padding
fix settings screens
remove spin animation on connect button
use cursor progress
add select-none on some elements
2024-01-11 06:56:01 +01:00
dependabot[bot] cfb9f3d356 Bump follow-redirects in /wasm/mix-fetch/internal-dev
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.2 to 1.15.4.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.2...v1.15.4)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-10 23:47:45 +00:00
dependabot[bot] ea386b6145 Bump follow-redirects in /wasm/node-tester/internal-dev
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.2 to 1.15.4.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.2...v1.15.4)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-10 23:47:41 +00:00
pierre 75221cfd3e wip 2024-01-10 18:25:35 +01:00
pierre 50f71a21e0 fix various design divergences 2024-01-10 18:16:55 +01:00
pierre cfa9ecfcc4 fix various design divergences 2024-01-10 17:52:10 +01:00
Jon Häggblad 549b33cd91 Fix clippy in beta toolchain (#4299)
* clippy::lines_filter_map_ok

* clippy::ineffective-open-options
2024-01-10 15:29:05 +01:00
Tommy Verrall be46da9906 Merge pull request #4302 from nymtech/dependabot/npm_and_yarn/nym-api/tests/follow-redirects-1.15.4
Bump follow-redirects from 1.15.1 to 1.15.4 in /nym-api/tests
2024-01-10 13:54:42 +00:00
Tommy Verrall 66d123312f Merge pull request #4301 from nymtech/dependabot/npm_and_yarn/testnet-faucet/follow-redirects-1.15.4
Bump follow-redirects from 1.14.8 to 1.15.4 in /testnet-faucet
2024-01-10 13:54:25 +00:00
Tommy Verrall a29f3db5fb Merge pull request #4304 from nymtech/jon/update-openssl
cargo update -p openssl
2024-01-10 13:53:13 +00:00
dependabot[bot] d0fa1792e2 Bump follow-redirects in /clients/native/examples/js-examples/websocket
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.14.9 to 1.15.4.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.14.9...v1.15.4)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-09 19:35:21 +00:00
Jon Häggblad f452d97979 cargo update -p openssl 2024-01-09 14:14:42 +01:00
Jędrzej Stuczyński 9b2d224e54 clippy 2024-01-09 11:03:05 +01:00
Jędrzej Stuczyński 3f504d7500 fixed builds of other binaries 2024-01-09 11:03:05 +01:00
Jędrzej Stuczyński 67701290d3 cargo fmt 2024-01-09 11:03:05 +01:00
Jędrzej Stuczyński 22541f5a79 smoothing some rough edges 2024-01-09 11:03:05 +01:00
Jędrzej Stuczyński bd8f666405 config template 2024-01-09 11:03:05 +01:00
Jędrzej Stuczyński a3c1541660 actually sending the rewards 2024-01-09 11:03:05 +01:00
Jędrzej Stuczyński 6c1d14a4bc clippy 2024-01-09 11:03:05 +01:00
Jędrzej Stuczyński defd148d73 cli arguments + balance check 2024-01-09 11:03:05 +01:00
Jędrzej Stuczyński 162ff71814 more granual configs 2024-01-09 11:03:05 +01:00
Jędrzej Stuczyński 5c864cb055 monitoring done 2024-01-09 11:03:05 +01:00
Jędrzej Stuczyński cfc13671a4 most of the issuance monitoring logic 2024-01-09 11:03:05 +01:00
Jędrzej Stuczyński 668a255e0d issuance score calculation logic 2024-01-09 11:03:05 +01:00
Jędrzej Stuczyński 31d8352621 persisting rewarding data 2024-01-09 11:03:05 +01:00
Jędrzej Stuczyński 4f6fe88b4c starting on persistence 2024-01-09 11:03:05 +01:00
Jędrzej Stuczyński 397ef8723d block signing related code 2024-01-09 11:03:05 +01:00
Jędrzej Stuczyński 2c2223947c wip 2024-01-09 11:03:04 +01:00
Jędrzej Stuczyński e1c0638f1e getting signing rewards 2024-01-09 11:02:41 +01:00
Jędrzej Stuczyński 13fa2119fc wip 2024-01-09 11:02:41 +01:00
Jędrzej Stuczyński 8f24e8f208 starting to integrate scraper into the rewarder 2024-01-09 11:02:40 +01:00
Jędrzej Stuczyński 37dd20ded1 wip2 2024-01-09 11:01:17 +01:00
Jędrzej Stuczyński b4b32bb907 wip 2024-01-09 10:59:52 +01:00
dependabot[bot] c1718154cb Bump follow-redirects from 1.15.3 to 1.15.4
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.3 to 1.15.4.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.3...v1.15.4)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-09 09:32:28 +00:00
dependabot[bot] 3eb7710a12 Bump follow-redirects from 1.15.1 to 1.15.4 in /nym-api/tests
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.1 to 1.15.4.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.1...v1.15.4)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-09 09:30:41 +00:00
dependabot[bot] d1c9251904 Bump follow-redirects from 1.14.8 to 1.15.4 in /testnet-faucet
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.14.8 to 1.15.4.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.14.8...v1.15.4)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-09 09:30:36 +00:00
Tommy Verrall 62894e2b40 Merge pull request #4300 from nymtech/jon/update-lock
Update Cargo.lock files
2024-01-09 09:29:23 +00:00
Jon Häggblad bd7fd1a61c Update Cargo.lock files 2024-01-09 10:12:02 +01:00
pierre 28f118c73b provide lato font 2024-01-08 17:22:25 +01:00
Tommy Verrall 65699736ee Merge pull request #4018 from nymtech/feature/nym-node-api-tests
Initial step to adding nym-node functional tests
2024-01-08 15:49:37 +00:00
benedettadavico caba594c95 update workflow file 2024-01-08 16:23:57 +01:00
benedettadavico 746d52d017 Merge remote-tracking branch 'origin/develop' into feature/nym-node-api-tests 2024-01-08 16:11:38 +01:00
Zane Schepke e323c05b33 Merge pull request #4297 from nymtech/chore/vpndesktop_update_deps
chore(vpn_desktop): update js dependencies
2024-01-08 09:42:34 -05:00
pierre abdf071448 update js dependencies 2024-01-08 15:35:37 +01:00
Zane Schepke a6f2b0e8c8 feat(vpn_desktop): add entry node location selector support (#4296)
* entry selector read from state

* remove entry_node_location from config

---------

Co-authored-by: pierre <dommerc.pierre@gmail.com>
2024-01-08 12:17:53 +01:00
Tommy Verrall f78b4a1742 Merge pull request #4242 from nymtech/feature/basic-scraper
Feature/basic scraper
2024-01-08 11:04:09 +00:00
Tommy Verrall d4fde7b788 Merge pull request #4194 from nymtech/dependabot/cargo/nym-vpn/ui/src-tauri/openssl-0.10.60
Bump openssl from 0.10.59 to 0.10.60 in /nym-vpn/ui/src-tauri
2024-01-08 10:02:19 +00:00
Tommy Verrall d5a2952ef9 Merge pull request #4255 from nymtech/jon/add-cargo-about
Add cargo-about files
2024-01-08 09:59:16 +00:00
Tommy Verrall 206b6ba742 Merge pull request #4274 from nymtech/dependabot/npm_and_yarn/tauri-apps/cli-1.5.6
Bump @tauri-apps/cli from 1.5.2 to 1.5.6
2024-01-08 09:58:13 +00:00
Zane Schepke 67449b1c19 Merge pull request #4295 from nymtech/chore/vpndesktop_bump_version
chore(vpn_desktop): bump version
2024-01-05 12:37:49 -05:00
pierre 8d6e5d4fff bump version 2024-01-05 18:30:19 +01:00
Zane Schepke f448355b35 Merge pull request #4294 from nymtech/feat/vpnapp_overflow_scroll
fix(vpn_desktop): fix vertical overflow
2024-01-05 11:15:52 -05:00
pierre cf78af6b98 redo cursor pointer 2024-01-05 17:01:18 +01:00
pierre d041cfe5c5 disalbe settings not implemented yet 2024-01-05 16:55:33 +01:00
pierre 85b0b6d73d fix vertical overflow 2024-01-05 13:44:01 +01:00
Zane Schepke 685019884f feat(vpn-desktop): support screen UI and routing (#4279)
* add base setting screen and routing

* add version

* add logs icons

* fix comments

* refactor to nested routes

* add settingslayout

* remove useless note

* fix typecheck

* fix exports and optionals

* fix icon

---------

Co-authored-by: pierre <dommerc.pierre@gmail.com>
2024-01-04 15:55:46 +01:00
Tommy Verrall cd9d4eebd3 Merge pull request #4258 from nymtech/jon/exit-policy-for-portless
Apply exit policy check on destination ips without port
2024-01-03 09:55:09 +00:00
Jędrzej Stuczyński 78610c7e28 Chore/nym api commands (#4225)
* created run and init commands for nym-api + nasty mnemonic workaround

* removed dead code

* cargo fmt + clippy

* fixed key loader

* made announce address optional and removed the nonobvious fallback value

* clippy

* removed contract addresses from config template

* fixed conflicting arguments macro

* post-rebasing fixes: applied client macro to balance method
2024-01-03 10:49:10 +01:00
Tommy Verrall 496870b5f6 Merge pull request #4275 from nymtech/dependabot/npm_and_yarn/testnet-faucet/msgpackr-1.10.1
Bump msgpackr from 1.5.5 to 1.10.1 in /testnet-faucet
2024-01-03 08:55:42 +00:00
serinko 7eac5e3529 url correction (#4281) 2024-01-02 14:22:47 +00:00
Jędrzej Stuczyński 4ad4072709 naively resolve gateway credential spending race condition (#4229)
a non-naive solution would require queuing up pending request and batch executing them
2024-01-02 14:47:01 +01:00
serinko bd3711892a remove ccc event pages (#4280) 2024-01-02 13:36:06 +00:00
Jędrzej Stuczyński b5926def85 fixed rebasing artifact 2024-01-02 12:54:12 +00:00
Jędrzej Stuczyński 50cc8bd0bf improved startup sync 2024-01-02 12:54:12 +00:00
Jędrzej Stuczyński fb1b58b5fb fixed startup sync 2024-01-02 12:54:11 +00:00
Jędrzej Stuczyński c3a9ceae52 fixed update_last_processed 2024-01-02 12:54:11 +00:00
Jędrzej Stuczyński c92a7e3e35 Squashed scraper
globally updated sqlx to 0.6.3

wip

basic processing loop

wip

starting on modules

all of the requesting logic, catching up, etc

remaining work includes persisting the data

wip

persisting block data

initial and extremely basic nyxd block scraper
2024-01-02 12:54:11 +00:00
Jędrzej Stuczyński e152c9a99e Merge pull request #4278 from nymtech/bugfix/api-tests
bugfix: gateway average uptime test
2024-01-02 13:29:59 +01:00
Jędrzej Stuczyński 78cce00adf bugfix: gateway average uptime test 2024-01-02 12:11:51 +00:00
Jędrzej Stuczyński 55e00a9a38 Feature/remove feegrant (#4227)
* nym-api using own funds when voting in multisig

* startup check for token balance

* dead code
2024-01-02 13:06:17 +01:00
Jędrzej Stuczyński 6d1b26daeb Merge pull request #4277 from nymtech/chore/1.75.0-lints
Chore/1.75.0 lints
2024-01-02 11:42:50 +01:00
Jędrzej Stuczyński 2d9e34cc81 clippy 2024-01-02 10:27:43 +00:00
Jędrzej Stuczyński 7232fd83d1 ignoring pedantic clippy lints from ephemera 2024-01-02 10:09:03 +00:00
Jędrzej Stuczyński 3816142479 ignoring unused imports created in macro expansions 2024-01-02 10:06:26 +00:00
dependabot[bot] 112ecc2e4c Bump msgpackr from 1.5.5 to 1.10.1 in /testnet-faucet
Bumps [msgpackr](https://github.com/kriszyp/msgpackr) from 1.5.5 to 1.10.1.
- [Release notes](https://github.com/kriszyp/msgpackr/releases)
- [Commits](https://github.com/kriszyp/msgpackr/commits/v1.10.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-28 21:37:47 +00:00
dependabot[bot] 2f0fbd5ebd Bump @tauri-apps/cli from 1.5.2 to 1.5.6
Bumps [@tauri-apps/cli](https://github.com/tauri-apps/tauri) from 1.5.2 to 1.5.6.
- [Release notes](https://github.com/tauri-apps/tauri/releases)
- [Commits](https://github.com/tauri-apps/tauri/compare/@tauri-apps/cli-v1.5.2...@tauri-apps/cli-v1.5.6)

---
updated-dependencies:
- dependency-name: "@tauri-apps/cli"
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-28 05:09:29 +00:00
serinko f45a803139 [DOCs]: ccc-event-hotfix (#4273)
* edit script, form warning, faq landing page

* Update nym-vpn.md

update script to include wg changes if need be

* update script tests.sh

* add tests-wireguard.sh script

* syntax edit

* Update nym-vpn.md

updated testing

* remove leftover flag

* edit testing guide

* final copy edits

---------

Co-authored-by: Tommy Verrall <60836166+tommyv1987@users.noreply.github.com>
2023-12-27 08:42:38 +00:00
serinko 7e16932c4a [DOC]: CCC 23 event pages + NymVPN guide (#4261)
* initialise ccc event pages

* compose existing FAQs

* edit flow

* restructure faq

* add vpn install steps

* add testing section

* initialise troubleshooting

* note add warning

* add warning

* add run cli steps

* add args

* document running flow

* add mulwald info to args desc

* add consent warning

* add FAQ questions

* add FAQ questions

* syntax fix

* edit guide flow

* add error troubleshooting point

* add warning to use two gateways

* remove reduntant message

* add clarification on testing

* rename to 37c3 to respect convention

* delete mixnet overview, replace with a link

* delete redundant

* grammar tweaks

* add gui config steps

* syntax fix

* syntax fix

* add releases and simple command info

* syntax edits

* add clarification on test logs

* describe TUN and IP flag

* add embedded video

* spellcheck

---------

Co-authored-by: mfahampshire <mfahampshire@pm.me>
2023-12-22 14:37:28 +00:00
Zane Schepke a0a44509af Merge pull request #4271 from nymtech/feat/vpnapp_ui_zoom
feat(vpn-deskop): add ui zoom level setting
2023-12-22 07:43:35 -05:00
Zane Schepke 852ed78e5d update README 2023-12-21 12:33:58 -05:00
pierre 1ea78e8e97 add ui zoom level setting 2023-12-21 14:25:15 +01:00
Jon Häggblad 10ff165c18 Apply exit policy check on portless packets 2023-12-19 10:33:38 +01:00
Jon Häggblad eec3cc4c47 Add cargo-about files 2023-12-19 09:24:44 +01:00
durch 3126053cbe Add three measurements grace period 2023-12-14 14:50:53 +01:00
durch 54266fd5df Only use good nodes for test route construction 2023-12-13 16:39:21 +01:00
dependabot[bot] 7407872b71 Bump openssl from 0.10.59 to 0.10.60 in /nym-vpn/ui/src-tauri
Bumps [openssl](https://github.com/sfackler/rust-openssl) from 0.10.59 to 0.10.60.
- [Release notes](https://github.com/sfackler/rust-openssl/releases)
- [Commits](https://github.com/sfackler/rust-openssl/compare/openssl-v0.10.59...openssl-v0.10.60)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-28 21:56:44 +00:00
benedettadavico 8d9387d3ac Merge remote-tracking branch 'origin/develop' into feature/nym-node-api-tests 2023-11-09 11:22:22 +01:00
benedettadavico e8bc2c7a01 Merge remote-tracking branch 'origin/develop' into feature/nym-node-api-tests 2023-11-06 16:46:31 +01:00
benedettadavico 0486cd2e63 refactoring the utils 2023-10-24 11:16:06 +02:00
benedettadavico 604098844a node-api test updates 2023-10-23 10:59:03 +02:00
benedettadavico 65e35bd2b0 Initial step to adding nym-node functional tests 2023-10-19 12:52:46 +02:00
670 changed files with 11630 additions and 24443 deletions
+3
View File
@@ -17,6 +17,9 @@ jobs:
steps:
- uses: actions/checkout@v2
- name: install yarn in root
run: cd ../.. yarn install
- name: Install npm
run: npm install
-40
View File
@@ -1,40 +0,0 @@
name: ci-nym-vpn-ui-js
on:
workflow_dispatch:
pull_request:
paths:
- 'nym-vpn/ui/src/**'
- 'nym-vpn/ui/package.json'
- 'nym-vpn/ui/index.html'
jobs:
check:
runs-on: custom-linux
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Node
uses: actions/setup-node@v3
with:
node-version: 18
- name: Install Yarn
run: npm install -g yarn
- name: Install dependencies
working-directory: nym-vpn/ui
run: yarn
- name: Type-check
working-directory: nym-vpn/ui
run: yarn typecheck
- name: Check lint
working-directory: nym-vpn/ui
run: yarn lint
- name: Check formatting
working-directory: nym-vpn/ui
run: yarn fmt:check
# - name: Run tests
# working-directory: nym-vpn/ui
# run: yarn test
- name: Check build
working-directory: nym-vpn/ui
run: yarn build
-63
View File
@@ -1,63 +0,0 @@
name: ci-nym-vpn-ui-rust
on:
workflow_dispatch:
pull_request:
paths:
- 'nym-vpn/ui/src-tauri/**'
jobs:
build:
runs-on: custom-linux
env:
CARGO_TERM_COLOR: always
CARGOTOML_PATH: ./nym-vpn/ui/src-tauri/Cargo.toml
steps:
- name: Install Dependencies (Linux)
run: sudo apt-get update && sudo apt-get -y install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev squashfs-tools libayatana-appindicator3-dev
continue-on-error: true
- name: Checkout
uses: actions/checkout@v4
- name: Install rust toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
components: rustfmt, clippy
- name: Prepare build
run: mkdir nym-vpn/ui/dist
- name: Build
uses: actions-rs/cargo@v1
with:
command: build
args: --manifest-path ${{ env.CARGOTOML_PATH }} --features custom-protocol
# - name: Run all tests
# uses: actions-rs/cargo@v1
# with:
# command: test
# args: --manifest-path ${{ env.CARGOTOML_PATH }}
- name: Check formatting
uses: actions-rs/cargo@v1
with:
command: fmt
args: --manifest-path ${{ env.CARGOTOML_PATH }} --all -- --check
- name: Annotate with clippy checks
uses: actions-rs/clippy-check@v1
continue-on-error: true
with:
token: ${{ secrets.GITHUB_TOKEN }}
args: --manifest-path ${{ env.CARGOTOML_PATH }} --all-features
- name: Clippy
uses: actions-rs/cargo@v1
with:
command: clippy
args: --manifest-path ${{ env.CARGOTOML_PATH }} --all-features --all-targets -- -D warnings
Generated
+234 -208
View File
@@ -661,6 +661,22 @@ dependencies = [
"syn 2.0.38",
]
[[package]]
name = "async-tungstenite"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1e9efbe14612da0a19fb983059a0b621e9cf6225d7018ecab4f9988215540dc"
dependencies = [
"futures-io",
"futures-util",
"log",
"pin-project-lite 0.2.13",
"rustls-native-certs",
"tokio",
"tokio-rustls 0.24.1",
"tungstenite",
]
[[package]]
name = "asynchronous-codec"
version = "0.6.2"
@@ -674,15 +690,6 @@ dependencies = [
"pin-project-lite 0.2.13",
]
[[package]]
name = "atoi"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "616896e05fc0e2649463a93a15183c6a16bf03413a7af88ef1285ddedfa9cda5"
dependencies = [
"num-traits",
]
[[package]]
name = "atoi"
version = "1.0.0"
@@ -1552,6 +1559,26 @@ version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f"
[[package]]
name = "const_format"
version = "0.2.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3a214c7af3d04997541b18d432afaff4c455e79e2029079647e72fc2bd27673"
dependencies = [
"const_format_proc_macros",
]
[[package]]
name = "const_format_proc_macros"
version = "0.2.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7f6ff08fd20f4f299298a28e2dfa8a8ba1036e6cd2460ac1de7b425d76f2500"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]]
name = "constant_time_eq"
version = "0.3.0"
@@ -1622,6 +1649,16 @@ dependencies = [
"tendermint-proto",
]
[[package]]
name = "cosmos-sdk-proto"
version = "0.20.0"
source = "git+https://github.com/jstuczyn/cosmos-rust?branch=nym-temp/all-validator-features#41ed4631e146268b0300033c8bbb993d79a49f58"
dependencies = [
"prost 0.12.1",
"prost-types 0.12.1",
"tendermint-proto",
]
[[package]]
name = "cosmrs"
version = "0.15.0"
@@ -1629,7 +1666,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47126f5364df9387b9d8559dcef62e99010e1d4098f39eb3f7ee4b5c254e40ea"
dependencies = [
"bip32",
"cosmos-sdk-proto",
"cosmos-sdk-proto 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ecdsa 0.16.8",
"eyre",
"k256",
"rand_core 0.6.4",
"serde",
"serde_json",
"signature 2.1.0",
"subtle-encoding",
"tendermint",
"thiserror",
]
[[package]]
name = "cosmrs"
version = "0.15.0"
source = "git+https://github.com/jstuczyn/cosmos-rust?branch=nym-temp/all-validator-features#41ed4631e146268b0300033c8bbb993d79a49f58"
dependencies = [
"bip32",
"cosmos-sdk-proto 0.20.0 (git+https://github.com/jstuczyn/cosmos-rust?branch=nym-temp/all-validator-features)",
"ecdsa 0.16.8",
"eyre",
"k256",
@@ -1747,30 +1803,15 @@ dependencies = [
"toml 0.5.11",
]
[[package]]
name = "crc"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49fc9a695bca7f35f5f4c15cddc84415f66a74ea78eef08e90c5024f2b540e23"
dependencies = [
"crc-catalog 1.1.1",
]
[[package]]
name = "crc"
version = "3.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe"
dependencies = [
"crc-catalog 2.2.0",
"crc-catalog",
]
[[package]]
name = "crc-catalog"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccaeedb56da03b09f598226e25e80088cb4cd25f316e6e4df7d695f0feeb1403"
[[package]]
name = "crc-catalog"
version = "2.2.0"
@@ -2200,6 +2241,16 @@ dependencies = [
"darling_macro 0.14.4",
]
[[package]]
name = "darling"
version = "0.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e"
dependencies = [
"darling_core 0.20.3",
"darling_macro 0.20.3",
]
[[package]]
name = "darling_core"
version = "0.13.4"
@@ -2228,6 +2279,20 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "darling_core"
version = "0.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621"
dependencies = [
"fnv",
"ident_case",
"proc-macro2",
"quote",
"strsim",
"syn 2.0.38",
]
[[package]]
name = "darling_macro"
version = "0.13.4"
@@ -2250,6 +2315,17 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "darling_macro"
version = "0.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5"
dependencies = [
"darling_core 0.20.3",
"quote",
"syn 2.0.38",
]
[[package]]
name = "dashmap"
version = "5.5.3"
@@ -2564,12 +2640,6 @@ version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
[[package]]
name = "dotenv"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f"
[[package]]
name = "dotenvy"
version = "0.15.7"
@@ -2765,26 +2835,6 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "enum-iterator"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45a0ac4aeb3a18f92eaf09c6bb9b3ac30ff61ca95514fc58cbead1c9a6bf5401"
dependencies = [
"enum-iterator-derive",
]
[[package]]
name = "enum-iterator-derive"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eecf8589574ce9b895052fa12d69af7a233f99e6107f5cb8dd1044f2a17bfdcb"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.38",
]
[[package]]
name = "env_logger"
version = "0.7.1"
@@ -2981,9 +3031,9 @@ dependencies = [
[[package]]
name = "eyre"
version = "0.6.8"
version = "0.6.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c2b6b5a29c02cdc822728b7d7b8ae1bab3e3b05d44522770ddd49722eeac7eb"
checksum = "b6267a1fa6f59179ea4afc8e50fd8612a3cc60bc858f786ff877a4a8cb042799"
dependencies = [
"indenter",
"once_cell",
@@ -5864,6 +5914,15 @@ dependencies = [
"libc",
]
[[package]]
name = "num_threads"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44"
dependencies = [
"libc",
]
[[package]]
name = "nym-api"
version = "1.1.34"
@@ -5932,7 +5991,7 @@ dependencies = [
"serde",
"serde_derive",
"serde_json",
"sqlx 0.6.3",
"sqlx",
"tap",
"tempfile",
"thiserror",
@@ -5950,7 +6009,7 @@ name = "nym-api-requests"
version = "0.1.0"
dependencies = [
"bs58 0.4.0",
"cosmrs",
"cosmrs 0.15.0 (git+https://github.com/jstuczyn/cosmos-rust?branch=nym-temp/all-validator-features)",
"cosmwasm-std",
"getset",
"nym-coconut-interface",
@@ -6008,7 +6067,7 @@ name = "nym-bity-integration"
version = "0.1.0"
dependencies = [
"anyhow",
"cosmrs",
"cosmrs 0.15.0 (git+https://github.com/jstuczyn/cosmos-rust?branch=nym-temp/all-validator-features)",
"eyre",
"k256",
"nym-cli-commands",
@@ -6053,7 +6112,7 @@ dependencies = [
"cfg-if",
"clap 4.4.7",
"comfy-table",
"cosmrs",
"cosmrs 0.15.0 (git+https://github.com/jstuczyn/cosmos-rust?branch=nym-temp/all-validator-features)",
"cosmwasm-std",
"cw-utils",
"handlebars",
@@ -6160,7 +6219,7 @@ dependencies = [
"serde",
"serde_json",
"sha2 0.10.8",
"sqlx 0.6.3",
"sqlx",
"tap",
"tempfile",
"thiserror",
@@ -6297,7 +6356,7 @@ version = "0.1.0"
dependencies = [
"async-trait",
"log",
"sqlx 0.5.13",
"sqlx",
"thiserror",
"tokio",
]
@@ -6322,7 +6381,7 @@ name = "nym-credentials"
version = "0.1.0"
dependencies = [
"bls12_381",
"cosmrs",
"cosmrs 0.15.0 (git+https://github.com/jstuczyn/cosmos-rust?branch=nym-temp/all-validator-features)",
"log",
"nym-api-requests",
"nym-coconut-interface",
@@ -6484,7 +6543,7 @@ dependencies = [
"rand 0.8.5",
"serde",
"serde_json",
"sqlx 0.5.13",
"sqlx",
"subtle-encoding",
"thiserror",
"tokio",
@@ -6681,7 +6740,6 @@ dependencies = [
"nym-types",
"nym-validator-client",
"opentelemetry",
"pretty_env_logger",
"rand 0.7.3",
"serde",
"serde_json",
@@ -6806,7 +6864,7 @@ dependencies = [
"reqwest",
"serde",
"serde_json",
"sqlx 0.6.3",
"sqlx",
"tap",
"tempfile",
"thiserror",
@@ -6827,7 +6885,7 @@ dependencies = [
"pretty_env_logger",
"rocket",
"serde",
"sqlx 0.5.13",
"sqlx",
"thiserror",
"tokio",
]
@@ -7335,7 +7393,7 @@ dependencies = [
"reqwest",
"serde",
"serde_json",
"sqlx 0.5.13",
"sqlx",
"thiserror",
"tokio",
]
@@ -7410,7 +7468,7 @@ name = "nym-types"
version = "1.0.0"
dependencies = [
"base64 0.21.4",
"cosmrs",
"cosmrs 0.15.0 (git+https://github.com/jstuczyn/cosmos-rust?branch=nym-temp/all-validator-features)",
"cosmwasm-std",
"eyre",
"hmac 0.12.1",
@@ -7444,7 +7502,7 @@ dependencies = [
"bip32",
"bip39",
"colored",
"cosmrs",
"cosmrs 0.15.0 (git+https://github.com/jstuczyn/cosmos-rust?branch=nym-temp/all-validator-features)",
"cosmwasm-std",
"cw-controllers",
"cw-utils",
@@ -7486,6 +7544,40 @@ dependencies = [
"zeroize",
]
[[package]]
name = "nym-validator-rewarder"
version = "0.1.0"
dependencies = [
"anyhow",
"bip39",
"clap 4.4.7",
"cosmwasm-std",
"futures",
"humantime 2.1.0",
"humantime-serde",
"nym-bin-common",
"nym-coconut",
"nym-coconut-bandwidth-contract-common",
"nym-coconut-dkg-common",
"nym-config",
"nym-credentials",
"nym-crypto",
"nym-network-defaults",
"nym-task",
"nym-validator-client",
"nyxd-scraper",
"serde",
"serde_with",
"sha2 0.10.8",
"sqlx",
"thiserror",
"time",
"tokio",
"tracing",
"url",
"zeroize",
]
[[package]]
name = "nym-vesting-contract-common"
version = "0.7.0"
@@ -7504,7 +7596,7 @@ dependencies = [
name = "nym-wallet-types"
version = "1.0.0"
dependencies = [
"cosmrs",
"cosmrs 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cosmwasm-std",
"hex-literal",
"nym-config",
@@ -7582,6 +7674,28 @@ dependencies = [
"url",
]
[[package]]
name = "nyxd-scraper"
version = "0.1.0"
dependencies = [
"async-trait",
"const_format",
"cosmrs 0.15.0 (git+https://github.com/jstuczyn/cosmos-rust?branch=nym-temp/all-validator-features)",
"eyre",
"futures",
"nym-bin-common",
"sha2 0.10.8",
"sqlx",
"tendermint",
"tendermint-rpc",
"thiserror",
"tokio",
"tokio-stream",
"tokio-util",
"tracing",
"url",
]
[[package]]
name = "object"
version = "0.32.1"
@@ -7647,9 +7761,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
[[package]]
name = "openssl"
version = "0.10.57"
version = "0.10.62"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bac25ee399abb46215765b1cb35bc0212377e58a061560d8b29b024fd0430e7c"
checksum = "8cde4d2d9200ad5909f8dac647e29482e07c3a35de8a13fce7c9c7747ad9f671"
dependencies = [
"bitflags 2.4.1",
"cfg-if",
@@ -7688,9 +7802,9 @@ dependencies = [
[[package]]
name = "openssl-sys"
version = "0.9.93"
version = "0.9.98"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db4d56a4c0478783083cfafcc42493dd4a981d41669da64b4572a2a089b51b1d"
checksum = "c1665caf8ab2dc9aef43d1c0023bd904633a6a05cb30b0ad59bec2ae986e57a7"
dependencies = [
"cc",
"libc",
@@ -9790,6 +9904,35 @@ dependencies = [
"serde",
]
[[package]]
name = "serde_with"
version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64cd236ccc1b7a29e7e2739f27c0b2dd199804abc4290e32f59f3b68d6405c23"
dependencies = [
"base64 0.21.4",
"chrono",
"hex",
"indexmap 1.9.3",
"indexmap 2.0.2",
"serde",
"serde_json",
"serde_with_macros",
"time",
]
[[package]]
name = "serde_with_macros"
version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93634eb5f75a2323b16de4748022ac4297f9e76b6dced2be287a099f41b5e788"
dependencies = [
"darling 0.20.3",
"proc-macro2",
"quote",
"syn 2.0.38",
]
[[package]]
name = "serde_yaml"
version = "0.9.25"
@@ -10108,17 +10251,6 @@ dependencies = [
"der 0.7.8",
]
[[package]]
name = "sqlformat"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4b7922be017ee70900be125523f38bdd644f4f06a1b16e8fa5a8ee8c34bffd4"
dependencies = [
"itertools 0.10.5",
"nom",
"unicode_categories",
]
[[package]]
name = "sqlformat"
version = "0.2.2"
@@ -10130,70 +10262,14 @@ dependencies = [
"unicode_categories",
]
[[package]]
name = "sqlx"
version = "0.5.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "551873805652ba0d912fec5bbb0f8b4cdd96baf8e2ebf5970e5671092966019b"
dependencies = [
"sqlx-core 0.5.13",
"sqlx-macros 0.5.13",
]
[[package]]
name = "sqlx"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8de3b03a925878ed54a954f621e64bf55a3c1bd29652d0d1a17830405350188"
dependencies = [
"sqlx-core 0.6.3",
"sqlx-macros 0.6.3",
]
[[package]]
name = "sqlx-core"
version = "0.5.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e48c61941ccf5ddcada342cd59e3e5173b007c509e1e8e990dafc830294d9dc5"
dependencies = [
"ahash 0.7.6",
"atoi 0.4.0",
"bitflags 1.3.2",
"byteorder",
"bytes",
"chrono",
"crc 2.1.0",
"crossbeam-queue",
"either",
"event-listener",
"flume",
"futures-channel",
"futures-core",
"futures-executor",
"futures-intrusive",
"futures-util",
"hashlink 0.7.0",
"hex",
"indexmap 1.9.3",
"itoa",
"libc",
"libsqlite3-sys",
"log",
"memchr",
"once_cell",
"paste",
"percent-encoding",
"rustls 0.19.1",
"sha2 0.10.8",
"smallvec",
"sqlformat 0.1.8",
"sqlx-rt 0.5.13",
"stringprep",
"thiserror",
"tokio-stream",
"url",
"webpki 0.21.4",
"webpki-roots 0.21.1",
"sqlx-core",
"sqlx-macros",
]
[[package]]
@@ -10203,12 +10279,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa8241483a83a3f33aa5fff7e7d9def398ff9990b2752b6c6112b83c6d246029"
dependencies = [
"ahash 0.7.6",
"atoi 1.0.0",
"atoi",
"bitflags 1.3.2",
"byteorder",
"bytes",
"chrono",
"crc 3.0.1",
"crc",
"crossbeam-queue",
"dotenvy",
"either",
@@ -10234,34 +10310,16 @@ dependencies = [
"rustls-pemfile",
"sha2 0.10.8",
"smallvec",
"sqlformat 0.2.2",
"sqlx-rt 0.6.3",
"sqlformat",
"sqlx-rt",
"stringprep",
"thiserror",
"time",
"tokio-stream",
"url",
"webpki-roots 0.22.6",
]
[[package]]
name = "sqlx-macros"
version = "0.5.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc0fba2b0cae21fc00fe6046f8baa4c7fcb49e379f0f592b04696607f69ed2e1"
dependencies = [
"dotenv",
"either",
"heck 0.4.1",
"once_cell",
"proc-macro2",
"quote",
"sha2 0.10.8",
"sqlx-core 0.5.13",
"sqlx-rt 0.5.13",
"syn 1.0.109",
"url",
]
[[package]]
name = "sqlx-macros"
version = "0.6.3"
@@ -10275,23 +10333,12 @@ dependencies = [
"proc-macro2",
"quote",
"sha2 0.10.8",
"sqlx-core 0.6.3",
"sqlx-rt 0.6.3",
"sqlx-core",
"sqlx-rt",
"syn 1.0.109",
"url",
]
[[package]]
name = "sqlx-rt"
version = "0.5.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4db708cd3e459078f85f39f96a00960bd841f66ee2a669e90bf36907f5a79aae"
dependencies = [
"once_cell",
"tokio",
"tokio-rustls 0.22.0",
]
[[package]]
name = "sqlx-rt"
version = "0.6.3"
@@ -10424,7 +10471,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7e94b1ec00bad60e6410e058b52f1c66de3dc5fe4d62d09b3e52bb7d3b73e25"
dependencies = [
"base64 0.13.1",
"crc 3.0.1",
"crc",
"lazy_static",
"md-5",
"rand 0.8.5",
@@ -10658,6 +10705,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfbf0a4753b46a190f367337e0163d0b552a2674a6bac54e74f9f2cdcde2969b"
dependencies = [
"async-trait",
"async-tungstenite",
"bytes",
"flex-error",
"futures",
@@ -10769,6 +10817,8 @@ dependencies = [
"deranged",
"itoa",
"js-sys",
"libc",
"num_threads",
"powerfmt",
"serde",
"time-core",
@@ -10866,17 +10916,6 @@ dependencies = [
"tokio",
]
[[package]]
name = "tokio-rustls"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6"
dependencies = [
"rustls 0.19.1",
"tokio",
"webpki 0.21.4",
]
[[package]]
name = "tokio-rustls"
version = "0.23.4"
@@ -11412,6 +11451,7 @@ dependencies = [
"log",
"native-tls",
"rand 0.8.5",
"rustls 0.21.7",
"sha1",
"thiserror",
"url",
@@ -11729,18 +11769,13 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "vergen"
version = "7.4.3"
version = "8.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "447f9238a4553957277b3ee09d80babeae0811f1b3baefb093de1c0448437a37"
checksum = "1290fd64cc4e7d3c9b07d7f333ce0ce0007253e32870e632624835cc80b83939"
dependencies = [
"anyhow",
"cfg-if",
"enum-iterator",
"getset",
"git2",
"rustc_version 0.4.0",
"rustversion",
"thiserror",
"time",
]
@@ -12026,15 +12061,6 @@ dependencies = [
"untrusted 0.9.0",
]
[[package]]
name = "webpki-roots"
version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aabe153544e473b775453675851ecc86863d2a81d786d741f6b76778f2a48940"
dependencies = [
"webpki 0.21.4",
]
[[package]]
name = "webpki-roots"
version = "0.22.6"
@@ -12154,7 +12180,7 @@ checksum = "465a03cc11e9a7d7b4f9f99870558fe37a102b65b93f8045392fef7c67b39e80"
dependencies = [
"arc-swap",
"async-trait",
"crc 3.0.1",
"crc",
"log",
"rand 0.8.5",
"serde",
@@ -12205,7 +12231,7 @@ dependencies = [
"arc-swap",
"async-trait",
"bytes",
"crc 3.0.1",
"crc",
"log",
"rand 0.8.5",
"thiserror",
+9 -2
View File
@@ -67,6 +67,7 @@ members = [
"common/nymsphinx/params",
"common/nymsphinx/routing",
"common/nymsphinx/types",
"common/nyxd-scraper",
"common/pemstore",
"common/socks5-client-core",
"common/socks5/proxy-helpers",
@@ -101,6 +102,7 @@ members = [
"nym-node",
"nym-node/nym-node-requests",
"nym-outfox",
"nym-validator-rewarder",
"tools/internal/ssl-inject",
"tools/internal/sdk-version-bump",
"tools/nym-cli",
@@ -123,6 +125,7 @@ default-members = [
"nym-api",
"tools/nymvisor",
"explorer-api",
"nym-validator-rewarder",
]
exclude = ["explorer", "contracts", "nym-wallet", "nym-connect/mobile/src-tauri", "nym-connect/desktop", "nym-vpn/ui/src-tauri", "cpu-cycles"]
@@ -159,6 +162,7 @@ reqwest = "0.11.22"
schemars = "0.8.1"
serde = "1.0.152"
serde_json = "1.0.91"
sqlx = "0.6.3"
tap = "1.0.1"
time = "0.3.30"
thiserror = "1.0.48"
@@ -199,9 +203,12 @@ cw-controllers = { version = "=1.1.0" }
# cosmrs-related
bip32 = "0.5.1"
cosmrs = "=0.15.0"
tendermint-rpc = "0.34" # same version as used by cosmrs
# temporarily using a fork again (yay.) because we need staking and slashing support
cosmrs = { git = "https://github.com/jstuczyn/cosmos-rust", branch ="nym-temp/all-validator-features" }
#cosmrs = { git = "https://github.com/jstuczyn/cosmos-rust", branch = "nym-temp/all-validator-features" } # unfortuntely we need a fork by yours truly to get the staking support
tendermint = "0.34" # same version as used by cosmrs
tendermint-rpc = "0.34" # same version as used by cosmrs
prost = "0.12"
# wasm-related dependencies
+70
View File
@@ -0,0 +1,70 @@
<html>
<head>
<style>
@media (prefers-color-scheme: dark) {
body {
background: #333;
color: white;
}
a {
color: skyblue;
}
}
.container {
font-family: sans-serif;
max-width: 800px;
margin: 0 auto;
}
.intro {
text-align: center;
}
.licenses-list {
list-style-type: none;
margin: 0;
padding: 0;
}
.license-used-by {
margin-top: -10px;
}
.license-text {
max-height: 200px;
overflow-y: scroll;
white-space: pre-wrap;
}
</style>
</head>
<body>
<main class="container">
<div class="intro">
<h1>Third Party Licenses</h1>
<p>This page lists the licenses of the projects used in cargo-about.</p>
</div>
<h2>Overview of licenses:</h2>
<ul class="licenses-overview">
{{#each overview}}
<li><a href="#{{id}}">{{name}}</a> ({{count}})</li>
{{/each}}
</ul>
<h2>All license text:</h2>
<ul class="licenses-list">
{{#each licenses}}
<li class="license">
<h3 id="{{id}}">{{name}}</h3>
<h4>Used by:</h4>
<ul class="license-used-by">
{{#each used_by}}
<li><a href="{{#if crate.repository}} {{crate.repository}} {{else}} https://crates.io/crates/{{crate.name}} {{/if}}">{{crate.name}} {{crate.version}}</a></li>
{{/each}}
</ul>
<pre class="license-text">{{text}}</pre>
</li>
{{/each}}
</ul>
</main>
</body>
</html>
+19
View File
@@ -0,0 +1,19 @@
private = { ignore = true }
accepted = [
"0BSD",
"Apache-2.0",
"BSD-2-Clause",
"BSD-3-Clause",
"CC0-1.0",
"ISC",
"MIT",
"MPL-2.0",
"Unicode-DFS-2016",
"OpenSSL",
]
workarounds = [
"ring",
"rustls",
]
@@ -1667,9 +1667,9 @@
}
},
"node_modules/follow-redirects": {
"version": "1.14.9",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz",
"integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==",
"version": "1.15.4",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz",
"integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==",
"dev": true,
"funding": [
{
@@ -5800,9 +5800,9 @@
}
},
"follow-redirects": {
"version": "1.14.9",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz",
"integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==",
"version": "1.15.4",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz",
"integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==",
"dev": true
},
"forwarded": {
@@ -15,4 +15,4 @@ prod:
mixnode_identity: 3pMCJswCyA19MGYWGDWT5fBk2M8ybSZGXttyAoNY5gBB
gateway_identity: 2BuMSfMW3zpeAjKXyKLhmY4QW1DXurrtSPEJ6CjX3SEh
log_level: error
time_zone: utc
time_zone: utc
@@ -1,4 +1,6 @@
import { dir } from "console";
import { readFileSync } from "fs";
import { dirname } from "path";
import { TLogLevelName } from "tslog";
import YAML from "yaml";
@@ -10,9 +12,11 @@ class ConfigHandler {
public commonConfig: { request_headers: object };
private currentEnvironment: string;
public environment: string;
public environmnetConfig: {
public environmentConfig: {
log_level: TLogLevelName;
time_zone: string;
api_base_url: string;
@@ -35,8 +39,9 @@ class ConfigHandler {
private setCommonConfig(): void {
try {
const baseWorkingDirectory = __dirname;
this.commonConfig = YAML.parse(
readFileSync("src/config/config.yaml", "utf8")
readFileSync(baseWorkingDirectory + "/config.yaml", "utf8"),
).common;
} catch (error) {
throw Error(`Error reading common config: (${error})`);
@@ -46,14 +51,24 @@ class ConfigHandler {
private setEnvironmentConfig(environment: string): void {
this.ensureEnvironmentIsValid(environment);
try {
this.environmnetConfig = YAML.parse(
readFileSync("src/config/config.yaml", "utf8")
const baseWorkingDirectory = __dirname;
this.environmentConfig = YAML.parse(
readFileSync(baseWorkingDirectory + "/config.yaml", "utf8"),
)[environment];
} catch (error) {
console.log("fadsfasdfasdfsdfsa")
throw Error(`Error reading environment config: (${error})`);
}
}
public getEnvironmentConfig(environment: string): any {
const baseWorkingDirectory = __dirname;
return (
this.environmentConfig ||
YAML.parse(readFileSync(baseWorkingDirectory + "/config.yaml", "utf8"))[environment]
);
}
private ensureEnvironmentIsValid(environment: string): void {
if (this.validEnvironments.indexOf(environment) === -1) {
throw Error(`Config environment is not valid: "${environment}"`);
+4
View File
@@ -0,0 +1,4 @@
module.exports = {
ConfigHandler: require('./config/configHandler.ts'),
RestClient: require('./restClient/RestClient.ts')
};
@@ -13,9 +13,9 @@ import ConfigHandler from "../config/configHandler";
const config = ConfigHandler.getInstance();
const log = new Logger({
minLevel: config.environmnetConfig.log_level,
minLevel: config.environmentConfig.log_level,
dateTimeTimezone:
config.environmnetConfig.time_zone ||
config.environmentConfig.time_zone ||
Intl.DateTimeFormat().resolvedOptions().timeZone,
});
@@ -24,7 +24,7 @@ function isSet(property): boolean {
}
export class RestClient {
private static authToken: string;
public static authToken: string;
private axiosInstance: AxiosInstance;
@@ -83,7 +83,7 @@ export class RestClient {
data,
additionalConfigs,
params,
})
}),
);
await this.axiosInstance
@@ -214,7 +214,7 @@ export class RestClient {
if (isSet(additionalConfigs)) {
logRecord = `${logRecord}\nAdditional Configuration: ${stringify(
additionalConfigs
additionalConfigs,
)}`;
}
+2 -1
View File
@@ -35,9 +35,10 @@ opentelemetry = { version = "0.19.0", optional = true, features = ["rt-tokio"] }
[build-dependencies]
vergen = { version = "=7.4.3", default-features = false, features = [
vergen = { version = "=8.2.6", default-features = false, features = [
"build",
"git",
"gitcl",
"rustc",
"cargo",
] }
+8 -7
View File
@@ -1,13 +1,14 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use vergen::{vergen, Config};
use vergen::EmitBuilder;
fn main() {
let mut config = Config::default();
if std::env::var("DOCS_RS").is_ok() {
// If we don't have access to git information, such as in a docs.rs build, don't error
*config.git_mut().skip_if_error_mut() = true;
}
vergen(config).expect("failed to extract build metadata");
EmitBuilder::builder()
.all_build()
.all_git()
.all_rustc()
.all_cargo()
.emit()
.expect("failed to extract build metadata");
}
+10 -10
View File
@@ -40,9 +40,9 @@ pub struct BinaryBuildInformation {
/// Provides the rustc channel that was used for the build, for example `nightly`.
pub rustc_channel: &'static str,
// VERGEN_CARGO_PROFILE
/// Provides the cargo profile that was used for the build, for example `debug`.
pub cargo_profile: &'static str,
// VERGEN_CARGO_DEBUG
/// Provides the cargo debug mode that was used for the build.
pub cargo_debug: &'static str,
}
impl BinaryBuildInformation {
@@ -57,7 +57,7 @@ impl BinaryBuildInformation {
commit_branch: env!("VERGEN_GIT_BRANCH"),
rustc_version: env!("VERGEN_RUSTC_SEMVER"),
rustc_channel: env!("VERGEN_RUSTC_CHANNEL"),
cargo_profile: env!("VERGEN_CARGO_PROFILE"),
cargo_debug: env!("VERGEN_CARGO_DEBUG"),
}
}
@@ -71,7 +71,7 @@ impl BinaryBuildInformation {
commit_branch: self.commit_branch.to_owned(),
rustc_version: self.rustc_version.to_owned(),
rustc_channel: self.rustc_channel.to_owned(),
cargo_profile: self.cargo_profile.to_owned(),
cargo_debug: self.cargo_debug.to_owned(),
}
}
@@ -115,9 +115,9 @@ pub struct BinaryBuildInformationOwned {
/// Provides the rustc channel that was used for the build, for example `nightly`.
pub rustc_channel: String,
// VERGEN_CARGO_PROFILE
/// Provides the cargo profile that was used for the build, for example `debug`.
pub cargo_profile: String,
// VERGEN_CARGO_DEBUG
/// Provides the cargo debug mode that was used for the build.
pub cargo_debug: String,
}
impl Display for BinaryBuildInformationOwned {
@@ -151,8 +151,8 @@ impl Display for BinaryBuildInformationOwned {
self.rustc_version,
"rustc Channel:",
self.rustc_channel,
"cargo Profile:",
self.cargo_profile,
"cargo Debug:",
self.cargo_debug,
)
}
}
+2 -2
View File
@@ -59,7 +59,7 @@ features = ["time"]
version = "0.20.1"
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.sqlx]
version = "0.6.2"
workspace = true
features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"]
optional = true
@@ -90,7 +90,7 @@ tempfile = "3.1.0"
[build-dependencies]
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
sqlx = { version = "0.6.2", features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"] }
sqlx = { workspace = true, features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"] }
[features]
default = []
@@ -1,6 +1,8 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
#![allow(unused_imports)]
use std::time::Duration;
pub use wasmtimer::{std::Instant, tokio::*};
@@ -42,6 +42,14 @@ pub struct Config {
nyxd_config: nyxd::Config,
}
impl TryFrom<NymNetworkDetails> for Config {
type Error = ValidatorClientError;
fn try_from(value: NymNetworkDetails) -> Result<Self, Self::Error> {
Config::try_from_nym_network_details(&value)
}
}
impl Config {
pub fn try_from_nym_network_details(
details: &NymNetworkDetails,
@@ -5,21 +5,24 @@ use crate::nym_api::error::NymAPIError;
use crate::nym_api::routes::{CORE_STATUS_COUNT, SINCE_ARG};
use async_trait::async_trait;
use http_api_client::{ApiClient, NO_PARAMS};
use nym_api_requests::coconut::models::{
EpochCredentialsResponse, IssuedCredentialResponse, IssuedCredentialsResponse,
pub use nym_api_requests::{
coconut::{
models::{
EpochCredentialsResponse, IssuedCredential, IssuedCredentialBody,
IssuedCredentialResponse, IssuedCredentialsResponse,
},
BlindSignRequestBody, BlindedSignatureResponse, CredentialsRequestBody,
VerifyCredentialBody, VerifyCredentialResponse,
},
models::{
ComputeRewardEstParam, DescribedGateway, GatewayBondAnnotated, GatewayCoreStatusResponse,
GatewayStatusReportResponse, GatewayUptimeHistoryResponse, InclusionProbabilityResponse,
MixNodeBondAnnotated, MixnodeCoreStatusResponse, MixnodeStatusReportResponse,
MixnodeStatusResponse, MixnodeUptimeHistoryResponse, RewardEstimationResponse,
StakeSaturationResponse, UptimeResponse,
},
};
use nym_api_requests::coconut::{
BlindSignRequestBody, BlindedSignatureResponse, CredentialsRequestBody, VerifyCredentialBody,
VerifyCredentialResponse,
};
use nym_api_requests::models::{
ComputeRewardEstParam, DescribedGateway, GatewayBondAnnotated, GatewayCoreStatusResponse,
GatewayStatusReportResponse, GatewayUptimeHistoryResponse, InclusionProbabilityResponse,
MixNodeBondAnnotated, MixnodeCoreStatusResponse, MixnodeStatusReportResponse,
MixnodeStatusResponse, MixnodeUptimeHistoryResponse, RewardEstimationResponse,
StakeSaturationResponse, UptimeResponse,
};
use nym_coconut_dkg_common::types::EpochId;
pub use nym_coconut_dkg_common::types::EpochId;
use nym_mixnet_contract_common::mixnode::MixNodeDetails;
use nym_mixnet_contract_common::{GatewayBond, IdentityKeyRef, MixId};
use nym_name_service_common::response::NamesListResponse;
@@ -8,6 +8,8 @@ use cosmwasm_std::{Fraction, Uint128};
use serde::{Deserialize, Serialize};
use std::fmt;
use std::ops::Div;
use std::str::FromStr;
use thiserror::Error;
#[derive(Serialize, Deserialize, Clone, Copy, Default, Debug, PartialEq, Eq)]
pub struct MismatchedDenoms;
@@ -126,6 +128,37 @@ impl From<CosmWasmCoin> for Coin {
}
}
// unfortunately cosmwasm didn't re-export this correct so we just redefine its
#[derive(Error, Debug, PartialEq, Eq)]
pub enum CoinFromStrError {
#[error("Missing denominator")]
MissingDenom,
#[error("Missing amount or non-digit characters in amount")]
MissingAmount,
#[error("Invalid amount: {0}")]
InvalidAmount(#[from] std::num::ParseIntError),
}
impl FromStr for Coin {
type Err = CoinFromStrError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let pos = s
.find(|c: char| !c.is_ascii_digit())
.ok_or(CoinFromStrError::MissingDenom)?;
let (amount, denom) = s.split_at(pos);
if amount.is_empty() {
return Err(CoinFromStrError::MissingAmount);
}
Ok(Coin {
amount: amount.parse::<u128>()?,
denom: denom.to_string(),
})
}
}
pub trait CoinConverter {
type Target;
@@ -7,12 +7,12 @@ use crate::nyxd::error::NyxdError;
use crate::nyxd::CosmWasmClient;
use async_trait::async_trait;
use cosmrs::AccountId;
use nym_coconut_dkg_common::dealer::{
ContractDealing, DealerDetailsResponse, PagedDealerResponse, PagedDealingsResponse,
use nym_coconut_dkg_common::{
dealer::{ContractDealing, DealerDetailsResponse, PagedDealerResponse, PagedDealingsResponse},
msg::QueryMsg as DkgQueryMsg,
types::{DealerDetails, Epoch, EpochId, InitialReplacementData},
verification_key::{ContractVKShare, PagedVKSharesResponse},
};
use nym_coconut_dkg_common::msg::QueryMsg as DkgQueryMsg;
use nym_coconut_dkg_common::types::{DealerDetails, Epoch, EpochId, InitialReplacementData};
use nym_coconut_dkg_common::verification_key::{ContractVKShare, PagedVKSharesResponse};
use serde::Deserialize;
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
@@ -52,10 +52,6 @@ use wasmtimer::tokio::sleep;
pub const DEFAULT_BROADCAST_POLLING_RATE: Duration = Duration::from_secs(4);
pub const DEFAULT_BROADCAST_TIMEOUT: Duration = Duration::from_secs(60);
#[cfg(feature = "http-client")]
#[async_trait]
impl CosmWasmClient for cosmrs::rpc::HttpClient {}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
pub trait CosmWasmClient: TendermintRpcClient {
@@ -522,3 +518,7 @@ pub trait CosmWasmClient: TendermintRpcClient {
res.try_into()
}
}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl<T> CosmWasmClient for T where T: TendermintRpcClient {}
@@ -425,7 +425,7 @@ where
amount: amount.into_iter().map(Into::into).collect(),
}
.to_any()
.map_err(|_| NyxdError::SerializationError("MsgExecuteContract".to_owned()))
.map_err(|_| NyxdError::SerializationError("MsgSend".to_owned()))
})
.collect::<Result<_, _>>()?;
@@ -1,7 +1,7 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::nyxd::cosmwasm_client::client_traits::{CosmWasmClient, SigningCosmWasmClient};
use crate::nyxd::cosmwasm_client::client_traits::SigningCosmWasmClient;
use crate::nyxd::error::NyxdError;
use crate::nyxd::{Config, GasPrice, Hash, Height};
use crate::rpc::TendermintRpcClient;
@@ -26,6 +26,7 @@ use cosmrs::rpc::{HttpClient, HttpClientUrl};
pub mod client_traits;
mod helpers;
pub mod logs;
pub mod module_traits;
pub mod types;
#[derive(Debug)]
@@ -329,14 +330,6 @@ where
}
}
#[async_trait]
impl<C, S> CosmWasmClient for MaybeSigningClient<C, S>
where
C: TendermintRpcClient + Send + Sync,
S: Send + Sync,
{
}
#[async_trait]
impl<C, S> SigningCosmWasmClient for MaybeSigningClient<C, S>
where
@@ -0,0 +1,8 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub mod slashing;
pub mod staking;
pub use staking::query::StakingQueryClient;
// pub use slashing::query
@@ -0,0 +1,4 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub mod query;
@@ -0,0 +1,2 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
@@ -0,0 +1,8 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub mod query;
pub use cosmrs::staking::{
QueryHistoricalInfoResponse, QueryValidatorResponse, QueryValidatorsResponse, Validator,
};
@@ -0,0 +1,78 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use super::{QueryHistoricalInfoResponse, QueryValidatorResponse, QueryValidatorsResponse};
use crate::nyxd::error::NyxdError;
use crate::nyxd::{CosmWasmClient, PageRequest};
use async_trait::async_trait;
use cosmrs::proto::cosmos::staking::v1beta1::{
QueryHistoricalInfoRequest as ProtoQueryHistoricalInfoRequest,
QueryHistoricalInfoResponse as ProtoQueryHistoricalInfoResponse,
QueryValidatorRequest as ProtoQueryValidatorRequest,
QueryValidatorResponse as ProtoQueryValidatorResponse,
QueryValidatorsRequest as ProtoQueryValidatorsRequest,
QueryValidatorsResponse as ProtoQueryValidatorsResponse,
};
use cosmrs::staking::{QueryHistoricalInfoRequest, QueryValidatorRequest, QueryValidatorsRequest};
use cosmrs::AccountId;
// TODO: change trait restriction from `CosmWasmClient` to `TendermintRpcClient`
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
pub trait StakingQueryClient: CosmWasmClient {
async fn historical_info(&self, height: i64) -> Result<QueryHistoricalInfoResponse, NyxdError> {
let path = Some("/cosmos.staking.v1beta1.Query/HistoricalInfo".to_owned());
let req = QueryHistoricalInfoRequest { height };
let res = self
.make_abci_query::<ProtoQueryHistoricalInfoRequest, ProtoQueryHistoricalInfoResponse>(
path,
req.into(),
)
.await?;
Ok(res.try_into()?)
}
async fn validator(
&self,
validator_addr: AccountId,
) -> Result<QueryValidatorResponse, NyxdError> {
let path = Some("/cosmos.staking.v1beta1.Query/Validator".to_owned());
let req = QueryValidatorRequest { validator_addr };
let res = self
.make_abci_query::<ProtoQueryValidatorRequest, ProtoQueryValidatorResponse>(
path,
req.into(),
)
.await?;
Ok(res.try_into()?)
}
async fn validators(
&self,
status: String,
pagination: Option<PageRequest>,
) -> Result<QueryValidatorsResponse, NyxdError> {
let path = Some("/cosmos.staking.v1beta1.Query/Validators".to_owned());
let req = QueryValidatorsRequest { status, pagination };
let res = self
.make_abci_query::<ProtoQueryValidatorsRequest, ProtoQueryValidatorsResponse>(
path,
req.into(),
)
.await?;
Ok(res.try_into()?)
}
}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl<T> StakingQueryClient for T where T: CosmWasmClient {}
@@ -29,23 +29,30 @@ use tendermint_rpc::endpoint::*;
use tendermint_rpc::{Error as TendermintRpcError, Order};
use url::Url;
pub use crate::nyxd::cosmwasm_client::client_traits::{CosmWasmClient, SigningCosmWasmClient};
pub use crate::nyxd::fee::Fee;
pub use crate::nyxd::{
cosmwasm_client::{
client_traits::{CosmWasmClient, SigningCosmWasmClient},
module_traits::{self, StakingQueryClient},
},
fee::Fee,
};
pub use crate::rpc::TendermintRpcClient;
pub use coin::Coin;
pub use cosmrs::bank::MsgSend;
pub use cosmrs::tendermint::abci::{
response::DeliverTx, types::ExecTxResult, Event, EventAttribute,
pub use cosmrs::{
bank::MsgSend,
bip32,
crypto::PublicKey,
query::{PageRequest, PageResponse},
tendermint::{
abci::{response::DeliverTx, types::ExecTxResult, Event, EventAttribute},
block::Height,
hash::{self, Algorithm, Hash},
validator::Info as TendermintValidatorInfo,
Time as TendermintTime,
},
tx::{self, Msg},
AccountId, Any, Coin as CosmosCoin, Denom, Gas,
};
pub use cosmrs::tendermint::block::Height;
pub use cosmrs::tendermint::hash::{self, Algorithm, Hash};
pub use cosmrs::tendermint::validator::Info as TendermintValidatorInfo;
pub use cosmrs::tendermint::Time as TendermintTime;
pub use cosmrs::tx::Msg;
pub use cosmrs::tx::{self};
pub use cosmrs::Coin as CosmosCoin;
pub use cosmrs::Gas;
pub use cosmrs::{bip32, AccountId, Denom};
pub use cosmwasm_std::Coin as CosmWasmCoin;
pub use cw2;
pub use cw3;
@@ -55,9 +62,8 @@ pub use fee::{gas_price::GasPrice, GasAdjustable, GasAdjustment};
pub use tendermint_rpc::{
endpoint::{tx::Response as TxResponse, validators::Response as ValidatorResponse},
query::Query,
Paging,
Paging, Request, Response, SimpleRequest,
};
pub use tendermint_rpc::{Request, Response, SimpleRequest};
#[cfg(feature = "http-client")]
use crate::http_client;
@@ -97,6 +103,14 @@ impl Config {
}
}
impl TryFrom<NymNetworkDetails> for Config {
type Error = NyxdError;
fn try_from(value: NymNetworkDetails) -> Result<Self, Self::Error> {
Config::try_from_nym_network_details(&value)
}
}
#[derive(Debug)]
pub struct NyxdClient<C, S = NoSigner> {
client: MaybeSigningClient<C, S>,
@@ -728,7 +742,7 @@ where
where
H: Into<Height> + Send,
{
self.client.validators(height, paging).await
TendermintRpcClient::validators(&self.client, height, paging).await
}
async fn latest_consensus_params(
@@ -803,14 +817,6 @@ where
}
}
#[async_trait]
impl<C, S> CosmWasmClient for NyxdClient<C, S>
where
C: TendermintRpcClient + Send + Sync,
S: Send + Sync,
{
}
impl<C, S> OfflineSigner for NyxdClient<C, S>
where
S: OfflineSigner,
+1 -1
View File
@@ -73,7 +73,7 @@ where
P: AsRef<Path>,
{
let path = path.as_ref();
log::debug!("trying to save config file to {}", path.display());
log::info!("saving config file to {}", path.display());
if let Some(parent) = path.parent() {
create_dir_all(parent)?;
+3 -3
View File
@@ -14,14 +14,14 @@ thiserror = { workspace = true }
tokio = { version = "1.24.1", features = ["sync"]}
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.sqlx]
version = "0.5"
workspace = true
features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"]
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio]
version = "1.24.1"
workspace = true
features = [ "rt-multi-thread", "net", "signal", "fs" ]
[build-dependencies]
sqlx = { version = "0.5", features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"] }
sqlx = { workspace = true, features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"] }
tokio = { version = "1.24.1", features = ["rt-multi-thread", "macros"] }
+63
View File
@@ -81,6 +81,15 @@ ExitPolicy accept6 *6:119
ExitPolicy accept *4:120
ExitPolicy reject6 [FC00::]/7:*
# Portless
ExitPolicy accept *:0
ExitPolicy accept *4:0
ExitPolicy accept *6:0
ExitPolicy reject *:0
ExitPolicy reject *4:0
ExitPolicy reject *6:0
#ExitPolicy accept *:8080 #and another comment here
ExitPolicy reject FE80:0000:0000:0000:0202:B3FF:FE1E:8329:*
@@ -184,6 +193,60 @@ ExitPolicy reject *:*
},
);
// ExitPolicy accept *:0
expected.push(
Accept,
AddressPortPattern {
ip_pattern: IpPattern::Star,
ports: PortRange::new_zero(),
},
);
// ExitPolicy accept *4:0
expected.push(
Accept,
AddressPortPattern {
ip_pattern: IpPattern::V4Star,
ports: PortRange::new_zero(),
},
);
// ExitPolicy accept *6:0
expected.push(
Accept,
AddressPortPattern {
ip_pattern: IpPattern::V6Star,
ports: PortRange::new_zero(),
},
);
// ExitPolicy reject *:0
expected.push(
Reject,
AddressPortPattern {
ip_pattern: IpPattern::Star,
ports: PortRange::new_zero(),
},
);
// ExitPolicy reject *4:0
expected.push(
Reject,
AddressPortPattern {
ip_pattern: IpPattern::V4Star,
ports: PortRange::new_zero(),
},
);
// ExitPolicy reject *6:0
expected.push(
Reject,
AddressPortPattern {
ip_pattern: IpPattern::V6Star,
ports: PortRange::new_zero(),
},
);
// ExitPolicy FE80:0000:0000:0000:0202:B3FF:FE1E:8329:*
expected.push(
Reject,
+37 -14
View File
@@ -264,7 +264,13 @@ mod stringified_ip_pattern {
impl AddressPortPattern {
/// Return true iff this pattern matches a given address and port.
pub fn matches(&self, addr: &IpAddr, port: u16) -> bool {
self.ip_pattern.matches(addr) && self.ports.contains(port)
// For backward compatibility, we treat port 0 as a wildcard until all gateways have
// upgraded, at which point we can add *:0 to the policy list.
if port == 0 {
self.ip_pattern.matches(addr)
} else {
self.ip_pattern.matches(addr) && self.ports.contains(port)
}
}
/// As matches, but accept a SocketAddr.
@@ -395,19 +401,9 @@ fn parse_addr(s: &str) -> Result<IpAddr, PolicyError> {
})
}
/// Helper: try to parse a port making sure it's non-zero
fn parse_port(s: &str) -> Result<u16, PolicyError> {
let port = s
.parse::<u16>()
.map_err(|_| PolicyError::InvalidPort { raw: s.to_string() })?;
if port == 0 {
Err(PolicyError::InvalidPort {
raw: port.to_string(),
})
} else {
Ok(port)
}
s.parse::<u16>()
.map_err(|_| PolicyError::InvalidPort { raw: s.to_string() })
}
impl FromStr for IpPattern {
@@ -494,6 +490,10 @@ impl PortRange {
PortRange::new_unchecked(1, 65535)
}
pub fn new_zero() -> Self {
PortRange { start: 0, end: 0 }
}
/// Create a new PortRange.
///
/// The Portrange contains all ports between `start` and `end` inclusive.
@@ -574,6 +574,7 @@ mod test {
check("marzipan:80");
check("1.2.3.4:90-80");
check("1.2.3.4:0-80");
check("1.2.3.4/100:8888");
check("[1.2.3.4]/16:80");
check("[::1]/130:8888");
@@ -612,6 +613,22 @@ mod test {
check("0.0.0.0/0:*", &["127.0.0.1:80"], &["[f00b::]:80"]);
check("[::]/0:*", &["[f00b::]:80"], &["127.0.0.1:80"]);
check(
"*:0",
&["1.2.3.4:0", "[::1]:0", "9.0.0.0:0"],
&["1.2.3.4:443", "[::1]:500", "9.0.0.0:80", "[::1]:80"],
);
check(
"*4:0",
&["1.2.3.4:0", "9.0.0.0:0"],
&["1.2.3.4:443", "9.0.0.0:80", "[::1]:0", "[::1]:80"],
);
check(
"*6:0",
&["[::1]:0"],
&["[::1]:80", "1.2.3.4:0", "1.2.3.4:443"],
);
}
#[test]
@@ -620,6 +637,7 @@ mod test {
policy.push(AddressPolicyAction::Accept, "*:443".parse()?);
policy.push(AddressPolicyAction::Accept, "[::1]:80".parse()?);
policy.push(AddressPolicyAction::Reject, "*:80".parse()?);
policy.push(AddressPolicyAction::Accept, "*:0".parse()?);
let policy = policy; // drop mut
assert!(policy
@@ -640,6 +658,9 @@ mod test {
assert!(policy
.allows_sockaddr(&"127.0.0.1:66".parse().unwrap())
.is_none());
assert!(policy
.allows_sockaddr(&"127.0.0.1:0".parse().unwrap())
.unwrap());
Ok(())
}
@@ -672,7 +693,6 @@ mod test {
assert_eq!("*".parse::<PortRange>().unwrap(), PortRange::new_all());
assert!("hello".parse::<PortRange>().is_err());
assert!("0".parse::<PortRange>().is_err());
assert!("65536".parse::<PortRange>().is_err());
assert!("65537".parse::<PortRange>().is_err());
assert!("1-2-3".parse::<PortRange>().is_err());
@@ -680,6 +700,9 @@ mod test {
assert!("1-".parse::<PortRange>().is_err());
assert!("-2".parse::<PortRange>().is_err());
assert!("-".parse::<PortRange>().is_err());
assert_eq!("0".parse::<PortRange>().unwrap(), PortRange::new_zero(),);
assert!("0-1".parse::<PortRange>().is_err());
}
#[test]
+13 -1
View File
@@ -111,6 +111,7 @@ impl NymNetworkDetails {
.with_additional_validator_endpoint(ValidatorDetails::new(
var(var_names::NYXD).expect("nyxd validator not set"),
Some(var(var_names::NYM_API).expect("nym api not set")),
get_optional_env(var_names::NYXD_WEBSOCKET),
))
.with_mixnet_contract(Some(
var(var_names::MIXNET_CONTRACT_ADDRESS).expect("mixnet contract not set"),
@@ -340,6 +341,9 @@ impl DenomDetailsOwned {
pub struct ValidatorDetails {
// it is assumed those values are always valid since they're being provided in our defaults file
pub nyxd_url: String,
//
pub websocket_url: Option<String>,
// Right now api_url is optional as we are not running the api reliably on all validators
// however, later on it should be a mandatory field
pub api_url: Option<String>,
@@ -347,9 +351,10 @@ pub struct ValidatorDetails {
}
impl ValidatorDetails {
pub fn new<S: Into<String>>(nyxd_url: S, api_url: Option<S>) -> Self {
pub fn new<S: Into<String>>(nyxd_url: S, api_url: Option<S>, websocket_url: Option<S>) -> Self {
ValidatorDetails {
nyxd_url: nyxd_url.into(),
websocket_url: websocket_url.map(Into::into),
api_url: api_url.map(Into::into),
}
}
@@ -357,6 +362,7 @@ impl ValidatorDetails {
pub fn new_nyxd_only<S: Into<String>>(nyxd_url: S) -> Self {
ValidatorDetails {
nyxd_url: nyxd_url.into(),
websocket_url: None,
api_url: None,
}
}
@@ -372,6 +378,12 @@ impl ValidatorDetails {
.as_ref()
.map(|url| url.parse().expect("the provided api url is invalid!"))
}
pub fn websocket_url(&self) -> Option<Url> {
self.websocket_url
.as_ref()
.map(|url| url.parse().expect("the provided websocket url is invalid!"))
}
}
fn fix_deprecated_environmental_variables() {
+15 -1
View File
@@ -3,6 +3,7 @@
use crate::var_names;
use crate::{DenomDetails, ValidatorDetails};
use std::str::FromStr;
pub const NETWORK_NAME: &str = "mainnet";
@@ -25,6 +26,7 @@ pub const REWARDING_VALIDATOR_ADDRESS: &str = "n10yyd98e2tuwu0f7ypz9dy3hhjw7v772
pub const STATISTICS_SERVICE_DOMAIN_ADDRESS: &str = "https://mainnet-stats.nymte.ch:8090/";
pub const NYXD_URL: &str = "https://rpc.nymtech.net";
pub const NYM_API: &str = "https://validator.nymtech.net/api/";
pub const NYXD_WS: &str = "wss://rpc.nymtech.net/websocket";
pub const EXPLORER_API: &str = "https://explorer.nymtech.net/api/";
// I'm making clippy mad on purpose, because that url HAS TO be updated and deployed before merging
@@ -32,7 +34,11 @@ pub const EXIT_POLICY_URL: &str =
"https://nymtech.net/.wellknown/network-requester/exit-policy.txt";
pub(crate) fn validators() -> Vec<ValidatorDetails> {
vec![ValidatorDetails::new(NYXD_URL, Some(NYM_API))]
vec![ValidatorDetails::new(
NYXD_URL,
Some(NYM_API),
Some(NYXD_WS),
)]
}
const DEFAULT_SUFFIX: &str = "_MAINNET_DEFAULT";
@@ -60,6 +66,12 @@ pub fn read_var_if_not_default(var: &str) -> Option<String> {
}
}
pub fn read_parsed_var_if_not_default<T: FromStr>(var: &str) -> Option<Result<T, T::Err>> {
read_var_if_not_default(var)
.as_deref()
.map(FromStr::from_str)
}
pub fn export_to_env() {
set_var_to_default(var_names::CONFIGURED, "true");
set_var_to_default(var_names::NETWORK_NAME, NETWORK_NAME);
@@ -104,6 +116,7 @@ pub fn export_to_env() {
);
set_var_to_default(var_names::NYXD, NYXD_URL);
set_var_to_default(var_names::NYM_API, NYM_API);
set_var_to_default(var_names::NYXD_WEBSOCKET, NYXD_WS);
set_var_to_default(var_names::EXPLORER_API, EXPLORER_API);
set_var_to_default(var_names::EXIT_POLICY_URL, EXIT_POLICY_URL);
}
@@ -152,6 +165,7 @@ pub fn export_to_env_if_not_set() {
);
set_var_conditionally_to_default(var_names::NYXD, NYXD_URL);
set_var_conditionally_to_default(var_names::NYM_API, NYM_API);
set_var_conditionally_to_default(var_names::NYXD_WEBSOCKET, NYXD_WS);
set_var_conditionally_to_default(var_names::EXPLORER_API, EXPLORER_API);
set_var_conditionally_to_default(var_names::EXIT_POLICY_URL, EXIT_POLICY_URL);
}
+1
View File
@@ -26,6 +26,7 @@ pub const SERVICE_PROVIDER_DIRECTORY_CONTRACT_ADDRESS: &str =
pub const NAME_SERVICE_CONTRACT_ADDRESS: &str = "NAME_SERVICE_CONTRACT_ADDRESS";
pub const NYXD: &str = "NYXD";
pub const NYM_API: &str = "NYM_API";
pub const NYXD_WEBSOCKET: &str = "NYXD_WS";
pub const EXPLORER_API: &str = "EXPLORER_API";
pub const EXIT_POLICY_URL: &str = "EXIT_POLICY";
+1 -1
View File
@@ -255,7 +255,7 @@ fn bench_coconut(c: &mut Criterion) {
b.iter(|| {
verify_partial_blind_signature(
&params,
&blind_sign_request,
blind_sign_request.get_private_attributes_pedersen_commitments(),
&public_attributes,
random_blind_signature,
partial_verification_key,
+2
View File
@@ -43,3 +43,5 @@ mod utils;
pub type Attribute = bls12_381::Scalar;
pub type PrivateAttribute = Attribute;
pub type PublicAttribute = Attribute;
pub use bls12_381::G1Projective;
+7 -8
View File
@@ -362,12 +362,12 @@ pub fn blind_sign(
/// The function returns `true` if the partial blind signature is valid, and `false` otherwise.
pub fn verify_partial_blind_signature(
params: &Parameters,
blind_sign_request: &BlindSignRequest,
private_attribute_commitments: &[G1Projective],
public_attributes: &[&Attribute],
blind_sig: &BlindedSignature,
partial_verification_key: &VerificationKey,
) -> bool {
let num_private_attributes = blind_sign_request.private_attributes_commitments.len();
let num_private_attributes = private_attribute_commitments.len();
if num_private_attributes + public_attributes.len() > partial_verification_key.beta_g2.len() {
return false;
}
@@ -388,8 +388,7 @@ pub fn verify_partial_blind_signature(
];
// for each private attribute, add (cm_i, beta_i) to the miller terms
for (private_attr_commit, beta_g2) in blind_sign_request
.private_attributes_commitments
for (private_attr_commit, beta_g2) in private_attribute_commitments
.iter()
.zip(&partial_verification_key.beta_g2)
{
@@ -422,7 +421,7 @@ pub fn verify_partial_blind_signature(
// is equivalent to checking e(a, b) • e(c, d)^{-1} == id
// and thus to e(a, b) • e(c^{-1}, d) == id
//
// compute e(c^1, g2) • e(s, alpha) • e(cm_0, beta_0) • e(cm_i, beta_i) • (s^pub_0, beta_{i+1}) (s^pub_j, beta_{i + j})
// compute e(c^{-1}, g2) • e(s, alpha) • e(cm_0, beta_0) • e(cm_i, beta_i) • (s^pub_0, beta_{i+1}) (s^pub_j, beta_{i + j})
multi_miller_loop(&terms_refs)
.final_exponentiation()
.is_identity()
@@ -519,7 +518,7 @@ mod tests {
assert!(verify_partial_blind_signature(
&params,
&request,
&request.private_attributes_commitments,
&public_attributes,
&blind_sig,
validator_keypair.verification_key()
@@ -539,7 +538,7 @@ mod tests {
assert!(verify_partial_blind_signature(
&params,
&request,
&request.private_attributes_commitments,
&[],
&blind_sig,
validator_keypair.verification_key()
@@ -568,7 +567,7 @@ mod tests {
// this assertion should fail, as we try to verify with a wrong validator key
assert!(!verify_partial_blind_signature(
&params,
&request,
&request.private_attributes_commitments,
&public_attributes,
&blind_sig,
validator2_keypair.verification_key()
+11 -11
View File
@@ -108,6 +108,17 @@ pub fn transpose_matrix<T: Debug>(matrix: Vec<Vec<T>>) -> Vec<Vec<T>> {
.collect::<Vec<_>>()
}
#[macro_export]
macro_rules! random_scalars_refs {
( $x: ident, $params: expr, $n: expr ) => {
let _vec = $params.n_random_scalars($n);
#[allow(clippy::map_identity)]
let $x = _vec.iter().collect::<Vec<_>>();
};
}
pub use random_scalars_refs;
#[cfg(test)]
pub mod tests {
use super::*;
@@ -170,14 +181,3 @@ pub mod tests {
.collect()
}
}
#[macro_export]
macro_rules! random_scalars_refs {
( $x: ident, $params: expr, $n: expr ) => {
let _vec = $params.n_random_scalars($n);
#[allow(clippy::map_identity)]
let $x = _vec.iter().collect::<Vec<_>>();
};
}
pub use random_scalars_refs;
+37
View File
@@ -0,0 +1,37 @@
[package]
name = "nyxd-scraper"
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]
async-trait.workspace = true
const_format = "0.2.32"
cosmrs.workspace = true
eyre = "0.6.9"
futures.workspace = true
sha2 = "0.10.8"
sqlx = { workspace = true, features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate", "time"] }
tendermint.workspace = true
tendermint-rpc = { workspace = true, features = ["websocket-client", "http-client"] }
thiserror.workspace = true
tokio = { workspace = true, features = ["full"] }
tokio-stream = "0.1.14"
tokio-util = { version = "0.7.10", features = ["rt"]}
tracing.workspace = true
url.workspace = true
# TEMP
nym-bin-common = { path = "../bin-common", features = ["basic_tracing"]}
[build-dependencies]
sqlx = { workspace = true, features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"] }
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
+28
View File
@@ -0,0 +1,28 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
#[tokio::main]
async fn main() {
use sqlx::{Connection, SqliteConnection};
use std::env;
let out_dir = env::var("OUT_DIR").unwrap();
let database_path = format!("{out_dir}/scraper-example.sqlite");
let mut conn = SqliteConnection::connect(&format!("sqlite://{database_path}?mode=rwc"))
.await
.expect("Failed to create SQLx database connection");
sqlx::migrate!("./sql_migrations")
.run(&mut conn)
.await
.expect("Failed to perform SQLx migrations");
#[cfg(target_family = "unix")]
println!("cargo:rustc-env=DATABASE_URL=sqlite://{}", &database_path);
#[cfg(target_family = "windows")]
// for some strange reason we need to add a leading `/` to the windows path even though it's
// not a valid windows path... but hey, it works...
println!("cargo:rustc-env=DATABASE_URL=sqlite:///{}", &database_path);
}
@@ -0,0 +1,10 @@
/*
* Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
* SPDX-License-Identifier: Apache-2.0
*/
CREATE TABLE METADATA
(
id INTEGER PRIMARY KEY CHECK (id = 0),
last_processed_height INTEGER NOT NULL
);
@@ -0,0 +1,76 @@
CREATE TABLE validator
(
consensus_address TEXT NOT NULL PRIMARY KEY, /* Validator consensus address */
consensus_pubkey TEXT NOT NULL UNIQUE /* Validator consensus public key */
);
CREATE TABLE pre_commit
(
validator_address TEXT NOT NULL REFERENCES validator (consensus_address),
height BIGINT NOT NULL,
timestamp TIMESTAMP WITHOUT TIME ZONE NOT NULL,
voting_power BIGINT NOT NULL,
proposer_priority BIGINT NOT NULL,
UNIQUE (validator_address, timestamp)
);
CREATE INDEX pre_commit_validator_address_index ON pre_commit (validator_address);
CREATE INDEX pre_commit_height_index ON pre_commit (height);
CREATE TABLE block
(
height BIGINT UNIQUE PRIMARY KEY,
hash TEXT NOT NULL UNIQUE,
num_txs INTEGER DEFAULT 0,
total_gas BIGINT DEFAULT 0,
proposer_address TEXT REFERENCES validator (consensus_address),
timestamp TIMESTAMP WITHOUT TIME ZONE NOT NULL
);
CREATE INDEX block_height_index ON block (height);
CREATE INDEX block_hash_index ON block (hash);
CREATE INDEX block_proposer_address_index ON block (proposer_address);
-- no JSONB in sqlite (yet) : )
CREATE TABLE "transaction"
(
hash TEXT UNIQUE NOT NULL,
height BIGINT NOT NULL REFERENCES block (height),
"index" INTEGER NOT NULL,
success BOOLEAN NOT NULL,
/* Body */
num_messages INTEGER NOT NULL,
-- messages JSONB NOT NULL,-- DEFAULT '[]'::JSONB,
memo TEXT,
-- signatures TEXT[] NOT NULL,
/* AuthInfo */
-- signer_infos JSONB NOT NULL,-- DEFAULT '[]'::JSONB,
-- fee JSONB NOT NULL,-- DEFAULT '{}'::JSONB,
/* Tx response */
gas_wanted BIGINT DEFAULT 0,
gas_used BIGINT DEFAULT 0,
raw_log TEXT
-- logs JSONB
);
CREATE INDEX transaction_hash_index ON "transaction" (hash);
CREATE INDEX transaction_height_index ON "transaction" (height);
CREATE TABLE message
(
transaction_hash TEXT NOT NULL REFERENCES "transaction" (hash),
"index" BIGINT NOT NULL,
type TEXT NOT NULL,
-- value JSONB NOT NULL,
-- involved_accounts_addresses TEXT[] NOT NULL,
height BIGINT NOT NULL,
CONSTRAINT unique_message_per_tx UNIQUE (transaction_hash, "index")
);
CREATE INDEX message_transaction_hash_index ON message (transaction_hash);
CREATE INDEX message_type_index ON message (type);
CREATE TABLE pruning
(
last_pruned_height BIGINT NOT NULL
);
@@ -0,0 +1,65 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::block_processor::MAX_RANGE_SIZE;
use std::cmp::min;
use std::collections::VecDeque;
use std::ops::Range;
pub(crate) fn split_request_range(request_range: Range<u32>) -> VecDeque<Range<u32>> {
let mut requests = VecDeque::new();
let mut start = request_range.start;
let mut end = min(request_range.end, start + MAX_RANGE_SIZE as u32);
loop {
requests.push_back(start..end);
start = min(start + MAX_RANGE_SIZE as u32, request_range.end);
end = min(end + MAX_RANGE_SIZE as u32, request_range.end);
if start == end {
break;
}
}
requests
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn splitting_request_range() {
let range = 0..100;
let mut expected = VecDeque::new();
expected.push_back(0..30);
expected.push_back(30..60);
expected.push_back(60..90);
expected.push_back(90..100);
assert_eq!(expected, split_request_range(range));
let range = 0..30;
let mut expected = VecDeque::new();
expected.push_back(0..30);
assert_eq!(expected, split_request_range(range));
let range = 0..60;
let mut expected = VecDeque::new();
expected.push_back(0..30);
expected.push_back(30..60);
assert_eq!(expected, split_request_range(range));
let range = 0..5;
let mut expected = VecDeque::new();
expected.push_back(0..5);
assert_eq!(expected, split_request_range(range));
let range = 123..200;
let mut expected = VecDeque::new();
expected.push_back(123..153);
expected.push_back(153..183);
expected.push_back(183..200);
assert_eq!(expected, split_request_range(range));
}
}
@@ -0,0 +1,332 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::block_processor::helpers::split_request_range;
use crate::block_processor::types::BlockToProcess;
use crate::block_requester::BlockRequest;
use crate::error::ScraperError;
use crate::modules::{BlockModule, MsgModule, TxModule};
use crate::rpc_client::RpcClient;
use crate::storage::{persist_block, ScraperStorage};
use futures::StreamExt;
use std::collections::{BTreeMap, HashSet, VecDeque};
use std::ops::{Add, Range};
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::mpsc::{Sender, UnboundedReceiver};
use tokio::sync::Notify;
use tokio::time::{interval_at, Instant};
use tokio_stream::wrappers::UnboundedReceiverStream;
use tokio_util::sync::CancellationToken;
use tracing::{debug, error, info, warn};
mod helpers;
pub(crate) mod types;
const MISSING_BLOCKS_CHECK_INTERVAL: Duration = Duration::from_secs(30);
const MAX_MISSING_BLOCKS_DELAY: Duration = Duration::from_secs(15);
const MAX_RANGE_SIZE: usize = 30;
#[derive(Debug, Default)]
struct PendingSync {
request_in_flight: HashSet<u32>,
queued_requests: VecDeque<Range<u32>>,
}
impl PendingSync {
fn is_empty(&self) -> bool {
self.request_in_flight.is_empty() && self.queued_requests.is_empty()
}
}
pub struct BlockProcessor {
cancel: CancellationToken,
synced: Arc<Notify>,
last_processed_height: u32,
last_processed_at: Instant,
pending_sync: PendingSync,
queued_blocks: BTreeMap<u32, BlockToProcess>,
rpc_client: RpcClient,
incoming: UnboundedReceiverStream<BlockToProcess>,
block_requester: Sender<BlockRequest>,
storage: ScraperStorage,
// future work: rather than sending each msg to every msg module,
// let them subscribe based on `type_url` inside the message itself
// (like "/cosmwasm.wasm.v1.MsgExecuteContract")
block_modules: Vec<Box<dyn BlockModule + Send>>,
tx_modules: Vec<Box<dyn TxModule + Send>>,
msg_modules: Vec<Box<dyn MsgModule + Send>>,
}
impl BlockProcessor {
pub async fn new(
cancel: CancellationToken,
synced: Arc<Notify>,
incoming: UnboundedReceiver<BlockToProcess>,
block_requester: Sender<BlockRequest>,
storage: ScraperStorage,
rpc_client: RpcClient,
) -> Result<Self, ScraperError> {
let last_processed = storage.get_last_processed_height().await?;
Ok(BlockProcessor {
cancel,
synced,
last_processed_height: last_processed.try_into().unwrap_or_default(),
last_processed_at: Instant::now(),
pending_sync: Default::default(),
queued_blocks: Default::default(),
rpc_client,
incoming: incoming.into(),
block_requester,
storage,
block_modules: vec![],
tx_modules: vec![],
msg_modules: vec![],
})
}
async fn process_block(&mut self, block: BlockToProcess) -> Result<(), ScraperError> {
info!("processing block at height {}", block.height);
let full_info = self.rpc_client.try_get_full_details(block).await?;
debug!(
"this block has {} transaction(s)",
full_info.transactions.len()
);
for tx in &full_info.transactions {
debug!("{} has {} message(s)", tx.hash, tx.tx.body.messages.len());
for (index, msg) in tx.tx.body.messages.iter().enumerate() {
debug!("{index}: {:?}", msg.type_url)
}
}
// process the entire block as a transaction so that if anything fails,
// we won't end up with a corrupted storage.
let mut tx = self.storage.begin_processing_tx().await?;
persist_block(&full_info, &mut tx).await?;
// let the modules do whatever they want
// the ones wanting the full block:
for block_module in &mut self.block_modules {
block_module.handle_block(&full_info, &mut tx).await?;
}
// the ones wanting transactions:
for block_tx in full_info.transactions {
for tx_module in &mut self.tx_modules {
tx_module.handle_tx(&block_tx, &mut tx).await?;
}
// the ones concerned with individual messages
for (index, msg) in block_tx.tx.body.messages.iter().enumerate() {
for msg_module in &mut self.msg_modules {
msg_module
.handle_msg(index, msg, &block_tx, &mut tx)
.await?
}
}
}
tx.commit()
.await
.map_err(|source| ScraperError::StorageTxCommitFailure { source })?;
self.last_processed_height = full_info.block.header.height.value() as u32;
self.last_processed_at = Instant::now();
Ok(())
}
pub fn set_block_modules(&mut self, modules: Vec<Box<dyn BlockModule + Send>>) {
self.block_modules = modules;
}
pub fn set_tx_modules(&mut self, modules: Vec<Box<dyn TxModule + Send>>) {
self.tx_modules = modules;
}
pub fn set_msg_modules(&mut self, modules: Vec<Box<dyn MsgModule + Send>>) {
self.msg_modules = modules;
}
async fn maybe_request_missing_blocks(&mut self) -> Result<(), ScraperError> {
// we're still processing, so we're good
if self.last_processed_at.elapsed() < MAX_MISSING_BLOCKS_DELAY {
debug!("no need to request missing blocks");
return Ok(());
}
if self.try_request_pending().await {
return Ok(());
}
// TODO: properly fill in the gaps later with BlockRequest::Specific,
let request_range = if let Some((next_available, _)) = self.queued_blocks.first_key_value()
{
self.last_processed_height + 1..*next_available
} else {
let current_height = self.rpc_client.current_block_height().await? as u32;
self.last_processed_height + 1..current_height + 1
};
self.request_missing_blocks(request_range).await?;
Ok(())
}
async fn request_missing_blocks(
&mut self,
request_range: Range<u32>,
) -> Result<(), ScraperError> {
let request_range = if request_range.len() > MAX_RANGE_SIZE {
let mut split = split_request_range(request_range);
// SAFETY: we know that after the split of a non-empty range we have AT LEAST one value
#[allow(clippy::unwrap_used)]
let first = split.pop_front().unwrap();
self.pending_sync.queued_requests = split;
self.pending_sync.request_in_flight = first.clone().collect();
first
} else {
request_range
};
self.send_blocks_request(request_range).await
}
// technically we're not mutating self here,
// but we need it to help the compiler figure out the future is `Send`
async fn send_blocks_request(&mut self, request_range: Range<u32>) -> Result<(), ScraperError> {
debug!("requesting missing blocks: {request_range:?}");
self.block_requester
.send(BlockRequest::Range(request_range))
.await?;
Ok(())
}
async fn next_incoming(&mut self, block: BlockToProcess) {
let height = block.height;
self.pending_sync.request_in_flight.remove(&height);
if self.last_processed_height == 0 {
// this is the first time we've started up the process
debug!("setting up initial processing height");
self.last_processed_height = height - 1
}
if height <= self.last_processed_height {
warn!("we have already processed block for height {height}");
return;
}
if self.last_processed_height + 1 != height {
if self.queued_blocks.insert(height, block).is_some() {
warn!("we have already queued up block for height {height}");
}
return;
}
if let Err(err) = self.process_block(block).await {
error!("failed to process block at height {height}: {err}");
return;
}
// process as much as we can from the queue
let mut next = height + 1;
while let Some(next_block) = self.queued_blocks.remove(&next) {
if let Err(err) = self.process_block(next_block).await {
error!("failed to process queued-up block at height {next}: {err}")
}
next += 1;
}
self.try_request_pending().await;
if self.pending_sync.is_empty() {
self.synced.notify_one();
}
}
async fn try_request_pending(&mut self) -> bool {
if self.pending_sync.request_in_flight.is_empty() {
if let Some(next_sync) = self.pending_sync.queued_requests.pop_front() {
debug!(
"current request range has been resolved. requesting another bunch of blocks"
);
if let Err(err) = self.send_blocks_request(next_sync.clone()).await {
error!("failed to request resync blocks: {err}");
self.pending_sync.queued_requests.push_front(next_sync);
} else {
self.pending_sync.request_in_flight = next_sync.collect()
}
return true;
}
}
false
}
// technically we're not mutating self here,
// but we need it to help the compiler figure out the future is `Send`
async fn startup_resync(&mut self) -> Result<(), ScraperError> {
assert!(self.pending_sync.is_empty());
let latest_block = self.rpc_client.current_block_height().await? as u32;
if latest_block > self.last_processed_height && self.last_processed_height != 0 {
let request_range = self.last_processed_height + 1..latest_block + 1;
info!("we need to request {request_range:?} to resync");
self.request_missing_blocks(request_range).await?;
}
Ok(())
}
pub(crate) async fn run(&mut self) {
info!("starting processing loop");
// sure, we could be more efficient and reset it on every processed block,
// but the overhead is so minimal that it doesn't matter
let mut missing_check_interval = interval_at(
Instant::now().add(MISSING_BLOCKS_CHECK_INTERVAL),
MISSING_BLOCKS_CHECK_INTERVAL,
);
if let Err(err) = self.startup_resync().await {
error!("failed to perform startup sync: {err}");
self.cancel.cancel();
return;
};
loop {
tokio::select! {
_ = self.cancel.cancelled() => {
info!("received cancellation token");
break
}
_ = missing_check_interval.tick() => {
if let Err(err) = self.maybe_request_missing_blocks().await {
error!("failed to request missing blocks: {err}")
}
}
block = self.incoming.next() => {
match block {
Some(block) => self.next_incoming(block).await,
None => {
warn!("stopped receiving new blocks");
self.cancel.cancel();
break
}
}
}
}
}
}
}
@@ -0,0 +1,121 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::error::ScraperError;
use crate::helpers;
use tendermint::{abci, block, tx, Block, Hash};
use tendermint_rpc::endpoint::{block as block_endpoint, block_results, validators};
use tendermint_rpc::event::{Event, EventData};
// just get all everything out of tx::Response, but parse raw `tx` bytes
#[derive(Clone, Debug)]
pub struct ParsedTransactionResponse {
/// The hash of the transaction.
///
/// Deserialized from a hex-encoded string (there is a discrepancy between
/// the format used for the request and the format used for the response in
/// the Tendermint RPC).
pub hash: Hash,
pub height: block::Height,
pub index: u32,
pub tx_result: abci::types::ExecTxResult,
pub tx: cosmrs::tx::Tx,
pub proof: Option<tx::Proof>,
}
#[derive(Debug)]
pub struct FullBlockInformation {
/// Basic block information, including its signers.
pub block: Block,
/// All of the emitted events alongside any tx results.
pub results: block_results::Response,
/// Validator set for this particular block
pub validators: validators::Response,
/// Transaction results from this particular block
pub transactions: Vec<ParsedTransactionResponse>,
}
impl FullBlockInformation {
pub fn ensure_proposer(&self) -> Result<(), ScraperError> {
let block_proposer = self.block.header.proposer_address;
if !self
.validators
.validators
.iter()
.any(|v| v.address == block_proposer)
{
let proposer = helpers::validator_consensus_address(block_proposer)?;
return Err(ScraperError::BlockProposerNotInValidatorSet {
height: self.block.header.height.value() as u32,
proposer: proposer.to_string(),
});
}
Ok(())
}
}
pub(crate) struct BlockToProcess {
pub(crate) height: u32,
pub(crate) block: Block,
}
impl From<Block> for BlockToProcess {
fn from(block: Block) -> Self {
BlockToProcess {
height: block.header.height.value() as u32,
block,
}
}
}
impl TryFrom<Event> for BlockToProcess {
type Error = ScraperError;
fn try_from(event: Event) -> Result<Self, Self::Error> {
let query = event.query.clone();
// TODO: we're losing `result_begin_block` and `result_end_block` here but maybe that's fine?
let maybe_block = match event.data {
// we don't care about `NewBlock` until CometBFT 0.38, i.e. until we upgrade to wasmd 0.50
EventData::NewBlock { .. } => {
return Err(ScraperError::InvalidSubscriptionEvent {
query,
kind: "NewBlock".to_string(),
})
}
EventData::LegacyNewBlock { block, .. } => block,
EventData::Tx { .. } => {
return Err(ScraperError::InvalidSubscriptionEvent {
query,
kind: "Tx".to_string(),
})
}
EventData::GenericJsonEvent(_) => {
return Err(ScraperError::InvalidSubscriptionEvent {
query,
kind: "GenericJsonEvent".to_string(),
})
}
};
let Some(block) = maybe_block else {
return Err(ScraperError::EmptyBlockData { query });
};
Ok((*block).into())
}
}
impl From<block_endpoint::Response> for BlockToProcess {
fn from(value: block_endpoint::Response) -> Self {
value.block.into()
}
}
@@ -0,0 +1,91 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::block_processor::types::BlockToProcess;
use crate::error::ScraperError;
use crate::rpc_client::RpcClient;
use futures::StreamExt;
use std::ops::Range;
use tokio::sync::mpsc::{Receiver, UnboundedSender};
use tokio_stream::wrappers::ReceiverStream;
use tokio_util::sync::CancellationToken;
use tracing::{error, info, instrument, warn};
#[derive(Debug)]
pub enum BlockRequest {
Range(Range<u32>),
// UNIMPLEMENTED:
#[allow(dead_code)]
Specific(Vec<u32>),
}
pub(crate) struct BlockRequester {
cancel: CancellationToken,
rpc_client: RpcClient,
requests: ReceiverStream<BlockRequest>,
blocks: UnboundedSender<BlockToProcess>,
}
impl BlockRequester {
pub(crate) fn new(
cancel: CancellationToken,
rpc_client: RpcClient,
requests: Receiver<BlockRequest>,
blocks: UnboundedSender<BlockToProcess>,
) -> Self {
BlockRequester {
cancel,
rpc_client,
requests: requests.into(),
blocks,
}
}
async fn request_and_send(&self, height: u32) -> Result<(), ScraperError> {
let block = self.rpc_client.get_basic_block_details(height).await?;
self.blocks.send(block.into())?;
Ok(())
}
async fn request_blocks<I: IntoIterator<Item = u32>>(&self, heights: I) {
futures::stream::iter(heights)
.for_each_concurrent(4, |height| async move {
if let Err(err) = self.request_and_send(height).await {
error!("failed to request block data: {err}")
}
})
.await
}
#[instrument(skip(self))]
async fn handle_blocks_request(&self, request: BlockRequest) {
info!("received request for missed blocks");
match request {
BlockRequest::Range(range) => self.request_blocks(range).await,
BlockRequest::Specific(heights) => self.request_blocks(heights).await,
}
}
pub(crate) async fn run(&mut self) {
loop {
tokio::select! {
_ = self.cancel.cancelled() => {
info!("received cancellation token");
break
}
maybe_request = self.requests.next() => {
match maybe_request {
Some(request) => self.handle_blocks_request(request).await,
None => {
warn!("stopped receiving new requests");
self.cancel.cancel();
break
}
}
}
}
}
}
}
+47
View File
@@ -0,0 +1,47 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use const_format::concatcp;
// TODO: make those configurable via 'NymNetworkDetails'
// BECH32_PREFIX defines the main SDK Bech32 prefix of an account's address
pub const BECH32_PREFIX: &str = "n";
// ACCOUNT_PREFIX is the prefix for account keys
pub const ACCOUNT_PREFIX: &str = "acc";
// VALIDATOR_PREFIX is the prefix for validator keys
pub const VALIDATOR_PREFIX: &str = "val";
// CONSENSUS_PREFIX is the prefix for consensus keys
pub const CONSENSUS_PREFIX: &str = "cons";
// PUBKEY_PREFIX is the prefix for public keys
pub const PUBKEY_PREFIX: &str = "pub";
// OPERATOR_PREFIX is the prefix for operator keys
pub const OPERATOR_PREFIX: &str = "oper";
// ADDRESS_PREFIX is the prefix for addresses
pub const ADDRESS_PREFIX: &str = "addr";
// BECH32_ACCOUNT_ADDRESS_PREFIX defines the Bech32 prefix of an account's address
pub const BECH32_ACCOUNT_ADDRESS_PREFIX: &str = BECH32_PREFIX;
// BECH32_ACCOUNT_PUBKEY_PREFIX defines the Bech32 prefix of an account's public key
pub const BECH32_ACCOUNT_PUBKEY_PREFIX: &str = concatcp!(BECH32_PREFIX, PUBKEY_PREFIX);
// BECH32_VALIDATOR_ADDRESS_PREFIX defines the Bech32 prefix of a validator's operator address
pub const BECH32_VALIDATOR_ADDRESS_PREFIX: &str =
concatcp!(BECH32_PREFIX, VALIDATOR_PREFIX, OPERATOR_PREFIX);
// BECH32_VALIDATOR_PUBKEY_PREFIX defines the Bech32 prefix of a validator's operator public key
pub const BECH32_VALIDATOR_PUBKEY_PREFIX: &str = concatcp!(
BECH32_PREFIX,
VALIDATOR_PREFIX,
OPERATOR_PREFIX,
PUBKEY_PREFIX
);
// BECH32_CONSENSUS_ADDRESS_PREFIX defines the Bech32 prefix of a consensus node address
pub const BECH32_CONSENSUS_ADDRESS_PREFIX: &str =
concatcp!(BECH32_PREFIX, VALIDATOR_PREFIX, CONSENSUS_PREFIX);
// BECH32_CONESNSUS_PUBKEY_PREFIX defines the Bech32 prefix of a consensus node public key
pub const BECH32_CONESNSUS_PUBKEY_PREFIX: &str = concatcp!(
BECH32_PREFIX,
VALIDATOR_PREFIX,
CONSENSUS_PREFIX,
PUBKEY_PREFIX
);
+131
View File
@@ -0,0 +1,131 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use tendermint::Hash;
use thiserror::Error;
use tokio::sync::mpsc::error::SendError;
#[derive(Debug, Error)]
pub enum ScraperError {
#[error("experienced internal database error: {0}")]
InternalDatabaseError(#[from] sqlx::Error),
#[error("failed to perform startup SQL migration: {0}")]
StartupMigrationFailure(#[from] sqlx::migrate::MigrateError),
#[error("can't add any modules to the scraper as it's already running")]
ScraperAlreadyRunning,
#[error("failed to establish websocket connection to {url}: {source}")]
WebSocketConnectionFailure {
url: String,
#[source]
source: tendermint_rpc::Error,
},
#[error("failed to establish rpc connection to {url}: {source}")]
HttpConnectionFailure {
url: String,
#[source]
source: tendermint_rpc::Error,
},
#[error("failed to create chain subscription: {source}")]
ChainSubscriptionFailure {
#[source]
source: tendermint_rpc::Error,
},
#[error("could not obtain basic block information at height: {height}: {source}")]
BlockQueryFailure {
height: u32,
#[source]
source: tendermint_rpc::Error,
},
#[error("could not obtain block results information at height: {height}: {source}")]
BlockResultsQueryFailure {
height: u32,
#[source]
source: tendermint_rpc::Error,
},
#[error("could not obtain validators information at height: {height}: {source}")]
ValidatorsQueryFailure {
height: u32,
#[source]
source: tendermint_rpc::Error,
},
#[error("could not obtain tx results for tx: {hash}: {source}")]
TxResultsQueryFailure {
hash: Hash,
#[source]
source: tendermint_rpc::Error,
},
#[error("could not obtain current abci info: {source}")]
AbciInfoQueryFailure {
#[source]
source: tendermint_rpc::Error,
},
#[error("could not parse tx {hash}: {source}")]
TxParseFailure {
hash: Hash,
#[source]
source: cosmrs::ErrorReport,
},
#[error("received an invalid chain subscription event of kind {kind} while we were waiting for new block data (query: '{query}')")]
InvalidSubscriptionEvent { query: String, kind: String },
#[error("received block data was empty (query: '{query}')")]
EmptyBlockData { query: String },
#[error("reached maximum number of allowed errors for subscription events")]
MaximumSubscriptionFailures,
#[error("failed to begin storage tx: {source}")]
StorageTxBeginFailure {
#[source]
source: sqlx::Error,
},
#[error("failed to commit storage tx: {source}")]
StorageTxCommitFailure {
#[source]
source: sqlx::Error,
},
#[error("failed to send on a closed channel")]
ClosedChannelError,
#[error("failed to parse validator's address: {source}")]
MalformedValidatorAddress {
#[source]
source: eyre::Report,
},
#[error("failed to parse validator's address: {source}")]
MalformedValidatorPubkey {
#[source]
source: eyre::Report,
},
#[error(
"could not find the block proposer ('{proposer}') for height {height} in the validator set"
)]
BlockProposerNotInValidatorSet { height: u32, proposer: String },
#[error(
"could not find validator information for {address}; the validator has signed a commit"
)]
MissingValidatorInfoCommitted { address: String },
}
impl<T> From<SendError<T>> for ScraperError {
fn from(_: SendError<T>) -> Self {
ScraperError::ClosedChannelError
}
}
+46
View File
@@ -0,0 +1,46 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::block_processor::types::ParsedTransactionResponse;
use crate::constants::{BECH32_CONESNSUS_PUBKEY_PREFIX, BECH32_CONSENSUS_ADDRESS_PREFIX};
use crate::error::ScraperError;
use cosmrs::AccountId;
use sha2::{Digest, Sha256};
use tendermint::{account, PublicKey};
use tendermint::{validator, Hash};
use tendermint_rpc::endpoint::validators;
pub(crate) fn tx_hash<M: AsRef<[u8]>>(raw_tx: M) -> Hash {
Hash::Sha256(Sha256::digest(raw_tx).into())
}
pub(crate) fn validator_pubkey_to_bech32(pubkey: PublicKey) -> Result<AccountId, ScraperError> {
// TODO: this one seem to attach additional prefix to they pubkeys, is that what we want instead maybe?
// Ok(pubkey.to_bech32(BECH32_CONESNSUS_PUBKEY_PREFIX))
AccountId::new(BECH32_CONESNSUS_PUBKEY_PREFIX, &pubkey.to_bytes())
.map_err(|source| ScraperError::MalformedValidatorPubkey { source })
}
pub(crate) fn validator_consensus_address(id: account::Id) -> Result<AccountId, ScraperError> {
AccountId::new(BECH32_CONSENSUS_ADDRESS_PREFIX, id.as_ref())
.map_err(|source| ScraperError::MalformedValidatorAddress { source })
}
pub(crate) fn tx_gas_sum(txs: &[ParsedTransactionResponse]) -> i64 {
txs.iter().map(|tx| tx.tx_result.gas_used).sum()
}
pub(crate) fn validator_info(
id: account::Id,
validators: &validators::Response,
) -> Result<&validator::Info, ScraperError> {
match validators.validators.iter().find(|v| v.address == id) {
Some(info) => Ok(info),
None => {
let addr = validator_consensus_address(id)?;
Err(ScraperError::MissingValidatorInfoCommitted {
address: addr.to_string(),
})
}
}
}
+19
View File
@@ -0,0 +1,19 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
#![warn(clippy::expect_used)]
#![warn(clippy::unwrap_used)]
pub(crate) mod block_processor;
pub(crate) mod block_requester;
pub mod constants;
pub mod error;
pub(crate) mod helpers;
pub mod modules;
pub(crate) mod rpc_client;
pub(crate) mod scraper;
pub mod storage;
pub use modules::{BlockModule, MsgModule, TxModule};
pub use scraper::{Config, NyxdScraper};
pub use storage::models;
@@ -0,0 +1,16 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::block_processor::types::FullBlockInformation;
use crate::error::ScraperError;
use crate::storage::StorageTransaction;
use async_trait::async_trait;
#[async_trait]
pub trait BlockModule {
async fn handle_block(
&mut self,
block: &FullBlockInformation,
storage_tx: &mut StorageTransaction,
) -> Result<(), ScraperError>;
}
+10
View File
@@ -0,0 +1,10 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
mod block_module;
mod msg_module;
mod tx_module;
pub use block_module::BlockModule;
pub use msg_module::MsgModule;
pub use tx_module::TxModule;
@@ -0,0 +1,19 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::block_processor::types::ParsedTransactionResponse;
use crate::error::ScraperError;
use crate::storage::StorageTransaction;
use async_trait::async_trait;
use cosmrs::Any;
#[async_trait]
pub trait MsgModule {
async fn handle_msg(
&mut self,
index: usize,
msg: &Any,
tx: &ParsedTransactionResponse,
storage_tx: &mut StorageTransaction,
) -> Result<(), ScraperError>;
}
@@ -0,0 +1,16 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::block_processor::types::ParsedTransactionResponse;
use crate::error::ScraperError;
use crate::storage::StorageTransaction;
use async_trait::async_trait;
#[async_trait]
pub trait TxModule {
async fn handle_tx(
&mut self,
tx: &ParsedTransactionResponse,
storage_tx: &mut StorageTransaction,
) -> Result<(), ScraperError>;
}
+175
View File
@@ -0,0 +1,175 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::block_processor::types::{
BlockToProcess, FullBlockInformation, ParsedTransactionResponse,
};
use crate::error::ScraperError;
use crate::helpers::tx_hash;
use futures::future::join3;
use futures::StreamExt;
use std::collections::BTreeMap;
use std::sync::Arc;
use tendermint::Hash;
use tendermint_rpc::endpoint::{block, block_results, tx, validators};
use tendermint_rpc::{Client, HttpClient, Paging};
use tokio::sync::Mutex;
use tracing::{debug, instrument};
use url::Url;
#[derive(Clone)]
pub struct RpcClient {
// right now I don't care about anything nym specific, so a simple http client is sufficient,
// once this is inadequate, we can switch to a NyxdClient
inner: Arc<HttpClient>,
}
impl RpcClient {
pub fn new(url: &Url) -> Result<Self, ScraperError> {
let http_client = HttpClient::new(url.as_str()).map_err(|source| {
ScraperError::HttpConnectionFailure {
url: url.to_string(),
source,
}
})?;
Ok(RpcClient {
inner: Arc::new(http_client),
})
}
#[instrument(skip(self, block), fields(height = block.height))]
pub async fn try_get_full_details(
&self,
block: BlockToProcess,
) -> Result<FullBlockInformation, ScraperError> {
debug!("getting complete block details");
let height = block.height;
// make all the http requests concurrently
let (results, validators, raw_transactions) = join3(
self.get_block_results(height),
self.get_validators_details(height),
self.get_transaction_results(&block.block.data),
)
.await;
let raw_transactions = raw_transactions?;
let mut transactions = Vec::with_capacity(raw_transactions.len());
for tx in raw_transactions {
transactions.push(ParsedTransactionResponse {
hash: tx.hash,
height: tx.height,
index: tx.index,
tx_result: tx.tx_result,
tx: cosmrs::Tx::from_bytes(&tx.tx).map_err(|source| {
ScraperError::TxParseFailure {
hash: tx.hash,
source,
}
})?,
proof: tx.proof,
})
}
Ok(FullBlockInformation {
block: block.block,
results: results?,
validators: validators?,
transactions,
})
}
#[instrument(skip(self), err(Display))]
pub async fn get_basic_block_details(
&self,
height: u32,
) -> Result<block::Response, ScraperError> {
debug!("getting basic block details");
self.inner
.block(height)
.await
.map_err(|source| ScraperError::BlockQueryFailure { height, source })
}
#[instrument(skip(self), err(Display))]
pub async fn get_block_results(
&self,
height: u32,
) -> Result<block_results::Response, ScraperError> {
debug!("getting block results");
self.inner
.block_results(height)
.await
.map_err(|source| ScraperError::BlockResultsQueryFailure { height, source })
}
pub(crate) async fn current_block_height(&self) -> Result<u64, ScraperError> {
debug!("getting current block height");
let info = self
.inner
.abci_info()
.await
.map_err(|source| ScraperError::AbciInfoQueryFailure { source })?;
Ok(info.last_block_height.value())
}
async fn get_transaction_results(
&self,
raw: &[Vec<u8>],
) -> Result<Vec<tx::Response>, ScraperError> {
let ordered_results = Arc::new(Mutex::new(BTreeMap::new()));
// "Data is just a wrapper for a list of transactions, where transactions are arbitrary byte arrays"
// source: https://github.com/tendermint/spec/blob/d46cd7f573a2c6a2399fcab2cde981330aa63f37/spec/core/data_structures.md#data
//
// I hate that zip as much as you, dear reader, but for some reason the compiler didn't let me remove the `move`
futures::stream::iter(
raw.iter()
.map(tx_hash)
.enumerate()
.zip(std::iter::repeat(ordered_results.clone())),
)
.for_each_concurrent(4, |((id, tx_hash), ordered_results)| async move {
let res = self.get_transaction_result(tx_hash).await;
ordered_results.lock().await.insert(id, res);
})
.await;
// safety the futures have completed so we MUST have the only arc reference
#[allow(clippy::unwrap_used)]
let inner = Arc::into_inner(ordered_results).unwrap().into_inner();
// BTreeMap is ordered by its keys so we're guaranteed to get txs in correct order
inner.into_values().collect()
}
#[instrument(skip(self, tx_hash), fields(tx_hash = %tx_hash), err(Display))]
async fn get_transaction_result(&self, tx_hash: Hash) -> Result<tx::Response, ScraperError> {
debug!("getting tx results");
self.inner
.tx(tx_hash, false)
.await
.map_err(|source| ScraperError::TxResultsQueryFailure {
hash: tx_hash,
source,
})
}
#[instrument(skip(self))]
pub async fn get_validators_details(
&self,
height: u32,
) -> Result<validators::Response, ScraperError> {
debug!("getting validators set");
self.inner
.validators(height, Paging::All)
.await
.map_err(|source| ScraperError::ValidatorsQueryFailure { height, source })
}
}
+210
View File
@@ -0,0 +1,210 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::block_processor::BlockProcessor;
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::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;
use tokio_util::task::TaskTracker;
use tracing::info;
use url::Url;
mod subscriber;
pub struct Config {
/// Url to the websocket endpoint of a validator, for example `wss://rpc.nymtech.net/websocket`
pub websocket_url: Url,
/// Url to the rpc endpoint of a validator, for example `https://rpc.nymtech.net/`
pub rpc_url: Url,
pub database_path: PathBuf,
}
pub struct NyxdScraperBuilder {
config: Config,
block_modules: Vec<Box<dyn BlockModule + Send>>,
tx_modules: Vec<Box<dyn TxModule + Send>>,
msg_modules: Vec<Box<dyn MsgModule + Send>>,
}
impl NyxdScraperBuilder {
pub async fn build_and_start(self) -> Result<NyxdScraper, ScraperError> {
let scraper = NyxdScraper::new(self.config).await?;
let (processing_tx, processing_rx) = unbounded_channel();
let (req_tx, req_rx) = channel(5);
let rpc_client = RpcClient::new(&scraper.config.rpc_url)?;
// create the tasks
let block_requester = BlockRequester::new(
scraper.cancel_token.clone(),
rpc_client.clone(),
req_rx,
processing_tx.clone(),
);
let mut block_processor = BlockProcessor::new(
scraper.cancel_token.clone(),
scraper.startup_sync.clone(),
processing_rx,
req_tx,
scraper.storage.clone(),
rpc_client,
)
.await?;
block_processor.set_block_modules(self.block_modules);
block_processor.set_tx_modules(self.tx_modules);
block_processor.set_msg_modules(self.msg_modules);
let mut chain_subscriber = ChainSubscriber::new(
&scraper.config.websocket_url,
scraper.cancel_token.clone(),
processing_tx,
)
.await?;
let ws_driver = chain_subscriber.ws_driver();
scraper.start_tasks(
block_requester,
block_processor,
chain_subscriber,
ws_driver,
);
Ok(scraper)
}
pub fn new(config: Config) -> Self {
NyxdScraperBuilder {
config,
block_modules: vec![],
tx_modules: vec![],
msg_modules: vec![],
}
}
pub fn with_block_module<M: BlockModule + Send + 'static>(mut self, module: M) -> Self {
self.block_modules.push(Box::new(module));
self
}
pub fn with_tx_module<M: TxModule + Send + 'static>(mut self, module: M) -> Self {
self.tx_modules.push(Box::new(module));
self
}
pub fn with_msg_module<M: MsgModule + Send + 'static>(mut self, module: M) -> Self {
self.msg_modules.push(Box::new(module));
self
}
}
pub struct NyxdScraper {
config: Config,
task_tracker: TaskTracker,
cancel_token: CancellationToken,
startup_sync: Arc<Notify>,
pub storage: ScraperStorage,
}
impl NyxdScraper {
pub fn builder(config: Config) -> NyxdScraperBuilder {
NyxdScraperBuilder::new(config)
}
pub async fn new(config: Config) -> Result<Self, ScraperError> {
let storage = ScraperStorage::init(&config.database_path).await?;
Ok(NyxdScraper {
config,
task_tracker: TaskTracker::new(),
cancel_token: CancellationToken::new(),
startup_sync: Arc::new(Default::default()),
storage,
})
}
fn start_tasks(
&self,
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 });
self.task_tracker
.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();
}
pub async fn start(&self) -> Result<(), ScraperError> {
let (processing_tx, processing_rx) = unbounded_channel();
let (req_tx, req_rx) = channel(5);
let rpc_client = RpcClient::new(&self.config.rpc_url)?;
// create the tasks
let block_requester = BlockRequester::new(
self.cancel_token.clone(),
rpc_client.clone(),
req_rx,
processing_tx.clone(),
);
let block_processor = BlockProcessor::new(
self.cancel_token.clone(),
self.startup_sync.clone(),
processing_rx,
req_tx,
self.storage.clone(),
rpc_client,
)
.await?;
let mut chain_subscriber = ChainSubscriber::new(
&self.config.websocket_url,
self.cancel_token.clone(),
processing_tx,
)
.await?;
let ws_driver = chain_subscriber.ws_driver();
// spawn them
self.start_tasks(
block_requester,
block_processor,
chain_subscriber,
ws_driver,
);
Ok(())
}
pub async fn wait_for_startup_sync(&self) {
info!("awaiting startup chain sync");
self.startup_sync.notified().await
}
pub async fn stop(self) {
info!("stopping the chain scrapper");
assert!(self.task_tracker.is_closed());
self.cancel_token.cancel();
self.task_tracker.wait().await
}
}
@@ -0,0 +1,129 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::block_processor::types::BlockToProcess;
use crate::error::ScraperError;
use tendermint_rpc::event::Event;
use tendermint_rpc::query::EventType;
use tendermint_rpc::{SubscriptionClient, WebSocketClient, WebSocketClientDriver};
use tokio::sync::mpsc::UnboundedSender;
use tokio_stream::StreamExt;
use tokio_util::sync::CancellationToken;
use tracing::{error, info, warn};
use url::Url;
const MAX_FAILURES: usize = 10;
pub struct ChainSubscriber {
cancel: CancellationToken,
block_sender: UnboundedSender<BlockToProcess>,
websocket_client: WebSocketClient,
websocket_driver: Option<WebSocketClientDriver>,
}
impl ChainSubscriber {
pub async fn new(
websocket_endpoint: &Url,
cancel: CancellationToken,
block_sender: UnboundedSender<BlockToProcess>,
) -> Result<Self, ScraperError> {
// sure, we could have just used websocket client entirely, but let's keep the logic for
// getting current blocks and historical blocks completely separate with the dual connection
let (client, driver) = WebSocketClient::new(websocket_endpoint.as_str())
.await
.map_err(|source| ScraperError::WebSocketConnectionFailure {
url: websocket_endpoint.to_string(),
source,
})?;
Ok(ChainSubscriber {
cancel,
block_sender,
websocket_client: client,
websocket_driver: Some(driver),
})
}
fn handle_new_event(&mut self, event: Event) -> Result<(), ScraperError> {
if let Err(err) = self.block_sender.send(event.try_into()?) {
// this error has nothing to do with the websocket or chain
error!("failed to send block for processing: {err} - are we shutting down?")
}
Ok(())
}
pub(crate) async fn run(&mut self) -> Result<(), ScraperError> {
let _drop_guard = self.cancel.clone().drop_guard();
info!("creating chain subscription");
let mut subs = self
.websocket_client
.subscribe(EventType::NewBlock.into())
.await
.map_err(|source| ScraperError::ChainSubscriptionFailure { source })?;
let mut failures = 0;
info!("starting processing loop");
loop {
tokio::select! {
_ = self.cancel.cancelled() => {
info!("received cancellation token");
break
}
maybe_event = subs.next() => {
let Some(maybe_event) = maybe_event else {
warn!("stopped receiving new events");
break;
};
match maybe_event {
Ok(event) => {
if let Err(err) = self.handle_new_event(event) {
error!("failed to process received block: {err}");
failures += 1
} else {
failures = 0;
}
}
Err(err) => {
error!("failed to receive a valid subscription event: {err}");
failures += 1
}
}
if failures >= MAX_FAILURES {
// note: the drop_guard will get dropped and thus cause a shutdown
return Err(ScraperError::MaximumSubscriptionFailures);
}
}
}
}
Ok(())
}
pub(crate) fn ws_driver(&mut self) -> WebSocketClientDriver {
#[allow(clippy::expect_used)]
self.websocket_driver
.take()
.expect("websocket driver has already been started!")
}
}
pub async fn run_websocket_driver(driver: WebSocketClientDriver, cancel: CancellationToken) {
info!("starting websocket driver");
tokio::select! {
_ = 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}")
}
}
cancel.cancel()
}
}
}
@@ -0,0 +1,2 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
+352
View File
@@ -0,0 +1,352 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::storage::models::{CommitSignature, Validator};
use sqlx::types::time::OffsetDateTime;
use sqlx::{Executor, Sqlite};
use tracing::{instrument, trace};
#[derive(Clone)]
pub(crate) struct StorageManager {
pub(crate) connection_pool: sqlx::SqlitePool,
}
impl StorageManager {
pub(crate) async fn set_initial_metadata(&self) -> Result<(), sqlx::Error> {
if sqlx::query("SELECT * from metadata")
.fetch_optional(&self.connection_pool)
.await?
.is_none()
{
sqlx::query("INSERT INTO metadata (id, last_processed_height) VALUES (0, 0)")
.execute(&self.connection_pool)
.await?;
}
Ok(())
}
pub(crate) async fn get_first_block_height_after(
&self,
time: OffsetDateTime,
) -> Result<Option<i64>, sqlx::Error> {
let maybe_record = sqlx::query!(
r#"
SELECT height
FROM block
WHERE timestamp > ?
ORDER BY timestamp
LIMIT 1
"#,
time
)
.fetch_optional(&self.connection_pool)
.await?;
if let Some(row) = maybe_record {
Ok(row.height)
} else {
Ok(None)
}
}
pub(crate) async fn get_last_block_height_before(
&self,
time: OffsetDateTime,
) -> Result<Option<i64>, sqlx::Error> {
let maybe_record = sqlx::query!(
r#"
SELECT height
FROM block
WHERE timestamp < ?
ORDER BY timestamp DESC
LIMIT 1
"#,
time
)
.fetch_optional(&self.connection_pool)
.await?;
if let Some(row) = maybe_record {
Ok(row.height)
} else {
Ok(None)
}
}
pub(crate) async fn get_signed_between(
&self,
consensus_address: &str,
start_height: i64,
end_height: i64,
) -> Result<i32, sqlx::Error> {
let count = sqlx::query!(
r#"
SELECT COUNT(*) as count FROM pre_commit
WHERE
validator_address == ?
AND height >= ?
AND height <= ?
"#,
consensus_address,
start_height,
end_height
)
.fetch_one(&self.connection_pool)
.await?
.count;
Ok(count)
}
pub(crate) async fn get_precommit(
&self,
consensus_address: &str,
height: i64,
) -> Result<Option<CommitSignature>, sqlx::Error> {
sqlx::query_as(
r#"
SELECT * FROM pre_commit
WHERE validator_address = ?
AND height = ?
"#,
)
.bind(consensus_address)
.bind(height)
.fetch_optional(&self.connection_pool)
.await
}
pub(crate) async fn get_block_validators(
&self,
height: i64,
) -> Result<Vec<Validator>, sqlx::Error> {
sqlx::query_as!(
Validator,
r#"
SELECT * FROM validator
WHERE EXISTS (
SELECT 1 FROM pre_commit
WHERE height == ?
AND pre_commit.validator_address = validator.consensus_address
)
"#,
height
)
.fetch_all(&self.connection_pool)
.await
}
pub(crate) async fn get_validators(&self) -> Result<Vec<Validator>, sqlx::Error> {
sqlx::query_as("SELECT * FROM validator")
.fetch_all(&self.connection_pool)
.await
}
pub(crate) async fn get_last_processed_height(&self) -> Result<i64, sqlx::Error> {
let maybe_record = sqlx::query!(
r#"
SELECT last_processed_height FROM metadata
"#
)
.fetch_optional(&self.connection_pool)
.await?;
if let Some(row) = maybe_record {
Ok(row.last_processed_height)
} else {
Ok(-1)
}
}
}
// make those generic over executor so that they could be performed over connection pool and a tx
#[instrument(skip(executor))]
pub(crate) async fn insert_validator<'a, E>(
consensus_address: String,
consensus_pubkey: String,
executor: E,
) -> Result<(), sqlx::Error>
where
E: Executor<'a, Database = Sqlite>,
{
trace!("insert validator");
sqlx::query!(
r#"
INSERT INTO validator (consensus_address, consensus_pubkey)
VALUES (?, ?)
ON CONFLICT DO NOTHING
"#,
consensus_address,
consensus_pubkey
)
.execute(executor)
.await?;
Ok(())
}
#[instrument(skip(executor))]
pub(crate) async fn insert_block<'a, E>(
height: i64,
hash: String,
num_txs: u32,
total_gas: i64,
proposer_address: String,
timestamp: OffsetDateTime,
executor: E,
) -> Result<(), sqlx::Error>
where
E: Executor<'a, Database = Sqlite>,
{
trace!("insert block");
sqlx::query!(
r#"
INSERT INTO block (height, hash, num_txs, total_gas, proposer_address, timestamp)
VALUES (?, ?, ?, ?, ?, ?)
ON CONFLICT DO NOTHING
"#,
height,
hash,
num_txs,
total_gas,
proposer_address,
timestamp
)
.execute(executor)
.await?;
Ok(())
}
#[instrument(skip(executor))]
pub(crate) async fn insert_precommit<'a, E>(
validator_address: String,
height: i64,
timestamp: OffsetDateTime,
voting_power: i64,
proposer_priority: i64,
executor: E,
) -> Result<(), sqlx::Error>
where
E: Executor<'a, Database = Sqlite>,
{
trace!("insert precommit");
sqlx::query!(
r#"
INSERT INTO pre_commit (validator_address, height, timestamp, voting_power, proposer_priority)
VALUES (?, ?, ?, ?, ?)
ON CONFLICT (validator_address, timestamp) DO NOTHING
"#,
validator_address,
height,
timestamp,
voting_power,
proposer_priority
)
.execute(executor)
.await?;
Ok(())
}
#[instrument(skip(executor))]
#[allow(clippy::too_many_arguments)]
pub(crate) async fn insert_transaction<'a, E>(
hash: String,
height: i64,
index: i64,
success: bool,
messages: i64,
memo: String,
gas_wanted: i64,
gas_used: i64,
raw_log: String,
executor: E,
) -> Result<(), sqlx::Error>
where
E: Executor<'a, Database = Sqlite>,
{
trace!("insert transaction");
sqlx::query!(
r#"
INSERT INTO "transaction" (hash, height, "index", success, num_messages, memo, gas_wanted, gas_used, raw_log)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
ON CONFLICT (hash) DO UPDATE
SET height = excluded.height,
"index" = excluded."index",
success = excluded.success,
num_messages = excluded.num_messages,
memo = excluded.memo,
gas_wanted = excluded.gas_wanted,
gas_used = excluded.gas_used,
raw_log = excluded.raw_log
"#,
hash,
height,
index,
success,
messages,
memo,
gas_wanted,
gas_used,
raw_log,
)
.execute(executor)
.await?;
Ok(())
}
#[instrument(skip(executor))]
pub(crate) async fn insert_message<'a, E>(
transaction_hash: String,
index: i64,
typ: String,
height: i64,
executor: E,
) -> Result<(), sqlx::Error>
where
E: Executor<'a, Database = Sqlite>,
{
trace!("insert message");
sqlx::query!(
r#"
INSERT INTO message (transaction_hash, "index", type, height)
VALUES (?, ?, ?, ?)
ON CONFLICT (transaction_hash, "index") DO UPDATE
SET height = excluded.height,
type = excluded.type
"#,
transaction_hash,
index,
typ,
height
)
.execute(executor)
.await?;
Ok(())
}
#[instrument(skip(executor))]
pub(crate) async fn update_last_processed<'a, E>(
height: i64,
executor: E,
) -> Result<(), sqlx::Error>
where
E: Executor<'a, Database = Sqlite>,
{
trace!("update_last_processed");
sqlx::query!("UPDATE metadata SET last_processed_height = ?", height)
.execute(executor)
.await?;
Ok(())
}
+327
View File
@@ -0,0 +1,327 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::block_processor::types::{FullBlockInformation, ParsedTransactionResponse};
use crate::error::ScraperError;
use crate::storage::manager::{
insert_block, insert_message, insert_precommit, insert_transaction, insert_validator,
update_last_processed, StorageManager,
};
use crate::storage::models::{CommitSignature, Validator};
use sqlx::types::time::OffsetDateTime;
use sqlx::{ConnectOptions, Sqlite, Transaction};
use std::fmt::Debug;
use std::path::Path;
use tendermint::block::{Commit, CommitSig};
use tendermint::Block;
use tendermint_rpc::endpoint::validators;
use tracing::{debug, error, info, instrument, trace, warn};
mod helpers;
mod manager;
pub mod models;
pub type StorageTransaction = Transaction<'static, Sqlite>;
#[derive(Clone)]
pub struct ScraperStorage {
pub(crate) manager: StorageManager,
}
impl ScraperStorage {
#[instrument]
pub async fn init<P: AsRef<Path> + Debug>(database_path: P) -> Result<Self, ScraperError> {
let mut opts = sqlx::sqlite::SqliteConnectOptions::new()
.filename(database_path)
.create_if_missing(true);
// TODO: do we want auto_vacuum ?
opts.disable_statement_logging();
let connection_pool = match sqlx::SqlitePool::connect_with(opts).await {
Ok(db) => db,
Err(err) => {
error!("Failed to connect to SQLx database: {err}");
return Err(err.into());
}
};
if let Err(err) = sqlx::migrate!("./sql_migrations")
.run(&connection_pool)
.await
{
error!("Failed to initialize SQLx database: {err}");
return Err(err.into());
}
info!("Database migration finished!");
let manager = StorageManager { connection_pool };
manager.set_initial_metadata().await?;
let storage = ScraperStorage { manager };
Ok(storage)
}
#[instrument(skip_all)]
pub async fn begin_processing_tx(&self) -> Result<StorageTransaction, ScraperError> {
debug!("starting storage tx");
self.manager
.connection_pool
.begin()
.await
.map_err(|source| ScraperError::StorageTxBeginFailure { source })
}
pub async fn get_first_block_height_after(
&self,
time: OffsetDateTime,
) -> Result<Option<i64>, ScraperError> {
Ok(self.manager.get_first_block_height_after(time).await?)
}
pub async fn get_last_block_height_before(
&self,
time: OffsetDateTime,
) -> Result<Option<i64>, ScraperError> {
Ok(self.manager.get_last_block_height_before(time).await?)
}
pub async fn get_blocks_between(
&self,
start_time: OffsetDateTime,
end_time: OffsetDateTime,
) -> Result<i64, ScraperError> {
let Some(block_start) = self.get_first_block_height_after(start_time).await? else {
return Ok(0);
};
let Some(block_end) = self.get_last_block_height_before(end_time).await? else {
return Ok(0);
};
Ok(block_end - block_start)
}
pub async fn get_signed_between(
&self,
consensus_address: &str,
start_height: i64,
end_height: i64,
) -> Result<i32, ScraperError> {
Ok(self
.manager
.get_signed_between(consensus_address, start_height, end_height)
.await?)
}
pub async fn get_signed_between_times(
&self,
consensus_address: &str,
start_time: OffsetDateTime,
end_time: OffsetDateTime,
) -> Result<i32, ScraperError> {
let Some(block_start) = self.get_first_block_height_after(start_time).await? else {
return Ok(0);
};
let Some(block_end) = self.get_last_block_height_before(end_time).await? else {
return Ok(0);
};
self.get_signed_between(consensus_address, block_start, block_end)
.await
}
pub async fn get_precommit(
&self,
consensus_address: &str,
height: i64,
) -> Result<Option<CommitSignature>, ScraperError> {
Ok(self
.manager
.get_precommit(consensus_address, height)
.await?)
}
pub async fn get_block_signers(&self, height: i64) -> Result<Vec<Validator>, ScraperError> {
Ok(self.manager.get_block_validators(height).await?)
}
pub async fn get_all_known_validators(&self) -> Result<Vec<Validator>, ScraperError> {
Ok(self.manager.get_validators().await?)
}
pub async fn get_last_processed_height(&self) -> Result<i64, ScraperError> {
Ok(self.manager.get_last_processed_height().await?)
}
}
pub async fn persist_block(
block: &FullBlockInformation,
tx: &mut StorageTransaction,
) -> Result<(), ScraperError> {
let total_gas = crate::helpers::tx_gas_sum(&block.transactions);
// SANITY CHECK: make sure the block proposer is present in the validator set
block.ensure_proposer()?;
// persist validators
persist_validators(&block.validators, tx).await?;
// persist block data
persist_block_data(&block.block, total_gas, tx).await?;
// persist commits
if let Some(commit) = &block.block.last_commit {
persist_commits(commit, &block.validators, tx).await?;
} else {
warn!("no commits for block {}", block.block.header.height)
}
// persist txs
persist_txs(&block.transactions, tx).await?;
// persist messages (inside the transactions)
persist_messages(&block.transactions, tx).await?;
update_last_processed(block.block.header.height.into(), tx).await?;
Ok(())
}
async fn persist_validators(
validators: &validators::Response,
tx: &mut StorageTransaction,
) -> Result<(), ScraperError> {
debug!("persisting {} validators", validators.total);
for validator in &validators.validators {
let consensus_address = crate::helpers::validator_consensus_address(validator.address)?;
let consensus_pubkey = crate::helpers::validator_pubkey_to_bech32(validator.pub_key)?;
insert_validator(
consensus_address.to_string(),
consensus_pubkey.to_string(),
&mut *tx,
)
.await?;
}
Ok(())
}
async fn persist_block_data(
block: &Block,
total_gas: i64,
tx: &mut StorageTransaction,
) -> Result<(), ScraperError> {
let proposer_address =
crate::helpers::validator_consensus_address(block.header.proposer_address)?.to_string();
insert_block(
block.header.height.into(),
block.header.hash().to_string(),
block.data.len() as u32,
total_gas,
proposer_address,
block.header.time.into(),
tx,
)
.await?;
Ok(())
}
async fn persist_commits(
commits: &Commit,
validators: &validators::Response,
tx: &mut StorageTransaction,
) -> Result<(), ScraperError> {
debug!("persisting up to {} commits", commits.signatures.len());
let height: i64 = commits.height.into();
for commit_sig in &commits.signatures {
let (validator_id, timestamp, signature) = match commit_sig {
CommitSig::BlockIdFlagAbsent => {
trace!("absent signature");
continue;
}
CommitSig::BlockIdFlagCommit {
validator_address,
timestamp,
signature,
} => (validator_address, timestamp, signature),
CommitSig::BlockIdFlagNil {
validator_address,
timestamp,
signature,
} => (validator_address, timestamp, signature),
};
let validator = crate::helpers::validator_info(*validator_id, validators)?;
let validator_address = crate::helpers::validator_consensus_address(*validator_id)?;
if signature.is_none() {
warn!("empty signature for {validator_address} at height {height}");
continue;
}
insert_precommit(
validator_address.to_string(),
height,
(*timestamp).into(),
validator.power.into(),
validator.proposer_priority.value(),
&mut *tx,
)
.await?;
}
Ok(())
}
async fn persist_txs(
txs: &[ParsedTransactionResponse],
tx: &mut StorageTransaction,
) -> Result<(), ScraperError> {
debug!("persisting {} txs", txs.len());
for chain_tx in txs {
insert_transaction(
chain_tx.hash.to_string(),
chain_tx.height.into(),
chain_tx.index as i64,
chain_tx.tx_result.code.is_ok(),
chain_tx.tx.body.messages.len() as i64,
chain_tx.tx.body.memo.clone(),
chain_tx.tx_result.gas_wanted,
chain_tx.tx_result.gas_used,
chain_tx.tx_result.log.clone(),
&mut *tx,
)
.await?;
}
Ok(())
}
async fn persist_messages(
txs: &[ParsedTransactionResponse],
tx: &mut StorageTransaction,
) -> Result<(), ScraperError> {
debug!("persisting messages");
for chain_tx in txs {
for (index, msg) in chain_tx.tx.body.messages.iter().enumerate() {
insert_message(
chain_tx.hash.to_string(),
index as i64,
msg.type_url.clone(),
chain_tx.height.into(),
&mut *tx,
)
.await?
}
}
Ok(())
}
+30
View File
@@ -0,0 +1,30 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use sqlx::types::time::OffsetDateTime;
use sqlx::FromRow;
#[derive(Debug, Clone, Eq, PartialEq, Hash, FromRow)]
pub struct Validator {
pub consensus_address: String,
pub consensus_pubkey: String,
}
#[derive(Debug, Clone, FromRow)]
pub struct Block {
pub height: i64,
pub hash: String,
pub num_txs: u32,
pub total_gas: i64,
pub proposer_address: String,
pub timestamp: OffsetDateTime,
}
#[derive(Debug, Clone, FromRow)]
pub struct CommitSignature {
pub height: i64,
pub validator_address: String,
pub voting_power: i64,
pub proposer_priority: i64,
pub timestamp: OffsetDateTime,
}
+1 -1
View File
@@ -15,6 +15,6 @@ log = { workspace = true }
reqwest = { workspace = true, features = ["json"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1"
sqlx = { version = "0.5", features = ["runtime-tokio-rustls", "chrono"]}
sqlx = { workspace = true, features = ["runtime-tokio-rustls", "chrono"]}
thiserror = { workspace = true }
tokio = { version = "1.24.1", features = [ "time" ] }
+3 -3
View File
@@ -116,8 +116,8 @@ impl TaskManager {
}
#[cfg(not(target_arch = "wasm32"))]
pub async fn catch_interrupt(mut self) -> Result<(), SentError> {
let res = crate::wait_for_signal_and_error(&mut self).await;
pub async fn catch_interrupt(&mut self) -> Result<(), SentError> {
let res = crate::wait_for_signal_and_error(self).await;
log::info!("Sending shutdown");
self.signal_shutdown().ok();
@@ -629,7 +629,7 @@ impl TaskHandle {
#[cfg(not(target_arch = "wasm32"))]
pub async fn wait_for_shutdown(self) -> Result<(), SentError> {
match self {
TaskHandle::Internal(task_manager) => task_manager.catch_interrupt().await,
TaskHandle::Internal(mut task_manager) => task_manager.catch_interrupt().await,
TaskHandle::External(mut task_client) => {
task_client.recv().await;
Ok(())
+3 -1
View File
@@ -16,10 +16,12 @@ base64 = "0.21.3"
# version mismatch with x25519-dalek/curve25519-dalek that is resolved in the
# latest commit. So pick that for now.
x25519-dalek = "2.0.0"
defguard_wireguard_rs = { git = "https://github.com/neacsu/wireguard-rs.git", rev = "c2cd0c1119f699f4bc43f5e6ffd6fc242caa42ed" }
ip_network = "0.4.1"
log.workspace = true
nym-network-defaults = { path = "../network-defaults" }
nym-task = { path = "../task" }
nym-wireguard-types = { path = "../wireguard-types" }
tokio = { workspace = true, features = ["rt-multi-thread", "net", "io-util"] }
[target."cfg(target_os = \"linux\")".dependencies]
defguard_wireguard_rs = { git = "https://github.com/neacsu/wireguard-rs.git", rev = "c2cd0c1119f699f4bc43f5e6ffd6fc242caa42ed" }
+13 -21
View File
@@ -5,26 +5,20 @@
pub mod setup;
use nym_wireguard_types::registration::GatewayClientRegistry;
use std::sync::Arc;
// Currently the module related to setting up the virtual network device is platform specific.
#[cfg(target_os = "linux")]
use crate::setup::{peer_allowed_ips, peer_static_public_key, PRIVATE_KEY};
use defguard_wireguard_rs::WGApi;
#[cfg(target_os = "linux")]
use defguard_wireguard_rs::{
host::Peer, key::Key, net::IpAddrMask, InterfaceConfiguration, WireguardInterfaceApi,
};
#[cfg(target_os = "linux")]
use nym_network_defaults::{WG_PORT, WG_TUN_DEVICE_ADDRESS};
/// Start wireguard device
#[cfg(target_os = "linux")]
pub async fn start_wireguard(
mut task_client: nym_task::TaskClient,
_gateway_client_registry: Arc<GatewayClientRegistry>,
) -> Result<WGApi, Box<dyn std::error::Error + Send + Sync + 'static>> {
_gateway_client_registry: std::sync::Arc<
nym_wireguard_types::registration::GatewayClientRegistry,
>,
) -> Result<defguard_wireguard_rs::WGApi, Box<dyn std::error::Error + Send + Sync + 'static>> {
use crate::setup::{peer_allowed_ips, peer_static_public_key, PRIVATE_KEY};
use defguard_wireguard_rs::{
host::Peer, key::Key, net::IpAddrMask, InterfaceConfiguration, WGApi, WireguardInterfaceApi,
};
use nym_network_defaults::{WG_PORT, WG_TUN_DEVICE_ADDRESS};
let ifname = String::from("wg0");
let wgapi = WGApi::new(ifname.clone(), false)?;
wgapi.create_interface()?;
@@ -48,10 +42,8 @@ pub async fn start_wireguard(
Ok(wgapi)
}
#[cfg(not(target_os = "linux"))]
pub async fn start_wireguard(
_task_client: nym_task::TaskClient,
_gateway_client_registry: Arc<GatewayClientRegistry>,
) -> Result<WGApi, Box<dyn std::error::Error + Send + Sync + 'static>> {
todo!("WireGuard is currently only supported on Linux")
pub async fn start_wireguard() {
todo!("WireGuard is currently only supported on Linux");
}
+5
View File
@@ -55,6 +55,11 @@ wallet_release_version = "1.2.8"
command = "mdbook-last-changed"
renderer = ["html"]
# used for grabbing output of binary commands for automation
# https://github.com/FauconFan/mdbook-cmdrun
[preprocessor.cmdrun]
#########
# BUILD #
#########
+10 -4
View File
@@ -24,7 +24,7 @@
- [NymConnect X Electrum](tutorials/electrum.md)
- [NymConnect X Firo wallet](tutorials/firo.md)
# Code Examples
# Code Examples
- [Custom Service Providers](examples/custom-services.md)
- [Apps Using Network Requesters](examples/using-nrs.md)
@@ -49,9 +49,9 @@
- [Preparing Your Service](tutorials/cosmos-service/service.md)
- [Preparing Your Service pt2](tutorials/cosmos-service/service-src.md)
- [Querying the Chain](tutorials/cosmos-service/querying.md)
- [Typescript](tutorials/typescript.md)
- [Simple Service Provider](tutorials/simple-service-provider/simple-service-provider.md)
- [Simple Service Provider](tutorials/simple-service-provider/simple-service-provider.md)
- [Tutorial Overview](tutorials/simple-service-provider/overview.md)
- [Preparing Your User Client Environment](tutorials/simple-service-provider/preparating-env.md)
- [Building Your User Client](tutorials/simple-service-provider/user-client.md)
@@ -68,6 +68,12 @@
# Events
- [CryptoTalk](events/cryptotalk/nym-vpn.md)
- [NymVPN Linux Guide](events/cryptotalk/nym-vpn-linux.md)
- [NymVPN MacOS Guide](events/cryptotalk/nym-vpn-mac.md)
- [NymVPN Testing](events/cryptotalk/nym-vpn-testing.md)
- [NymVPN Troubleshooting](events/cryptotalk/nym-vpn-troubleshooting.md)
- [NymVPN FAQ](events/cryptotalk/nym-vpn-faq.md)
- [Web3Privacy Now](./events/web3-privacy.md)
- [HCPP23-serinko](./events/hcpp23-serinko.md)
- [HCPP23-max](./events/hcpp23-max.md)
@@ -83,7 +89,7 @@
- [Community Applications and Guides](community-resources/community-applications-and-guides.md)
- [Change Service Grantee Information](info-request.md)
---
---
# Misc.
- [Code of Conduct](coc.md)
- [Licensing](licensing.md)
Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

@@ -0,0 +1,69 @@
# Frequently Asked Questions
This is a FAQ page tailored for this event. If you interested to read more about Nym platform, you can have a look at [Nym general FAQ](https://nymtech.net/developers/faq/general-faq.html) and read through Nym's technical [documentation](https://nymtech.net/docs), [Developer Portal](https://nymtech.net/developers) and [Operators Guide](https://nymtech.net/operators).
## NymVPN
If this your first time hearing about NymVPN, make sure you visit [NymVPN webpage](https://nymvpn.com/en), the official NymVPN [support & FAQ page](https://nymvpn.com/en/support) and the proceed to the introduction and guide on how to [install, run and test](./nym-vpn.md) the client.
Below are some extra FAQs which came out during the initial alpha testing round.
### What's the difference between 2-hops and 5-hops
The default is 5-hops (including Entry and Exit Gateways), which means that the traffic goes from the local client to Entry Gateway -> through 3 layers of Mix Nodes -> to Exit Gateway -> internet. this option uses all the Nym Mixnet features for maximum privacy.
```
┌─►mix──┐ mix mix
│ │
Entry │ │ Exit
client ───► Gateway ──┘ mix │ mix ┌─►mix ───► Gateway ───► internet
│ │
│ │
mix └─►mix──┘ mix
```
The 2-hop option is going from the local client -> Entry Gateway -> directly to Exit Gateway -> internet. This option is good for operations demanding faster connection. Keep in mind that this setup by-passes the 3 layers of Mix Nodes. The anonymising features done by your local client like breaking data into same-size packets with inserting additional "dummy" ones to break the time and volume patterns is done in both options.
```
Entry Exit
client ───► Gateway ────► Gateway ───► internet
```
We highly recommend to read more about [Nym network overview](https://nymtech.net/docs/architecture/network-overview.html) and the [Mixnet traffic flow](https://nymtech.net/docs/architecture/traffic-flow.html).
### Why do I see different sizes of packets in my terminal log?
One of features of Nym Mixnet's clients is to break data into the same size packets called Sphinx, which is currently ~2kb. When running NymVPN, the data log shows payload sizes, which are the raw sizes of the IP packets, not Sphinx. The payload sizes will be capped by the configured MTU, which is set around 1500 bytes.
### What is 'poisson filter' about?
By default `--enable-poisson` is disabled and packets are sent from the local client to the Entry Gateway as quickly as possible. With the poisson process enabled the Nym client will send packets at a steady stream to the Entry Gateway. By default it's on average one sphinx packet per 20ms, but there is some randomness (poisson distribution). When there are no real data to fill the sphinx packets with, cover packets are generated instead.
Enabling the poisson filter is one of the key mechanisms to de-correlate input and output traffic to the Mixnet. The performance impact however is dramatic:
1 packer per 20ms is 50 packets / sec so ballpark 100kb/s.
For mobile clients that means constantly sending data eating up data allowance.
## Nym Mixnet Architecture and Rewards
We have a list of questions related to Nym Nodes and the incentives behind running them under [FAQ pages](https://nymtech.net/operators/faq/mixnodes-faq.html) in our [Operators Guide](https://nymtech.net/operators). For better knowledge about Nym architecture we recommend to read [Nym network overview](https://nymtech.net/docs/architecture/network-overview.html) and the [Mixnet traffic flow](https://nymtech.net/docs/architecture/traffic-flow.html) in our [technical documentation](https://nymtech.net/docs).
## Project Smoosh
Project Smoosh is a code name for a process in which different components of Nym Mixnet architecture get *smooshed* into one binary. Check out [Smoosh FAQ](https://nymtech.net/operators/faq/smoosh-faq.html) in Operators Guide to read more.
## Exit Gateway
Part of the the transition under code name [Project Smoosh](./nym-vpn-faq.md#project-smoosh) is a creation of [Nym Exit Gateway](https://nymtech.net/operators/legal/exit-gateway.html) functionality. The operators running Gateways would have to “open” their nodes to a wider range of online services, in a similar fashion to Tor exit relays. The main change will be to expand the original short [allowed.list](https://nymtech.net/.wellknown/network-requester/standard-allowed-list.txt) to a more permissive setup. An [exit policy](https://nymtech.net/.wellknown/network-requester/exit-policy.txt) will constrain the hosts that the users of the Nym VPN and Mixnet can connect to. This will be done in an effort to protect the operators, as Gateways will act both as SOCKS5 Network Requesters, and exit nodes for IP traffic from Nym VPN and Mixnet clients.
* Read more how the exit policy gets implemented [here](https://nymtech.net/operators/faq/smoosh-faq.html#how-will-the-exit-policy-be-implemented)
* Check out [Nym Operators Legal Forum](https://nymtech.net/operators/legal/exit-gateway.html)
* Do reach out to us during 37c3 with any experiences you may have running Tor Exit relays or legal findings and suggestions for Nym Exit Gateway operators
## Nym Integrations and SDKs
If you are a dev who is interested to integrate Nym, have a look on our SDK tutorials:
* [Rust SDKs](https://nymtech.net/developers/tutorials/cosmos-service/intro.html)
* [TypeScript SDKs](https://sdk.nymtech.net/)
* [Integration FAQ](https://nymtech.net/developers/faq/integrations-faq.html)
@@ -0,0 +1,198 @@
# NymVPN alpha: Guide for GNU/Linux
```admonish warning
NymVPN is an experimental software and it's for [testing](./nym-vpn-testing.md) purposes only. All users testing the client are expected to sign GDPR Information Sheet and Consent Form (shared at the event), and follow the steps listed in the form [*NymVPN User research*](https://opnform.com/forms/nymvpn-user-research-at-37c3-yccqko-2).
```
## Preparation
> Any syntax in `<>` brackets is a user's/version unique variable. Exchange with a corresponding name without the `<>` brackets.
You can use our automated script of follow the steps below to download, verify, install and run NymVPN client.
### Automated Script CLI Installation
Open a terminal and follow these steps:
* Download the script
```sh
curl -o nym-vpn-cli-executor.sh -L https://gist.githubusercontent.com/tommyv1987/87267ded27e1eb7651aa9cc745ddf4af/raw/99cea8f4d80f2d002802ed1cbedba288bfca4488/execute-nym-vpn-cli-binary.sh
```
* Make the script executable
```sh
chmod u+x nym-vpn-cli-executor.sh
```
* Run the script
```sh
./nym-vpn-cli-executor.sh
```
* When prompted to verify `sha256sum` paste one from the [release page](https://github.com/nymtech/nym/releases/tag/nym-vpn-alpha-0.0.2) including the binary name (all as one input with a space in between), for example:
```sh
61a08de46881411e140f66cdb4940041150adb573c10e3f510a58a86a32f08fb nym-vpn-cli-ubuntu-22.04.zip
```
The script will automatically start the client. Follow the instructions:
* The script will print a JSON view of existing Gateways and prompt you to:
- ***(Make sure to use two different Gateways for entry and exit!)***
- `enter a gateway ID:` paste one of the values labeled with a key `"identityKey"` printed above (without `" "`)
- `enter an exit address:` paste one of the values labeled with a key `"address"` printed above (without `" "`)
- `do you want five hop or two hop?`: type `five` or `two`
### Manual CLI and GUI Installation
* Visit the [releases page](https://github.com/nymtech/nym/releases/tag/nym-vpn-alpha-0.0.2) to download the binary for Debian based Linux
* Open terminal in the same directory and check the the `sha256sum` by running:
```sh
# for CLI
sha256sum ./nym-vpn-cli_0.1.0_ubuntu-22.04_amd64.zip
# for GUI
sha256sum ./nym-vpn-ui_0.0.2_ubuntu-22.04_amd64.zip
```
* Compare the result with the sha256 hash shared on the [release page](https://github.com/nymtech/nym/releases/tag/nym-vpn-alpha-0.0.2)
* Extract files with `unzip` command or manually as you are used to
* If you prefer to run `.AppImage` make executable by running:
```sh
# for CLI
chmod +x ./nym-vpn-cli
# for GUI
chmod +x ./appimage/nym-vpn_0.0.2_amd64.AppImage
# make sure your path to package is correct and the package name as well
```
* If you prefer to use the `.deb` version for installation (Linux only), open terminal in the same directory and run:
```sh
cd deb
sudo dpkg -i ./nym-vpn_0.0.2_amd64.deb
# or
sudo apt-get install -f ./nym-vpn_0.0.2_amd64.deb
```
* **For CLI**: Create Sandbox environment config file by saving [this](https://raw.githubusercontent.com/nymtech/nym/develop/envs/sandbox.env) as `sandbox.env` in the same directory as your NymVPN binaries. In case of GUI setup, see the steps below.
### GUI configuration
* Create a NymVPN config directory called `nym-vpn` in your `~/.config`, either manually or by a command:
```sh
# for Linux
mkdir $HOME/.config/nym-vpn/
```
* Create the network config by saving [this](https://raw.githubusercontent.com/nymtech/nym/develop/envs/sandbox.env) as `sandbox.env` in the directory `nym-vpn` you just created
* Create the main config file called `config.toml` in the same directory with this content:
```toml
# change <USER> to your username
env_config_file = "/home/<USER>/.config/nym-vpn/sandbox.env"
entry_node_location = "DE" # two letters country code
```
## Run NymVPN
* **For NymVPN to work, all other VPNs must be switched off!**
* At this alpha stage of NymVPN, network connection (wifi) must be re-connected after or in-between NymVPN testing rounds.
### Run CLI
Make sure your terminal is open in the same directory as your `nym-vpn-cli` binary.
* Find the entire command with all the needed arguments' values and your wireguard private key for testing purposes at [nymvpn.com/en/alpha](https://nymvpn.com/en/alpha)
* Run it with `sudo` as root, the command will look like this with specified arguments:
```sh
sudo ./nym-vpn-cli -c ./sandbox.env --entry-gateway-id <ENTRY_GATEWAY_ID> --exit-router-address <EXIT_ROUTER_ADDRESS> --enable-wireguard --private-key <PRIVATE_KEY> --wg-ip <WG_IP>
```
* To chose different Gateways, visit [nymvpn.com/en/alpha/api/gateways](https://nymvpn.com/en/alpha/api/gateways)
* To see all possibilities see [command explanation](#cli-commands-and-options) below
### Run GUI
In case you used `.deb` package and installed the client, you may be able to have a NymVPN application icon in your app menu. However this may not work as the application needs root permission.
Make sure you went through the GUI configuration in the [preparation section](#gui-configuration). Then open terminal in the same directory where you [installed](#preparation) the binary and run:
```sh
# .AppImage must be run from the same directory
sudo -E ./nym-vpn_0.0.2_amd64.AppImage
# .deb installation shall be executable from anywhere as
sudo -E nym-vpn
```
In case of errors, see [troubleshooting section](./nym-vpn-troubleshooting.md).
### CLI Commands and Options
The basic syntax of `nym-vpn-cli` is:
```sh
sudo ./nym-vpn-cli -c ./sandbox.env --entry-gateway-id <ENTRY_GATEWAY_ID> --exit-router-address <EXIT_ROUTER_ADDRESS> --enable-wireguard --private-key <PRIVATE_KEY> --wg-ip <WG_IP>
```
* To chose different Gateways, visit [nymvpn.com/en/alpha/api/gateways](https://nymvpn.com/en/alpha/api/gateways)
* To see all possibilities run with `--help` flag:
```sh
./nym-vpn-cli --help
```
~~~admonish example collapsible=true title="Console output"
```
Usage: nym-vpn-cli [OPTIONS]
Options:
-c, --config-env-file <CONFIG_ENV_FILE>
Path pointing to an env file describing the network
--mixnet-client-path <MIXNET_CLIENT_PATH>
Path to the data directory of a previously initialised mixnet client, where the keys reside
--entry-gateway-id <ENTRY_GATEWAY_ID>
Mixnet public ID of the entry gateway
--entry-gateway-country <ENTRY_GATEWAY_COUNTRY>
Auto-select entry gateway by country ISO
--exit-router-address <EXIT_ROUTER_ADDRESS>
Mixnet recipient address
--exit-gateway-id <EXIT_GATEWAY_ID>
--exit-router-country <EXIT_ROUTER_COUNTRY>
Mixnet recipient address
--enable-wireguard
Enable the wireguard traffic between the client and the entry gateway
--private-key <PRIVATE_KEY>
Associated private key
--wg-ip <WG_IP>
The IP address of the wireguard interface
--ip <IP>
The IP address of the TUN device
--mtu <MTU>
The MTU of the TUN device
--disable-routing
Disable routing all traffic through the VPN TUN device
--enable-two-hop
Enable two-hop mixnet traffic. This means that traffic jumps directly from entry gateway to exit gateway
--enable-poisson-rate
Enable Poisson process rate limiting of outbound traffic
-h, --help
Print help
-V, --version
Print version
```
~~~
**Fundamental commands and arguments**
Here is a list of the options and their descriptions. Some are essential, some are more technical and not needed to adjusted by users:
- `-c` is a path to the [Sandbox config](https://raw.githubusercontent.com/nymtech/nym/develop/envs/sandbox.env) file saved as `sandbox.env`
- `--entry-gateway-id`: paste one of the values labeled with a key `"identityKey"` (without `" "`) from [here](https://nymvpn.com/en/alpha/api/gateways)
- `--exit-router-address`: paste one of the values labeled with a key `"address"` (without `" "`) from here [here](https://nymvpn.com/en/alpha/api/gateways)
- `--enable-wireguard`: Enable the wireguard traffic between the client and the entry gateway. NymVPN uses Mullvad libraries for wrapping `wireguard-go` and to setup local routing rules to route all traffic to the TUN virtual network device
- `--wg-ip`: The address of the wireguard interface, you can get it [here](https://nymvpn.com/en/alpha)
- `--private-key`: get your private key for testing purposes [here](https://nymvpn.com/en/alpha)
- `--enable-two-hop` is a faster setup where the traffic is routed from the client to Entry Gateway and directly to Exit Gateway (default is 5-hops)
**Advanced options**
- `--enable-poisson`: Enables process rate limiting of outbound traffic (disabled by default). It means that NymVPN client will send packets at a steady stream to the Entry Gateway. By default it's on average one sphinx packet per 20ms, but there is some randomness (poisson distribution). When there are no real data to fill the sphinx packets with, cover packets are generated instead.
- `--ip` is the IP address of the TUN device. That is the IP address of the local private network that is set up between local client and the Exit Gateway.
- `--mtu`: The MTU of the TUN device. That is the max IP packet size of the local private network that is set up between local client and the Exit Gateway.
- `--disable-routing`: Disable routing all traffic through the VPN TUN device.
@@ -0,0 +1,191 @@
# NymVPN alpha: Guide for Mac OS
```admonish warning
NymVPN is an experimental software and it's for [testing](./nym-vpn-testing.md) purposes only. All users testing the client are expected to sign GDPR Information Sheet and Consent Form (shared at the event), and follow the steps listed in the form [*NymVPN User research*](https://opnform.com/forms/nymvpn-user-research-at-37c3-yccqko-2).
```
## Preparation
> Any syntax in `<>` brackets is a user's/version unique variable. Exchange with a corresponding name without the `<>` brackets.
### CLI Installation
You can use our automated script of follow the steps below to download, verify, install and run NymVPN client.
#### Automated Script CLI Installation
Open a terminal and follow these steps:
* Download the script
```sh
curl -o nym-vpn-client-executor.sh -L https://gist.githubusercontent.com/tommyv1987/87267ded27e1eb7651aa9cc745ddf4af/raw/99cea8f4d80f2d002802ed1cbedba288bfca4488/execute-nym-vpn-cli-binary.sh
```
* Make the script executable
```sh
chmod u+x nym-vpn-client-executor.sh
```
* Run the script
```sh
./nym-vpn-client-executor.sh
```
* When prompted to verify `sha256sum` paste one from the [release page](https://github.com/nymtech/nym/releases/tag/nym-vpn-alpha-0.0.2) including the binary name (all as one input with a space in between), for example:
```sh
96623ccc69bc4cc0e4e3e18528b6dae6be69f645d0a592d926a3158ce2d0c269 nym-vpn-cli_0.1.0_macos_x86_64.zip
```
The script will automatically start the client. Follow the instructions:
* The script will print a JSON view of existing Gateways and prompt you to:
- ***(Make sure to use two different Gateways for entry and exit!)***
- `enter a gateway ID:` paste one of the values labeled with a key `"identityKey"` printed above (without `" "`)
- `enter an exit address:` paste one of the values labeled with a key `"address"` printed above (without `" "`)
- `do you want five hop or two hop?`: type `five` or `two`
#### Manual CLI Installation
* Visit the [releases page](https://github.com/nymtech/nym/releases/tag/nym-vpn-alpha-0.0.2) to download the binary for Debian based Linux
* Open terminal in the same directory and check the the `sha256sum` by running:
```sh
# x86_64
sha256sum ./nym-vpn-cli_0.1.0_macos_x86_64.zip
# aarch64
sha256sum ./nym-vpn-cli_0.1.0_macos_aarch64.zip
```
* Compare the result with the sha256 hash shared on the [release page](https://github.com/nymtech/nym/releases/tag/nym-vpn-alpha-0.0.2)
* Extract files with `unzip` command or manually as you are used to
```sh
# for CLI
chmod +x ./nym-vpn-cli
```
* Create Sandbox environment config file by saving [this](https://raw.githubusercontent.com/nymtech/nym/develop/envs/sandbox.env) as `sandbox.env` in the same directory as your NymVPN binaries. In case of GUI setup, see the steps below.
### GUI Installation
We created a [script](https://gist.github.com/tommyv1987/7d210d4daa8f7abc61f9a696d0321f19) which does download, extraction, installation and configuration for MacOS users automatically following the steps below:
* To download the script, open a terminal in a directory where you want to download the script and run:
```sh
curl -o nym-vpn-client-executor.sh - L https://gist.githubusercontent.com/tommyv1987/7d210d4daa8f7abc61f9a696d0321f19/raw/4397365b4cf74594c7f99c1ef5d690b2f5b41192/nym-vpn-client-macos-executor.sh
```
* Make executable
```sh
chmod u+x nym-vpn-client-macos-executor.sh
```
* Run
```sh
./nym-vpn-client-macos-executor.sh
```
* When prompted to verify `sha256sum` paste one from the [release page](https://github.com/nymtech/nym/releases/tag/nym-vpn-alpha-0.0.2) including the binary name (all as one input with a space in between), for example:
```sh
06c7c82f032f230187da1002a9a9a88242d3bbf6c5c09dc961a71df151d768d0 nym-vpn-ui_0.0.2_macos_x86_64.zip
```
* The script will run the application and it will prompt you for a country code to exit, chose one of the offered options
In case of errors check out the [troubleshooting](./nym-vpn-troubleshooting.html#installing-gui-on-macos-not-working) section.
## Run NymVPN
* **For NymVPN to work, all other VPNs must be switched off!**
* At this alpha stage of NymVPN, network connection (wifi) must be re-connected after or in-between NymVPN testing rounds.
### Run CLI
Make sure your terminal is open in the same directory as your `nym-vpn-cli` binary.
* Find the entire command with all the needed arguments' values and your wireguard private key for testing purposes at [nymvpn.com/en/alpha](https://nymvpn.com/en/alpha)
* Run it with `sudo` as root, the command will look like this with specified arguments:
```sh
sudo ./nym-vpn-cli -c ./sandbox.env --entry-gateway-id <ENTRY_GATEWAY_ID> --exit-router-address <EXIT_ROUTER_ADDRESS> --enable-wireguard --private-key <PRIVATE_KEY> --wg-ip <WG_IP>
```
* To chose different Gateways, visit [nymvpn.com/en/alpha/api/gateways](https://nymvpn.com/en/alpha/api/gateways)
* To see all possibilities see [command explanation](#cli-commands-and-options) below
### Run GUI
Make sure you went through the GUI configuration in the [preparation section](#gui-installation).
You may be able to have a NymVPN application icon in your app menu. However this may not work as the application needs root permission.
* Run GUI from terminal:
```sh
sudo $nym_vpn_dir/nym-vpn
```
In case of errors, see [troubleshooting section](./nym-vpn-troubleshooting.md#macos-alert-on-nymvpn-ui-startup).
### CLI Commands and Options
The basic syntax of `nym-vpn-cli` is:
```sh
sudo ./nym-vpn-cli -c ./sandbox.env --entry-gateway-id <ENTRY_GATEWAY_ID> --exit-router-address <EXIT_ROUTER_ADDRESS> --enable-wireguard --private-key <PRIVATE_KEY> --wg-ip <WG_IP>
```
* To chose different Gateways, visit [nymvpn.com/en/alpha/api/gateways](https://nymvpn.com/en/alpha/api/gateways)
* To see all possibilities run with `--help` flag:
```sh
./nym-vpn-cli --help
```
~~~admonish example collapsible=true title="Console output"
```
Usage: nym-vpn-cli [OPTIONS]
Options:
-c, --config-env-file <CONFIG_ENV_FILE>
Path pointing to an env file describing the network
--mixnet-client-path <MIXNET_CLIENT_PATH>
Path to the data directory of a previously initialised mixnet client, where the keys reside
--entry-gateway-id <ENTRY_GATEWAY_ID>
Mixnet public ID of the entry gateway
--entry-gateway-country <ENTRY_GATEWAY_COUNTRY>
Auto-select entry gateway by country ISO
--exit-router-address <EXIT_ROUTER_ADDRESS>
Mixnet recipient address
--exit-gateway-id <EXIT_GATEWAY_ID>
--exit-router-country <EXIT_ROUTER_COUNTRY>
Mixnet recipient address
--enable-wireguard
Enable the wireguard traffic between the client and the entry gateway
--private-key <PRIVATE_KEY>
Associated private key
--wg-ip <WG_IP>
The IP address of the wireguard interface
--ip <IP>
The IP address of the TUN device
--mtu <MTU>
The MTU of the TUN device
--disable-routing
Disable routing all traffic through the VPN TUN device
--enable-two-hop
Enable two-hop mixnet traffic. This means that traffic jumps directly from entry gateway to exit gateway
--enable-poisson-rate
Enable Poisson process rate limiting of outbound traffic
-h, --help
Print help
-V, --version
Print version
```
~~~
**Fundamental commands and arguments**
Here is a list of the options and their descriptions. Some are essential, some are more technical and not needed to adjusted by users:
- `-c` is a path to the [Sandbox config](https://raw.githubusercontent.com/nymtech/nym/develop/envs/sandbox.env) file saved as `sandbox.env`
- `--entry-gateway-id`: paste one of the values labeled with a key `"identityKey"` (without `" "`) from [here](https://nymvpn.com/en/alpha/api/gateways)
- `--exit-router-address`: paste one of the values labeled with a key `"address"` (without `" "`) from here [here](https://nymvpn.com/en/alpha/api/gateways)
- `--enable-wireguard`: Enable the wireguard traffic between the client and the entry gateway. NymVPN uses Mullvad libraries for wrapping `wireguard-go` and to setup local routing rules to route all traffic to the TUN virtual network device
- `--wg-ip`: The address of the wireguard interface, you can get it [here](https://nymvpn.com/en/alpha)
- `--private-key`: get your private key for testing purposes [here](https://nymvpn.com/en/alpha)
- `--enable-two-hop` is a faster setup where the traffic is routed from the client to Entry Gateway and directly to Exit Gateway (default is 5-hops)
**Advanced options**
- `--enable-poisson`: Enables process rate limiting of outbound traffic (disabled by default). It means that NymVPN client will send packets at a steady stream to the Entry Gateway. By default it's on average one sphinx packet per 20ms, but there is some randomness (poisson distribution). When there are no real data to fill the sphinx packets with, cover packets are generated instead.
- `--ip` is the IP address of the TUN device. That is the IP address of the local private network that is set up between local client and the Exit Gateway.
- `--mtu`: The MTU of the TUN device. That is the max IP packet size of the local private network that is set up between local client and the Exit Gateway.
- `--disable-routing`: Disable routing all traffic through the VPN TUN device.
@@ -0,0 +1,195 @@
# Testing NymVPN alpha
> Before you get into testing NymVPN, make sure to go through the preparation steps for [Linux](nym-vpn-linux.md) or [MacOS](nym-vpn-mac.md).
One of the main aims of NymVPN alpha release is testing; your results will help us to make NymVPN robust and stabilise both the client and the network through provided measurements.
## Steps to test NymVPN
> Any syntax in `<>` brackets is a user's/version unique variable. Exchange with a corresponding name without the `<>` brackets.
1. Create a directory called `nym-vpn-tests` and copy your `nym-vpn-cli` binary and [`sandbox.env`](https://raw.githubusercontent.com/nymtech/nym/develop/envs/sandbox.env) to that directory
2. Copy the [block below](#testssh) and save it as `tests.sh` to the same folder
3. Open terminal in the same directory
4. Turn off any existing VPN's (including the NymVPN instances), reconnect your wifi and make the script executable by running `chmod +x ./tests.sh`
5. Run the tests: `sudo ./tests.sh`
6. In case of errors, see the [troubleshooting section](./nym-vpn-troubleshooting.md#missing-jq-error) below
7. The script will print a JSON view of existing Gateways and prompt you to:
- ***(Make sure to use two different Gateways for entry and exit!)***
- `enter a gateway ID:` paste one of the values labeled with a key `"identityKey"` printed above (without `" "`)
- `enter an exit address:` paste one of the values labeled with a key `"address"` printed above (without `" "`)
- `enable WireGuard? (yes/no):` if you chose yes, find your private key and wireguard IP [here](https://nymvpn.com/en/alpha)
8. Note that the testing script doesn't print many logs, in case of doubts you can check logs in the log file `temp_log.txt` located in the same directory.
9. The script shall run the tests and generate a folder called `tests_<LONG_STRING>` and files `perf_test_results.log` or `two_hop_perf_test_results.log` as well as some temp files. This is how the directory structure will look like:
```sh
nym-vpn-tests
├── tests.sh
├── nym-vpn-cli
├── sandbox.env
├── perf_test_results.log
├── tests_<LONG_STRING>
│   ├── api_response_times.txt
│   ├── download_time.txt
│   └── ping_results.txt
├── timeout
└── two_hop_perf_test_results.log
```
10. In case of errors, see [troubleshooting section](./nym-vpn-troubleshooting.md#missing-jq-error) below
11. When the tests are finished, remove the `nym-vpn-cli` binary from the folder and compress the entire folder as `nym-vpn-tests.zip`
12. Upload this compressed file to the [questionnaire](https://opnform.com/forms/nymvpn-user-research-at-37c3-yccqko-2) upload field when prompted
#### tests.sh
This is the testing script which needs to be copied and saved as `tests.sh` to your `nym-vpn-tests` folder and then run from there as described [above](#steps-to-test-nymvpn).
```sh
#!/bin/bash
ENDPOINT="https://sandbox-nym-api1.nymtech.net/api/v1/gateways/described"
json_array=()
echo "🚀 🏎 - please be patient, i'm fetching you your entry points - 🚀 🏎 "
data=$(curl -s "$ENDPOINT" | jq -c '.[] | {host: .bond.gateway.host, hostname: .self_described.host_information.hostname, identity_key: .bond.gateway.identity_key, exitGateway: .self_described.ip_packet_router.address}')
while IFS= read -r entry; do
host=$(echo "$entry" | jq -r '.host')
hostname=$(echo "$entry" | jq -r '.hostname')
identity_key=$(echo "$entry" | jq -r '.identity_key')
exit_gateway_address=$(echo "$entry" | jq -r '.exitGateway // empty')
valid_ip=$(echo "$host")
if [ -n "$exit_gateway_address" ]; then
exit_gateway="{\"address\": \"${exit_gateway_address}\"}"
else
exit_gateway="{}"
fi
if [[ $valid_ip =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
country_info=$(curl -s "http://ipinfo.io/${valid_ip}/country" | tr -d '\n')
country_info_escaped=$(echo "$country_info" | tr -d '\n' | jq -aRs . | tr -d '"')
else
country_info_escaped=""
fi
json_object="{\"hostname\": \"${hostname}\", \"identityKey\": \"${identity_key}\", \"exitGateway\": ${exit_gateway}, \"location\": \"${country_info_escaped}\"}"
json_array+=("$json_object")
done < <(echo "$data")
if [ $? -ne 0 ]; then
echo "error fetching data from endpoint"
exit 1
fi
download_file() {
local file_url=$1
local output_file=$2
local time_file=$3
echo "starting download speed test..."
local start_time=$(date +%s)
if [[ "$OSTYPE" == "linux-gnu"* ]]; then
wget -O $output_file $file_url
elif [[ "$OSTYPE" == "darwin"* ]]; then
curl -o $output_file $file_url
fi
local end_time=$(date +%s)
local elapsed_time=$((end_time - start_time))
echo "download speed test completed in $elapsed_time seconds." >"$time_file"
}
if ! command -v jq &>/dev/null; then
echo "jq is not installed. Please install jq to proceed."
exit 1
fi
temp_log_file="temp_log.txt"
perform_tests() {
local gateway_id=$1
local exit_address=$2
local test_directory="tests_${gateway_id}_${exit_address}"
local file_url="http://ipv4.download.thinkbroadband.com/2MB.zip"
mkdir -p "$test_directory"
local ping_results_file="${test_directory}/ping_results.txt"
local download_time_file="${test_directory}/download_time.txt"
local api_response_file="${test_directory}/api_response_times.txt"
# ping test
echo "starting ping test..."
for site in google.com youtube.com facebook.com baidu.com wikipedia.org amazon.com twitter.com instagram.com yahoo.com ebay.com netflix.com; do
ping -c 4 $site >>"$ping_results_file"
done
echo "ping test completed. Results saved in $ping_results_file"
# download speed test
download_file $file_url /dev/null "$download_time_file"
# api test
local api_endpoint="https://validator.nymtech.net/api/v1/mixnodes"
local iterations=10
>"$api_response_file"
for i in $(seq 1 $iterations); do
local start_time=$(date +%s)
local response=$(curl -s -o /dev/null -w '%{http_code}' $api_endpoint)
local end_time=$(date +%s)
local elapsed_seconds=$((end_time - start_time))
local hours=$((elapsed_seconds / 3600))
local minutes=$(((elapsed_seconds % 3600) / 60))
local seconds=$((elapsed_seconds % 60))
local human_readable_time=$(printf "%02dh:%02dm:%02ds" $hours $minutes $seconds)
echo "iteration $i: response Time = ${human_readable_time}, status code = $response" >>"$api_response_file"
done
echo "api response test completed. Results saved in $api_response_file."
}
printf "%s\n" "${json_array[@]}" | jq -s .
read -p "enter a gateway ID: " identity_key
read -p "enter an exit address: " exit_address
while true; do
read -p "enable WireGuard? (yes/no): " enable_wireguard
enable_wireguard=$(echo "$enable_wireguard" | tr '[:upper:]' '[:lower:]')
case "$enable_wireguard" in
"yes")
read -p "enter your WireGuard private key: " priv_key
read -p "enter your WireGuard IP: " wg_ip
wireguard_options="--enable-wireguard --private-key $priv_key --wg-ip $wg_ip"
break
;;
"no")
wireguard_options=""
break
;;
*)
echo "invalid response. please enter 'yes' or 'no'."
;;
esac
done
sudo ./nym-vpn-cli -c sandbox.env --entry-gateway-id ${identity_key} --exit-router-address ${exit_address} --enable-two-hop $wireguard_options >"$temp_log_file" 2>&1 &
timeout=15
start_time=$(date +%s)
while true; do
current_time=$(date +%s)
if grep -q "received plain" "$temp_log_file"; then
echo "successful configuration with identity_key: $identity_key and exit address: $exit_address" >>perf_test_results.log
perform_tests "$identity_key" "$exit_address"
break
fi
if ((current_time - start_time > timeout)); then
echo "failed to connect with identity_key: $identity_key using the exit address: $exit_address" >>perf_test_results.log
break
fi
sleep 1
done
echo "terminating nym-vpn-cli..."
pkill -f './nym-vpn-cli'
sleep 5
rm -f "$temp_log_file"
```
@@ -0,0 +1,100 @@
# Troubleshooting
Below are listed some points which may need to be addressed when testing NymVPN alpha. If you crashed into any errors which are not listed, please contact us at the event.
#### Problems with the newest version `nym-vpn-alpha-0.0.2 demo`
Try the previous version `nym-vpn-alpha-0.0.1 demo` which was tested multiple times by downloading the client from this [release page(https://github.com/nymtech/nym/releases/tag/nym-vpn-alpha-0.0.1) and doing all the steps with the name of your downloaded binary.
#### Installing GUI on MacOS not working
In case there was a problem running the script, try a manual configuration:
* Visit the [releases page](https://github.com/nymtech/nym/releases/tag/nym-vpn-alpha-0.0.1) to download the binary for Debian based Linux
* Open terminal in the same directory and check the the `sha256sum` by running:
```sh
# for MacOS GUI
sha256sum ./nym-vpn-ui-macos-latest.zip
```
* Compare the result with the sha256 hash shared on the [release page](https://github.com/nymtech/nym/releases/tag/nym-vpn-alpha-0.0.1)
* Extract files with `unzip` command or manually as you are used toi
* Create a NymVPN config directory called `nym-vpn` in your `~/.config`, either manually or by a command:
```sh
mkdir $HOME/Library/Application Support/nym-vpn/
```
* Create the network config by saving [this](https://raw.githubusercontent.com/nymtech/nym/develop/envs/sandbox.env) as `sandbox.env` in the config directory `nym-vpn` you just created
* Create the main config file called `config.toml` in the same directory with this content:
```toml
env_config_file = "$HOME/Library/Application Support/nym-vpn/"
entry_node_location = "DE" # two letters country code
```
**Note:** Some users had a problem to access their home config folder on macOS, in that case save the configuration files in the same directory where you downloaded your `nym-vpn` binary as following:
* Create the network config by saving [this](https://raw.githubusercontent.com/nymtech/nym/develop/envs/sandbox.env) as `.env` (yes just like that) in the same directory like `nym-vpn` binaries.
* Create the main config file called `config.toml` in the very same directory with this content:
```toml
env_config_file = "$HOME/Library/Application Support/nym-vpn/"
entry_node_location = "DE" # two letters country code
```
#### Thread `main` panicked
If you see a message like:
```sh
thread 'main' panicked at /Users/runner/.cargo/git/checkouts/mullvadvpn-app-a575cf705b5dfd76/ccfbaa2/talpid-routing/src/unix.rs:301:30:
```
Restart your wifi connection and start again.
#### macOS alert on NymVPN UI startup
If you are running NymVPN on mac OS for the first time, you may see this alert message:
![](images/image3.png)
1. Head to System Settings -> Privacy & Security and click `Allow anyway`
![](images/image5.png)
2. Confirm with your password or TouchID
3. Possibly you may have to confirm again upon running the application
#### Missing `jq` error
In case of missing `jq` on Linux (Debian) install it with:
```sh
# Linux (Debian)
sudo apt-get install jq
# macOS
brew install jq
```
On some Linux distributions however the [script](./nym-vpn.md#testssh) returns `jq` error even if your system claims that `jq is already the newest version`.
In that case, comment the `jq` check in the script as follows:
```sh
#if ! command -v jq &>/dev/null; then
# echo "jq is not installed. Please install jq to proceed."
# exit 1
#fi
```
#### Error current_time: not found
When running `sudo sh ./test.sh` you may see an error like: `93: current_time: not found`. This has something to do with the `current_time` setup of your system and on itself shall not have a negative impact on the test. It has nothing to do with the client at all as it only relates to the code in our testing script.
#### Not connecting to the endpoint
In case the automatic download of all the Gateways fail (and it shouldn't), you do an easy manual work around:
1. Open the list of Gateways created by API [here](https://nymvpn.com/en/ccc/api/gateways)
2. On top click on `JSON` option (shall be default view) and `Save`
3. Save it as `data.json` to the `nym-vpn-tests` folder
4. Replace line 3 in the [script `tests.sh`](./nym-vpn.md#testssh) with:
```sh
NEW_ENDPOINT="http://localhost:8000/data.json"
```
5. In a new terminal window run:
```sh
python3 -m http.server 8000
```
6. Continue with the steps listed in [testing section](./nym-vpn.md#testing)
@@ -0,0 +1,51 @@
# NymVPN alpha
<div style="padding:56.25% 0 0 0;position:relative;"><iframe src="https://player.vimeo.com/video/897010658?h=1f55870fe6&amp;badge=0&amp;autopause=0&amp;player_id=0&amp;app_id=58479" frameborder="0" allow="autoplay; fullscreen; picture-in-picture" style="position:absolute;top:0;left:0;width:100%;height:100%;" title="NYMVPN alpha demo 37C3"></iframe></div><script src="https://player.vimeo.com/api/player.js"></script>
We are honored to present NymVPN, a client that uses [Nym Mixnet](https://nymtech.net) to anonymise all of a user's internet traffic through either a 5-hop mixnet (for a full network privacy) or the faster 2-hop decentralised VPN (with some extra features). At this event we have the unique opportunity to be part of the initial alpha testing. The following pages provide a how-to guide, explaining steps to install and run NymVPN CLI and GUI on our testing environment Nym Sandbox.
## NymVPN
The following overview provides an easy introduction to the NymVPN alpha client. We recommend interested developers to begin with [Nym network overview](https://nymtech.net/docs/architecture/network-overview.html) and the [Mixnet traffic flow](https://nymtech.net/docs/architecture/traffic-flow.html) pages.
The default is to run in 5-hop mode:
```
┌─►mix──┐ mix mix
│ │
Entry │ │ Exit
client ───► Gateway ──┘ mix │ mix ┌─►mix ───► Gateway ───► internet
│ │
│ │
mix └─►mix──┘ mix
```
Users can switch to 2-hop only mode, which is a faster but less private option. In this mode traffic is only sent between the two Gateways, and is not passed between Mix Nodes.
The client can optionally do the first hop (local client to Entry Gateway) using Wireguard. NymVPN uses Mullvad libraries for wrapping `wireguard-go` and to setup local routing rules to route all traffic to the TUN virtual network device.
## NymVPN Guide & FAQ pages
To download, install and test NymVPN alpha, visit pages listed below:
* [GNU/Linux](nym-vpn-linux.md)
* [Mac OS](nym-vpn-mac.md)
* [Testing scripts](nym-vpn-testing.md)
* [Troubleshooting](nym-vpn-troubleshooting.md)
* [NymVPN FAQ](nym-vpn-faq.md)
## Goals of testing
This alpha testing will help:
* Stabilise NymVPN client
* Understand NymVPN client behavior in various setups (OS, connectivity, etc.)
* Stabilize the VPN infrastructure and improve its reliability / speed / features (e.g. IPv6 support)
* Load test the network in Sandbox environment and identify / anticipate potential weaknesses
```admonish info
Our alpha testing round is done live with some participants at the event. This guide will not work for everyone, as the NymVPN binaries aren't publicly accessible yet. Note that this setup of Nym testnet Sandbox environment is limited event and some of the configurations will not work in the future.
**If you commit to test NymVPN alpha, please start with the [user research form](https://opnform.com/forms/nymvpn-user-research-at-37c3-yccqko-2) where all the steps will be provided**.
```
@@ -2,4 +2,4 @@
The Rust SDK allows developers building applications in Rust to import and interact with Nym clients as they would any other dependency, instead of running the client as a seperate process on their machine.
[Read the docs](https://nymtech.net/docs/sdk/rust.html).
[Read the docs](https://nymtech.net/docs/sdk/rust/rust.html).
+1
View File
@@ -22,6 +22,7 @@ REWARDING_VALIDATOR_ADDRESS=n10yyd98e2tuwu0f7ypz9dy3hhjw7v772q6287gy
STATISTICS_SERVICE_DOMAIN_ADDRESS="https://mainnet-stats.nymte.ch:8090"
NYXD="https://rpc.nymtech.net"
NYM_API="https://validator.nymtech.net/api/"
NYXD_WS="wss://rpc.nymtech.net/websocket"
EXPLORER_API="https://explorer.nymtech.net/api/"
DKG_TIME_CONFIGURATION="259200,300,300,60,60,1209600"
+8 -6
View File
@@ -13,15 +13,17 @@ DENOMS_EXPONENT=6
REWARDING_VALIDATOR_ADDRESS=n1pefc2utwpy5w78p2kqdsfmpjxfwmn9d39k5mqa
MIXNET_CONTRACT_ADDRESS=n1xr3rq8yvd7qplsw5yx90ftsr2zdhg4e9z60h5duusgxpv72hud3sjkxkav
VESTING_CONTRACT_ADDRESS=n1unyuj8qnmygvzuex3dwmg9yzt9alhvyeat0uu0jedg2wj33efl5qackslz
COCONUT_BANDWIDTH_CONTRACT_ADDRESS=n16a32stm6kknhq5cc8rx77elr66pygf2hfszw7wvpq746x3uffylqkjar4l
GROUP_CONTRACT_ADDRESS=n1pd7kfgvr5tpcv0xnlv46c4jsq9jg2r799xxrcwqdm4l2jhq2pjwqrmz5ju
MULTISIG_CONTRACT_ADDRESS=n14ph4e660eyqz0j36zlkaey4zgzexm5twkmjlqaequxr2cjm9eprqsmad6k
COCONUT_DKG_CONTRACT_ADDRESS=n1ahg0erc2fs6xx3j5m8sfx3ryuzdjh6kf6qm9plsf865fltekyrfsesac6a
EPHEMERA_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0
BANDWIDTH_CLAIM_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0
COCONUT_BANDWIDTH_CONTRACT_ADDRESS=n13902g92xfefeyzuyed49snlm5fxv5ms6mdq5kvrut27hasdw5a9q9vyw6c
GROUP_CONTRACT_ADDRESS=n18nczmqw6adwxg2wnlef3hf0etf8anccafp2pjpul5rrtmv96umyq5mv7t5
MULTISIG_CONTRACT_ADDRESS=n1q3zzxl78rlmxv3vn0uf4vkyz285lk8q2xzne299yt9x6mpfgk90qukuzmv
COCONUT_DKG_CONTRACT_ADDRESS=n1jsz20ggp5a6v76j060erkzvxmeus8htlpl77yxp878f0gf95cyaq6p2pee
NAME_SERVICE_CONTRACT_ADDRESS=n12ne7qtmdwd0j03t9t5es8md66wq4e5xg9neladrsag8fx3y89rcs36asfp
SERVICE_PROVIDER_DIRECTORY_CONTRACT_ADDRESS=n1ps5yutd7sufwg058qd7ac7ldnlazsvmhzqwucsfxmm445d70u8asqxpur4
EPHEMERA_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0
STATISTICS_SERVICE_DOMAIN_ADDRESS="http://0.0.0.0"
EXPLORER_API=https://sandbox-explorer.nymtech.net/api
NYXD="https://rpc.sandbox.nymtech.net"
NYXD="https://sandbox-validator1.nymtech.net"
NYM_API="https://sandbox-nym-api1.nymtech.net/api"
+1
View File
@@ -43,6 +43,7 @@ pub(crate) enum BlockManagerError {
BlockManager(#[from] anyhow::Error),
}
#[allow(clippy::struct_field_names)] // this should get resolved properly at some point, but not now...
/// It helps to use atomic state management for new blocks.
pub(crate) struct BlockChainState {
pub(crate) last_blocks: LruCache<Hash, Block>,
+1 -1
View File
@@ -60,7 +60,7 @@ impl RunExternalNodeCmd {
.with_members_provider(members_provider)?
.build();
let shutdown = TaskManager::new(10);
let mut shutdown = TaskManager::new(10);
let shutdown_listener = shutdown.subscribe();
tokio::spawn(ephemera.run(shutdown_listener));
+2
View File
@@ -102,6 +102,8 @@ pub(crate) struct Peer {
/// assert_eq!(peer_id, public_key.peer_id());
///
/// ```
#[allow(clippy::struct_field_names)]
// this should get resolved properly at some point, but not now...
pub peer_id: PeerId,
/// The peer's public key. It matches PeerId.
pub public_key: PublicKey,
@@ -1,3 +1,6 @@
// due to the macro expansion of rather old rocket macros...
#![allow(unused_imports)]
use crate::country_statistics::country_nodes_distribution::CountryNodesDistribution;
use crate::state::ExplorerApiStateContext;
use rocket::serde::json::Json;
+3
View File
@@ -1,6 +1,9 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
// due to the macro expansion of rather old rocket macros...
#![allow(unused_imports)]
use nym_explorer_api_requests::PrettyDetailedGatewayBond;
use rocket::response::status::NotFound;
use rocket::serde::json::Json;
+1 -1
View File
@@ -61,7 +61,7 @@ impl FailedIpAddresses {
if Path::new(&file_path).exists() {
if let Ok(file) = File::open(&file_path) {
let lines = io::BufReader::new(file).lines();
for ip in lines.flatten() {
for ip in lines.map_while(Result::ok) {
failed_ips.insert(ip);
}
}
+1 -1
View File
@@ -79,7 +79,7 @@ impl ExplorerApi {
self.wait_for_interrupt(shutdown).await
}
async fn wait_for_interrupt(&self, shutdown: TaskManager) {
async fn wait_for_interrupt(&self, mut shutdown: TaskManager) {
let _res = shutdown.catch_interrupt().await;
log::info!("Stopping explorer API");
}
+3
View File
@@ -1,6 +1,9 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
// due to the macro expansion of rather old rocket macros...
#![allow(unused_imports)]
use crate::mix_node::delegations::{
get_single_mixnode_delegations, get_single_mixnode_delegations_summed,
};
+3
View File
@@ -1,3 +1,6 @@
// due to the macro expansion of rather old rocket macros...
#![allow(unused_imports)]
use crate::mix_nodes::models::{MixNodeActiveSetSummary, MixNodeSummary};
use crate::state::ExplorerApiStateContext;
use nym_explorer_api_requests::{MixnodeStatus, PrettyDetailedMixNodeBond};
+3
View File
@@ -1,3 +1,6 @@
// due to the macro expansion of rather old rocket macros...
#![allow(unused_imports)]
use rocket::serde::json::Json;
use rocket::{Route, State};
use rocket_okapi::okapi::openapi3::OpenApi;
+3
View File
@@ -1,6 +1,9 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
// due to the macro expansion of rather old rocket macros...
#![allow(unused_imports)]
use nym_mixnet_contract_common::{MixId, MixNode};
use rocket::serde::json::Json;
use rocket::{Route, State};
@@ -1,3 +1,6 @@
// due to the macro expansion of rather old rocket macros...
#![allow(unused_imports)]
use crate::service_providers::models::{
DirectoryService, DirectorySpDetailed, HarbourMasterService, PagedResult,
};
+3
View File
@@ -1,6 +1,9 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
// due to the macro expansion of rather old rocket macros...
#![allow(unused_imports)]
use rocket::response::status::NotFound;
use rocket::serde::json::Json;
use rocket::{Route, State};
+2 -2
View File
@@ -36,7 +36,7 @@ pretty_env_logger = "0.4"
rand = "0.7"
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
sqlx = { version = "0.5", features = [
sqlx = { workspace = true, features = [
"runtime-tokio-rustls",
"sqlite",
"macros",
@@ -89,7 +89,7 @@ hyper = "0.14.27"
[build-dependencies]
tokio = { version = "1.24.1", features = ["rt-multi-thread", "macros"] }
sqlx = { version = "0.5", features = [
sqlx = { workspace = true, features = [
"runtime-tokio-rustls",
"sqlite",
"macros",
@@ -5,6 +5,7 @@ use super::authenticated::RequestHandlingError;
use log::*;
use nym_coconut_interface::Credential;
use nym_validator_client::coconut::all_coconut_api_clients;
use nym_validator_client::nyxd::AccountId;
use nym_validator_client::{
nyxd::{
contract_traits::{
@@ -12,26 +13,27 @@ use nym_validator_client::{
MultisigSigningClient,
},
cosmwasm_client::logs::{find_attribute, BANDWIDTH_PROPOSAL_ID},
Coin, Fee,
Coin,
},
CoconutApiClient, DirectSigningHttpRpcNyxdClient,
};
use std::time::{Duration, SystemTime};
const ONE_HOUR_SEC: u64 = 3600;
const MAX_FEEGRANT_UNYM: u128 = 10000;
use std::ops::Deref;
use tokio::sync::RwLock;
pub(crate) struct CoconutVerifier {
nyxd_client: DirectSigningHttpRpcNyxdClient,
address: AccountId,
nyxd_client: RwLock<DirectSigningHttpRpcNyxdClient>,
mix_denom_base: String,
}
impl CoconutVerifier {
pub fn new(nyxd_client: DirectSigningHttpRpcNyxdClient) -> Self {
pub async fn new(nyxd_client: DirectSigningHttpRpcNyxdClient) -> Self {
let mix_denom_base = nyxd_client.current_chain_details().mix_denom.base.clone();
let address = nyxd_client.address();
CoconutVerifier {
nyxd_client,
address,
nyxd_client: RwLock::new(nyxd_client),
mix_denom_base,
}
}
@@ -39,7 +41,13 @@ impl CoconutVerifier {
pub async fn all_current_coconut_api_clients(
&self,
) -> Result<Vec<CoconutApiClient>, RequestHandlingError> {
let epoch_id = self.nyxd_client.get_current_epoch().await?.epoch_id;
let epoch_id = self
.nyxd_client
.read()
.await
.get_current_epoch()
.await?
.epoch_id;
self.all_coconut_api_clients(epoch_id).await
}
@@ -47,7 +55,7 @@ impl CoconutVerifier {
&self,
epoch_id: u64,
) -> Result<Vec<CoconutApiClient>, RequestHandlingError> {
Ok(all_coconut_api_clients(&self.nyxd_client, epoch_id).await?)
Ok(all_coconut_api_clients(self.nyxd_client.read().await.deref(), epoch_id).await?)
}
pub async fn release_funds(
@@ -55,19 +63,17 @@ impl CoconutVerifier {
api_clients: Vec<CoconutApiClient>,
credential: &Credential,
) -> Result<(), RequestHandlingError> {
// Use a custom multiplier for revoke, as the default one (1.3)
// isn't enough
let revoke_fee = Some(Fee::Auto(Some(1.5)));
let res = self
.nyxd_client
.write()
.await
.spend_credential(
Coin::new(
credential.voucher_value().into(),
self.mix_denom_base.clone(),
),
credential.blinded_serial_number(),
self.nyxd_client.address().to_string(),
self.address.to_string(),
None,
)
.await?;
@@ -81,7 +87,12 @@ impl CoconutVerifier {
reason: String::from("proposal id could not be parsed to u64"),
})?;
let proposal = self.nyxd_client.query_proposal(proposal_id).await?;
let proposal = self
.nyxd_client
.read()
.await
.query_proposal(proposal_id)
.await?;
if !credential.has_blinded_serial_number(&proposal.description)? {
return Err(RequestHandlingError::ProposalIdError {
reason: String::from("proposal has different serial number"),
@@ -91,28 +102,10 @@ impl CoconutVerifier {
let req = nym_api_requests::coconut::VerifyCredentialBody::new(
credential.clone(),
proposal_id,
self.nyxd_client.address(),
self.address.clone(),
);
for client in api_clients {
self.nyxd_client
.grant_allowance(
&client.cosmos_address,
vec![Coin::new(MAX_FEEGRANT_UNYM, self.mix_denom_base.clone())],
SystemTime::now().checked_add(Duration::from_secs(ONE_HOUR_SEC)),
// It would be nice to be able to filter deeper, but for now only the msg type filter is avaialable
vec![String::from("/cosmwasm.wasm.v1.MsgExecuteContract")],
"Create allowance to vote the release of funds".to_string(),
None,
)
.await?;
let ret = client.api_client.verify_bandwidth_credential(&req).await;
self.nyxd_client
.revoke_allowance(
&client.cosmos_address,
"Cleanup the previous allowance for releasing funds".to_string(),
revoke_fee.clone(),
)
.await?;
match ret {
Ok(res) => {
if !res.verification_result {
@@ -125,7 +118,11 @@ impl CoconutVerifier {
}
}
self.nyxd_client.execute_proposal(proposal_id, None).await?;
self.nyxd_client
.write()
.await
.execute_proposal(proposal_id, None)
.await?;
Ok(())
}

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