Compare commits

..

166 Commits

Author SHA1 Message Date
Jędrzej Stuczyński 69a34418ab ignore precommits from missing validators 2025-06-23 10:46:01 +01:00
Jędrzej Stuczyński fd00405245 allow conversion from CometBFT block subscription 2025-02-19 14:56:40 +00:00
Jędrzej Stuczyński 7d77a9231c old validator rewarder with updated cosmrs 2025-02-19 14:33:37 +00:00
Jędrzej Stuczyński e37145422c Merge pull request #4942 from nymtech/bugfix/rewarder-post-pruning-adjustments
Bugfix/rewarder post pruning adjustments
2024-09-27 18:23:32 +01:00
Jędrzej Stuczyński 4ad52accc0 fixed stabilised clippy issue 2024-09-27 18:10:59 +01:00
Jędrzej Stuczyński 784fae2204 fix logic for determining end height for processing block ranges 2024-09-27 17:53:55 +01:00
Jędrzej Stuczyński 8aa5711bee fixed query for historical validator data 2024-09-27 17:48:56 +01:00
Jędrzej Stuczyński 07022314fc fixed typos and formatting 2024-09-27 16:51:45 +01:00
Jędrzej Stuczyński 76c3081470 introduced rewarding resync alongside recovery instructions 2024-09-27 16:41:07 +01:00
Jędrzej Stuczyński d399161d31 adjust 'process_until' command to allow empty stop height 2024-09-27 16:35:53 +01:00
Jędrzej Stuczyński 27fb4ae0cc log error when rewarding fails due to missing blocks 2024-09-27 16:21:27 +01:00
Jędrzej Stuczyński 74392a2886 don't request useless blocks during startup sync 2024-09-27 15:53:16 +01:00
Jędrzej Stuczyński 457c478a03 introduced cli command to process a block range 2024-09-27 15:15:13 +01:00
Jędrzej Stuczyński 5e95992427 introduced cli command to process an individual block 2024-09-27 14:57:39 +01:00
Jędrzej Stuczyński d7eecd481c decreased SOCKET_FAILURE_RESET
the previous value of 2h was way too big. especially since it was quite likely for multiple failures to occur hourly during increased validator load when mixnet epoch was getting transitioned
2024-09-27 14:13:09 +01:00
import this e08fc4894b [DOCs/operators]: Release notes v2024.11-wedel (#4939)
* finish release notes

* add a note
2024-09-27 13:12:15 +00:00
Bogdan-Ștefan Neacşu fabd48b7ea Fix broken build after merge (#4937) 2024-09-26 18:44:21 +02:00
Bogdan-Ștefan Neacşu 894e0bd1bf Add more conversions for responses of authenticator messages (#4929)
* More conversions for responses

* Expose version
2024-09-26 18:00:13 +02:00
benedetta davico f76300669a Merge pull request #4931 from nymtech/feature/wedel-merge-conflicts
Wedel release to develop
2024-09-26 13:46:24 +02:00
Jędrzej Stuczyński 333ace1f97 Merge branch 'release/2024.11-wedel' into feature/wedel-merge-conflicts 2024-09-26 08:56:11 +01:00
Dinko Zdravac 487bf6732e Assume offline mode in sqlx (#4926)
* Assume offline mode

* PR feedback
2024-09-25 13:28:36 +02:00
Jędrzej Stuczyński 5d4a0fef55 Merge pull request #4871 from nymtech/chore/remove-another-mixnet-migration
chore: remove queued migration for adding explicit admin
2024-09-25 09:37:49 +01:00
Jon Häggblad 1627146c0e Make ip-packet-request VERSION pub (#4925) 2024-09-25 09:56:32 +02:00
Dinko Zdravac ae40a00b8f Data Observatory stub (#4905)
* Data Observatory stub

* Fix sqlx in CI

* Add troubleshooting tips for sqlx

* Update CI paths to trigger for this package

* Add this to CI upload binary build
2024-09-24 16:48:15 +02:00
Jon Häggblad 7f3c0470e0 Fix argument to cargo-deny action (#4922) 2024-09-24 13:17:35 +02:00
Bogdan-Ștefan Neacşu 1bc26ed79f Expose error type (#4924) 2024-09-24 11:48:54 +02:00
mx 60fa5cfeb8 Max/rust sdk stream abstraction (#4743)
* add TcpProxyClient and TcpProxyServer abstractions to SDK 
* add single connection example
* add multi-connection example 
* add simple echo server to `tools/`: used for multi-connection example 
* update FFI toml files: switched to local imports 
* add proxy bindings to `ffi/shared`
* add proxy bindings and example to `ffi/go` 
* add note to `ffi/cpp` about lack of Proxy bindings for the moment
2024-09-24 09:29:46 +00:00
Jon Häggblad 3b7088aeea Fix nymvpn.com url in mainnet defaults (#4920) 2024-09-24 10:25:27 +02:00
Bogdan-Ștefan Neacşu 179d214e21 Check both version and type in message header (#4918)
* Move client type to the client code

* Check both version and type in header
2024-09-23 17:57:03 +02:00
Jon Häggblad 2a94ce6443 Bump http-api-client default timeout to 30 sec (#4917) 2024-09-23 15:45:47 +02:00
Bogdan-Ștefan Neacşu 95ec91daa1 Entry wireguard tickets (#4888)
* Create credential verifier in authenticator

* Add new version of peer storage with client id

* Fix v1 to what it was before

* Compact storage into ecash verifier

* Fix non-linux build

* Less overlapping conditions

* Remove moved code

* Use handler thread for each peer

* Re-spawn stored handles at startup

* Keep new function without async & Result

* Put query peer in function too

* Query bandwidth

* Fix clippy

* Replace tap with inspect_err

* Fix copyright year

* Handle version 2 on the reqeust deser

* Add protocol type in req/resp messages
2024-09-23 14:49:18 +02:00
benedettadavico 803850be74 bump versions & update changelog 2024-09-23 10:00:20 +02:00
Drazen Urch 2f267cf787 Update network monitor entrypoint (#4893)
* Update entrypoint

* Update CI action

* Rollback ci changes
2024-09-20 10:58:50 +02:00
Jędrzej Stuczyński 0d2418ef6a Merge pull request #4885 from nymtech/feature/updated-gateway-registration
Feature/updated gateway registration
2024-09-20 09:09:28 +01:00
Bogdan-Ștefan Neacşu 6f0c8dbe73 Fix missing duplication of modified tables (#4904) 2024-09-19 18:25:21 +02:00
mx 2198c1bd7b added new instructions for building locally (#4902) 2024-09-19 15:48:51 +00:00
Jędrzej Stuczyński be7f00fe52 replaced an assertion with an error return instead 2024-09-19 15:59:04 +01:00
Jon Häggblad 35c94f5c4b Update cargo deny (#4901)
* Regenerate deny.toml

* Backport old settings to deny.toml

* Explicitly allow GPL-3 only on our own specific crates

* Update deny.toml for latest changes

* Fix cargo-deny warnings for duplicate crates

* Update cargo-deny-action to v2
2024-09-19 12:53:27 +02:00
Jędrzej Stuczyński f5863b9668 fixed client key upgrade due to extra Arc 2024-09-19 11:06:50 +01:00
dependabot[bot] 963c54fea2 build(deps): bump semver from 0.11.0 to 1.0.23 (#4881)
* build(deps): bump semver from 0.11.0 to 1.0.23

Bumps [semver](https://github.com/dtolnay/semver) from 0.11.0 to 1.0.23.
- [Release notes](https://github.com/dtolnay/semver/releases)
- [Commits](https://github.com/dtolnay/semver/compare/0.11.0...1.0.23)

---
updated-dependencies:
- dependency-name: semver
  dependency-type: direct:production
  update-type: version-update:semver-major
...

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

* Update for 1.0

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jon Häggblad <jon.haggblad@gmail.com>
2024-09-19 11:55:10 +02:00
dependabot[bot] db55a96f91 build(deps): bump toml from 0.5.11 to 0.8.14 (#4805)
* build(deps): bump toml from 0.5.11 to 0.8.14

Bumps [toml](https://github.com/toml-rs/toml) from 0.5.11 to 0.8.14.
- [Commits](https://github.com/toml-rs/toml/compare/toml-v0.5.11...toml-v0.8.14)

---
updated-dependencies:
- dependency-name: toml
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

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

* Use workspace dependency

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jon Häggblad <jon.haggblad@gmail.com>
2024-09-19 11:10:34 +02:00
Jędrzej Stuczyński 7c0235ab26 fixed wasm build and trait impl 2024-09-19 10:06:59 +01:00
dependabot[bot] 92af6f7024 build(deps): bump hyper from 1.3.1 to 1.4.1 (#4879)
Bumps [hyper](https://github.com/hyperium/hyper) from 1.3.1 to 1.4.1.
- [Release notes](https://github.com/hyperium/hyper/releases)
- [Changelog](https://github.com/hyperium/hyper/blob/master/CHANGELOG.md)
- [Commits](https://github.com/hyperium/hyper/compare/v1.3.1...v1.4.1)

---
updated-dependencies:
- dependency-name: hyper
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-19 11:06:49 +02:00
Sachin Kamath 7146c4c012 docs: add hostname instructions for wss (#4900) 2024-09-19 08:55:28 +00:00
Jędrzej Stuczyński 3dc62a9a60 Merge pull request #4892 from nymtech/bugfix/ticketbook-false-double-spending
Bugfix/ticketbook false double spending
2024-09-19 09:44:43 +01:00
Jędrzej Stuczyński b3d7c26443 added key upgrade mechanism 2024-09-18 17:43:47 +01:00
Jędrzej Stuczyński 9efeef881a split types.rs + added additional helpers 2024-09-18 17:43:44 +01:00
Jędrzej Stuczyński 9d8369a5b2 generate pseudorandom salt for deriving aes256gcm-siv key 2024-09-18 17:43:43 +01:00
Jędrzej Stuczyński cc32eb3904 fixed wasm build 2024-09-18 17:43:43 +01:00
Jędrzej Stuczyński 8cf4977021 assert new gateway keys zeroize on drop 2024-09-18 17:43:43 +01:00
Jędrzej Stuczyński 2c2748832c cargo fmt 2024-09-18 17:43:42 +01:00
Jędrzej Stuczyński 114db3c1cf post-rebasing fixes 2024-09-18 17:43:42 +01:00
Jędrzej Stuczyński a65df5a0ab clippy 2024-09-18 17:43:42 +01:00
Jędrzej Stuczyński b6f07fbfce warning for unimplemented upgrade 2024-09-18 17:43:42 +01:00
Jędrzej Stuczyński c39d42b7dd fixed deserialisation of updated gateway shared materials 2024-09-18 17:43:41 +01:00
Jędrzej Stuczyński 21e9df488f compatibility with legacy clients 2024-09-18 17:43:40 +01:00
Jędrzej Stuczyński 94113206b2 completing handshake using legacy keys 2024-09-18 17:43:07 +01:00
Jędrzej Stuczyński 71532484a9 updated client handshake to allow derivation of different key types 2024-09-18 17:43:07 +01:00
Jędrzej Stuczyński 8756763875 added support for aead in nym-crypto 2024-09-18 17:43:06 +01:00
Jędrzej Stuczyński 5753b79997 slightly refactored bandwidth tracking 2024-09-18 11:27:35 +01:00
Jędrzej Stuczyński 2a6aa13ecd fixed client bandwidth being not correctly deducted 2024-09-18 11:12:24 +01:00
Jon Häggblad 9213e02b43 Remove clippy annotation (#4896) 2024-09-18 11:47:41 +02:00
Jon Häggblad ede4b23e8a Fix clippy::too-long-first-doc-paragraph (#4897) 2024-09-18 10:25:49 +02:00
Jon Häggblad 2e95ea16f9 Update nym-vpn metapackage and replace nymvpn-x with nym-vpn-app (#4889)
* Update nym-vpn metapackage to 0.2.0 and replace nymvpn-x with nym-vpn-app

* Fix compression

* Update description
2024-09-18 09:16:17 +01:00
benedetta davico d5c9e1d8cb Merge pull request #4899 from nymtech/jon/cherry-pick-4894-into-wedel
Backport #4894 to fix ci
2024-09-18 09:28:15 +02:00
dependabot[bot] 0c955817fd build(deps): bump the patch-updates group across 1 directory with 9 updates (#4898)
Bumps the patch-updates group with 9 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [anyhow](https://github.com/dtolnay/anyhow) | `1.0.87` | `1.0.89` |
| [clap_complete](https://github.com/clap-rs/clap) | `4.5.5` | `4.5.28` |
| [clap_complete_fig](https://github.com/clap-rs/clap) | `4.5.1` | `4.5.2` |
| [curve25519-dalek](https://github.com/dalek-cryptography/curve25519-dalek) | `4.1.2` | `4.1.3` |
| [getset](https://github.com/jbaublitz/getset) | `0.1.2` | `0.1.3` |
| [log](https://github.com/rust-lang/log) | `0.4.21` | `0.4.22` |
| [quote](https://github.com/dtolnay/quote) | `1.0.36` | `1.0.37` |
| [safer-ffi](https://github.com/getditto/safer_ffi) | `0.1.12` | `0.1.13` |
| [url](https://github.com/servo/rust-url) | `2.5.1` | `2.5.2` |



Updates `anyhow` from 1.0.87 to 1.0.89
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.87...1.0.89)

Updates `clap_complete` from 4.5.5 to 4.5.28
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.5...clap_complete-v4.5.28)

Updates `clap_complete_fig` from 4.5.1 to 4.5.2
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete_fig-v4.5.1...clap_complete_fig-v4.5.2)

Updates `curve25519-dalek` from 4.1.2 to 4.1.3
- [Release notes](https://github.com/dalek-cryptography/curve25519-dalek/releases)
- [Commits](https://github.com/dalek-cryptography/curve25519-dalek/compare/curve25519-4.1.2...curve25519-4.1.3)

Updates `getset` from 0.1.2 to 0.1.3
- [Release notes](https://github.com/jbaublitz/getset/releases)
- [Commits](https://github.com/jbaublitz/getset/compare/0.1.2...0.1.3)

Updates `log` from 0.4.21 to 0.4.22
- [Release notes](https://github.com/rust-lang/log/releases)
- [Changelog](https://github.com/rust-lang/log/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/log/compare/0.4.21...0.4.22)

Updates `quote` from 1.0.36 to 1.0.37
- [Release notes](https://github.com/dtolnay/quote/releases)
- [Commits](https://github.com/dtolnay/quote/compare/1.0.36...1.0.37)

Updates `safer-ffi` from 0.1.12 to 0.1.13
- [Release notes](https://github.com/getditto/safer_ffi/releases)
- [Commits](https://github.com/getditto/safer_ffi/commits)

Updates `url` from 2.5.1 to 2.5.2
- [Release notes](https://github.com/servo/rust-url/releases)
- [Commits](https://github.com/servo/rust-url/compare/v2.5.1...v2.5.2)

---
updated-dependencies:
- dependency-name: anyhow
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-updates
- dependency-name: clap_complete
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-updates
- dependency-name: clap_complete_fig
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-updates
- dependency-name: curve25519-dalek
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-updates
- dependency-name: getset
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-updates
- dependency-name: log
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-updates
- dependency-name: quote
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-updates
- dependency-name: safer-ffi
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-updates
- dependency-name: url
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-updates
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-18 09:15:11 +02:00
Jon Häggblad 87751894d9 Fix apt install in ci-build-upload-binaries.yml (#4894) 2024-09-18 09:07:45 +02:00
dependabot[bot] ec3c4fb1aa build(deps): bump sysinfo from 0.30.12 to 0.30.13 (#4880)
Bumps [sysinfo](https://github.com/GuillaumeGomez/sysinfo) from 0.30.12 to 0.30.13.
- [Changelog](https://github.com/GuillaumeGomez/sysinfo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/GuillaumeGomez/sysinfo/commits/v0.30.13)

---
updated-dependencies:
- dependency-name: sysinfo
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-18 00:33:44 +02:00
dependabot[bot] 789221f144 build(deps): bump comfy-table from 6.2.0 to 7.1.1 (#4882)
Bumps [comfy-table](https://github.com/nukesor/comfy-table) from 6.2.0 to 7.1.1.
- [Release notes](https://github.com/nukesor/comfy-table/releases)
- [Changelog](https://github.com/Nukesor/comfy-table/blob/main/CHANGELOG.md)
- [Commits](https://github.com/nukesor/comfy-table/compare/v6.2.0...v7.1.1)

---
updated-dependencies:
- dependency-name: comfy-table
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-18 00:30:46 +02:00
Jon Häggblad 5b925d8b68 Fix apt install in ci-build-upload-binaries.yml (#4894) 2024-09-17 23:29:36 +02:00
Jędrzej Stuczyński c8c3928575 put client bandwidth (gateway-side) behind shared pointer 2024-09-17 18:40:24 +01:00
Jędrzej Stuczyński 2fa8da8117 making sure there can be only a single client task claiming more bandwidth 2024-09-17 18:39:49 +01:00
Jędrzej Stuczyński 4548ef4d05 adding extra logs 2024-09-17 18:39:01 +01:00
Jędrzej Stuczyński 7f147ee2b0 Merge pull request #4891 from nymtech/bugfix/ticketbook-aux-imports
fix: allow updating globally stored signatures
2024-09-17 15:33:34 +01:00
Jędrzej Stuczyński 48bcd7e802 fix: allow updating globally stored signatures 2024-09-17 14:21:42 +01:00
Drazen Urch 6598d677da Build and Push CI (#4887) 2024-09-17 10:26:45 +02:00
import this e736a01ecc [DOCs/operators]: Document changelog for patch/2024.10-caramello (#4886)
* changelog for patched release

* fix typo
2024-09-17 08:26:22 +00:00
Jędrzej Stuczyński a708fa2d4a Merge pull request #4873 from nymtech/feature/stateless-gateway-requests
allow clients to send stateless gateway requests without prior registration
2024-09-16 17:00:15 +01:00
Drazen Urch a512217382 Few fixes (#4883) 2024-09-16 17:15:40 +02:00
Jon Häggblad 086611c7ac Use serde from workspace (#4833)
* cargo autoinherit for serde

* cargo autoinherit for bs58 and vergen in cosmwasm-smart-contracts
2024-09-16 11:16:21 +02:00
import this 05d6652177 [DOCs/operators]: Post release docs updates (#4874)
* update proxy setup syntax

* update known errors and bugs

* docs: simplify wss

---------

Co-authored-by: Sachin Kamath <github@skamath.me>
2024-09-13 13:16:34 +00:00
Bogdan-Ștefan Neacşu 9c514fe3b7 Fix snake case serde (#4875) 2024-09-13 11:53:39 +02:00
benedettadavico aad028be3f update qa env 2024-09-13 11:48:49 +02:00
Jędrzej Stuczyński 924160b3e7 removed unused import 2024-09-12 17:29:34 +01:00
Jędrzej Stuczyński 23d14b60de allow handling multiple of stateless requests on the same underlying connection 2024-09-12 16:54:56 +01:00
Jędrzej Stuczyński a4b47ef3a5 allow clients to send stateless gateway requests without prior registration 2024-09-12 15:57:38 +01:00
Bogdan-Ștefan Neacşu 6db3b34bcb Bump defguard to github latest version (#4872)
* Bump defguard to github latest version

* Fix comment location
2024-09-12 13:49:33 +02:00
Jędrzej Stuczyński f9383578da chore: remove queued migration for adding explicit admin 2024-09-12 11:03:51 +01:00
Bogdan-Ștefan Neacşu 47303bcf48 Gateway database modifications for different modes (#4868)
* Gateway db modifications for different modes

* Add exit mixnet and replace whitespaces
2024-09-12 11:58:20 +02:00
Jon Häggblad 60917ec9e7 Remove the push trigger for ci-nym-wallet-rust (#4869) 2024-09-12 10:23:00 +02:00
dependabot[bot] f616b3c15a build(deps): bump strum from 0.25.0 to 0.26.3 (#4848)
* build(deps): bump strum from 0.25.0 to 0.26.3

Bumps [strum](https://github.com/Peternator7/strum) from 0.25.0 to 0.26.3.
- [Release notes](https://github.com/Peternator7/strum/releases)
- [Changelog](https://github.com/Peternator7/strum/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Peternator7/strum/commits/v0.26.3)

---
updated-dependencies:
- dependency-name: strum
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

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

* Update to handle deprecation error

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jon Häggblad <jon.haggblad@gmail.com>
2024-09-11 23:18:11 +02:00
Jędrzej Stuczyński be2b44c46b Merge pull request #4867 from nymtech/feature/2024.10-caramello-merge
Feature/2024.10 caramello merge
2024-09-11 15:10:02 +01:00
dependabot[bot] f6db1a87c6 build(deps): bump gloo-timers from 0.2.6 to 0.3.0 (#4852)
Bumps [gloo-timers](https://github.com/rustwasm/gloo) from 0.2.6 to 0.3.0.
- [Release notes](https://github.com/rustwasm/gloo/releases)
- [Changelog](https://github.com/rustwasm/gloo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rustwasm/gloo/compare/gloo-timers-v0.2.6...0.3.0)

---
updated-dependencies:
- dependency-name: gloo-timers
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-11 15:23:15 +02:00
Jędrzej Stuczyński e8e86c273e Merge branch 'release/2024.10-caramello' into feature/2024.10-caramello-merge 2024-09-11 12:43:39 +01:00
Jon Häggblad b67ad8c23e Disable push trigger and add missing paths in ci-build (#4864)
* Disable push trigger and add missing paths

* Remove commented out
2024-09-11 13:39:07 +02:00
Jędrzej Stuczyński f655fe81d2 Merge pull request #4865 from nymtech/chore/remove-mixnet-migration
chore: removed completed queued mixnet migration
2024-09-11 11:57:42 +01:00
Jędrzej Stuczyński 86fa7024a2 removed mutability of deps 2024-09-11 11:28:00 +01:00
Jędrzej Stuczyński cbea1d554a chore: removed completed queued mixnet migration 2024-09-11 11:19:30 +01:00
dependabot[bot] c08e7d2b11 build(deps): bump the patch-updates group with 22 updates (#4846)
Bumps the patch-updates group with 22 updates:

| Package | From | To |
| --- | --- | --- |
| [anyhow](https://github.com/dtolnay/anyhow) | `1.0.83` | `1.0.87` |
| [async-trait](https://github.com/dtolnay/async-trait) | `0.1.81` | `0.1.82` |
| [clap](https://github.com/clap-rs/clap) | `4.5.16` | `4.5.17` |
| [clap_complete](https://github.com/clap-rs/clap) | `4.5.2` | `4.5.26` |
| [clap_complete_fig](https://github.com/clap-rs/clap) | `4.5.0` | `4.5.2` |
| [const_format](https://github.com/rodrimati1992/const_format_crates) | `0.2.32` | `0.2.33` |
| [curve25519-dalek](https://github.com/dalek-cryptography/curve25519-dalek) | `4.1.2` | `4.1.3` |
| [log](https://github.com/rust-lang/log) | `0.4.21` | `0.4.22` |
| [parking_lot](https://github.com/Amanieu/parking_lot) | `0.12.2` | `0.12.3` |
| [quote](https://github.com/dtolnay/quote) | `1.0.36` | `1.0.37` |
| [schemars](https://github.com/GREsau/schemars) | `0.8.19` | `0.8.21` |
| [serde](https://github.com/serde-rs/serde) | `1.0.209` | `1.0.210` |
| [serde_derive](https://github.com/serde-rs/serde) | `1.0.209` | `1.0.210` |
| [serde_json](https://github.com/serde-rs/json) | `1.0.127` | `1.0.128` |
| [tar](https://github.com/alexcrichton/tar-rs) | `0.4.40` | `0.4.41` |
| [tokio-stream](https://github.com/tokio-rs/tokio) | `0.1.15` | `0.1.16` |
| [tokio-util](https://github.com/tokio-rs/tokio) | `0.7.11` | `0.7.12` |
| [url](https://github.com/servo/rust-url) | `2.5.0` | `2.5.2` |
| [wasm-bindgen-test](https://github.com/rustwasm/wasm-bindgen) | `0.3.42` | `0.3.43` |
| [http-body-util](https://github.com/hyperium/http-body) | `0.1.1` | `0.1.2` |
| [hyper-util](https://github.com/hyperium/hyper-util) | `0.1.3` | `0.1.5` |
| [tokio-tun](https://github.com/yaa110/tokio-tun) | `0.11.4` | `0.11.5` |

Updates `anyhow` from 1.0.83 to 1.0.87
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.83...1.0.87)

Updates `async-trait` from 0.1.81 to 0.1.82
- [Release notes](https://github.com/dtolnay/async-trait/releases)
- [Commits](https://github.com/dtolnay/async-trait/compare/0.1.81...0.1.82)

Updates `clap` from 4.5.16 to 4.5.17
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.16...clap_complete-v4.5.17)

Updates `clap_complete` from 4.5.2 to 4.5.26
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.2...clap_complete-v4.5.26)

Updates `clap_complete_fig` from 4.5.0 to 4.5.2
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete_fig-v4.5.0...clap_complete_fig-v4.5.2)

Updates `const_format` from 0.2.32 to 0.2.33
- [Release notes](https://github.com/rodrimati1992/const_format_crates/releases)
- [Changelog](https://github.com/rodrimati1992/const_format_crates/blob/master/Changelog.md)
- [Commits](https://github.com/rodrimati1992/const_format_crates/commits)

Updates `curve25519-dalek` from 4.1.2 to 4.1.3
- [Release notes](https://github.com/dalek-cryptography/curve25519-dalek/releases)
- [Commits](https://github.com/dalek-cryptography/curve25519-dalek/compare/curve25519-4.1.2...curve25519-4.1.3)

Updates `log` from 0.4.21 to 0.4.22
- [Release notes](https://github.com/rust-lang/log/releases)
- [Changelog](https://github.com/rust-lang/log/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/log/compare/0.4.21...0.4.22)

Updates `parking_lot` from 0.12.2 to 0.12.3
- [Changelog](https://github.com/Amanieu/parking_lot/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Amanieu/parking_lot/compare/0.12.2...0.12.3)

Updates `quote` from 1.0.36 to 1.0.37
- [Release notes](https://github.com/dtolnay/quote/releases)
- [Commits](https://github.com/dtolnay/quote/compare/1.0.36...1.0.37)

Updates `schemars` from 0.8.19 to 0.8.21
- [Release notes](https://github.com/GREsau/schemars/releases)
- [Changelog](https://github.com/GREsau/schemars/blob/master/CHANGELOG.md)
- [Commits](https://github.com/GREsau/schemars/compare/v0.8.19...v0.8.21)

Updates `serde` from 1.0.209 to 1.0.210
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.209...v1.0.210)

Updates `serde_derive` from 1.0.209 to 1.0.210
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.209...v1.0.210)

Updates `serde_json` from 1.0.127 to 1.0.128
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/1.0.127...1.0.128)

Updates `tar` from 0.4.40 to 0.4.41
- [Commits](https://github.com/alexcrichton/tar-rs/compare/0.4.40...0.4.41)

Updates `tokio-stream` from 0.1.15 to 0.1.16
- [Release notes](https://github.com/tokio-rs/tokio/releases)
- [Commits](https://github.com/tokio-rs/tokio/compare/tokio-stream-0.1.15...tokio-stream-0.1.16)

Updates `tokio-util` from 0.7.11 to 0.7.12
- [Release notes](https://github.com/tokio-rs/tokio/releases)
- [Commits](https://github.com/tokio-rs/tokio/compare/tokio-util-0.7.11...tokio-util-0.7.12)

Updates `url` from 2.5.0 to 2.5.2
- [Release notes](https://github.com/servo/rust-url/releases)
- [Commits](https://github.com/servo/rust-url/compare/v2.5.0...v2.5.2)

Updates `wasm-bindgen-test` from 0.3.42 to 0.3.43
- [Release notes](https://github.com/rustwasm/wasm-bindgen/releases)
- [Changelog](https://github.com/rustwasm/wasm-bindgen/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rustwasm/wasm-bindgen/commits)

Updates `http-body-util` from 0.1.1 to 0.1.2
- [Release notes](https://github.com/hyperium/http-body/releases)
- [Commits](https://github.com/hyperium/http-body/compare/http-body-util-v0.1.1...http-body-util-v0.1.2)

Updates `hyper-util` from 0.1.3 to 0.1.5
- [Release notes](https://github.com/hyperium/hyper-util/releases)
- [Changelog](https://github.com/hyperium/hyper-util/blob/master/CHANGELOG.md)
- [Commits](https://github.com/hyperium/hyper-util/compare/v0.1.3...v0.1.5)

Updates `tokio-tun` from 0.11.4 to 0.11.5
- [Commits](https://github.com/yaa110/tokio-tun/compare/0.11.4...0.11.5)

---
updated-dependencies:
- dependency-name: anyhow
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-updates
- dependency-name: async-trait
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-updates
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-updates
- dependency-name: clap_complete
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-updates
- dependency-name: clap_complete_fig
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-updates
- dependency-name: const_format
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-updates
- dependency-name: curve25519-dalek
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-updates
- dependency-name: log
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-updates
- dependency-name: parking_lot
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-updates
- dependency-name: quote
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-updates
- dependency-name: schemars
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-updates
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-updates
- dependency-name: serde_derive
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-updates
- dependency-name: serde_json
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-updates
- dependency-name: tar
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-updates
- dependency-name: tokio-stream
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-updates
- dependency-name: tokio-util
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-updates
- dependency-name: url
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-updates
- dependency-name: wasm-bindgen-test
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-updates
- dependency-name: http-body-util
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-updates
- dependency-name: hyper-util
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-updates
- dependency-name: tokio-tun
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-updates
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-11 10:22:42 +02:00
Jon Häggblad c3eb433960 Remove golang workaround in ci-sdk-wasm (#4858) 2024-09-11 09:57:03 +02:00
Jon Häggblad 4c5147390a Fix linux conditional in ci-build.yml (#4863) 2024-09-11 09:39:08 +02:00
dependabot[bot] f70e8a3b9e build(deps): bump dirs from 4.0.0 to 5.0.1 (#4849)
Bumps [dirs](https://github.com/soc/dirs-rs) from 4.0.0 to 5.0.1.
- [Commits](https://github.com/soc/dirs-rs/commits)

---
updated-dependencies:
- dependency-name: dirs
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-11 08:16:35 +02:00
Tommy Verrall 5f7499604d Merge pull request #4861 from nymtech/update-windows-runner
Update windows runner details
2024-09-10 18:22:45 +02:00
Tommy Verrall 8c021e9537 install yarn 2024-09-10 18:02:55 +02:00
Tommy Verrall f2d56882fe adding correct runner name 2024-09-10 17:44:23 +02:00
Tommy Verrall 891fdeb4b5 Update and rename publish-nym-wallet-win10.yml to publish-nym-wallet-win11.yml 2024-09-10 17:22:34 +02:00
import this 12e8d3468b [DOCs/operators]: Release notes v2024.10-caramello, nym-node simplier setup & wg troubleshooting configuration (#4860)
* add icmp and dns configuration command

* add parameters and vars table and make explicit steps

* add features to changelog

* add operators updates & tasks to changelog

* syntax edits
2024-09-10 13:05:19 +00:00
benedettadavico 01221a8e8c bump wallet version 2024-09-10 13:51:11 +02:00
Jon Häggblad 58c74199d1 Fix upload-artifacts 2024-09-10 10:59:21 +02:00
Jon Häggblad eb98c1bf33 Fix incorrect sed search-replace 2024-09-10 10:39:30 +02:00
Jon Häggblad d6393c1496 Update download-artifact action to v4 2024-09-10 10:23:40 +02:00
Jon Häggblad 48dfc24c33 Update upload-artifact action to v4 2024-09-10 10:23:02 +02:00
benedettadavico 61eaffe91b update changelog 2024-09-10 09:49:52 +02:00
Bogdan-Ștefan Neacşu 63d0ab49e1 Move credential verification into common crate (#4853)
* Move cred verification to common crate

* Put crate in toml file manually
2024-09-09 19:13:10 +02:00
Jędrzej Stuczyński 0f59fd4eee Merge pull request #4856 from nymtech/bugfix/client-registration-vol2
Bugfix/client registration vol2
2024-09-09 16:49:31 +01:00
Jon Häggblad 55694f0341 Revert runner for ci-docs (#4855)
* Use arc-ubuntu-20.04-dind for ci-docs

* Revert back to ubuntu 20.04-16-core for now
2024-09-09 16:57:48 +02:00
Jędrzej Stuczyński c3aec2b01f update wireguard peers without replacing rows 2024-09-09 15:22:20 +01:00
Jędrzej Stuczyński c023c8fb9f updating shared keys without deleting the row 2024-09-09 15:22:20 +01:00
Jędrzej Stuczyński 1162de3673 additional logs 2024-09-09 15:22:20 +01:00
Jędrzej Stuczyński 74252269bc utility to convert private keys into keypairs 2024-09-09 15:22:19 +01:00
Jędrzej Stuczyński fe88321a50 Merge pull request #4857 from nymtech/jon/backport-fixes
Backport 4844 and 4845
2024-09-09 15:21:51 +01:00
Jon Häggblad babc84779c Backport 4844 and 4845 2024-09-09 16:19:04 +02:00
Jon Häggblad 7e40207d46 Fix test failure in ipr request size (#4844)
* Fix test failure in ipr request size that suddenly appeared

* Use fixed date in unit test
2024-09-09 12:01:24 +02:00
Jon Häggblad 85758be9ca Start switching over jobs to arc-ubuntu-20.04 (#4843)
* Switch ci-build to arc-ubuntu-20.04

* Trigger on wf file chanes

* Add IPR and authenticator to default workspace

* Move over a few more

* and more

* Revert two builds that require docker

* typo in label

* Revert two more

* Fix go

* update

* update
2024-09-09 12:00:28 +02:00
Jon Häggblad dd70192508 Create nym-repo-setup debian package and nym-vpn meta package (#4837)
* Create nym-repo-setup

* update

* Add postrm

* Add README

* Add Makefile

* Move to subdir

* Bundle the list file instead

* Create nym-vpn metapackage

* Rename top-level dir

* Set version of meta package to 0.1

* Add dpkg-name

* Create workflow for creating the debs

* Restrict to amd64 only

* Rename to build-deb-meta.yml

* name to upload-artifact

* Set names

* typo

* Extend version and set amd64 only

* Bump to 1.0.1
2024-09-09 11:21:57 +02:00
Jędrzej Stuczyński f884331284 Merge pull request #4827 from nymtech/feature/ticketbook-utils
revamped ticketbook serialisation and exposed additional cli methods
2024-09-09 09:54:54 +01:00
Jędrzej Stuczyński 45e6011961 vol3 2024-09-09 09:10:19 +01:00
Jędrzej Stuczyński 04a2f59034 fixed clap group names 2024-09-09 09:10:19 +01:00
Jędrzej Stuczyński 43b0b3eb37 i hate the ipr build process vol2 2024-09-09 09:10:19 +01:00
Jędrzej Stuczyński 27afe645c6 i hate the ipr build process 2024-09-09 09:10:18 +01:00
Jędrzej Stuczyński 052dbeaef8 adjusting the API and fixing CI 2024-09-09 09:10:18 +01:00
Jędrzej Stuczyński fb0b9da14f revamped ticketbook serialisation and exposed additional cli methods 2024-09-09 09:10:17 +01:00
Jon Häggblad 230e4393c5 Fix clippy for nym-wallet and latest rustc (#4845) 2024-09-08 19:14:05 +02:00
Jędrzej Stuczyński f3fcef60c3 Merge pull request #4822 from nymtech/feature/mixnet-contract-update-admin
added explicit updateable admin to the mixnet contract
2024-09-04 10:22:39 +01:00
Jędrzej Stuczyński ed7a84a1ce made 'owner' field optional to prepare for its future removal 2024-09-04 09:55:23 +01:00
Jędrzej Stuczyński 48e18684a2 Merge pull request #4821 from nymtech/bugfix/bonding-signature
using legacy signing payload in CLI and verifying both variants in contract
2024-09-03 16:36:09 +01:00
Jędrzej Stuczyński e76c8e06be updated contract schema 2024-09-03 16:35:42 +01:00
Jędrzej Stuczyński 858b6c6094 restored (and deprecated) 'owner' field in ContractState 2024-09-03 14:31:11 +01:00
Bogdan-Ștefan Neacşu 7b4dc78f41 Remove wireguard feature flag and pass runtime enabled flag (#4839)
* Remove wireguard feature flag

* Use wg enabled runtime flag

* Fix unintended flag removal
2024-09-03 15:25:05 +02:00
Bogdan-Ștefan Neacşu bb7a8e84e4 Eliminate cancel unsafe sig awaiting (#4834)
* Eliminate cancel unsafe sig awaiting

* Fix wasm build

* Simplify spawn call

* Fix wasm lint
2024-09-03 15:24:49 +02:00
benedetta davico a3183ab313 Merge pull request #4819 from nymtech/add-ecash-contract
adding ecash contract address
2024-08-29 07:26:46 +02:00
benedettadavico 74cd73a58f fmt 2024-08-28 17:35:37 +02:00
benedetta davico dd89026065 Merge pull request #4802 from nymtech/fix/nym-cli-params
Check profit margin of node before defaulting to hardcoded value
2024-08-28 17:34:56 +02:00
Jędrzej Stuczyński 07c80e5150 naming consistency 2024-08-28 16:32:02 +01:00
Jędrzej Stuczyński c17f0ac3f8 added explicit updateable admin to the mixnet contract 2024-08-28 16:30:30 +01:00
Jędrzej Stuczyński 7ae56b08b3 using legacy signing payload in CLI and verifying both variants in contract 2024-08-28 15:19:55 +01:00
benedettadavico 73fc2d6bb2 remove unused import 2024-08-28 11:43:16 +02:00
benedettadavico a5289cd431 update test env with ecash too 2024-08-28 11:39:49 +02:00
benedettadavico ec0e1b67a0 adding ecash contract address 2024-08-28 11:28:17 +02:00
benedettadavico eafbed6c9f wording 2024-08-28 11:21:11 +02:00
benedettadavico 4635db73f1 update code and fmt again 2024-08-27 16:20:44 +02:00
benedettadavico 9a43d1079a cargo fmt 2024-08-27 15:15:45 +02:00
benedettadavico 3238722ade WIP 2024-08-27 14:55:47 +02:00
Bogdan-Ștefan Neacşu a6ad6c7d49 Sync last_seen_bandwidth immediately (#4774) 2024-08-21 14:17:01 +02:00
Jędrzej Stuczyński cbc977c491 Merge pull request #4773 from nymtech/feature/additional-ecash-nym-cli-utils
Feature/additional ecash nym cli utils
2024-08-21 09:36:47 +01:00
Jędrzej Stuczyński f40c05a34c fixed incorrect propagation of client_id in the sdk 2024-08-20 17:01:43 +01:00
Jędrzej Stuczyński 776443131e fixed full display being always printed 2024-08-20 16:57:18 +01:00
Bogdan-Ștefan Neacşu b5eab7f07f Better storage error logging (#4772)
* Better storage error logging

* Print without including error returned to clients
2024-08-20 17:49:27 +02:00
Jędrzej Stuczyński eeeb4b3246 fixed incorrect assertion when validating maximum time between redemption 2024-08-20 16:43:36 +01:00
Jędrzej Stuczyński e3e4dc6db9 added an utility nym-cli command to output binary representation of ecash tickets 2024-08-20 16:31:00 +01:00
Jędrzej Stuczyński 461b7bcfb7 updated sandbox.env 2024-08-20 15:25:40 +01:00
Jędrzej Stuczyński bbf0d06583 updated constants depending on all 30 days expiration 2024-08-20 12:54:42 +01:00
Jędrzej Stuczyński 6393d6093f changed parsing of 'credential_data' when importing ticketbooks 2024-08-20 11:31:07 +01:00
benedettadavico c0ea599913 update changelog and version bump 2024-08-19 10:32:21 +02:00
benedetta davico 16d09a35ba Merge pull request #4764 from nymtech/bugfix/post-050-dkg
bugfix: make sure DKG parses data out of events if logs are empty
2024-08-16 13:46:15 +02:00
Jędrzej Stuczyński e6c5eddbe5 bugfix: make sure DKG parse data out of events if logs are empty
this will be the case on post 0.50 chains
2024-08-16 11:56:48 +01:00
429 changed files with 15376 additions and 4913 deletions
+31 -1
View File
@@ -5,5 +5,35 @@ on:
jobs:
build:
runs-on: arc-ubuntu-22.04
steps:
run: "echo hello"
- name: Checkout Repository
uses: actions/checkout@v4
- name: Set up Build Environment
run: sudo apt-get update && sudo apt-get install -y make dpkg-dev
- name: Build Debian Packages
working-directory: ppa/packages
run: make
- name: Find .deb files
working-directory: ppa/packages
run: |
echo "file1=$(ls nym-repo-setup*.deb)" >> $GITHUB_ENV
echo "file2=$(ls nym-vpn*.deb)" >> $GITHUB_ENV
- name: Upload nym-repo-setup
uses: actions/upload-artifact@v4
with:
name: ${{ env.file1 }}
path: ppa/packages/nym-repo-setup*.deb
retention-days: 10
- name: Upload nym-vpn
uses: actions/upload-artifact@v4
with:
name: ${{ env.file2 }}
path: ppa/packages/nym-vpn*.deb
retention-days: 10
@@ -13,6 +13,7 @@ on:
- 'mixnode/**'
- 'sdk/rust/nym-sdk/**'
- 'service-providers/**'
- '.github/workflows/ci-binary-config-checker.yml'
pull_request:
paths:
- 'clients/**'
@@ -22,6 +23,7 @@ on:
- 'mixnode/**'
- 'sdk/rust/nym-sdk/**'
- 'service-providers/**'
- '.github/workflows/ci-binary-config-checker.yml'
env:
NETWORK: mainnet
@@ -31,7 +33,7 @@ jobs:
strategy:
fail-fast: false
matrix:
platform: [custom-linux]
platform: [arc-ubuntu-20.04]
runs-on: ${{ matrix.platform }}
steps:
+1
View File
@@ -5,6 +5,7 @@ on:
paths:
- "ts-packages/**"
- "sdk/typescript/**"
- ".github/workflows/ci-build-ts.yml"
jobs:
build:
@@ -26,18 +26,20 @@ on:
- "nym-api/**"
- "nym-node/**"
- "nym-outfox/**"
- 'nym-data-observatory/**'
- "nym-validator-rewarder/**"
- "sdk/rust/nym-sdk/**"
- "service-providers/**"
- "tools/**"
- "nymvisor/**"
- ".github/workflows/ci-build-upload-binaries.yml"
jobs:
publish-nym:
strategy:
fail-fast: false
matrix:
platform: [ ubuntu-20.04 ]
platform: [ arc-ubuntu-20.04 ]
runs-on: ${{ matrix.platform }}
env:
@@ -55,17 +57,13 @@ jobs:
echo $OUTPUT_DIR
- name: Install Dependencies (Linux)
run: sudo apt update && sudo apt install libudev-dev
run: sudo apt-get update && sudo apt-get -y install libudev-dev
- name: Sets env vars for tokio if set in manual dispatch inputs
run: |
echo 'RUSTFLAGS="--cfg tokio_unstable"' >> $GITHUB_ENV
if: github.event_name == 'workflow_dispatch' && inputs.add_tokio_unstable == true
- name: Set CARGO_FEATURES
run: |
echo 'CARGO_FEATURES=--features wireguard' >> $GITHUB_ENV
- name: Install Rust stable
uses: actions-rs/toolchain@v1
with:
@@ -99,6 +97,7 @@ jobs:
target/release/nym-socks5-client
target/release/nym-api
target/release/nym-network-requester
target/release/nym-data-observatory
target/release/nym-cli
target/release/nymvisor
target/release/nym-node
@@ -116,6 +115,7 @@ jobs:
cp target/release/nym-socks5-client $OUTPUT_DIR
cp target/release/nym-api $OUTPUT_DIR
cp target/release/nym-network-requester $OUTPUT_DIR
cp target/release/nym-data-observatory $OUTPUT_DIR
cp target/release/nymvisor $OUTPUT_DIR
cp target/release/nym-node $OUTPUT_DIR
cp target/release/nym-cli $OUTPUT_DIR
+19 -41
View File
@@ -1,23 +1,6 @@
name: ci-build
on:
push:
paths:
- 'clients/**'
- 'common/**'
- 'explorer-api/**'
- 'gateway/**'
- 'integrations/**'
- 'mixnode/**'
- 'sdk/lib/socks5-listener/**'
- 'sdk/rust/nym-sdk/**'
- 'service-providers/**'
- 'nym-api/**'
- 'nym-outfox/**'
- 'tools/nym-cli/**'
- 'tools/nym-nr-query/**'
- 'tools/ts-rs-cli/**'
- 'Cargo.toml'
pull_request:
paths:
- 'clients/**'
@@ -26,15 +9,20 @@ on:
- 'gateway/**'
- 'integrations/**'
- 'mixnode/**'
- 'sdk/lib/socks5-listener/**'
- 'sdk/rust/nym-sdk/**'
- 'sdk/rust/**'
- 'sdk/lib/**'
- 'service-providers/**'
- 'nym-network-monitor/**'
- 'nym-api/**'
- 'nym-node/**'
- 'nym-outfox/**'
- 'tools/nym-cli/**'
- 'tools/nym-nr-query/**'
- 'tools/ts-rs-cli/**'
- 'nym-data-observatory/**'
- 'nym-validator-rewarder/**'
- 'tools/**'
- 'wasm/**'
- 'Cargo.toml'
- 'Cargo.lock'
- '.github/workflows/ci-build.yml'
workflow_dispatch:
jobs:
@@ -42,7 +30,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [custom-linux, custom-runner-mac-m1]
os: [arc-ubuntu-20.04, custom-runner-mac-m1]
runs-on: ${{ matrix.os }}
env:
CARGO_TERM_COLOR: always
@@ -50,7 +38,7 @@ jobs:
- name: Install Dependencies (Linux)
run: sudo apt-get update && sudo apt-get -y install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools protobuf-compiler
continue-on-error: true
if: matrix.os == 'custom-linux'
if: contains(matrix.os, 'ubuntu')
- name: Check out repository code
uses: actions/checkout@v4
@@ -73,8 +61,6 @@ jobs:
uses: actions-rs/cargo@v1
with:
command: build
# Enable wireguard by default on linux only
args: --workspace --features wireguard
# while disabled by default, this build ensures nothing is broken within
# `axum` feature
@@ -85,36 +71,28 @@ jobs:
args: --features axum
- name: Build all examples
if: matrix.os == 'custom-linux'
if: contains(matrix.os, 'ubuntu')
uses: actions-rs/cargo@v1
with:
command: build
args: --workspace --examples --features wireguard
args: --workspace --examples
- name: Run all tests
if: matrix.os == 'custom-linux'
if: contains(matrix.os, 'ubuntu')
uses: actions-rs/cargo@v1
with:
command: test
args: --workspace --features wireguard
args: --workspace
- name: Run expensive tests
if: (github.ref == 'refs/heads/develop' || github.event.pull_request.base.ref == 'develop' || github.event.pull_request.base.ref == 'master') && matrix.os == 'custom-linux'
if: (github.ref == 'refs/heads/develop' || github.event.pull_request.base.ref == 'develop' || github.event.pull_request.base.ref == 'master') && contains(matrix.os, 'ubuntu')
uses: actions-rs/cargo@v1
with:
command: test
args: --workspace --features wireguard -- --ignored
- name: Annotate with clippy checks
if: matrix.os == 'custom-linux'
uses: actions-rs/clippy-check@v1
continue-on-error: true
with:
token: ${{ secrets.GITHUB_TOKEN }}
args: --workspace --features wireguard
args: --workspace -- --ignored
- name: Clippy
uses: actions-rs/cargo@v1
with:
command: clippy
args: --workspace --all-targets --features wireguard,axum -- -D warnings
args: --workspace --all-targets --features axum -- -D warnings
+7 -3
View File
@@ -2,10 +2,14 @@ name: ci-cargo-deny
on:
workflow_dispatch:
pull_request:
paths:
- 'Cargo.toml'
- 'Cargo.lock'
- '.github/workflows/ci-cargo-deny.yml'
jobs:
cargo-deny:
runs-on: ubuntu-22.04
runs-on: arc-ubuntu-22.04-dind
strategy:
matrix:
checks:
@@ -14,8 +18,8 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: EmbarkStudios/cargo-deny-action@v1
- uses: EmbarkStudios/cargo-deny-action@v2
with:
log-level: warn
command: check ${{ matrix.checks }}
argument: --all-features
arguments: --all-features
+2 -1
View File
@@ -6,11 +6,12 @@ on:
paths:
- 'contracts/**'
- 'common/**'
- '.github/workflows/ci-contracts-schema.yml'
jobs:
check-schema:
name: Generate and check schema
runs-on: custom-linux
runs-on: arc-ubuntu-20.04
env:
CARGO_TERM_COLOR: always
steps:
@@ -6,6 +6,7 @@ on:
paths:
- 'common/**'
- 'contracts/**'
- '.github/workflows/ci-contracts-upload-binaries.yml'
env:
NETWORK: mainnet
@@ -15,7 +16,7 @@ jobs:
strategy:
fail-fast: false
matrix:
platform: [ ubuntu-20.04 ]
platform: arc-ubuntu-20.04
runs-on: ${{ matrix.platform }}
env:
+2 -1
View File
@@ -9,10 +9,11 @@ on:
paths:
- 'contracts/**'
- 'common/**'
- '.github/workflows/ci-contracts.yml'
jobs:
matrix_prep:
runs-on: ubuntu-20.04
runs-on: arc-ubuntu-20.04
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
+1
View File
@@ -6,6 +6,7 @@ on:
branches-ignore: master
paths:
- 'documentation/docs/**'
- '.github/workflows/ci-docs.yml'
jobs:
build:
+1
View File
@@ -10,6 +10,7 @@ on:
- "nym-wallet/src/**"
- "nym-wallet/package.json"
- "explorer/**"
- ".github/workflows/ci-lint-typescript.yml"
jobs:
build:
@@ -5,6 +5,7 @@ on:
push:
paths:
- 'explorer/**'
- '.github/workflows/ci-nym-network-explorer.yml'
defaults:
run:
+2 -7
View File
@@ -1,22 +1,17 @@
name: ci-nym-wallet-rust
on:
push:
paths:
- 'nym-wallet/**'
- 'common/**'
- 'contracts/vesting/**'
- 'nym-api/nym-api-requests/**'
pull_request:
paths:
- 'nym-wallet/**'
- 'common/**'
- 'contracts/vesting/**'
- 'nym-api/nym-api-requests/**'
- '.github/workflows/ci-nym-wallet-rust.yml'
jobs:
build:
runs-on: [ self-hosted, custom-linux ]
runs-on: arc-ubuntu-20.04
env:
CARGO_TERM_COLOR: always
steps:
@@ -4,6 +4,7 @@ on:
pull_request:
paths:
- 'nym-wallet/**'
- '.github/workflows/ci-nym-wallet-storybook.yml'
jobs:
build:
@@ -5,6 +5,7 @@ on:
paths:
- "sdk/typescript/**"
- "wasm/**"
- '.github/workflows/ci-sdk-docs-typescript.yml'
jobs:
build:
+2 -1
View File
@@ -6,10 +6,11 @@ on:
- 'wasm/**'
- 'clients/client-core/**'
- 'common/**'
- '.github/workflows/ci-sdk-wasm.yml'
jobs:
wasm:
runs-on: [custom-linux]
runs-on: arc-ubuntu-20.04
env:
CARGO_TERM_COLOR: always
steps:
@@ -51,10 +51,6 @@ jobs:
echo 'RUSTFLAGS="--cfg tokio_unstable"' >> $GITHUB_ENV
if: github.event_name == 'workflow_dispatch' && inputs.add_tokio_unstable == true
- name: Set CARGO_FEATURES
run: |
echo 'CARGO_FEATURES=--features wireguard' >> $GITHUB_ENV
- name: Install Rust stable
uses: actions-rs/toolchain@v1
with:
@@ -1,4 +1,4 @@
name: publish-nym-wallet-win10
name: publish-nym-wallet-win11
on:
workflow_dispatch:
release:
@@ -14,7 +14,7 @@ jobs:
strategy:
fail-fast: false
matrix:
platform: [windows10]
platform: [custom-windows-11]
runs-on: ${{ matrix.platform }}
outputs:
@@ -62,6 +62,9 @@ jobs:
fileName: '.env'
encodedString: ${{ secrets.WALLET_ADMIN_ADDRESS }}
- name: Install Yarn
run: npm install -g yarn
- name: Install project dependencies
shell: bash
run: cd .. && yarn --network-timeout 100000
@@ -0,0 +1,55 @@
name: Build and upload Network monitor container to harbor.nymte.ch
on:
workflow_dispatch:
env:
WORKING_DIRECTORY: "."
CONTAINER_NAME: "network-monitor"
jobs:
build-container:
runs-on: arc-ubuntu-22.04-dind
steps:
- name: Login to Harbor
uses: docker/login-action@v3
with:
registry: harbor.nymte.ch
username: ${{ secrets.HARBOR_ROBOT_USERNAME }}
password: ${{ secrets.HARBOR_ROBOT_SECRET }}
- name: Checkout repo
uses: actions/checkout@v4
- name: Configure git identity
run: |
git config --global user.email "lawrence@nymtech.net"
git config --global user.name "Lawrence Stalder"
- name: Get version from package.json
uses: sergeysova/jq-action@v2
id: get_version
with:
cmd: jq -r '.version' ${{ env.WORKING_DIRECTORY }}/package.json
- name: Check if tag exists
run: |
if git rev-parse ${{ steps.get_version.outputs.value }} >/dev/null 2>&1; then
echo "Tag ${{ steps.get_version.outputs.value }} already exists"
fi
- name: Remove existing tag if exists
run: |
if git rev-parse ${{ steps.get_version.outputs.value }} >/dev/null 2>&1; then
git push --delete origin ${{ steps.get_version.outputs.value }}
git tag -d ${{ steps.get_version.outputs.value }}
fi
- name: Create tag
run: |
git tag -a ${{ steps.get_version.outputs.value }} -m "Version ${{ steps.get_version.outputs.value }}"
git push origin ${{ steps.get_version.outputs.value }}
- name: BuildAndPushImageOnHarbor
run: |
docker build -f nym-network-monitor.dockerfile ${{ env.WORKING_DIRECTORY }} -t harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }}:${{ steps.get_version.outputs.value }} -t harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }}:latest
docker push harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }} --all-tags
+130
View File
@@ -4,6 +4,136 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
## [Unreleased]
## [2024.11-wedel] (2024-09-23)
- Backport #4894 to fix ci ([#4899])
- Bugfix/ticketbook false double spending ([#4892])
- fix: allow updating globally stored signatures ([#4891])
- [DOCs/operators]: Document changelog for patch/2024.10-caramello ([#4886])
- [DOCs/operators]: Post release docs updates ([#4874])
- Bump defguard to github latest version ([#4872])
- chore: removed completed queued mixnet migration ([#4865])
- Disable push trigger and add missing paths in ci-build ([#4864])
- Fix linux conditional in ci-build.yml ([#4863])
- Remove golang workaround in ci-sdk-wasm ([#4858])
- Revert runner for ci-docs ([#4855])
- Move credential verification into common crate ([#4853])
- Fix test failure in ipr request size ([#4844])
- Start switching over jobs to arc-ubuntu-20.04 ([#4843])
- Use ecash credential type for bandwidth value ([#4840])
- Create nym-repo-setup debian package and nym-vpn meta package ([#4837])
- Remove serde_crate named import ([#4832])
- Run cargo autoinherit following last weeks dependabot updates ([#4831])
- revamped ticketbook serialisation and exposed additional cli methods ([#4827])
- Expose wireguard details on self described endpoint ([#4825])
- Remove unused wireguard flag from SDK ([#4823])
- Add `axum` server to `nym-api` ([#4803])
- Run cargo-autoinherit for a few new crates ([#4801])
- Update dependabot ([#4796])
- Fix clippy for unwrap_or_default ([#4783])
- Enable dependabot version upgrades for root rust workspace ([#4778])
- Persist used wireguard private IPs ([#4771])
- Avoid race on ip and registration structures ([#4766])
- docs/hotfix ([#4765])
- chore: remove repetitive words ([#4763])
- Make gateway latency check generic ([#4759])
- Remove duplicate stat count for retransmissions ([#4756])
- Update peer refresh value ([#4754])
- Remove deprecated mark_as_success and use new disarm ([#4751])
- Add get_mixnodes_described to validator_client ([#4725])
- New Network Monitor ([#4610])
[#4899]: https://github.com/nymtech/nym/pull/4899
[#4892]: https://github.com/nymtech/nym/pull/4892
[#4891]: https://github.com/nymtech/nym/pull/4891
[#4886]: https://github.com/nymtech/nym/pull/4886
[#4874]: https://github.com/nymtech/nym/pull/4874
[#4872]: https://github.com/nymtech/nym/pull/4872
[#4865]: https://github.com/nymtech/nym/pull/4865
[#4864]: https://github.com/nymtech/nym/pull/4864
[#4863]: https://github.com/nymtech/nym/pull/4863
[#4858]: https://github.com/nymtech/nym/pull/4858
[#4855]: https://github.com/nymtech/nym/pull/4855
[#4853]: https://github.com/nymtech/nym/pull/4853
[#4844]: https://github.com/nymtech/nym/pull/4844
[#4843]: https://github.com/nymtech/nym/pull/4843
[#4840]: https://github.com/nymtech/nym/pull/4840
[#4837]: https://github.com/nymtech/nym/pull/4837
[#4832]: https://github.com/nymtech/nym/pull/4832
[#4831]: https://github.com/nymtech/nym/pull/4831
[#4827]: https://github.com/nymtech/nym/pull/4827
[#4825]: https://github.com/nymtech/nym/pull/4825
[#4823]: https://github.com/nymtech/nym/pull/4823
[#4803]: https://github.com/nymtech/nym/pull/4803
[#4801]: https://github.com/nymtech/nym/pull/4801
[#4796]: https://github.com/nymtech/nym/pull/4796
[#4783]: https://github.com/nymtech/nym/pull/4783
[#4778]: https://github.com/nymtech/nym/pull/4778
[#4771]: https://github.com/nymtech/nym/pull/4771
[#4766]: https://github.com/nymtech/nym/pull/4766
[#4765]: https://github.com/nymtech/nym/pull/4765
[#4763]: https://github.com/nymtech/nym/pull/4763
[#4759]: https://github.com/nymtech/nym/pull/4759
[#4756]: https://github.com/nymtech/nym/pull/4756
[#4754]: https://github.com/nymtech/nym/pull/4754
[#4751]: https://github.com/nymtech/nym/pull/4751
[#4725]: https://github.com/nymtech/nym/pull/4725
[#4610]: https://github.com/nymtech/nym/pull/4610
## [2024.10-caramello] (2024-09-10)
- Backport 4844 and 4845 ([#4857])
- Bugfix/client registration vol2 ([#4856])
- Remove wireguard feature flag and pass runtime enabled flag ([#4839])
- Eliminate cancel unsafe sig awaiting ([#4834])
- added explicit updateable admin to the mixnet contract ([#4822])
- using legacy signing payload in CLI and verifying both variants in contract ([#4821])
- adding ecash contract address ([#4819])
- Check profit margin of node before defaulting to hardcoded value ([#4802])
- Sync last_seen_bandwidth immediately ([#4774])
- Feature/additional ecash nym cli utils ([#4773])
- Better storage error logging ([#4772])
- bugfix: make sure DKG parses data out of events if logs are empty ([#4764])
- Fix clippy on rustc beta toolchain ([#4746])
- Fix clippy for beta toolchain ([#4742])
- Disable testnet-manager on non-unix ([#4741])
- Don't set NYM_VPN_API to default ([#4740])
- Update publish-nym-binaries.yml ([#4739])
- Update ci-build-upload-binaries.yml ([#4738])
- Add NYM_VPN_API to network config ([#4736])
- Re-export RecipientFormattingError in nym sdk ([#4735])
- Persist wireguard peers ([#4732])
- Fix tokio error in 1.39 ([#4730])
- Feature/vesting purge plus ranged cost params ([#4716])
- Fix (some) feature unification build failures ([#4681])
- Feature Compact Ecash : The One PR ([#4623])
[#4857]: https://github.com/nymtech/nym/pull/4857
[#4856]: https://github.com/nymtech/nym/pull/4856
[#4839]: https://github.com/nymtech/nym/pull/4839
[#4834]: https://github.com/nymtech/nym/pull/4834
[#4822]: https://github.com/nymtech/nym/pull/4822
[#4821]: https://github.com/nymtech/nym/pull/4821
[#4819]: https://github.com/nymtech/nym/pull/4819
[#4802]: https://github.com/nymtech/nym/pull/4802
[#4774]: https://github.com/nymtech/nym/pull/4774
[#4773]: https://github.com/nymtech/nym/pull/4773
[#4772]: https://github.com/nymtech/nym/pull/4772
[#4764]: https://github.com/nymtech/nym/pull/4764
[#4746]: https://github.com/nymtech/nym/pull/4746
[#4742]: https://github.com/nymtech/nym/pull/4742
[#4741]: https://github.com/nymtech/nym/pull/4741
[#4740]: https://github.com/nymtech/nym/pull/4740
[#4739]: https://github.com/nymtech/nym/pull/4739
[#4738]: https://github.com/nymtech/nym/pull/4738
[#4736]: https://github.com/nymtech/nym/pull/4736
[#4735]: https://github.com/nymtech/nym/pull/4735
[#4732]: https://github.com/nymtech/nym/pull/4732
[#4730]: https://github.com/nymtech/nym/pull/4730
[#4716]: https://github.com/nymtech/nym/pull/4716
[#4681]: https://github.com/nymtech/nym/pull/4681
[#4623]: https://github.com/nymtech/nym/pull/4623
## [2024.9-topdeck] (2024-07-26)
- chore: fix 1.80 lint issues ([#4731])
Generated
+922 -503
View File
File diff suppressed because it is too large Load Diff
+47 -36
View File
@@ -45,6 +45,7 @@ members = [
"common/credentials",
"common/credential-utils",
"common/credentials-interface",
"common/credential-verification",
"common/crypto",
"common/dkg",
"common/ecash-double-spending",
@@ -80,6 +81,7 @@ members = [
"common/nyxd-scraper",
"common/pemstore",
"common/serde-helpers",
"common/service-provider-requests-common",
"common/socks5-client-core",
"common/socks5/proxy-helpers",
"common/socks5/requests",
@@ -101,6 +103,9 @@ members = [
"mixnode",
"sdk/lib/socks5-listener",
"sdk/rust/nym-sdk",
"sdk/ffi/shared",
"sdk/ffi/go",
"sdk/ffi/cpp",
"service-providers/authenticator",
"service-providers/common",
"service-providers/ip-packet-router",
@@ -109,13 +114,17 @@ members = [
"nym-api",
"nym-browser-extension/storage",
"nym-api/nym-api-requests",
"nym-data-observatory",
"nym-node",
"nym-node/nym-node-http-api",
"nym-node/nym-node-requests",
"nym-outfox",
"nym-validator-rewarder",
"tools/echo-server",
"tools/internal/ssl-inject",
# "tools/internal/sdk-version-bump",
"tools/internal/testnet-manager",
"tools/internal/testnet-manager/dkg-bypass-contract",
"tools/nym-cli",
"tools/nym-id-cli",
"tools/nym-nr-query",
@@ -128,19 +137,23 @@ members = [
"wasm/zknym-lib",
"tools/internal/testnet-manager",
"tools/internal/testnet-manager/dkg-bypass-contract",
"tools/echo-server",
]
default-members = [
"clients/native",
"clients/socks5",
"explorer-api",
"gateway",
"service-providers/network-requester",
"mixnode",
"nym-api",
"tools/nymvisor",
"explorer-api",
"nym-validator-rewarder",
"nym-data-observatory",
"nym-node",
"nym-validator-rewarder",
"service-providers/authenticator",
"service-providers/ip-packet-router",
"service-providers/network-requester",
"tools/nymvisor",
]
exclude = [
@@ -149,7 +162,6 @@ exclude = [
"nym-wallet",
"nym-vpn/ui/src-tauri",
"cpu-cycles",
"sdk/ffi/cpp",
]
[workspace.package]
@@ -166,9 +178,11 @@ readme = "README.md"
addr = "0.15.6"
aes = "0.8.1"
aes-gcm = "0.10.1"
anyhow = "1.0.71"
aes-gcm-siv = "0.11.1"
aead = "0.5.2"
anyhow = "1.0.89"
argon2 = "0.5.0"
async-trait = "0.1.81"
async-trait = "0.1.82"
axum = "0.7.5"
axum-extra = "0.9.3"
base64 = "0.22.1"
@@ -191,25 +205,26 @@ chacha20 = "0.9.0"
chacha20poly1305 = "0.10.1"
chrono = "0.4.31"
cipher = "0.4.3"
clap = "4.5.16"
clap = "4.5.17"
clap_complete = "4.5"
clap_complete_fig = "4.5"
colored = "2.0"
comfy-table = "6.0.0"
comfy-table = "7.1.1"
console = "0.15.8"
console-subscriber = "0.1.1"
console_error_panic_hook = "0.1"
const-str = "0.5.6"
const_format = "0.2.32"
const_format = "0.2.33"
criterion = "0.4"
csv = "1.3.0"
ctr = "0.9.1"
cupid = "0.6.1"
curve25519-dalek = "4.1"
dashmap = "5.5.3"
defguard_wireguard_rs = "0.4.2"
# We want https://github.com/DefGuard/wireguard-rs/pull/64 , but there's no crates.io release being pushed out anymore
defguard_wireguard_rs = { git = "https://github.com/DefGuard/wireguard-rs.git", rev = "v0.4.7" }
digest = "0.10.7"
dirs = "4.0"
dirs = "5.0"
doc-comment = "0.3"
dotenvy = "0.15.6"
ecdsa = "0.16"
@@ -221,7 +236,7 @@ flate2 = "1.0.33"
futures = "0.3.28"
generic-array = "0.14.7"
getrandom = "0.2.10"
getset = "0.1.1"
getset = "0.1.3"
handlebars = "3.5.5"
headers = "0.4.0"
hex = "0.4.3"
@@ -229,10 +244,12 @@ hex-literal = "0.3.3"
hkdf = "0.12.3"
hmac = "0.12.1"
http = "1"
http-body-util = "0.1"
httpcodec = "0.2.3"
humantime = "2.1.0"
humantime-serde = "1.1.1"
hyper = "1.3.1"
hyper = "1.4.1"
hyper-util = "0.1"
indicatif = "0.17.8"
inquire = "0.6.2"
ip_network = "0.4.1"
@@ -252,7 +269,7 @@ okapi = "0.7.0"
once_cell = "1.7.2"
opentelemetry = "0.19.0"
opentelemetry-jaeger = "0.18.0"
parking_lot = "0.12.1"
parking_lot = "0.12.3"
pem = "0.8"
petgraph = "0.6.5"
pin-project = "1.0"
@@ -260,9 +277,7 @@ pretty_env_logger = "0.4.0"
publicsuffix = "2.2.3"
quote = "1"
rand = "0.8.5"
rand-07 = "0.7.3"
rand_chacha = "0.3"
rand_chacha_02 = "0.2"
rand_core = "0.6.3"
rand_distr = "0.4"
rand_pcg = "0.3.1"
@@ -273,13 +288,13 @@ reqwest = { version = "0.12.4", default-features = false }
rocket = "0.5.0"
rocket_cors = "0.6.0"
rocket_okapi = "0.8.0"
safer-ffi = "0.1.12"
schemars = "0.8.1"
safer-ffi = "0.1.13"
schemars = "0.8.21"
semver = "1.0.23"
serde = "1.0.209"
serde = "1.0.210"
serde_bytes = "0.11.15"
serde_derive = "1.0"
serde_json = "1.0.127"
serde_json = "1.0.128"
serde_repr = "0.1"
serde_with = "3.9.0"
serde_yaml = "0.9.25"
@@ -287,20 +302,21 @@ sha2 = "0.10.8"
si-scale = "0.2.3"
sphinx-packet = "0.1.1"
sqlx = "0.6.3"
strum = "0.25"
strum = "0.26"
subtle-encoding = "0.5"
syn = "1"
sysinfo = "0.30.12"
sysinfo = "0.30.13"
tap = "1.0.1"
tar = "0.4.40"
tar = "0.4.41"
tempfile = "3.5.0"
thiserror = "1.0.63"
time = "0.3.30"
tokio = "1.39"
tokio-stream = "0.1.15"
tokio-stream = "0.1.16"
tokio-test = "0.4.4"
tokio-tun = "0.11.5"
tokio-tungstenite = { version = "0.20.1" }
tokio-util = "0.7.11"
tokio-util = "0.7.12"
toml = "0.8.14"
tower = "0.4.13"
tower-http = "0.5.2"
@@ -312,13 +328,12 @@ ts-rs = "7.0.0"
tungstenite = { version = "0.20.1", default-features = false }
url = "2.5"
utoipa = "4.2"
utoipa-rapidoc = "4.0"
utoipa-swagger-ui = "7.1"
utoipauto = "0.1"
uuid = "*"
vergen = { version = "=8.3.1", default-features = false }
walkdir = "2"
wasm-bindgen-test = "0.3.36"
wasm-bindgen-test = "0.3.43"
x25519-dalek = "2.0.0"
zeroize = "1.6.0"
@@ -333,7 +348,6 @@ group = { version = "0.13.0", default-features = false }
ff = { version = "0.13.0", default-features = false }
# cosmwasm-related
cosmwasm-derive = "=1.4.3"
cosmwasm-schema = "=1.4.3"
cosmwasm-std = "=1.4.3"
# use 0.5.0 as that's the version used by cosmwasm-std 1.4.3
@@ -351,13 +365,10 @@ cw-controllers = { version = "=1.1.0" }
# cosmrs-related
bip32 = { version = "0.5.2", default-features = false }
# temporarily using a fork again (yay.) because we need staking and slashing support (which are already on main but not released)
# plus response message parsing (which is, as of the time of writing this message, waiting to get merged)
#cosmrs = { path = "../cosmos-rust-fork/cosmos-rust/cosmrs" }
cosmrs = { git = "https://github.com/cosmos/cosmos-rust", rev = "4b1332e6d8258ac845cef71589c8d362a669675a" } # unfortuntely we need a fork by yours truly to get the staking support
tendermint = "0.37.0" # same version as used by cosmrs
tendermint-rpc = "0.37.0" # same version as used by cosmrs
prost = { version = "0.12", default-features = false }
cosmrs = { version = "0.21.1" }
tendermint = "0.40.0"
tendermint-rpc = "0.40.0"
prost = { version = "0.13", default-features = false }
# wasm-related dependencies
gloo-utils = "0.2.0"
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-client"
version = "1.1.39"
version = "1.1.41"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
description = "Implementation of the Nym Client"
edition = "2021"
@@ -0,0 +1,16 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::commands::CliNativeClient;
use crate::error::ClientError;
use nym_client_core::cli_helpers::client_import_coin_index_signatures::{
import_coin_index_signatures, CommonClientImportCoinIndexSignaturesArgs,
};
pub(crate) async fn execute(
args: CommonClientImportCoinIndexSignaturesArgs,
) -> Result<(), ClientError> {
import_coin_index_signatures::<CliNativeClient, _>(args).await?;
println!("successfully imported coin index signatures!");
Ok(())
}
@@ -4,10 +4,10 @@
use crate::commands::CliNativeClient;
use crate::error::ClientError;
use nym_client_core::cli_helpers::client_import_credential::{
import_credential, CommonClientImportCredentialArgs,
import_credential, CommonClientImportTicketBookArgs,
};
pub(crate) async fn execute(args: CommonClientImportCredentialArgs) -> Result<(), ClientError> {
pub(crate) async fn execute(args: CommonClientImportTicketBookArgs) -> Result<(), ClientError> {
import_credential::<CliNativeClient, _>(args).await?;
println!("successfully imported credential!");
Ok(())
@@ -0,0 +1,16 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::commands::CliNativeClient;
use crate::error::ClientError;
use nym_client_core::cli_helpers::client_import_expiration_date_signatures::{
import_expiration_date_signatures, CommonClientImportExpirationDateSignaturesArgs,
};
pub(crate) async fn execute(
args: CommonClientImportExpirationDateSignaturesArgs,
) -> Result<(), ClientError> {
import_expiration_date_signatures::<CliNativeClient, _>(args).await?;
println!("successfully imported expiration date signatures!");
Ok(())
}
@@ -0,0 +1,16 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::commands::CliNativeClient;
use crate::error::ClientError;
use nym_client_core::cli_helpers::client_import_master_verification_key::{
import_master_verification_key, CommonClientImportMasterVerificationKeyArgs,
};
pub(crate) async fn execute(
args: CommonClientImportMasterVerificationKeyArgs,
) -> Result<(), ClientError> {
import_master_verification_key::<CliNativeClient, _>(args).await?;
println!("successfully imported master verification key!");
Ok(())
}
+59
View File
@@ -0,0 +1,59 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::{Args, Subcommand};
use nym_client_core::cli_helpers::client_import_coin_index_signatures::CommonClientImportCoinIndexSignaturesArgs;
use nym_client_core::cli_helpers::client_import_credential::CommonClientImportTicketBookArgs;
use nym_client_core::cli_helpers::client_import_expiration_date_signatures::CommonClientImportExpirationDateSignaturesArgs;
use nym_client_core::cli_helpers::client_import_master_verification_key::CommonClientImportMasterVerificationKeyArgs;
use std::error::Error;
pub(crate) mod import_coin_index_signatures;
pub(crate) mod import_credential;
pub(crate) mod import_expiration_date_signatures;
pub(crate) mod import_master_verification_key;
pub(crate) mod show_ticketbooks;
#[derive(Args)]
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
pub struct Ecash {
#[clap(subcommand)]
pub command: EcashCommands,
}
impl Ecash {
pub async fn execute(self) -> Result<(), Box<dyn Error + Send + Sync>> {
match self.command {
EcashCommands::ShowTicketBooks(args) => show_ticketbooks::execute(args).await?,
EcashCommands::ImportTicketBook(args) => import_credential::execute(args).await?,
EcashCommands::ImportCoinIndexSignatures(args) => {
import_coin_index_signatures::execute(args).await?
}
EcashCommands::ImportExpirationDateSignatures(args) => {
import_expiration_date_signatures::execute(args).await?
}
EcashCommands::ImportMasterVerificationKey(args) => {
import_master_verification_key::execute(args).await?
}
}
Ok(())
}
}
#[derive(Subcommand)]
pub enum EcashCommands {
/// Display information associated with the imported ticketbooks,
ShowTicketBooks(show_ticketbooks::Args),
/// Import a pre-generated ticketbook
ImportTicketBook(CommonClientImportTicketBookArgs),
/// Import coin index signatures needed for ticketbooks
ImportCoinIndexSignatures(CommonClientImportCoinIndexSignaturesArgs),
/// Import expiration date signatures needed for ticketbooks
ImportExpirationDateSignatures(CommonClientImportExpirationDateSignaturesArgs),
/// Import master verification key needed for ticketbooks
ImportMasterVerificationKey(CommonClientImportMasterVerificationKeyArgs),
}
+5 -10
View File
@@ -6,13 +6,13 @@ use crate::client::config::old_config_v1_1_20::ConfigV1_1_20;
use crate::client::config::old_config_v1_1_20_2::ConfigV1_1_20_2;
use crate::client::config::old_config_v1_1_33::ConfigV1_1_33;
use crate::client::config::{BaseClientConfig, Config};
use crate::commands::ecash::Ecash;
use crate::error::ClientError;
use clap::CommandFactory;
use clap::{Parser, Subcommand};
use log::{error, info};
use nym_bin_common::bin_info;
use nym_bin_common::completions::{fig_generate, ArgShell};
use nym_client_core::cli_helpers::client_import_credential::CommonClientImportCredentialArgs;
use nym_client_core::cli_helpers::CliClient;
use nym_client_core::client::base_client::storage::migration_helpers::v1_1_33;
use nym_config::OptionalSet;
@@ -22,11 +22,10 @@ use std::sync::OnceLock;
mod add_gateway;
pub(crate) mod build_info;
pub(crate) mod import_credential;
pub(crate) mod ecash;
pub(crate) mod init;
mod list_gateways;
pub(crate) mod run;
mod show_ticketbooks;
mod switch_gateway;
pub(crate) struct CliNativeClient;
@@ -73,8 +72,8 @@ pub(crate) enum Commands {
/// Run the Nym client with provided configuration client optionally overriding set parameters
Run(run::Run),
/// Import a pre-generated credential
ImportCredential(CommonClientImportCredentialArgs),
/// Ecash-related functionalities
Ecash(Ecash),
/// List all registered with gateways
ListGateways(list_gateways::Args),
@@ -85,9 +84,6 @@ pub(crate) enum Commands {
/// Change the currently active gateway. Note that you must have already registered with the new gateway!
SwitchGateway(switch_gateway::Args),
/// Display information associated with the imported ticketbooks,
ShowTicketbooks(show_ticketbooks::Args),
/// Show build information of this binary
BuildInfo(build_info::BuildInfo),
@@ -116,11 +112,10 @@ pub(crate) async fn execute(args: Cli) -> Result<(), Box<dyn Error + Send + Sync
match args.command {
Commands::Init(m) => init::execute(m).await?,
Commands::Run(m) => run::execute(m).await?,
Commands::ImportCredential(m) => import_credential::execute(m).await?,
Commands::Ecash(ecash) => ecash.execute().await?,
Commands::ListGateways(args) => list_gateways::execute(args).await?,
Commands::AddGateway(args) => add_gateway::execute(args).await?,
Commands::SwitchGateway(args) => switch_gateway::execute(args).await?,
Commands::ShowTicketbooks(args) => show_ticketbooks::execute(args).await?,
Commands::BuildInfo(m) => build_info::execute(m),
Commands::Completions(s) => s.generate(&mut Cli::command(), bin_name),
Commands::GenerateFigSpec => fig_generate(&mut Cli::command(), bin_name),
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-socks5-client"
version = "1.1.39"
version = "1.1.41"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
edition = "2021"
@@ -0,0 +1,16 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::commands::CliSocks5Client;
use crate::error::Socks5ClientError;
use nym_client_core::cli_helpers::client_import_coin_index_signatures::{
import_coin_index_signatures, CommonClientImportCoinIndexSignaturesArgs,
};
pub(crate) async fn execute(
args: CommonClientImportCoinIndexSignaturesArgs,
) -> Result<(), Socks5ClientError> {
import_coin_index_signatures::<CliSocks5Client, _>(args).await?;
println!("successfully imported coin index signatures!");
Ok(())
}
@@ -4,12 +4,10 @@
use crate::commands::CliSocks5Client;
use crate::error::Socks5ClientError;
use nym_client_core::cli_helpers::client_import_credential::{
import_credential, CommonClientImportCredentialArgs,
import_credential, CommonClientImportTicketBookArgs,
};
pub(crate) async fn execute(
args: CommonClientImportCredentialArgs,
) -> Result<(), Socks5ClientError> {
pub async fn execute(args: CommonClientImportTicketBookArgs) -> Result<(), Socks5ClientError> {
import_credential::<CliSocks5Client, _>(args).await?;
println!("successfully imported credential!");
Ok(())
@@ -0,0 +1,16 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::commands::CliSocks5Client;
use crate::error::Socks5ClientError;
use nym_client_core::cli_helpers::client_import_expiration_date_signatures::{
import_expiration_date_signatures, CommonClientImportExpirationDateSignaturesArgs,
};
pub(crate) async fn execute(
args: CommonClientImportExpirationDateSignaturesArgs,
) -> Result<(), Socks5ClientError> {
import_expiration_date_signatures::<CliSocks5Client, _>(args).await?;
println!("successfully imported expiration date signatures!");
Ok(())
}
@@ -0,0 +1,16 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::commands::CliSocks5Client;
use crate::error::Socks5ClientError;
use nym_client_core::cli_helpers::client_import_master_verification_key::{
import_master_verification_key, CommonClientImportMasterVerificationKeyArgs,
};
pub(crate) async fn execute(
args: CommonClientImportMasterVerificationKeyArgs,
) -> Result<(), Socks5ClientError> {
import_master_verification_key::<CliSocks5Client, _>(args).await?;
println!("successfully imported master verification key!");
Ok(())
}
+59
View File
@@ -0,0 +1,59 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::{Args, Subcommand};
use nym_client_core::cli_helpers::client_import_coin_index_signatures::CommonClientImportCoinIndexSignaturesArgs;
use nym_client_core::cli_helpers::client_import_credential::CommonClientImportTicketBookArgs;
use nym_client_core::cli_helpers::client_import_expiration_date_signatures::CommonClientImportExpirationDateSignaturesArgs;
use nym_client_core::cli_helpers::client_import_master_verification_key::CommonClientImportMasterVerificationKeyArgs;
use std::error::Error;
pub(crate) mod import_coin_index_signatures;
pub(crate) mod import_credential;
pub(crate) mod import_expiration_date_signatures;
pub(crate) mod import_master_verification_key;
pub(crate) mod show_ticketbooks;
#[derive(Args)]
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
pub struct Ecash {
#[clap(subcommand)]
pub command: EcashCommands,
}
impl Ecash {
pub async fn execute(self) -> Result<(), Box<dyn Error + Send + Sync>> {
match self.command {
EcashCommands::ShowTicketBooks(args) => show_ticketbooks::execute(args).await?,
EcashCommands::ImportTicketBook(args) => import_credential::execute(args).await?,
EcashCommands::ImportCoinIndexSignatures(args) => {
import_coin_index_signatures::execute(args).await?
}
EcashCommands::ImportExpirationDateSignatures(args) => {
import_expiration_date_signatures::execute(args).await?
}
EcashCommands::ImportMasterVerificationKey(args) => {
import_master_verification_key::execute(args).await?
}
}
Ok(())
}
}
#[derive(Subcommand)]
pub enum EcashCommands {
/// Display information associated with the imported ticketbooks,
ShowTicketBooks(show_ticketbooks::Args),
/// Import a pre-generated ticketbook
ImportTicketBook(CommonClientImportTicketBookArgs),
/// Import coin index signatures needed for ticketbooks
ImportCoinIndexSignatures(CommonClientImportCoinIndexSignaturesArgs),
/// Import expiration date signatures needed for ticketbooks
ImportExpirationDateSignatures(CommonClientImportExpirationDateSignaturesArgs),
/// Import master verification key needed for ticketbooks
ImportMasterVerificationKey(CommonClientImportMasterVerificationKeyArgs),
}
@@ -9,7 +9,7 @@ use nym_client_core::cli_helpers::client_show_ticketbooks::{
};
#[derive(clap::Args)]
pub(crate) struct Args {
pub struct Args {
#[command(flatten)]
common_args: CommonShowTicketbooksArgs,
@@ -23,7 +23,7 @@ impl AsRef<CommonShowTicketbooksArgs> for Args {
}
}
pub(crate) async fn execute(args: Args) -> Result<(), Socks5ClientError> {
pub async fn execute(args: Args) -> Result<(), Socks5ClientError> {
let output = args.output;
let res = show_ticketbooks::<CliSocks5Client, _>(args).await?;
+5 -10
View File
@@ -1,6 +1,7 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::commands::ecash::Ecash;
use crate::config::old_config_v1_1_13::OldConfigV1_1_13;
use crate::config::old_config_v1_1_20::ConfigV1_1_20;
use crate::config::old_config_v1_1_20_2::ConfigV1_1_20_2;
@@ -13,7 +14,6 @@ use clap::{Parser, Subcommand};
use log::{error, info};
use nym_bin_common::bin_info;
use nym_bin_common::completions::{fig_generate, ArgShell};
use nym_client_core::cli_helpers::client_import_credential::CommonClientImportCredentialArgs;
use nym_client_core::cli_helpers::CliClient;
use nym_client_core::client::base_client::storage::migration_helpers::v1_1_33;
use nym_client_core::client::topology_control::geo_aware_provider::CountryGroup;
@@ -26,11 +26,10 @@ use std::sync::OnceLock;
mod add_gateway;
pub(crate) mod build_info;
mod import_credential;
pub mod ecash;
pub mod init;
mod list_gateways;
pub(crate) mod run;
mod show_ticketbooks;
mod switch_gateway;
pub(crate) struct CliSocks5Client;
@@ -77,8 +76,8 @@ pub(crate) enum Commands {
/// Run the Nym client with provided configuration client optionally overriding set parameters
Run(run::Run),
/// Import a pre-generated credential
ImportCredential(CommonClientImportCredentialArgs),
/// Ecash-related functionalities
Ecash(Ecash),
/// List all registered with gateways
ListGateways(list_gateways::Args),
@@ -89,9 +88,6 @@ pub(crate) enum Commands {
/// Change the currently active gateway. Note that you must have already registered with the new gateway!
SwitchGateway(switch_gateway::Args),
/// Display information associated with the imported ticketbooks,
ShowTicketbooks(show_ticketbooks::Args),
/// Show build information of this binary
BuildInfo(build_info::BuildInfo),
@@ -123,11 +119,10 @@ pub(crate) async fn execute(args: Cli) -> Result<(), Box<dyn Error + Send + Sync
match args.command {
Commands::Init(m) => init::execute(m).await?,
Commands::Run(m) => run::execute(m).await?,
Commands::ImportCredential(m) => import_credential::execute(m).await?,
Commands::Ecash(ecash) => ecash.execute().await?,
Commands::ListGateways(args) => list_gateways::execute(args).await?,
Commands::AddGateway(args) => add_gateway::execute(args).await?,
Commands::SwitchGateway(args) => switch_gateway::execute(args).await?,
Commands::ShowTicketbooks(args) => show_ticketbooks::execute(args).await?,
Commands::BuildInfo(m) => build_info::execute(m),
Commands::Completions(s) => s.generate(&mut Cli::command(), bin_name),
Commands::GenerateFigSpec => fig_generate(&mut Cli::command(), bin_name),
+15
View File
@@ -9,9 +9,24 @@ edition.workspace = true
license.workspace = true
[dependencies]
base64 = { workspace = true }
bincode = { workspace = true }
rand = { workspace = true }
serde = { workspace = true, features = ["derive"] }
thiserror = { workspace = true }
nym-credentials-interface = { path = "../credentials-interface" }
nym-crypto = { path = "../crypto", features = ["asymmetric"] }
nym-service-provider-requests-common = { path = "../service-provider-requests-common" }
nym-sphinx = { path = "../nymsphinx" }
nym-wireguard-types = { path = "../wireguard-types" }
## verify:
hmac = { workspace = true, optional = true }
sha2 = { workspace = true, optional = true }
x25519-dalek = { workspace = true, features = ["static_secrets"] }
[features]
default = ["verify"]
# this is moved to a separate feature as we really need clients to import it (especially, *cough*, wasm)
verify = ["hmac", "sha2"]
@@ -0,0 +1,22 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use thiserror::Error;
#[derive(Debug, Error)]
pub enum Error {
#[error("the provided base64-encoded client MAC ('{mac}') was malformed: {source}")]
MalformedClientMac {
mac: String,
#[source]
source: base64::DecodeError,
},
#[cfg(feature = "verify")]
#[error("failed to verify mac provided by '{client}': {source}")]
FailedClientMacVerification {
client: String,
#[source]
source: hmac::digest::MacError,
},
}
+7 -1
View File
@@ -2,8 +2,14 @@
// SPDX-License-Identifier: Apache-2.0
pub mod v1;
pub mod v2;
pub const CURRENT_VERSION: u8 = 1;
mod error;
pub use error::Error;
pub use v2 as latest;
pub const CURRENT_VERSION: u8 = 2;
fn make_bincode_serializer() -> impl bincode::Options {
use bincode::Options;
+7 -1
View File
@@ -1,7 +1,13 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub mod registration;
pub mod request;
pub mod response;
const VERSION: u8 = 1;
pub use registration::{ClientMac, GatewayClient, InitMessage, Nonce};
#[cfg(feature = "verify")]
pub use registration::HmacSha256;
pub const VERSION: u8 = 1;
@@ -0,0 +1,218 @@
// Copyright 2023-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::error::Error;
use base64::{engine::general_purpose, Engine};
use nym_wireguard_types::PeerPublicKey;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::net::IpAddr;
use std::time::SystemTime;
use std::{fmt, ops::Deref, str::FromStr};
#[cfg(feature = "verify")]
use hmac::{Hmac, Mac};
#[cfg(feature = "verify")]
use nym_crypto::asymmetric::encryption::PrivateKey;
#[cfg(feature = "verify")]
use sha2::Sha256;
pub type PendingRegistrations = HashMap<PeerPublicKey, RegistrationData>;
pub type PrivateIPs = HashMap<IpAddr, Taken>;
#[cfg(feature = "verify")]
pub type HmacSha256 = Hmac<Sha256>;
pub type Nonce = u64;
pub type Taken = Option<SystemTime>;
pub const BANDWIDTH_CAP_PER_DAY: i64 = 1024 * 1024 * 1024; // 1 GB
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct InitMessage {
/// Base64 encoded x25519 public key
pub pub_key: PeerPublicKey,
}
impl InitMessage {
pub fn new(pub_key: PeerPublicKey) -> Self {
InitMessage { pub_key }
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct RegistrationData {
pub nonce: u64,
pub gateway_data: GatewayClient,
pub wg_port: u16,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct RegistredData {
pub pub_key: PeerPublicKey,
pub private_ip: IpAddr,
pub wg_port: u16,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct RemainingBandwidthData {
pub available_bandwidth: u64,
pub suspended: bool,
}
/// Client that wants to register sends its PublicKey bytes mac digest encrypted with a DH shared secret.
/// Gateway/Nym node can then verify pub_key payload using the same process
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct GatewayClient {
/// Base64 encoded x25519 public key
pub pub_key: PeerPublicKey,
/// Assigned private IP
pub private_ip: IpAddr,
/// Sha256 hmac on the data (alongside the prior nonce)
pub mac: ClientMac,
}
impl GatewayClient {
#[cfg(feature = "verify")]
pub fn new(
local_secret: &PrivateKey,
remote_public: x25519_dalek::PublicKey,
private_ip: IpAddr,
nonce: u64,
) -> Self {
// convert from 1.0 x25519-dalek private key into 2.0 x25519-dalek
#[allow(clippy::expect_used)]
let static_secret = x25519_dalek::StaticSecret::from(local_secret.to_bytes());
let local_public: x25519_dalek::PublicKey = (&static_secret).into();
let dh = static_secret.diffie_hellman(&remote_public);
// TODO: change that to use our nym_crypto::hmac module instead
#[allow(clippy::expect_used)]
let mut mac = HmacSha256::new_from_slice(dh.as_bytes())
.expect("x25519 shared secret is always 32 bytes long");
mac.update(local_public.as_bytes());
mac.update(private_ip.to_string().as_bytes());
mac.update(&nonce.to_le_bytes());
GatewayClient {
pub_key: PeerPublicKey::new(local_public),
private_ip,
mac: ClientMac(mac.finalize().into_bytes().to_vec()),
}
}
// Reusable secret should be gateways Wireguard PK
// Client should perform this step when generating its payload, using its own WG PK
#[cfg(feature = "verify")]
pub fn verify(&self, gateway_key: &PrivateKey, nonce: u64) -> Result<(), Error> {
// convert from 1.0 x25519-dalek private key into 2.0 x25519-dalek
#[allow(clippy::expect_used)]
let static_secret = x25519_dalek::StaticSecret::from(gateway_key.to_bytes());
let dh = static_secret.diffie_hellman(&self.pub_key);
// TODO: change that to use our nym_crypto::hmac module instead
#[allow(clippy::expect_used)]
let mut mac = HmacSha256::new_from_slice(dh.as_bytes())
.expect("x25519 shared secret is always 32 bytes long");
mac.update(self.pub_key.as_bytes());
mac.update(self.private_ip.to_string().as_bytes());
mac.update(&nonce.to_le_bytes());
mac.verify_slice(&self.mac)
.map_err(|source| Error::FailedClientMacVerification {
client: self.pub_key.to_string(),
source,
})
}
pub fn pub_key(&self) -> PeerPublicKey {
self.pub_key
}
}
// TODO: change the inner type into generic array of size HmacSha256::OutputSize
// TODO2: rely on our internal crypto/hmac
#[derive(Debug, Clone)]
pub struct ClientMac(Vec<u8>);
impl fmt::Display for ClientMac {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", general_purpose::STANDARD.encode(&self.0))
}
}
impl ClientMac {
#[allow(dead_code)]
pub fn new(mac: Vec<u8>) -> Self {
ClientMac(mac)
}
}
impl Deref for ClientMac {
type Target = Vec<u8>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl FromStr for ClientMac {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mac_bytes: Vec<u8> =
general_purpose::STANDARD
.decode(s)
.map_err(|source| Error::MalformedClientMac {
mac: s.to_string(),
source,
})?;
Ok(ClientMac(mac_bytes))
}
}
impl Serialize for ClientMac {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let encoded_key = general_purpose::STANDARD.encode(self.0.clone());
serializer.serialize_str(&encoded_key)
}
}
impl<'de> Deserialize<'de> for ClientMac {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let encoded_key = String::deserialize(deserializer)?;
ClientMac::from_str(&encoded_key).map_err(serde::de::Error::custom)
}
}
#[cfg(test)]
mod tests {
use super::*;
use nym_crypto::asymmetric::encryption;
#[test]
#[cfg(feature = "verify")]
fn client_request_roundtrip() {
let mut rng = rand::thread_rng();
let gateway_key_pair = encryption::KeyPair::new(&mut rng);
let client_key_pair = encryption::KeyPair::new(&mut rng);
let nonce = 1234567890;
let client = GatewayClient::new(
client_key_pair.private_key(),
x25519_dalek::PublicKey::from(gateway_key_pair.public_key().to_bytes()),
"10.0.0.42".parse().unwrap(),
nonce,
);
assert!(client.verify(gateway_key_pair.private_key(), nonce).is_ok())
}
}
@@ -1,8 +1,9 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use super::registration::{GatewayClient, InitMessage};
use nym_sphinx::addressing::Recipient;
use nym_wireguard_types::{GatewayClient, InitMessage, PeerPublicKey};
use nym_wireguard_types::PeerPublicKey;
use serde::{Deserialize, Serialize};
use crate::make_bincode_serializer;
@@ -82,3 +83,24 @@ pub enum AuthenticatorRequestData {
Final(GatewayClient),
QueryBandwidth(PeerPublicKey),
}
#[cfg(test)]
mod tests {
use super::*;
use std::str::FromStr;
#[test]
fn check_first_byte_version() {
let version = 2;
let data = AuthenticatorRequest {
version,
data: AuthenticatorRequestData::Initial(InitMessage::new(
PeerPublicKey::from_str("yvNUDpT5l7W/xDhiu6HkqTHDQwbs/B3J5UrLmORl1EQ=").unwrap(),
)),
reply_to: Recipient::try_from_base58_string("D1rrpsysCGCYXy9saP8y3kmNpGtJZUXN9SvFoUcqAsM9.9Ssso1ea5NfkbMASdiseDSjTN1fSWda5SgEVjdSN4CvV@GJqd3ZxpXWSNxTfx7B1pPtswpetH4LnJdFeLeuY5KUuN").unwrap(),
request_id: 1,
};
let bytes = data.to_bytes().unwrap();
assert_eq!(*bytes.first().unwrap(), version);
}
}
@@ -1,8 +1,8 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use super::registration::{RegistrationData, RegistredData, RemainingBandwidthData};
use nym_sphinx::addressing::Recipient;
use nym_wireguard_types::registration::{RegistrationData, RegistredData, RemainingBandwidthData};
use serde::{Deserialize, Serialize};
use crate::make_bincode_serializer;
@@ -0,0 +1,174 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use nym_service_provider_requests_common::{Protocol, ServiceProviderType};
use crate::{v1, v2};
impl From<v1::request::AuthenticatorRequest> for v2::request::AuthenticatorRequest {
fn from(authenticator_request: v1::request::AuthenticatorRequest) -> Self {
Self {
protocol: Protocol {
version: 2,
service_provider_type: ServiceProviderType::Authenticator,
},
data: authenticator_request.data.into(),
reply_to: authenticator_request.reply_to,
request_id: authenticator_request.request_id,
}
}
}
impl From<v1::request::AuthenticatorRequestData> for v2::request::AuthenticatorRequestData {
fn from(authenticator_request_data: v1::request::AuthenticatorRequestData) -> Self {
match authenticator_request_data {
v1::request::AuthenticatorRequestData::Initial(init_msg) => {
v2::request::AuthenticatorRequestData::Initial(init_msg.into())
}
v1::request::AuthenticatorRequestData::Final(gw_client) => {
v2::request::AuthenticatorRequestData::Final(gw_client.into())
}
v1::request::AuthenticatorRequestData::QueryBandwidth(pub_key) => {
v2::request::AuthenticatorRequestData::QueryBandwidth(pub_key)
}
}
}
}
impl From<v1::registration::InitMessage> for v2::registration::InitMessage {
fn from(init_msg: v1::registration::InitMessage) -> Self {
Self {
pub_key: init_msg.pub_key,
}
}
}
impl From<v1::registration::GatewayClient> for Box<v2::registration::FinalMessage> {
fn from(gw_client: v1::registration::GatewayClient) -> Self {
Box::new(v2::registration::FinalMessage {
gateway_client: gw_client.into(),
credential: None,
})
}
}
impl From<v1::registration::GatewayClient> for v2::registration::GatewayClient {
fn from(gw_client: v1::registration::GatewayClient) -> Self {
Self {
pub_key: gw_client.pub_key,
private_ip: gw_client.private_ip,
mac: gw_client.mac.into(),
}
}
}
impl From<v2::registration::GatewayClient> for v1::registration::GatewayClient {
fn from(gw_client: v2::registration::GatewayClient) -> Self {
Self {
pub_key: gw_client.pub_key,
private_ip: gw_client.private_ip,
mac: gw_client.mac.into(),
}
}
}
impl From<v1::registration::ClientMac> for v2::registration::ClientMac {
fn from(mac: v1::registration::ClientMac) -> Self {
Self::new(mac.to_vec())
}
}
impl From<v2::registration::ClientMac> for v1::registration::ClientMac {
fn from(mac: v2::registration::ClientMac) -> Self {
Self::new(mac.to_vec())
}
}
impl From<v2::response::AuthenticatorResponse> for v1::response::AuthenticatorResponse {
fn from(authenticator_response: v2::response::AuthenticatorResponse) -> Self {
Self {
version: authenticator_response.protocol.version,
data: authenticator_response.data.into(),
reply_to: authenticator_response.reply_to,
}
}
}
impl From<v2::response::AuthenticatorResponseData> for v1::response::AuthenticatorResponseData {
fn from(authenticator_response_data: v2::response::AuthenticatorResponseData) -> Self {
match authenticator_response_data {
v2::response::AuthenticatorResponseData::PendingRegistration(
pending_registration_response,
) => v1::response::AuthenticatorResponseData::PendingRegistration(
pending_registration_response.into(),
),
v2::response::AuthenticatorResponseData::Registered(registered_response) => {
v1::response::AuthenticatorResponseData::Registered(registered_response.into())
}
v2::response::AuthenticatorResponseData::RemainingBandwidth(
remaining_bandwidth_response,
) => v1::response::AuthenticatorResponseData::RemainingBandwidth(
remaining_bandwidth_response.into(),
),
}
}
}
impl From<v2::response::PendingRegistrationResponse> for v1::response::PendingRegistrationResponse {
fn from(value: v2::response::PendingRegistrationResponse) -> Self {
Self {
request_id: value.request_id,
reply_to: value.reply_to,
reply: value.reply.into(),
}
}
}
impl From<v2::response::RegisteredResponse> for v1::response::RegisteredResponse {
fn from(value: v2::response::RegisteredResponse) -> Self {
Self {
request_id: value.request_id,
reply_to: value.reply_to,
reply: value.reply.into(),
}
}
}
impl From<v2::response::RemainingBandwidthResponse> for v1::response::RemainingBandwidthResponse {
fn from(value: v2::response::RemainingBandwidthResponse) -> Self {
Self {
request_id: value.request_id,
reply_to: value.reply_to,
reply: value.reply.map(Into::into),
}
}
}
impl From<v2::registration::RegistrationData> for v1::registration::RegistrationData {
fn from(value: v2::registration::RegistrationData) -> Self {
Self {
nonce: value.nonce,
gateway_data: value.gateway_data.into(),
wg_port: value.wg_port,
}
}
}
impl From<v2::registration::RegistredData> for v1::registration::RegistredData {
fn from(value: v2::registration::RegistredData) -> Self {
Self {
pub_key: value.pub_key,
private_ip: value.private_ip,
wg_port: value.wg_port,
}
}
}
impl From<v2::registration::RemainingBandwidthData> for v1::registration::RemainingBandwidthData {
fn from(value: v2::registration::RemainingBandwidthData) -> Self {
Self {
available_bandwidth: value.available_bandwidth as u64,
suspended: false,
}
}
}
@@ -0,0 +1,9 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub mod conversion;
pub mod registration;
pub mod request;
pub mod response;
pub const VERSION: u8 = 2;
@@ -1,9 +1,10 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2023-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::error::Error;
use crate::PeerPublicKey;
use base64::{engine::general_purpose, Engine};
use nym_credentials_interface::CredentialSpendingData;
use nym_wireguard_types::PeerPublicKey;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::net::IpAddr;
@@ -29,32 +30,26 @@ pub type Taken = Option<SystemTime>;
pub const BANDWIDTH_CAP_PER_DAY: u64 = 1024 * 1024 * 1024; // 1 GB
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(tag = "type", rename_all = "camelCase")]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub enum ClientMessage {
Initial(InitMessage),
Final(GatewayClient),
Query(PeerPublicKey),
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct InitMessage {
/// Base64 encoded x25519 public key
#[cfg_attr(feature = "openapi", schema(value_type = String, format = Byte))]
pub pub_key: PeerPublicKey,
}
impl InitMessage {
pub fn pub_key(&self) -> PeerPublicKey {
self.pub_key
}
pub fn new(pub_key: PeerPublicKey) -> Self {
InitMessage { pub_key }
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct FinalMessage {
/// Gateway client data
pub gateway_client: GatewayClient,
/// Ecash credential
pub credential: Option<CredentialSpendingData>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct RegistrationData {
pub nonce: u64,
@@ -71,24 +66,20 @@ pub struct RegistredData {
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct RemainingBandwidthData {
pub available_bandwidth: u64,
pub suspended: bool,
pub available_bandwidth: i64,
}
/// Client that wants to register sends its PublicKey bytes mac digest encrypted with a DH shared secret.
/// Gateway/Nym node can then verify pub_key payload using the same process
#[derive(Serialize, Deserialize, Debug, Clone)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct GatewayClient {
/// Base64 encoded x25519 public key
#[cfg_attr(feature = "openapi", schema(value_type = String, format = Byte))]
pub pub_key: PeerPublicKey,
/// Assigned private IP
pub private_ip: IpAddr,
/// Sha256 hmac on the data (alongside the prior nonce)
#[cfg_attr(feature = "openapi", schema(value_type = String, format = Byte))]
pub mac: ClientMac,
}
@@ -0,0 +1,116 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use super::registration::{FinalMessage, InitMessage};
use nym_service_provider_requests_common::{Protocol, ServiceProviderType};
use nym_sphinx::addressing::Recipient;
use nym_wireguard_types::PeerPublicKey;
use serde::{Deserialize, Serialize};
use crate::make_bincode_serializer;
use super::VERSION;
fn generate_random() -> u64 {
use rand::RngCore;
let mut rng = rand::rngs::OsRng;
rng.next_u64()
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct AuthenticatorRequest {
pub protocol: Protocol,
pub data: AuthenticatorRequestData,
pub reply_to: Recipient,
pub request_id: u64,
}
impl AuthenticatorRequest {
pub fn from_reconstructed_message(
message: &nym_sphinx::receiver::ReconstructedMessage,
) -> Result<Self, bincode::Error> {
use bincode::Options;
make_bincode_serializer().deserialize(&message.message)
}
pub fn new_initial_request(init_message: InitMessage, reply_to: Recipient) -> (Self, u64) {
let request_id = generate_random();
(
Self {
protocol: Protocol {
service_provider_type: ServiceProviderType::Authenticator,
version: VERSION,
},
data: AuthenticatorRequestData::Initial(init_message),
reply_to,
request_id,
},
request_id,
)
}
pub fn new_final_request(final_message: FinalMessage, reply_to: Recipient) -> (Self, u64) {
let request_id = generate_random();
(
Self {
protocol: Protocol {
service_provider_type: ServiceProviderType::Authenticator,
version: VERSION,
},
data: AuthenticatorRequestData::Final(Box::new(final_message)),
reply_to,
request_id,
},
request_id,
)
}
pub fn new_query_request(peer_public_key: PeerPublicKey, reply_to: Recipient) -> (Self, u64) {
let request_id = generate_random();
(
Self {
protocol: Protocol {
service_provider_type: ServiceProviderType::Authenticator,
version: VERSION,
},
data: AuthenticatorRequestData::QueryBandwidth(peer_public_key),
reply_to,
request_id,
},
request_id,
)
}
pub fn to_bytes(&self) -> Result<Vec<u8>, bincode::Error> {
use bincode::Options;
make_bincode_serializer().serialize(self)
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum AuthenticatorRequestData {
Initial(InitMessage),
Final(Box<FinalMessage>),
QueryBandwidth(PeerPublicKey),
}
#[cfg(test)]
mod tests {
use super::*;
use std::str::FromStr;
#[test]
fn check_first_bytes_protocol() {
let version = 2;
let data = AuthenticatorRequest {
protocol: Protocol { version, service_provider_type: ServiceProviderType::Authenticator },
data: AuthenticatorRequestData::Initial(InitMessage::new(
PeerPublicKey::from_str("yvNUDpT5l7W/xDhiu6HkqTHDQwbs/B3J5UrLmORl1EQ=").unwrap(),
)),
reply_to: Recipient::try_from_base58_string("D1rrpsysCGCYXy9saP8y3kmNpGtJZUXN9SvFoUcqAsM9.9Ssso1ea5NfkbMASdiseDSjTN1fSWda5SgEVjdSN4CvV@GJqd3ZxpXWSNxTfx7B1pPtswpetH4LnJdFeLeuY5KUuN").unwrap(),
request_id: 1,
};
let bytes = *data.to_bytes().unwrap().first_chunk::<2>().unwrap();
assert_eq!(bytes, [version, ServiceProviderType::Authenticator as u8]);
}
}
@@ -0,0 +1,129 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use super::registration::{RegistrationData, RegistredData, RemainingBandwidthData};
use nym_service_provider_requests_common::{Protocol, ServiceProviderType};
use nym_sphinx::addressing::Recipient;
use serde::{Deserialize, Serialize};
use crate::make_bincode_serializer;
use super::VERSION;
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct AuthenticatorResponse {
pub protocol: Protocol,
pub data: AuthenticatorResponseData,
pub reply_to: Recipient,
}
impl AuthenticatorResponse {
pub fn new_pending_registration_success(
registration_data: RegistrationData,
request_id: u64,
reply_to: Recipient,
) -> Self {
Self {
protocol: Protocol {
service_provider_type: ServiceProviderType::Authenticator,
version: VERSION,
},
data: AuthenticatorResponseData::PendingRegistration(PendingRegistrationResponse {
reply: registration_data,
reply_to,
request_id,
}),
reply_to,
}
}
pub fn new_registered(
registred_data: RegistredData,
reply_to: Recipient,
request_id: u64,
) -> Self {
Self {
protocol: Protocol {
service_provider_type: ServiceProviderType::Authenticator,
version: VERSION,
},
data: AuthenticatorResponseData::Registered(RegisteredResponse {
reply: registred_data,
reply_to,
request_id,
}),
reply_to,
}
}
pub fn new_remaining_bandwidth(
remaining_bandwidth_data: Option<RemainingBandwidthData>,
reply_to: Recipient,
request_id: u64,
) -> Self {
Self {
protocol: Protocol {
service_provider_type: ServiceProviderType::Authenticator,
version: VERSION,
},
data: AuthenticatorResponseData::RemainingBandwidth(RemainingBandwidthResponse {
reply: remaining_bandwidth_data,
reply_to,
request_id,
}),
reply_to,
}
}
pub fn recipient(&self) -> Recipient {
self.reply_to
}
pub fn to_bytes(&self) -> Result<Vec<u8>, bincode::Error> {
use bincode::Options;
make_bincode_serializer().serialize(self)
}
pub fn from_reconstructed_message(
message: &nym_sphinx::receiver::ReconstructedMessage,
) -> Result<Self, bincode::Error> {
use bincode::Options;
make_bincode_serializer().deserialize(&message.message)
}
pub fn id(&self) -> Option<u64> {
match &self.data {
AuthenticatorResponseData::PendingRegistration(response) => Some(response.request_id),
AuthenticatorResponseData::Registered(response) => Some(response.request_id),
AuthenticatorResponseData::RemainingBandwidth(response) => Some(response.request_id),
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum AuthenticatorResponseData {
PendingRegistration(PendingRegistrationResponse),
Registered(RegisteredResponse),
RemainingBandwidth(RemainingBandwidthResponse),
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct PendingRegistrationResponse {
pub request_id: u64,
pub reply_to: Recipient,
pub reply: RegistrationData,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct RegisteredResponse {
pub request_id: u64,
pub reply_to: Recipient,
pub reply: RegistredData,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct RemainingBandwidthResponse {
pub request_id: u64,
pub reply_to: Recipient,
pub reply: Option<RemainingBandwidthData>,
}
+1 -1
View File
@@ -18,7 +18,7 @@ nym-ecash-time = { path = "../ecash-time" }
nym-credential-storage = { path = "../credential-storage" }
nym-credentials = { path = "../credentials" }
nym-credentials-interface = { path = "../credentials-interface" }
nym-crypto = { path = "../crypto", features = ["rand", "asymmetric", "symmetric", "aes", "hashing"] }
nym-crypto = { path = "../crypto", features = ["rand", "asymmetric", "stream_cipher", "aes", "hashing"] }
nym-network-defaults = { path = "../network-defaults" }
nym-validator-client = { path = "../client-libs/validator-client", default-features = false }
nym-ecash-contract-common = { path = "../cosmwasm-smart-contracts/ecash-contract" }
@@ -2,7 +2,9 @@
// SPDX-License-Identifier: Apache-2.0
use crate::error::BandwidthControllerError;
use crate::utils::{get_coin_index_signatures, get_expiration_date_signatures};
use crate::utils::{
get_aggregate_verification_key, get_coin_index_signatures, get_expiration_date_signatures,
};
use log::info;
use nym_credential_storage::storage::Storage;
use nym_credentials::ecash::bandwidth::IssuanceTicketBook;
@@ -55,7 +57,7 @@ where
))
}
pub async fn query_and_persist_required_global_signatures<S>(
pub async fn query_and_persist_required_global_data<S>(
storage: &S,
epoch_id: EpochId,
expiration_date: Date,
@@ -65,6 +67,10 @@ where
S: Storage,
<S as Storage>::StorageError: Send + Sync + 'static,
{
log::info!("Getting master verification key");
// this will also persist the key in the storage if was not there already
get_aggregate_verification_key(storage, epoch_id, apis.clone()).await?;
log::info!("Getting expiration date signatures");
// this will also persist the signatures in the storage if they were not there already
get_expiration_date_signatures(storage, epoch_id, expiration_date, apis.clone()).await?;
+4 -2
View File
@@ -16,7 +16,7 @@ use nym_credential_storage::models::RetrievedTicketbook;
use nym_credential_storage::storage::Storage;
use nym_credentials::ecash::bandwidth::CredentialSpendingData;
use nym_credentials_interface::{
AnnotatedCoinIndexSignature, AnnotatedExpirationDateSignature, NymPayInfo, VerificationKeyAuth,
AnnotatedCoinIndexSignature, AnnotatedExpirationDateSignature, VerificationKeyAuth,
};
use nym_ecash_time::Date;
use nym_validator_client::nym_api::EpochId;
@@ -165,7 +165,9 @@ impl<C, St: Storage> BandwidthController<C, St> {
.get_coin_index_signatures(epoch_id, &mut api_clients)
.await?;
let pay_info = NymPayInfo::generate(provider_pk);
let pay_info = retrieved_ticketbook
.ticketbook
.generate_pay_info(provider_pk);
let spend_request = retrieved_ticketbook.ticketbook.prepare_for_spending(
&verification_key,
+26 -6
View File
@@ -4,6 +4,10 @@
use crate::error::BandwidthControllerError;
use log::warn;
use nym_credential_storage::storage::Storage;
use nym_credentials::ecash::bandwidth::serialiser::keys::EpochVerificationKey;
use nym_credentials::ecash::bandwidth::serialiser::signatures::{
AggregatedCoinIndicesSignatures, AggregatedExpirationDateSignatures,
};
use nym_credentials_interface::{
AnnotatedCoinIndexSignature, AnnotatedExpirationDateSignature, VerificationKeyAuth,
};
@@ -94,13 +98,18 @@ where
.await?
.key;
let full = EpochVerificationKey {
epoch_id,
key: master_vk,
};
// store the retrieved key
storage
.insert_master_verification_key(epoch_id, &master_vk)
.insert_master_verification_key(&full)
.await
.map_err(BandwidthControllerError::credential_storage_error)?;
Ok(master_vk)
Ok(full.key)
}
pub(crate) async fn get_coin_index_signatures<St>(
@@ -132,13 +141,18 @@ where
.await?
.signatures;
let aggregated = AggregatedCoinIndicesSignatures {
epoch_id,
signatures: index_sigs,
};
// store the retrieved key
storage
.insert_coin_index_signatures(epoch_id, &index_sigs)
.insert_coin_index_signatures(&aggregated)
.await
.map_err(BandwidthControllerError::credential_storage_error)?;
Ok(index_sigs)
Ok(aggregated.signatures)
}
pub(crate) async fn get_expiration_date_signatures<St>(
@@ -171,11 +185,17 @@ where
.await?
.signatures;
let aggregated = AggregatedExpirationDateSignatures {
epoch_id,
expiration_date,
signatures: expiration_sigs,
};
// store the retrieved key
storage
.insert_expiration_date_signatures(epoch_id, expiration_date, &expiration_sigs)
.insert_expiration_date_signatures(&aggregated)
.await
.map_err(BandwidthControllerError::credential_storage_error)?;
Ok(expiration_sigs)
Ok(aggregated.signatures)
}
+2 -2
View File
@@ -8,14 +8,14 @@ license = { workspace = true }
repository = { workspace = true }
[dependencies]
const-str = { workspace = true }
clap = { workspace = true, features = ["derive"], optional = true }
clap_complete = { workspace = true, optional = true }
clap_complete_fig = { workspace = true, optional = true }
const-str = { workspace = true }
log = { workspace = true }
pretty_env_logger = { workspace = true }
semver = "0.11"
schemars = { workspace = true, features = ["preserve_order"], optional = true }
semver.workspace = true
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true, optional = true }
+3 -2
View File
@@ -1,9 +1,10 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use semver::SemVerError;
pub use semver::Version;
/// Checks if the version is minor version compatible.
///
/// Checks whether given `version` is compatible with a given semantic version requirement `req`
/// according to major-minor semver rules. The semantic version requirement can be passed as a full,
/// concrete version number, because that's what we'll have in our Cargo.toml files (e.g. 0.3.2).
@@ -22,7 +23,7 @@ pub fn is_minor_version_compatible(version: &str, req: &str) -> bool {
expected_version.major == req_version.major && expected_version.minor == req_version.minor
}
pub fn parse_version(raw_version: &str) -> Result<Version, SemVerError> {
pub fn parse_version(raw_version: &str) -> Result<Version, semver::Error> {
Version::parse(raw_version)
}
+7 -7
View File
@@ -14,7 +14,7 @@ base64 = { workspace = true }
bs58 = { workspace = true }
cfg-if = { workspace = true }
clap = { workspace = true, optional = true }
comfy-table = { version = "7.1.1", optional = true }
comfy-table = { workspace = true, optional = true }
futures = { workspace = true }
humantime-serde = { workspace = true }
log = { workspace = true }
@@ -59,19 +59,19 @@ nym-ecash-time = { path = "../ecash-time" }
### For serving prometheus metrics
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.hyper]
version = "1"
workspace = true
features = ["server", "http1"]
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.http-body-util]
version = "0.1"
workspace = true
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.hyper-util]
version = "0.1"
workspace = true
features = ["tokio"]
###
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-stream]
version = "0.1.11"
workspace = true
features = ["time"]
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio]
@@ -102,7 +102,7 @@ workspace = true
features = ["tokio"]
[target."cfg(target_arch = \"wasm32\")".dependencies.gloo-timers]
version = "0.2.4"
version = "0.3.0"
features = ["futures"]
[target."cfg(target_arch = \"wasm32\")".dependencies.wasm-utils]
@@ -110,7 +110,7 @@ path = "../wasm/utils"
features = ["websocket"]
[target."cfg(target_arch = \"wasm32\")".dependencies.time]
version = "0.3.17"
workspace = true
features = ["wasm-bindgen"]
[dev-dependencies]
@@ -0,0 +1,13 @@
/*
* Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
* SPDX-License-Identifier: Apache-2.0
*/
-- make aes128 key column nullable and add aes256 column
ALTER TABLE remote_gateway_details RENAME COLUMN derived_aes128_ctr_blake3_hmac_keys_bs58 TO derived_aes128_ctr_blake3_hmac_keys_bs58_old;
ALTER TABLE remote_gateway_details ADD COLUMN derived_aes128_ctr_blake3_hmac_keys_bs58 TEXT;
ALTER TABLE remote_gateway_details ADD COLUMN derived_aes256_gcm_siv_key BLOB;
UPDATE remote_gateway_details SET derived_aes128_ctr_blake3_hmac_keys_bs58 = derived_aes128_ctr_blake3_hmac_keys_bs58_old;
ALTER TABLE remote_gateway_details DROP COLUMN derived_aes128_ctr_blake3_hmac_keys_bs58_old;
@@ -155,11 +155,12 @@ impl StorageManager {
) -> Result<(), sqlx::Error> {
sqlx::query!(
r#"
INSERT INTO remote_gateway_details(gateway_id_bs58, derived_aes128_ctr_blake3_hmac_keys_bs58, gateway_owner_address, gateway_listener)
VALUES (?, ?, ?, ?)
INSERT INTO remote_gateway_details(gateway_id_bs58, derived_aes128_ctr_blake3_hmac_keys_bs58, derived_aes256_gcm_siv_key, gateway_owner_address, gateway_listener)
VALUES (?, ?, ?, ?, ?)
"#,
remote.gateway_id_bs58,
remote.derived_aes128_ctr_blake3_hmac_keys_bs58,
remote.derived_aes256_gcm_siv_key,
remote.gateway_owner_address,
remote.gateway_listener,
)
@@ -168,6 +169,30 @@ impl StorageManager {
Ok(())
}
pub(crate) async fn update_remote_gateway_key(
&self,
gateway_id_bs58: &str,
derived_aes128_ctr_blake3_hmac_keys_bs58: Option<&str>,
derived_aes256_gcm_siv_key: Option<&[u8]>,
) -> Result<(), sqlx::Error> {
sqlx::query!(
r#"
UPDATE remote_gateway_details
SET
derived_aes128_ctr_blake3_hmac_keys_bs58 = ?,
derived_aes256_gcm_siv_key = ?
WHERE gateway_id_bs58 = ?
"#,
derived_aes128_ctr_blake3_hmac_keys_bs58,
derived_aes256_gcm_siv_key,
gateway_id_bs58
)
.execute(&self.connection_pool)
.await?;
Ok(())
}
pub(crate) async fn remove_remote_gateway_details(
&self,
gateway_id: &str,
@@ -7,7 +7,8 @@ use crate::{
};
use async_trait::async_trait;
use manager::StorageManager;
use nym_crypto::asymmetric::identity::PublicKey;
use nym_crypto::asymmetric::ed25519;
use nym_gateway_requests::SharedSymmetricKey;
use std::path::Path;
pub mod error;
@@ -67,7 +68,7 @@ impl GatewaysDetailsStore for OnDiskGatewaysDetails {
Ok(registered)
}
async fn all_gateways_identities(&self) -> Result<Vec<PublicKey>, Self::StorageError> {
async fn all_gateways_identities(&self) -> Result<Vec<ed25519::PublicKey>, Self::StorageError> {
Ok(self
.manager
.registered_gateways()
@@ -132,6 +133,21 @@ impl GatewaysDetailsStore for OnDiskGatewaysDetails {
Ok(())
}
async fn upgrade_stored_remote_gateway_key(
&self,
gateway_id: ed25519::PublicKey,
updated_key: &SharedSymmetricKey,
) -> Result<(), Self::StorageError> {
self.manager
.update_remote_gateway_key(
&gateway_id.to_base58_string(),
None,
Some(updated_key.as_bytes()),
)
.await?;
Ok(())
}
// ideally all of those should be run under a storage tx to ensure storage consistency,
// but at that point it's fine
async fn remove_gateway_details(&self, gateway_id: &str) -> Result<(), Self::StorageError> {
@@ -2,8 +2,10 @@
// SPDX-License-Identifier: Apache-2.0
use crate::types::{ActiveGateway, GatewayRegistration};
use crate::{BadGateway, GatewaysDetailsStore};
use crate::{BadGateway, GatewayDetails, GatewaysDetailsStore};
use async_trait::async_trait;
use nym_crypto::asymmetric::ed25519::PublicKey;
use nym_gateway_requests::{SharedGatewayKey, SharedSymmetricKey};
use std::collections::HashMap;
use std::sync::Arc;
use thiserror::Error;
@@ -34,10 +36,6 @@ struct InMemStorageInner {
impl GatewaysDetailsStore for InMemGatewaysDetails {
type StorageError = InMemStorageError;
async fn has_gateway_details(&self, gateway_id: &str) -> Result<bool, Self::StorageError> {
Ok(self.inner.read().await.gateways.contains_key(gateway_id))
}
async fn active_gateway(&self) -> Result<ActiveGateway, Self::StorageError> {
let guard = self.inner.read().await;
@@ -68,6 +66,10 @@ impl GatewaysDetailsStore for InMemGatewaysDetails {
Ok(self.inner.read().await.gateways.values().cloned().collect())
}
async fn has_gateway_details(&self, gateway_id: &str) -> Result<bool, Self::StorageError> {
Ok(self.inner.read().await.gateways.contains_key(gateway_id))
}
async fn load_gateway_details(
&self,
gateway_id: &str,
@@ -94,6 +96,29 @@ impl GatewaysDetailsStore for InMemGatewaysDetails {
Ok(())
}
async fn upgrade_stored_remote_gateway_key(
&self,
gateway_id: PublicKey,
updated_key: &SharedSymmetricKey,
) -> Result<(), Self::StorageError> {
let mut guard = self.inner.write().await;
#[allow(clippy::unwrap_used)]
if let Some(target) = guard.gateways.get_mut(&gateway_id.to_string()) {
let GatewayDetails::Remote(details) = &mut target.details else {
return Ok(());
};
assert_eq!(Arc::strong_count(&details.shared_key), 1);
// eh. that's nasty, but it's only ever used for ephemeral clients so should be fine for now...
details.shared_key = Arc::new(SharedGatewayKey::Current(
SharedSymmetricKey::try_from_bytes(updated_key.as_bytes()).unwrap(),
))
}
Ok(())
}
async fn remove_gateway_details(&self, gateway_id: &str) -> Result<(), Self::StorageError> {
let mut guard = self.inner.write().await;
if let Some(active) = guard.active_gateway.as_ref() {
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
use nym_crypto::asymmetric::identity::Ed25519RecoveryError;
use nym_gateway_requests::registration::handshake::shared_key::SharedKeyConversionError;
use nym_gateway_requests::shared_key::SharedKeyConversionError;
use thiserror::Error;
#[derive(Debug, Error)]
@@ -36,6 +36,9 @@ pub enum BadGateway {
source: SharedKeyConversionError,
},
#[error("could not find any valid shared keys for gateway {gateway_id}")]
MissingSharedKey { gateway_id: String },
#[error(
"the listening address of gateway {gateway_id} ({raw_listener}) is malformed: {source}"
)]
@@ -5,6 +5,8 @@
#![warn(clippy::unwrap_used)]
use async_trait::async_trait;
use nym_crypto::asymmetric::identity;
use nym_gateway_requests::SharedSymmetricKey;
use std::error::Error;
pub mod backend;
@@ -18,7 +20,6 @@ pub use error::BadGateway;
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-gateways-storage"))]
pub use backend::fs_backend::{error::StorageError, OnDiskGatewaysDetails};
use nym_crypto::asymmetric::identity;
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
@@ -61,6 +62,12 @@ pub trait GatewaysDetailsStore {
details: &GatewayRegistration,
) -> Result<(), Self::StorageError>;
async fn upgrade_stored_remote_gateway_key(
&self,
gateway_id: identity::PublicKey,
updated_key: &SharedSymmetricKey,
) -> Result<(), Self::StorageError>;
/// Remove given gateway details from the underlying store.
async fn remove_gateway_details(&self, gateway_id: &str) -> Result<(), Self::StorageError>;
}
@@ -4,9 +4,10 @@
use crate::BadGateway;
use cosmrs::AccountId;
use nym_crypto::asymmetric::identity;
use nym_gateway_requests::registration::handshake::SharedKeys;
use nym_gateway_requests::shared_key::{LegacySharedKeys, SharedGatewayKey, SharedSymmetricKey};
use serde::{Deserialize, Serialize};
use std::fmt::{Display, Formatter};
use std::ops::Deref;
use std::str::FromStr;
use std::sync::Arc;
use time::OffsetDateTime;
@@ -64,13 +65,13 @@ impl From<GatewayDetails> for GatewayRegistration {
impl GatewayDetails {
pub fn new_remote(
gateway_id: identity::PublicKey,
derived_aes128_ctr_blake3_hmac_keys: Arc<SharedKeys>,
shared_key: Arc<SharedGatewayKey>,
gateway_owner_address: Option<AccountId>,
gateway_listener: Url,
) -> Self {
GatewayDetails::Remote(RemoteGatewayDetails {
gateway_id,
derived_aes128_ctr_blake3_hmac_keys,
shared_key,
gateway_owner_address,
gateway_listener,
})
@@ -87,9 +88,9 @@ impl GatewayDetails {
}
}
pub fn shared_key(&self) -> Option<&SharedKeys> {
pub fn shared_key(&self) -> Option<&SharedGatewayKey> {
match self {
GatewayDetails::Remote(details) => Some(&details.derived_aes128_ctr_blake3_hmac_keys),
GatewayDetails::Remote(details) => Some(&details.shared_key),
GatewayDetails::Custom(_) => None,
}
}
@@ -167,7 +168,8 @@ pub struct RegisteredGateway {
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
pub struct RawRemoteGatewayDetails {
pub gateway_id_bs58: String,
pub derived_aes128_ctr_blake3_hmac_keys_bs58: String,
pub derived_aes128_ctr_blake3_hmac_keys_bs58: Option<String>,
pub derived_aes256_gcm_siv_key: Option<Vec<u8>>,
pub gateway_owner_address: Option<String>,
pub gateway_listener: String,
}
@@ -184,13 +186,35 @@ impl TryFrom<RawRemoteGatewayDetails> for RemoteGatewayDetails {
}
})?;
let derived_aes128_ctr_blake3_hmac_keys = Arc::new(
SharedKeys::try_from_base58_string(&value.derived_aes128_ctr_blake3_hmac_keys_bs58)
.map_err(|source| BadGateway::MalformedSharedKeys {
gateway_id: value.gateway_id_bs58.clone(),
source,
})?,
);
let shared_key =
match (
&value.derived_aes256_gcm_siv_key,
&value.derived_aes128_ctr_blake3_hmac_keys_bs58,
) {
(None, None) => {
return Err(BadGateway::MissingSharedKey {
gateway_id: value.gateway_id_bs58.clone(),
})
}
(Some(aes256gcm_siv), _) => {
let current_key =
SharedSymmetricKey::try_from_bytes(aes256gcm_siv).map_err(|source| {
BadGateway::MalformedSharedKeys {
gateway_id: value.gateway_id_bs58.clone(),
source,
}
})?;
SharedGatewayKey::Current(current_key)
}
(None, Some(aes128ctr_hmac)) => {
let legacy_key = LegacySharedKeys::try_from_base58_string(aes128ctr_hmac)
.map_err(|source| BadGateway::MalformedSharedKeys {
gateway_id: value.gateway_id_bs58.clone(),
source,
})?;
SharedGatewayKey::Legacy(legacy_key)
}
};
let gateway_owner_address = value
.gateway_owner_address
@@ -216,7 +240,7 @@ impl TryFrom<RawRemoteGatewayDetails> for RemoteGatewayDetails {
Ok(RemoteGatewayDetails {
gateway_id,
derived_aes128_ctr_blake3_hmac_keys,
shared_key: Arc::new(shared_key),
gateway_owner_address,
gateway_listener,
})
@@ -225,11 +249,16 @@ impl TryFrom<RawRemoteGatewayDetails> for RemoteGatewayDetails {
impl<'a> From<&'a RemoteGatewayDetails> for RawRemoteGatewayDetails {
fn from(value: &'a RemoteGatewayDetails) -> Self {
let (derived_aes128_ctr_blake3_hmac_keys_bs58, derived_aes256_gcm_siv_key) =
match value.shared_key.deref() {
SharedGatewayKey::Current(key) => (None, Some(key.to_bytes())),
SharedGatewayKey::Legacy(key) => (Some(key.to_base58_string()), None),
};
RawRemoteGatewayDetails {
gateway_id_bs58: value.gateway_id.to_base58_string(),
derived_aes128_ctr_blake3_hmac_keys_bs58: value
.derived_aes128_ctr_blake3_hmac_keys
.to_base58_string(),
derived_aes128_ctr_blake3_hmac_keys_bs58,
derived_aes256_gcm_siv_key,
gateway_owner_address: value.gateway_owner_address.as_ref().map(|o| o.to_string()),
gateway_listener: value.gateway_listener.to_string(),
}
@@ -240,9 +269,7 @@ impl<'a> From<&'a RemoteGatewayDetails> for RawRemoteGatewayDetails {
pub struct RemoteGatewayDetails {
pub gateway_id: identity::PublicKey,
// note: `SharedKeys` implement ZeroizeOnDrop, meaning when `RemoteGatewayDetails` is dropped,
// the keys will be zeroized
pub derived_aes128_ctr_blake3_hmac_keys: Arc<SharedKeys>,
pub shared_key: Arc<SharedGatewayKey>,
pub gateway_owner_address: Option<AccountId>,
@@ -0,0 +1,68 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::cli_helpers::{CliClient, CliClientConfig};
use std::fs;
use std::path::PathBuf;
#[cfg(feature = "cli")]
fn parse_encoded_signatures_data(raw: &str) -> bs58::decode::Result<Vec<u8>> {
bs58::decode(raw).into_vec()
}
#[cfg_attr(feature = "cli", derive(clap::Args))]
#[cfg_attr(feature = "cli",
clap(
group(clap::ArgGroup::new("sig_data").required(true)),
))
]
pub struct CommonClientImportCoinIndexSignaturesArgs {
/// Id of client that is going to import the signatures
#[cfg_attr(feature = "cli", clap(long))]
pub id: String,
/// Config file of the client that is supposed to use the signatures.
#[cfg_attr(feature = "cli", clap(long))]
pub(crate) client_config: PathBuf,
/// Explicitly provide the encoded signatures data (as base58)
#[cfg_attr(feature = "cli", clap(long, group = "sig_data", value_parser = parse_encoded_signatures_data))]
pub(crate) signatures_data: Option<Vec<u8>>,
/// Specifies the path to file containing binary signatures data
#[cfg_attr(feature = "cli", clap(long, group = "sig_data"))]
pub(crate) signatures_path: Option<PathBuf>,
// currently hidden as there exists only a single serialization standard
#[cfg_attr(feature = "cli", clap(long, hide = true))]
pub(crate) version: Option<u8>,
}
pub async fn import_coin_index_signatures<C, A>(args: A) -> Result<(), C::Error>
where
A: Into<CommonClientImportCoinIndexSignaturesArgs>,
C: CliClient,
C::Error: From<std::io::Error> + From<nym_id::NymIdError>,
{
let common_args = args.into();
let id = &common_args.id;
let config = C::try_load_current_config(id).await?;
let paths = config.common_paths();
let credentials_store =
nym_credential_storage::initialise_persistent_storage(&paths.credentials_database).await;
let version = common_args.version;
let raw_key = match common_args.signatures_data {
Some(data) => data,
None => {
// SAFETY: one of those arguments must have been set
fs::read(common_args.signatures_path.unwrap())?
}
};
nym_id::import_coin_index_signatures(credentials_store, raw_key, version).await?;
Ok(())
}
@@ -11,9 +11,14 @@ fn parse_encoded_credential_data(raw: &str) -> bs58::decode::Result<Vec<u8>> {
}
#[cfg_attr(feature = "cli", derive(clap::Args))]
#[cfg_attr(feature = "cli", clap(group(clap::ArgGroup::new("cred_data").required(true))))]
#[cfg_attr(feature = "cli",
clap(
group(clap::ArgGroup::new("cred_data").required(true)),
group(clap::ArgGroup::new("type").required(true)),
))
]
#[derive(Debug, Clone)]
pub struct CommonClientImportCredentialArgs {
pub struct CommonClientImportTicketBookArgs {
/// Id of client that is going to import the credential
#[cfg_attr(feature = "cli", clap(long))]
pub id: String,
@@ -26,6 +31,15 @@ pub struct CommonClientImportCredentialArgs {
#[cfg_attr(feature = "cli", clap(long, group = "cred_data"))]
pub(crate) credential_path: Option<PathBuf>,
/// Specifies whether we're attempting to import a standalone ticketbook (i.e. serialised `IssuedTicketBook`)
#[cfg_attr(feature = "cli", clap(long, group = "type"))]
pub(crate) standalone: bool,
/// Specifies whether we're attempting to import full ticketboot
/// (i.e. one that **might** contain required global signatures; that is serialised `ImportableTicketBook`)
#[cfg_attr(feature = "cli", clap(long, group = "type"))]
pub(crate) full: bool,
// currently hidden as there exists only a single serialization standard
#[cfg_attr(feature = "cli", clap(long, hide = true))]
pub(crate) version: Option<u8>,
@@ -33,7 +47,7 @@ pub struct CommonClientImportCredentialArgs {
pub async fn import_credential<C, A>(args: A) -> Result<(), C::Error>
where
A: Into<CommonClientImportCredentialArgs>,
A: Into<CommonClientImportTicketBookArgs>,
C: CliClient,
C::Error: From<std::io::Error> + From<nym_id::NymIdError>,
{
@@ -54,6 +68,19 @@ where
}
};
nym_id::import_credential(credentials_store, raw_credential, common_args.version).await?;
if common_args.standalone {
nym_id::import_standalone_ticketbook(
credentials_store,
raw_credential,
common_args.version,
)
.await?;
} else {
// sanity check; clap should have ensured it
assert!(common_args.full);
nym_id::import_full_ticketbook(credentials_store, raw_credential, common_args.version)
.await?;
}
Ok(())
}
@@ -0,0 +1,68 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::cli_helpers::{CliClient, CliClientConfig};
use std::fs;
use std::path::PathBuf;
#[cfg(feature = "cli")]
fn parse_encoded_signatures_data(raw: &str) -> bs58::decode::Result<Vec<u8>> {
bs58::decode(raw).into_vec()
}
#[cfg_attr(feature = "cli", derive(clap::Args))]
#[cfg_attr(feature = "cli",
clap(
group(clap::ArgGroup::new("sig_data").required(true)),
))
]
pub struct CommonClientImportExpirationDateSignaturesArgs {
/// Id of client that is going to import the signatures
#[cfg_attr(feature = "cli", clap(long))]
pub id: String,
/// Config file of the client that is supposed to use the signatures.
#[cfg_attr(feature = "cli", clap(long))]
pub(crate) client_config: PathBuf,
/// Explicitly provide the encoded signatures data (as base58)
#[cfg_attr(feature = "cli", clap(long, group = "sig_data", value_parser = parse_encoded_signatures_data))]
pub(crate) signatures_data: Option<Vec<u8>>,
/// Specifies the path to file containing binary signatures data
#[cfg_attr(feature = "cli", clap(long, group = "sig_data"))]
pub(crate) signatures_path: Option<PathBuf>,
// currently hidden as there exists only a single serialization standard
#[cfg_attr(feature = "cli", clap(long, hide = true))]
pub(crate) version: Option<u8>,
}
pub async fn import_expiration_date_signatures<C, A>(args: A) -> Result<(), C::Error>
where
A: Into<CommonClientImportExpirationDateSignaturesArgs>,
C: CliClient,
C::Error: From<std::io::Error> + From<nym_id::NymIdError>,
{
let common_args = args.into();
let id = &common_args.id;
let config = C::try_load_current_config(id).await?;
let paths = config.common_paths();
let credentials_store =
nym_credential_storage::initialise_persistent_storage(&paths.credentials_database).await;
let version = common_args.version;
let raw_key = match common_args.signatures_data {
Some(data) => data,
None => {
// SAFETY: one of those arguments must have been set
fs::read(common_args.signatures_path.unwrap())?
}
};
nym_id::import_expiration_date_signatures(credentials_store, raw_key, version).await?;
Ok(())
}
@@ -0,0 +1,68 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::cli_helpers::{CliClient, CliClientConfig};
use std::fs;
use std::path::PathBuf;
#[cfg(feature = "cli")]
fn parse_encoded_key_data(raw: &str) -> bs58::decode::Result<Vec<u8>> {
bs58::decode(raw).into_vec()
}
#[cfg_attr(feature = "cli", derive(clap::Args))]
#[cfg_attr(feature = "cli",
clap(
group(clap::ArgGroup::new("key_data_group").required(true)),
))
]
pub struct CommonClientImportMasterVerificationKeyArgs {
/// Id of client that is going to import the key
#[cfg_attr(feature = "cli", clap(long))]
pub id: String,
/// Config file of the client that is supposed to use the key.
#[cfg_attr(feature = "cli", clap(long))]
pub(crate) client_config: PathBuf,
/// Explicitly provide the encoded key data (as base58)
#[cfg_attr(feature = "cli", clap(long, group = "key_data_group", value_parser = parse_encoded_key_data))]
pub(crate) key_data: Option<Vec<u8>>,
/// Specifies the path to file containing binary key data
#[cfg_attr(feature = "cli", clap(long, group = "key_data_group"))]
pub(crate) key_path: Option<PathBuf>,
// currently hidden as there exists only a single serialization standard
#[cfg_attr(feature = "cli", clap(long, hide = true))]
pub(crate) version: Option<u8>,
}
pub async fn import_master_verification_key<C, A>(args: A) -> Result<(), C::Error>
where
A: Into<CommonClientImportMasterVerificationKeyArgs>,
C: CliClient,
C::Error: From<std::io::Error> + From<nym_id::NymIdError>,
{
let common_args = args.into();
let id = &common_args.id;
let config = C::try_load_current_config(id).await?;
let paths = config.common_paths();
let credentials_store =
nym_credential_storage::initialise_persistent_storage(&paths.credentials_database).await;
let version = common_args.version;
let raw_key = match common_args.key_data {
Some(data) => data,
None => {
// SAFETY: one of those arguments must have been set
fs::read(common_args.key_path.unwrap())?
}
};
nym_id::import_master_verification_key(credentials_store, raw_key, version).await?;
Ok(())
}
@@ -2,7 +2,10 @@
// SPDX-License-Identifier: Apache-2.0
pub mod client_add_gateway;
pub mod client_import_coin_index_signatures;
pub mod client_import_credential;
pub mod client_import_expiration_date_signatures;
pub mod client_import_master_verification_key;
pub mod client_init;
pub mod client_list_gateways;
pub mod client_run;
@@ -354,12 +354,14 @@ where
config: &Config,
initialisation_result: InitialisationResult,
bandwidth_controller: Option<BandwidthController<C, S::CredentialStore>>,
details_store: &S::GatewaysDetailsStore,
packet_router: PacketRouter,
shutdown: TaskClient,
) -> Result<GatewayClient<C, S::CredentialStore>, ClientCoreError>
where
<S::KeyStore as KeyStore>::StorageError: Send + Sync + 'static,
<S::CredentialStore as CredentialStorage>::StorageError: Send + Sync + 'static,
<S::GatewaysDetailsStore as GatewaysDetailsStore>::StorageError: Sync + Send,
{
let managed_keys = initialisation_result.client_keys;
let GatewayDetails::Remote(details) = initialisation_result.gateway_registration.details
@@ -387,23 +389,57 @@ where
),
cfg,
managed_keys.identity_keypair(),
Some(details.derived_aes128_ctr_blake3_hmac_keys),
Some(details.shared_key),
packet_router,
bandwidth_controller,
shutdown,
)
};
gateway_client
.authenticate_and_start()
let gateway_failure = |err| {
log::error!("Could not authenticate and start up the gateway connection - {err}");
ClientCoreError::GatewayClientError {
gateway_id: details.gateway_id.to_base58_string(),
source: err,
}
};
// the gateway client startup procedure is slightly more complicated now
// we need to:
// - perform handshake (reg or auth)
// - check for key upgrade
// - maybe perform another upgrade handshake
// - check for bandwidth
// - start background tasks
let auth_res = gateway_client
.perform_initial_authentication()
.await
.map_err(|err| {
log::error!("Could not authenticate and start up the gateway connection - {err}");
ClientCoreError::GatewayClientError {
gateway_id: details.gateway_id.to_base58_string(),
source: err,
}
})?;
.map_err(gateway_failure)?;
if auth_res.requires_key_upgrade {
// drop the shared_key arc because we don't need it and we can't hold it for the purposes of upgrade
drop(auth_res);
let updated_key = gateway_client
.upgrade_key_authenticated()
.await
.map_err(gateway_failure)?;
details_store
.upgrade_stored_remote_gateway_key(gateway_client.gateway_identity(), &updated_key)
.await.map_err(|err| {
error!("failed to store upgraded gateway key! this connection might be forever broken now: {err}");
ClientCoreError::GatewaysDetailsStoreError { source: Box::new(err) }
})?
}
gateway_client
.claim_initial_bandwidth()
.await
.map_err(gateway_failure)?;
gateway_client
.start_listening_for_mixnet_messages()
.map_err(gateway_failure)?;
Ok(gateway_client)
}
@@ -413,12 +449,14 @@ where
config: &Config,
initialisation_result: InitialisationResult,
bandwidth_controller: Option<BandwidthController<C, S::CredentialStore>>,
details_store: &S::GatewaysDetailsStore,
packet_router: PacketRouter,
mut shutdown: TaskClient,
) -> Result<Box<dyn GatewayTransceiver + Send>, ClientCoreError>
where
<S::KeyStore as KeyStore>::StorageError: Send + Sync + 'static,
<S::CredentialStore as CredentialStorage>::StorageError: Send + Sync + 'static,
<S::GatewaysDetailsStore as GatewaysDetailsStore>::StorageError: Sync + Send,
{
// if we have setup custom gateway sender and persisted details agree with it, return it
if let Some(mut custom_gateway_transceiver) = custom_gateway_transceiver {
@@ -429,7 +467,7 @@ where
{
Err(ClientCoreError::CustomGatewaySelectionExpected)
} else {
// and make sure to invalidate the task client so we wouldn't cause premature shutdown
// and make sure to invalidate the task client, so we wouldn't cause premature shutdown
shutdown.disarm();
custom_gateway_transceiver.set_packet_router(packet_router)?;
Ok(custom_gateway_transceiver)
@@ -441,6 +479,7 @@ where
config,
initialisation_result,
bandwidth_controller,
details_store,
packet_router,
shutdown,
)
@@ -630,7 +669,8 @@ where
)
.await?;
let (reply_storage_backend, credential_store) = self.client_store.into_runtime_stores();
let (reply_storage_backend, credential_store, details_store) =
self.client_store.into_runtime_stores();
// channels for inter-component communication
// TODO: make the channels be internally created by the relevant components
@@ -705,6 +745,7 @@ where
self.config,
init_res,
bandwidth_controller,
&details_store,
gateway_packet_router,
shutdown.fork("gateway_transceiver"),
)
@@ -13,7 +13,7 @@ pub mod v1_1_33 {
use nym_client_core_gateways_storage::{
CustomGatewayDetails, GatewayDetails, GatewayRegistration, RemoteGatewayDetails,
};
use nym_gateway_requests::registration::handshake::SharedKeys;
use nym_gateway_requests::shared_key::LegacySharedKeys;
use serde::{Deserialize, Serialize};
use sha2::{digest::Digest, Sha256};
use std::ops::Deref;
@@ -58,7 +58,7 @@ pub mod v1_1_33 {
}
impl PersistedGatewayConfig {
fn verify(&self, shared_key: &SharedKeys) -> bool {
fn verify(&self, shared_key: &LegacySharedKeys) -> bool {
let key_bytes = Zeroizing::new(shared_key.to_bytes());
let mut key_hasher = Sha256::new();
@@ -74,7 +74,7 @@ pub mod v1_1_33 {
gateway_id: String,
}
fn load_shared_key<P: AsRef<Path>>(path: P) -> Result<SharedKeys, ClientCoreError> {
fn load_shared_key<P: AsRef<Path>>(path: P) -> Result<LegacySharedKeys, ClientCoreError> {
// the shared key was a simple pem file
Ok(nym_pemstore::load_key(path)?)
}
@@ -83,7 +83,7 @@ pub mod v1_1_33 {
gateway_id: String,
gateway_owner: String,
gateway_listener: String,
gateway_shared_key: SharedKeys,
gateway_shared_key: LegacySharedKeys,
) -> Result<GatewayDetails, ClientCoreError> {
Ok(GatewayDetails::Remote(RemoteGatewayDetails {
gateway_id: gateway_id
@@ -91,7 +91,7 @@ pub mod v1_1_33 {
.map_err(|err| ClientCoreError::UpgradeFailure {
message: format!("the stored gateway id was malformed: {err}"),
})?,
derived_aes128_ctr_blake3_hmac_keys: Arc::new(gateway_shared_key),
shared_key: Arc::new(gateway_shared_key.into()),
gateway_owner_address: Some(gateway_owner.parse().map_err(|err| {
ClientCoreError::UpgradeFailure {
message: format!("the stored gateway owner address was malformed: {err}"),
@@ -49,7 +49,13 @@ pub trait MixnetClientStorage {
type CredentialStore: CredentialStorage;
type GatewaysDetailsStore: GatewaysDetailsStore;
fn into_runtime_stores(self) -> (Self::ReplyStore, Self::CredentialStore);
fn into_runtime_stores(
self,
) -> (
Self::ReplyStore,
Self::CredentialStore,
Self::GatewaysDetailsStore,
);
fn key_store(&self) -> &Self::KeyStore;
fn reply_store(&self) -> &Self::ReplyStore;
@@ -77,8 +83,18 @@ impl MixnetClientStorage for Ephemeral {
type CredentialStore = EphemeralCredentialStorage;
type GatewaysDetailsStore = InMemGatewaysDetails;
fn into_runtime_stores(self) -> (Self::ReplyStore, Self::CredentialStore) {
(self.reply_store, self.credential_store)
fn into_runtime_stores(
self,
) -> (
Self::ReplyStore,
Self::CredentialStore,
Self::GatewaysDetailsStore,
) {
(
self.reply_store,
self.credential_store,
self.gateway_details_store,
)
}
fn key_store(&self) -> &Self::KeyStore {
@@ -168,8 +184,18 @@ impl MixnetClientStorage for OnDiskPersistent {
type CredentialStore = PersistentCredentialStorage;
type GatewaysDetailsStore = OnDiskGatewaysDetails;
fn into_runtime_stores(self) -> (Self::ReplyStore, Self::CredentialStore) {
(self.reply_store, self.credential_store)
fn into_runtime_stores(
self,
) -> (
Self::ReplyStore,
Self::CredentialStore,
Self::GatewaysDetailsStore,
) {
(
self.reply_store,
self.credential_store,
self.gateway_details_store,
)
}
fn key_store(&self) -> &Self::KeyStore {
@@ -3,7 +3,7 @@
use crate::client::key_manager::persistence::KeyStore;
use nym_crypto::asymmetric::{encryption, identity};
use nym_gateway_requests::registration::handshake::SharedKeys;
use nym_gateway_requests::shared_key::{LegacySharedKeys, SharedGatewayKey, SharedSymmetricKey};
use nym_sphinx::acknowledgements::AckKey;
use rand::{CryptoRng, RngCore};
use std::sync::Arc;
@@ -84,5 +84,7 @@ fn _assert_keys_zeroize_on_drop() {
_assert_zeroize_on_drop::<identity::KeyPair>();
_assert_zeroize_on_drop::<encryption::KeyPair>();
_assert_zeroize_on_drop::<AckKey>();
_assert_zeroize_on_drop::<SharedKeys>();
_assert_zeroize_on_drop::<LegacySharedKeys>();
_assert_zeroize_on_drop::<SharedSymmetricKey>();
_assert_zeroize_on_drop::<SharedGatewayKey>();
}
@@ -102,6 +102,7 @@ impl TopologyRefresher {
.current_topology()
.await
.ok_or(NymTopologyError::EmptyNetworkTopology)?;
if !topology.gateway_exists(gateway) {
return Err(NymTopologyError::NonExistentGatewayError {
identity_key: gateway.to_base58_string(),
+5
View File
@@ -214,6 +214,11 @@ pub enum ClientCoreError {
#[error("this client has already registered with gateway {gateway_id}")]
AlreadyRegistered { gateway_id: String },
#[error(
"fresh registration with gateway {gateway_id} somehow requires an additional key upgrade!"
)]
UnexpectedKeyUpgrade { gateway_id: String },
}
/// Set of messages that the client can send to listeners via the task manager
+11 -2
View File
@@ -320,7 +320,7 @@ pub(super) async fn register_with_gateway(
source: err,
}
})?;
let shared_keys = gateway_client
let auth_response = gateway_client
.perform_initial_authentication()
.await
.map_err(|err| {
@@ -330,8 +330,17 @@ pub(super) async fn register_with_gateway(
source: err,
}
})?;
// this should NEVER happen, if it did, it means the function was misused,
// because for any fresh **registration**, the derived key is always up to date
if auth_response.requires_key_upgrade {
return Err(ClientCoreError::UnexpectedKeyUpgrade {
gateway_id: gateway_id.to_base58_string(),
});
}
Ok(RegistrationResult {
shared_keys,
shared_keys: auth_response.initial_shared_key,
authenticated_ephemeral_client: gateway_client,
})
}
+2 -2
View File
@@ -11,7 +11,7 @@ use nym_client_core_gateways_storage::{
};
use nym_crypto::asymmetric::identity;
use nym_gateway_client::client::InitGatewayClient;
use nym_gateway_requests::registration::handshake::SharedKeys;
use nym_gateway_requests::shared_key::SharedGatewayKey;
use nym_sphinx::addressing::clients::Recipient;
use nym_topology::gateway;
use nym_validator_client::client::IdentityKey;
@@ -104,7 +104,7 @@ impl SelectedGateway {
/// - shared keys derived between ourselves and the node
/// - an authenticated handle of an ephemeral handle created for the purposes of registration
pub struct RegistrationResult {
pub shared_keys: Arc<SharedKeys>,
pub shared_keys: Arc<SharedGatewayKey>,
pub authenticated_ephemeral_client: InitGatewayClient,
}
+3 -2
View File
@@ -11,13 +11,14 @@ license.workspace = true
# TODO: (for this and other crates), similarly to 'tokio', import only required "futures" modules rather than
# the entire crate
futures = { workspace = true }
log = { workspace = true }
tracing = { workspace = true }
thiserror = { workspace = true }
url = { workspace = true }
rand = { workspace = true }
tokio = { workspace = true, features = ["macros"] }
si-scale = { workspace = true }
time.workspace = true
zeroize.workspace = true
# internal
nym-bandwidth-controller = { path = "../../bandwidth-controller" }
@@ -43,7 +44,7 @@ workspace = true
features = ["macros", "rt", "net", "sync", "time"]
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-stream]
version = "0.1.11"
workspace = true
features = ["net", "sync", "time"]
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-tungstenite]
@@ -2,21 +2,37 @@
// SPDX-License-Identifier: Apache-2.0
use si_scale::helpers::bibytes2;
use std::sync::atomic::{AtomicI64, Ordering};
use std::sync::atomic::{AtomicBool, AtomicI64, Ordering};
use std::sync::Arc;
use std::time::Duration;
use time::OffsetDateTime;
#[derive(Clone, Default)]
pub(crate) struct BandwidthClaimGuard {
inner: Arc<ClientBandwidthInner>,
}
impl Drop for BandwidthClaimGuard {
fn drop(&mut self) {
let old = self.inner.claiming_more.swap(false, Ordering::SeqCst);
assert!(
old,
"critical failure: there were multiple BandwidthClaimGuard existing"
)
}
}
#[derive(Clone)]
pub struct ClientBandwidth {
inner: Arc<ClientBandwidthInner>,
}
#[derive(Default)]
struct ClientBandwidthInner {
/// the actual bandwidth amount (in bytes) available
available: AtomicI64,
/// flag to indicate whether this client is currently in the process of claiming additional bandwidth
claiming_more: AtomicBool,
/// defines the timestamp when the bandwidth information has been logged to the logs stream
last_logged_ts: AtomicI64,
@@ -29,11 +45,28 @@ impl ClientBandwidth {
ClientBandwidth {
inner: Arc::new(ClientBandwidthInner {
available: AtomicI64::new(0),
claiming_more: AtomicBool::new(false),
last_logged_ts: AtomicI64::new(0),
last_updated_ts: AtomicI64::new(0),
}),
}
}
pub(crate) fn begin_bandwidth_claim(&self) -> Option<BandwidthClaimGuard> {
if self
.inner
.claiming_more
.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst)
.is_ok()
{
Some(BandwidthClaimGuard {
inner: self.inner.clone(),
})
} else {
None
}
}
pub(crate) fn remaining(&self) -> i64 {
self.inner.available.load(Ordering::Acquire)
}
@@ -53,9 +86,9 @@ impl ClientBandwidth {
let remaining_bi2 = bibytes2(remaining as f64);
if remaining < 0 {
log::warn!("OUT OF BANDWIDTH. remaining: {remaining_bi2}");
tracing::warn!("OUT OF BANDWIDTH. remaining: {remaining_bi2}");
} else {
log::info!("remaining bandwidth: {remaining_bi2}");
tracing::info!("remaining bandwidth: {remaining_bi2}");
}
self.inner
@@ -1,5 +1,6 @@
// Copyright 2021-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::bandwidth::ClientBandwidth;
use crate::client::config::GatewayClientConfig;
use crate::error::GatewayClientError;
@@ -11,24 +12,24 @@ use crate::socket_state::{ws_fd, PartiallyDelegatedHandle, SocketState};
use crate::traits::GatewayPacketRouter;
use crate::{cleanup_socket_message, try_decrypt_binary_message};
use futures::{SinkExt, StreamExt};
use log::*;
use nym_bandwidth_controller::{BandwidthController, BandwidthStatusMessage};
use nym_credential_storage::ephemeral_storage::EphemeralStorage as EphemeralCredentialStorage;
use nym_credential_storage::storage::Storage as CredentialStorage;
use nym_credentials::CredentialSpendingData;
use nym_crypto::asymmetric::identity;
use nym_gateway_requests::authentication::encrypted_address::EncryptedAddressBytes;
use nym_gateway_requests::iv::IV;
use nym_gateway_requests::registration::handshake::{client_handshake, SharedKeys};
use nym_gateway_requests::registration::handshake::client_handshake;
use nym_gateway_requests::{
BinaryRequest, ClientControlRequest, ServerResponse, CREDENTIAL_UPDATE_V2_PROTOCOL_VERSION,
CURRENT_PROTOCOL_VERSION,
BinaryRequest, ClientControlRequest, ClientRequest, SensitiveServerResponse, ServerResponse,
SharedGatewayKey, SharedSymmetricKey, AES_GCM_SIV_PROTOCOL_VERSION,
CREDENTIAL_UPDATE_V2_PROTOCOL_VERSION, CURRENT_PROTOCOL_VERSION,
};
use nym_sphinx::forwarding::packet::MixPacket;
use nym_task::TaskClient;
use nym_validator_client::nyxd::contract_traits::DkgQueryClient;
use rand::rngs::OsRng;
use std::sync::Arc;
use tracing::instrument;
use tracing::*;
use tungstenite::protocol::Message;
use url::Url;
@@ -45,6 +46,7 @@ use std::os::raw::c_int as RawFd;
use wasm_utils::websocket::JSWebsocket;
#[cfg(target_arch = "wasm32")]
use wasmtimer::tokio::sleep;
use zeroize::Zeroizing;
pub mod config;
@@ -71,6 +73,13 @@ impl GatewayConfig {
}
}
#[must_use]
#[derive(Debug)]
pub struct AuthenticationResponse {
pub initial_shared_key: Arc<SharedGatewayKey>,
pub requires_key_upgrade: bool,
}
// TODO: this should be refactored into a state machine that keeps track of its authentication state
pub struct GatewayClient<C, St = EphemeralCredentialStorage> {
pub cfg: GatewayClientConfig,
@@ -80,7 +89,7 @@ pub struct GatewayClient<C, St = EphemeralCredentialStorage> {
gateway_address: String,
gateway_identity: identity::PublicKey,
local_identity: Arc<identity::KeyPair>,
shared_key: Option<Arc<SharedKeys>>,
shared_key: Option<Arc<SharedGatewayKey>>,
connection: SocketState,
packet_router: PacketRouter,
bandwidth_controller: Option<BandwidthController<C, St>>,
@@ -98,7 +107,7 @@ impl<C, St> GatewayClient<C, St> {
gateway_config: GatewayConfig,
local_identity: Arc<identity::KeyPair>,
// TODO: make it mandatory. if you don't want to pass it, use `new_init`
shared_key: Option<Arc<SharedKeys>>,
shared_key: Option<Arc<SharedGatewayKey>>,
packet_router: PacketRouter,
bandwidth_controller: Option<BandwidthController<C, St>>,
task_client: TaskClient,
@@ -293,7 +302,7 @@ impl<C, St> GatewayClient<C, St> {
// as we need to be able to write the request and read the subsequent response
async fn send_websocket_message(
&mut self,
msg: Message,
msg: impl Into<Message>,
) -> Result<ServerResponse, GatewayClientError> {
let should_restart_mixnet_listener = if self.connection.is_partially_delegated() {
self.recover_socket_connection().await?;
@@ -307,7 +316,7 @@ impl<C, St> GatewayClient<C, St> {
SocketState::NotConnected => return Err(GatewayClientError::ConnectionNotEstablished),
_ => return Err(GatewayClientError::ConnectionInInvalidState),
};
conn.send(msg).await?;
conn.send(msg.into()).await?;
let response = self.read_control_response().await;
if should_restart_mixnet_listener {
@@ -398,13 +407,19 @@ impl<C, St> GatewayClient<C, St> {
}
}
async fn register(&mut self) -> Result<(), GatewayClientError> {
async fn register(
&mut self,
derive_aes256_gcm_siv_key: bool,
) -> Result<(), GatewayClientError> {
if !self.connection.is_established() {
return Err(GatewayClientError::ConnectionNotEstablished);
}
debug_assert!(self.connection.is_available());
log::debug!("Registering gateway");
log::debug!(
"registering with gateway. using legacy key derivation: {}",
!derive_aes256_gcm_siv_key
);
// it's fine to instantiate it here as it's only used once (during authentication or registration)
// and putting it into the GatewayClient struct would be a hassle
@@ -417,11 +432,15 @@ impl<C, St> GatewayClient<C, St> {
self.local_identity.as_ref(),
self.gateway_identity,
self.cfg.bandwidth.require_tickets,
derive_aes256_gcm_siv_key,
#[cfg(not(target_arch = "wasm32"))]
self.task_client.clone(),
)
.await
.map_err(GatewayClientError::RegistrationFailure),
_ => unreachable!(),
_ => return Err(GatewayClientError::ConnectionInInvalidState),
}?;
let (authentication_status, gateway_protocol) = match self.read_control_response().await? {
ServerResponse::Register {
protocol_version,
@@ -430,7 +449,7 @@ impl<C, St> GatewayClient<C, St> {
ServerResponse::Error { message } => {
return Err(GatewayClientError::GatewayError(message))
}
_ => return Err(GatewayClientError::UnexpectedResponse),
other => return Err(GatewayClientError::UnexpectedResponse { name: other.name() }),
};
self.check_gateway_protocol(gateway_protocol)?;
@@ -446,41 +465,93 @@ impl<C, St> GatewayClient<C, St> {
Ok(())
}
async fn authenticate(
pub async fn upgrade_key_authenticated(
&mut self,
shared_key: Option<SharedKeys>,
) -> Result<(), GatewayClientError> {
if shared_key.is_none() && self.shared_key.is_none() {
return Err(GatewayClientError::NoSharedKeyAvailable);
}
) -> Result<Zeroizing<SharedSymmetricKey>, GatewayClientError> {
info!("*** STARTING AES128CTR-HMAC KEY UPGRADE INTO AES256GCM-SIV***");
if !self.connection.is_established() {
return Err(GatewayClientError::ConnectionNotEstablished);
}
log::debug!("Authenticating with gateway");
// it's fine to instantiate it here as it's only used once (during authentication or registration)
// and putting it into the GatewayClient struct would be a hassle
let mut rng = OsRng;
if !self.authenticated {
return Err(GatewayClientError::NotAuthenticated);
}
let Some(shared_key) = self.shared_key.as_ref() else {
return Err(GatewayClientError::NoSharedKeyAvailable);
};
if !shared_key.is_legacy() {
return Err(GatewayClientError::KeyAlreadyUpgraded);
}
// make sure we have the only reference, so we could safely swap it
if Arc::strong_count(shared_key) != 1 {
return Err(GatewayClientError::KeyAlreadyInUse);
}
assert!(shared_key.is_legacy());
let legacy_key = shared_key.unwrap_legacy();
let (updated_key, hkdf_salt) = legacy_key.upgrade();
let derived_key_digest = updated_key.digest();
let upgrade_request = ClientRequest::UpgradeKey {
hkdf_salt,
derived_key_digest,
}
.encrypt(legacy_key)?;
info!("sending upgrade request and awaiting the acknowledgement back");
let (ciphertext, nonce) = match self.send_websocket_message(upgrade_request).await? {
ServerResponse::EncryptedResponse { ciphertext, nonce } => (ciphertext, nonce),
ServerResponse::Error { message } => {
return Err(GatewayClientError::GatewayError(message))
}
other => return Err(GatewayClientError::UnexpectedResponse { name: other.name() }),
};
// attempt to decrypt it using NEW key
let Ok(response) = SensitiveServerResponse::decrypt(&ciphertext, &nonce, &updated_key)
else {
return Err(GatewayClientError::FatalKeyUpgradeFailure);
};
match response {
SensitiveServerResponse::KeyUpgradeAck { .. } => {
info!("received key upgrade acknowledgement")
}
_ => return Err(GatewayClientError::FatalKeyUpgradeFailure),
}
// perform in memory swap and make a copy for updating storage
let zeroizing_updated_key = updated_key.zeroizing_clone();
self.shared_key = Some(Arc::new(updated_key.into()));
Ok(zeroizing_updated_key)
}
async fn authenticate(&mut self) -> Result<(), GatewayClientError> {
let Some(shared_key) = self.shared_key.as_ref() else {
return Err(GatewayClientError::NoSharedKeyAvailable);
};
if !self.connection.is_established() {
return Err(GatewayClientError::ConnectionNotEstablished);
}
debug!("authenticating with gateway");
// because of the previous check one of the unwraps MUST succeed
let shared_key = shared_key
.as_ref()
.unwrap_or_else(|| self.shared_key.as_ref().unwrap());
let iv = IV::new_random(&mut rng);
let self_address = self
.local_identity
.as_ref()
.public_key()
.derive_destination_address();
let encrypted_address = EncryptedAddressBytes::new(&self_address, shared_key, &iv);
let msg = ClientControlRequest::new_authenticate(
self_address,
encrypted_address,
iv,
shared_key,
self.cfg.bandwidth.require_tickets,
)
.into();
)?;
match self.send_websocket_message(msg).await? {
ServerResponse::Authenticate {
@@ -494,39 +565,101 @@ impl<C, St> GatewayClient<C, St> {
self.negotiated_protocol = protocol_version;
log::debug!("authenticated: {status}, bandwidth remaining: {bandwidth_remaining}");
self.task_client.send_status_msg(Box::new(
BandwidthStatusMessage::RemainingBandwidth(bandwidth_remaining),
));
Ok(())
}
ServerResponse::Error { message } => Err(GatewayClientError::GatewayError(message)),
_ => Err(GatewayClientError::UnexpectedResponse),
other => Err(GatewayClientError::UnexpectedResponse { name: other.name() }),
}
}
/// Helper method to either call register or authenticate based on self.shared_key value
#[instrument(skip_all,
fields(
gateway = %self.gateway_identity,
gateway_address = %self.gateway_address
)
)]
pub async fn perform_initial_authentication(
&mut self,
) -> Result<Arc<SharedKeys>, GatewayClientError> {
) -> Result<AuthenticationResponse, GatewayClientError> {
if !self.connection.is_established() {
self.establish_connection().await?;
}
// 1. check gateway's protocol version
let supports_aes_gcm_siv = match self.get_gateway_protocol().await {
Ok(protocol) => protocol >= AES_GCM_SIV_PROTOCOL_VERSION,
Err(_) => {
// if we failed to send the request, it means the gateway is running the old binary,
// so it has reset our connection - we have to reconnect
self.establish_connection().await?;
false
}
};
if !supports_aes_gcm_siv {
warn!("this gateway is on an old version that doesn't support AES256-GCM-SIV");
}
if self.authenticated {
debug!("Already authenticated");
return if let Some(shared_key) = &self.shared_key {
Ok(Arc::clone(shared_key))
Ok(AuthenticationResponse {
initial_shared_key: Arc::clone(shared_key),
requires_key_upgrade: shared_key.is_legacy() && supports_aes_gcm_siv,
})
} else {
Err(GatewayClientError::AuthenticationFailureWithPreexistingSharedKey)
};
}
if self.shared_key.is_some() {
self.authenticate(None).await?;
self.authenticate().await?;
if self.authenticated {
// if we are authenticated it means we MUST have an associated shared_key
let shared_key = self.shared_key.as_ref().unwrap();
let requires_key_upgrade = shared_key.is_legacy() && supports_aes_gcm_siv;
Ok(AuthenticationResponse {
initial_shared_key: Arc::clone(shared_key),
requires_key_upgrade,
})
} else {
Err(GatewayClientError::AuthenticationFailure)
}
} else {
self.register().await?;
self.register(supports_aes_gcm_siv).await?;
// if registration didn't return an error, we MUST have an associated shared key
let shared_key = self.shared_key.as_ref().unwrap();
// we're always registering with the highest supported protocol,
// so no upgrades are required
Ok(AuthenticationResponse {
initial_shared_key: Arc::clone(shared_key),
requires_key_upgrade: false,
})
}
if self.authenticated {
// if we are authenticated it means we MUST have an associated shared_key
Ok(Arc::clone(self.shared_key.as_ref().unwrap()))
} else {
Err(GatewayClientError::AuthenticationFailure)
}
pub async fn get_gateway_protocol(&mut self) -> Result<u8, GatewayClientError> {
if !self.connection.is_established() {
return Err(GatewayClientError::ConnectionNotEstablished);
}
match self
.send_websocket_message(ClientControlRequest::SupportedProtocol {})
.await?
{
ServerResponse::SupportedProtocol { version } => Ok(version),
ServerResponse::Error { message } => Err(GatewayClientError::GatewayError(message)),
other => Err(GatewayClientError::UnexpectedResponse { name: other.name() }),
}
}
@@ -534,22 +667,17 @@ impl<C, St> GatewayClient<C, St> {
&mut self,
credential: CredentialSpendingData,
) -> Result<(), GatewayClientError> {
let mut rng = OsRng;
let iv = IV::new_random(&mut rng);
let msg = ClientControlRequest::new_enc_ecash_credential(
credential,
self.shared_key.as_ref().unwrap(),
iv,
)
.into();
)?;
let bandwidth_remaining = match self.send_websocket_message(msg).await? {
ServerResponse::Bandwidth { available_total } => Ok(available_total),
ServerResponse::Error { message } => Err(GatewayClientError::GatewayError(message)),
ServerResponse::TypedError { error } => {
Err(GatewayClientError::TypedGatewayError(error))
}
_ => Err(GatewayClientError::UnexpectedResponse),
other => Err(GatewayClientError::UnexpectedResponse { name: other.name() }),
}?;
// TODO: create tracing span
@@ -560,11 +688,11 @@ impl<C, St> GatewayClient<C, St> {
}
async fn try_claim_testnet_bandwidth(&mut self) -> Result<(), GatewayClientError> {
let msg = ClientControlRequest::ClaimFreeTestnetBandwidth.into();
let msg = ClientControlRequest::ClaimFreeTestnetBandwidth;
let bandwidth_remaining = match self.send_websocket_message(msg).await? {
ServerResponse::Bandwidth { available_total } => Ok(available_total),
ServerResponse::Error { message } => Err(GatewayClientError::GatewayError(message)),
_ => Err(GatewayClientError::UnexpectedResponse),
other => Err(GatewayClientError::UnexpectedResponse { name: other.name() }),
}?;
info!("managed to claim testnet bandwidth");
@@ -596,6 +724,11 @@ impl<C, St> GatewayClient<C, St> {
return Err(GatewayClientError::NoBandwidthControllerAvailable);
}
let Some(_claim_guard) = self.bandwidth.begin_bandwidth_claim() else {
debug!("there's already an existing bandwidth claim ongoing");
return Ok(());
};
warn!("Not enough bandwidth. Trying to get more bandwidth, this might take a while");
if !self.cfg.bandwidth.require_tickets {
info!("The client is running in disabled credentials mode - attempting to claim bandwidth without a credential");
@@ -663,10 +796,10 @@ impl<C, St> GatewayClient<C, St> {
return Err(GatewayClientError::ConnectionNotEstablished);
}
let messages: Vec<_> = packets
let messages: Result<Vec<_>, _> = packets
.into_iter()
.map(|mix_packet| {
BinaryRequest::new_forward_request(mix_packet).into_ws_message(
BinaryRequest::ForwardSphinx { packet: mix_packet }.into_ws_message(
self.shared_key
.as_ref()
.expect("no shared key present even though we're authenticated!"),
@@ -675,7 +808,7 @@ impl<C, St> GatewayClient<C, St> {
.collect();
if let Err(err) = self
.batch_send_websocket_messages_without_response(messages)
.batch_send_websocket_messages_without_response(messages?)
.await
{
if err.is_closed_connection() && self.cfg.connection.should_reconnect_on_failure {
@@ -739,11 +872,11 @@ impl<C, St> GatewayClient<C, St> {
}
// note: into_ws_message encrypts the requests and adds a MAC on it. Perhaps it should
// be more explicit in the naming?
let msg = BinaryRequest::new_forward_request(mix_packet).into_ws_message(
let msg = BinaryRequest::ForwardSphinx { packet: mix_packet }.into_ws_message(
self.shared_key
.as_ref()
.expect("no shared key present even though we're authenticated!"),
);
)?;
self.send_with_reconnection_on_failure(msg).await
}
@@ -803,8 +936,8 @@ impl<C, St> GatewayClient<C, St> {
self.establish_connection().await?;
}
// TODO: the name of this method is very deceiving
self.perform_initial_authentication().await?;
// if we're reconnecting, because we lost connection, we need to re-authenticate the connection
self.authenticate().await?;
// this call is NON-blocking
self.start_listening_for_mixnet_messages()?;
@@ -818,16 +951,16 @@ impl<C, St> GatewayClient<C, St> {
Ok(())
}
pub async fn authenticate_and_start(&mut self) -> Result<Arc<SharedKeys>, GatewayClientError>
pub async fn claim_initial_bandwidth(&mut self) -> Result<(), GatewayClientError>
where
C: DkgQueryClient + Send + Sync,
St: CredentialStorage,
<St as CredentialStorage>::StorageError: Send + Sync + 'static,
{
if !self.connection.is_established() {
self.establish_connection().await?;
if !self.authenticated {
return Err(GatewayClientError::NotAuthenticated);
}
let shared_key = self.perform_initial_authentication().await?;
let bandwidth_remaining = self.bandwidth.remaining();
if bandwidth_remaining < self.cfg.bandwidth.remaining_bandwidth_threshold {
self.cfg
@@ -836,6 +969,20 @@ impl<C, St> GatewayClient<C, St> {
info!("Claiming more bandwidth with existing credentials. Stop the process now if you don't want that to happen.");
self.claim_bandwidth().await?;
}
Ok(())
}
#[deprecated(note = "this method does not deal with upgraded keys for legacy clients")]
pub async fn authenticate_and_start(
&mut self,
) -> Result<AuthenticationResponse, GatewayClientError>
where
C: DkgQueryClient + Send + Sync,
St: CredentialStorage,
<St as CredentialStorage>::StorageError: Send + Sync + 'static,
{
let shared_key = self.perform_initial_authentication().await?;
self.claim_initial_bandwidth().await?;
// this call is NON-blocking
self.start_listening_for_mixnet_messages()?;
+15 -3
View File
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
use nym_gateway_requests::registration::handshake::error::HandshakeError;
use nym_gateway_requests::SimpleGatewayRequestsError;
use nym_gateway_requests::{GatewayRequestsError, SimpleGatewayRequestsError};
use std::io;
use thiserror::Error;
use tungstenite::Error as WsError;
@@ -21,9 +21,21 @@ pub enum GatewayClientError {
#[error("gateway returned an error response: {0}")]
TypedGatewayError(SimpleGatewayRequestsError),
#[error("request error: {0}")]
RequestError(#[from] GatewayRequestsError),
#[error("There was a network error: {0}")]
NetworkError(#[from] WsError),
#[error("failed to upgrade our shared key - the gateway sent malformed response")]
FatalKeyUpgradeFailure,
#[error("the current key is already up to date! there's no need to upgrade it")]
KeyAlreadyUpgraded,
#[error("can't perform key upgrade as the key is already being used elsewhere")]
KeyAlreadyInUse,
#[cfg(target_arch = "wasm32")]
#[error("There was a network error: {0}")]
NetworkErrorWasm(#[from] JsError),
@@ -73,8 +85,8 @@ pub enum GatewayClientError {
cutoff_bi2: String,
},
#[error("Received an unexpected response")]
UnexpectedResponse,
#[error("received an unexpected response of type {name}")]
UnexpectedResponse { name: String },
#[error("Connection is in an invalid state - please send a bug report")]
ConnectionInInvalidState,
+10 -4
View File
@@ -2,12 +2,14 @@
// SPDX-License-Identifier: Apache-2.0
use crate::error::GatewayClientError;
use log::warn;
use nym_gateway_requests::BinaryResponse;
use tracing::{error, warn};
use tungstenite::{protocol::Message, Error as WsError};
pub use client::{config::GatewayClientConfig, GatewayClient, GatewayConfig};
pub use nym_gateway_requests::registration::handshake::SharedKeys;
pub use nym_gateway_requests::shared_key::{
LegacySharedKeys, SharedGatewayKey, SharedSymmetricKey,
};
pub use packet_router::{
AcknowledgementReceiver, AcknowledgementSender, MixnetMessageReceiver, MixnetMessageSender,
PacketRouter,
@@ -45,11 +47,15 @@ pub(crate) fn cleanup_socket_messages(
pub(crate) fn try_decrypt_binary_message(
bin_msg: Vec<u8>,
shared_keys: &SharedKeys,
shared_keys: &SharedGatewayKey,
) -> Option<Vec<u8>> {
match BinaryResponse::try_from_encrypted_tagged_bytes(bin_msg, shared_keys) {
Ok(bin_response) => match bin_response {
BinaryResponse::PushedMixMessage(plaintext) => Some(plaintext),
BinaryResponse::PushedMixMessage { message } => Some(message),
_ => {
error!("received unhandled binary response");
None
}
},
Err(err) => {
warn!("message received from the gateway was malformed! - {err}",);
@@ -44,7 +44,7 @@ impl PacketRouter {
// having already been dropped
if self.shutdown.is_shutdown_poll() || self.shutdown.is_dummy() {
// This should ideally not happen, but it's ok
log::warn!("Failed to send mixnet messages due to receiver task shutdown");
tracing::warn!("Failed to send mixnet messages due to receiver task shutdown");
return Err(GatewayClientError::ShutdownInProgress);
}
// This should never happen during ordinary operation the way it's currently used.
@@ -60,7 +60,7 @@ impl PacketRouter {
// having already been dropped
if self.shutdown.is_shutdown_poll() || self.shutdown.is_dummy() {
// This should ideally not happen, but it's ok
log::warn!("Failed to send acks due to receiver task shutdown");
tracing::warn!("Failed to send acks due to receiver task shutdown");
return Err(GatewayClientError::ShutdownInProgress);
}
// This should never happen during ordinary operation the way it's currently used.
@@ -9,18 +9,17 @@ use crate::{cleanup_socket_messages, try_decrypt_binary_message};
use futures::channel::oneshot;
use futures::stream::{SplitSink, SplitStream};
use futures::{SinkExt, StreamExt};
use log::*;
use nym_gateway_requests::registration::handshake::SharedKeys;
use nym_gateway_requests::shared_key::SharedGatewayKey;
use nym_gateway_requests::{ServerResponse, SimpleGatewayRequestsError};
use nym_task::TaskClient;
use si_scale::helpers::bibytes2;
use std::os::raw::c_int as RawFd;
use std::sync::Arc;
use tracing::*;
use tungstenite::{protocol::Message, Error as WsError};
use si_scale::helpers::bibytes2;
#[cfg(unix)]
use std::os::fd::AsRawFd;
use std::time::Duration;
#[cfg(not(target_arch = "wasm32"))]
use tokio::net::TcpStream;
#[cfg(not(target_arch = "wasm32"))]
@@ -63,7 +62,7 @@ pub(crate) struct PartiallyDelegatedHandle {
struct PartiallyDelegatedRouter {
packet_router: PacketRouter,
shared_key: Arc<SharedKeys>,
shared_key: Arc<SharedGatewayKey>,
client_bandwidth: ClientBandwidth,
stream_return: SplitStreamSender,
@@ -73,7 +72,7 @@ struct PartiallyDelegatedRouter {
impl PartiallyDelegatedRouter {
fn new(
packet_router: PacketRouter,
shared_key: Arc<SharedKeys>,
shared_key: Arc<SharedGatewayKey>,
client_bandwidth: ClientBandwidth,
stream_return: SplitStreamSender,
stream_return_requester: oneshot::Receiver<()>,
@@ -248,7 +247,7 @@ impl PartiallyDelegatedHandle {
pub(crate) fn split_and_listen_for_mixnet_messages(
conn: WsConn,
packet_router: PacketRouter,
shared_key: Arc<SharedKeys>,
shared_key: Arc<SharedGatewayKey>,
client_bandwidth: ClientBandwidth,
shutdown: TaskClient,
) -> Self {
@@ -286,20 +285,7 @@ impl PartiallyDelegatedHandle {
&mut self,
msg: Message,
) -> Result<(), GatewayClientError> {
log::info!("JON: PartiallyDelegated::send_without_response - sending a message");
// let r = self.sink_half.send(msg).await;
// Ok(r?)
let r = tokio::time::timeout(Duration::from_secs(3), self.sink_half.send(msg)).await;
let rr = match r {
Ok(rr) => Ok(rr?),
Err(_) => {
log::error!("JON: PartiallyDelegated::send_without_response - timeout sending a message");
Err(GatewayClientError::Timeout)
}
};
log::info!("JON: PartiallyDelegated::send_without_response - sent a message: {rr:?}");
rr
Ok(self.sink_half.send(msg).await?)
}
pub(crate) async fn batch_send_without_response(
@@ -308,9 +294,7 @@ impl PartiallyDelegatedHandle {
) -> Result<(), GatewayClientError> {
let stream_messages: Vec<_> = messages.into_iter().map(Ok).collect();
let mut send_stream = futures::stream::iter(stream_messages);
let r = Ok(self.sink_half.send_all(&mut send_stream).await?);
log::info!("JON: PartiallyDelegated::batch_send_without_response - sent messages");
r
Ok(self.sink_half.send_all(&mut send_stream).await?)
}
pub(crate) async fn merge(self) -> Result<WsConn, GatewayClientError> {
@@ -1,9 +1,9 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use log::{error, trace, warn};
use nym_sphinx::addressing::nodes::MAX_NODE_ADDRESS_UNPADDED_LEN;
use nym_sphinx::params::PacketSize;
use tracing::{error, trace, warn};
pub trait GatewayPacketRouter {
type Error: std::error::Error;
@@ -20,11 +20,13 @@ nym-coconut-bandwidth-contract-common = { path = "../../cosmwasm-smart-contracts
nym-ecash-contract-common = { path = "../../cosmwasm-smart-contracts/ecash-contract" }
nym-multisig-contract-common = { path = "../../cosmwasm-smart-contracts/multisig-contract" }
nym-group-contract-common = { path = "../../cosmwasm-smart-contracts/group-contract" }
nym-serde-helpers = { path = "../../serde-helpers", features = ["hex", "base64"] }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
nym-http-api-client = { path = "../../../common/http-api-client" }
thiserror = { workspace = true }
log = { workspace = true }
tracing = { workspace = true }
url = { workspace = true, features = ["serde"] }
tokio = { workspace = true, features = ["sync", "time"] }
time = { workspace = true, features = ["formatting"] }
@@ -42,6 +42,10 @@ pub trait MixnetQueryClient {
// state/sys-params-related
async fn admin(&self) -> Result<cw_controllers::AdminResponse, NyxdError> {
self.query_mixnet_contract(MixnetQueryMsg::Admin {}).await
}
async fn get_mixnet_contract_version(&self) -> Result<ContractBuildInformation, NyxdError> {
self.query_mixnet_contract(MixnetQueryMsg::GetContractVersion {})
.await
@@ -580,6 +584,7 @@ mod tests {
msg: MixnetQueryMsg,
) -> u32 {
match msg {
MixnetQueryMsg::Admin {} => client.admin().ignore(),
MixnetQueryMsg::GetAllFamiliesPaged { limit, start_after } => client
.get_all_family_members_paged(start_after, limit)
.ignore(),
@@ -31,6 +31,15 @@ pub trait MixnetSigningClient {
// state/sys-params-related
async fn update_admin(
&self,
admin: String,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_mixnet_contract(fee, MixnetExecuteMsg::UpdateAdmin { admin }, vec![])
.await
}
async fn update_rewarding_validator_address(
&self,
address: AccountId,
@@ -760,6 +769,7 @@ mod tests {
msg: MixnetExecuteMsg,
) {
match msg {
MixnetExecuteMsg::UpdateAdmin { admin } => client.update_admin(admin, None).ignore(),
MixnetExecuteMsg::AssignNodeLayer { mix_id, layer } => {
client.assign_node_layer(mix_id, layer, None).ignore()
}
@@ -5,7 +5,7 @@ use crate::nyxd;
use crate::nyxd::coin::Coin;
use crate::nyxd::cosmwasm_client::helpers::{create_pagination, next_page_key};
use crate::nyxd::cosmwasm_client::types::{
Account, CodeDetails, Contract, ContractCodeId, SequenceResponse, SimulateResponse,
Account, CodeDetails, Contract, ContractCodeId, Model, SequenceResponse, SimulateResponse,
};
use crate::nyxd::error::NyxdError;
use crate::nyxd::Query;
@@ -21,15 +21,14 @@ use cosmrs::proto::cosmos::tx::v1beta1::{
SimulateRequest, SimulateResponse as ProtoSimulateResponse,
};
use cosmrs::proto::cosmwasm::wasm::v1::{
QueryCodeRequest, QueryCodeResponse, QueryCodesRequest, QueryCodesResponse,
QueryContractHistoryRequest, QueryContractHistoryResponse, QueryContractInfoRequest,
QueryContractInfoResponse, QueryContractsByCodeRequest, QueryContractsByCodeResponse,
QueryRawContractStateRequest, QueryRawContractStateResponse, QuerySmartContractStateRequest,
QuerySmartContractStateResponse,
QueryAllContractStateRequest, QueryAllContractStateResponse, QueryCodeRequest,
QueryCodeResponse, QueryCodesRequest, QueryCodesResponse, QueryContractHistoryRequest,
QueryContractHistoryResponse, QueryContractInfoRequest, QueryContractInfoResponse,
QueryContractsByCodeRequest, QueryContractsByCodeResponse, QueryRawContractStateRequest,
QueryRawContractStateResponse, QuerySmartContractStateRequest, QuerySmartContractStateResponse,
};
use cosmrs::tendermint::{block, chain, Hash};
use cosmrs::{AccountId, Coin as CosmosCoin, Tx};
use log::trace;
use prost::Message;
use serde::{Deserialize, Serialize};
@@ -68,7 +67,7 @@ pub trait CosmWasmClient: TendermintRpcClient {
Res: Message + Default,
{
if let Some(ref abci_path) = path {
trace!("performing query on abci path {abci_path}")
tracing::trace!("performing query on abci path {abci_path}")
}
let mut buf = Vec::with_capacity(req.encoded_len());
req.encode(&mut buf)?;
@@ -154,13 +153,20 @@ pub trait CosmWasmClient: TendermintRpcClient {
let req = QueryAllBalancesRequest {
address: address.to_string(),
pagination,
resolve_denom: false,
};
let mut res = self
.make_abci_query::<_, QueryAllBalancesResponse>(path.clone(), req)
.await?;
let early_break = res.balances.is_empty();
raw_balances.append(&mut res.balances);
if early_break {
break;
}
if let Some(next_key) = next_page_key(res.pagination) {
pagination = Some(create_pagination(next_key))
} else {
@@ -188,7 +194,13 @@ pub trait CosmWasmClient: TendermintRpcClient {
.make_abci_query::<_, QueryTotalSupplyResponse>(path.clone(), req)
.await?;
let early_break = res.supply.is_empty();
supply.append(&mut res.supply);
if early_break {
break;
}
if let Some(next_key) = next_page_key(res.pagination) {
pagination = Some(create_pagination(next_key))
} else {
@@ -218,17 +230,19 @@ pub trait CosmWasmClient: TendermintRpcClient {
loop {
let mut res = self
.tx_search(query.clone(), false, page, 100, Order::Ascending)
.tx_search(query.clone(), false, page, per_page, Order::Ascending)
.await?;
results.append(&mut res.txs);
// sanity check for if tendermint's maximum per_page was modified -
// we don't want to accidentally be stuck in an infinite loop
if res.total_count == 0 || res.txs.is_empty() {
let early_break = res.total_count == 0 || res.txs.is_empty();
results.append(&mut res.txs);
if early_break {
break;
}
if res.total_count >= per_page {
if res.total_count > results.len() as u32 {
page += 1
} else {
break;
@@ -295,7 +309,7 @@ pub trait CosmWasmClient: TendermintRpcClient {
let start = Instant::now();
loop {
log::debug!(
tracing::debug!(
"Polling for result of including {} in a block...",
broadcasted.hash
);
@@ -327,7 +341,13 @@ pub trait CosmWasmClient: TendermintRpcClient {
.make_abci_query::<_, QueryCodesResponse>(path.clone(), req)
.await?;
let early_break = res.code_infos.is_empty();
raw_codes.append(&mut res.code_infos);
if early_break {
break;
}
if let Some(next_key) = next_page_key(res.pagination) {
pagination = Some(create_pagination(next_key))
} else {
@@ -372,7 +392,13 @@ pub trait CosmWasmClient: TendermintRpcClient {
.make_abci_query::<_, QueryContractsByCodeResponse>(path.clone(), req)
.await?;
let early_break = res.contracts.is_empty();
raw_contracts.append(&mut res.contracts);
if early_break {
break;
}
if let Some(next_key) = next_page_key(res.pagination) {
pagination = Some(create_pagination(next_key))
} else {
@@ -428,7 +454,13 @@ pub trait CosmWasmClient: TendermintRpcClient {
.make_abci_query::<_, QueryContractHistoryResponse>(path.clone(), req)
.await?;
let early_break = res.entries.is_empty();
raw_entries.append(&mut res.entries);
if early_break {
break;
}
if let Some(next_key) = next_page_key(res.pagination) {
pagination = Some(create_pagination(next_key))
} else {
@@ -442,6 +474,38 @@ pub trait CosmWasmClient: TendermintRpcClient {
.collect::<Result<_, _>>()?)
}
async fn query_all_contract_state(&self, address: &AccountId) -> Result<Vec<Model>, NyxdError> {
let path = Some("/cosmwasm.wasm.v1.Query/AllContractState".to_owned());
let mut models = Vec::new();
let mut pagination = None;
loop {
let req = QueryAllContractStateRequest {
address: address.to_string(),
pagination,
};
let mut res = self
.make_abci_query::<_, QueryAllContractStateResponse>(path.clone(), req)
.await?;
let empty_response = res.models.is_empty();
models.append(&mut res.models);
if empty_response {
break;
}
if let Some(next_key) = next_page_key(res.pagination) {
pagination = Some(create_pagination(next_key))
} else {
break;
}
}
Ok(models.into_iter().map(Into::into).collect())
}
async fn query_contract_raw(
&self,
address: &AccountId,
@@ -488,7 +552,7 @@ pub trait CosmWasmClient: TendermintRpcClient {
.make_abci_query::<_, QuerySmartContractStateResponse>(path, req)
.await?;
trace!("raw query response: {}", String::from_utf8_lossy(&res.data));
tracing::trace!("raw query response: {}", String::from_utf8_lossy(&res.data));
Ok(serde_json::from_slice(&res.data)?)
}
@@ -1,6 +1,7 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::nyxd::cosmwasm_client::types::ExecuteResult;
use crate::nyxd::error::NyxdError;
use base64::Engine;
use cosmrs::abci::TxMsgData;
@@ -10,7 +11,6 @@ use log::error;
use prost::bytes::Bytes;
use tendermint_rpc::endpoint::broadcast;
use crate::nyxd::cosmwasm_client::types::ExecuteResult;
pub use cosmrs::abci::MsgResponse;
pub fn parse_msg_responses(data: Bytes) -> Vec<MsgResponse> {
@@ -21,7 +21,8 @@ pub struct Log {
/// Searches in logs for the first event of the given event type and in that event
/// for the first attribute with the given attribute key.
pub fn find_attribute<'a>(
#[deprecated]
pub fn find_attribute_in_logs<'a>(
logs: &'a [Log],
event_type: &str,
attribute_key: &str,
@@ -35,6 +36,7 @@ pub fn find_attribute<'a>(
}
/// Search for the proposal id in the given log. It'll be in the LAST wasm event, with attribute key "proposal_id"
#[deprecated]
pub fn find_proposal_id(logs: &[Log]) -> Result<u64, NyxdError> {
let maybe_attributes = logs
.iter()
@@ -27,13 +27,34 @@ use cosmrs::vesting::{
};
use cosmrs::{AccountId, Any, Coin as CosmosCoin};
use prost::Message;
use serde::Serialize;
use serde::{Deserialize, Serialize};
pub use cosmrs::abci::GasInfo;
pub use cosmrs::abci::MsgResponse;
pub type ContractCodeId = u64;
// yet another thing to put in cosmrs
#[derive(Serialize, Deserialize)]
pub struct Model {
#[serde(with = "nym_serde_helpers::hex")]
pub key: Vec<u8>,
#[serde(with = "nym_serde_helpers::base64")]
pub value: Vec<u8>,
}
// follow the cosmwasm serialisation format, i.e. hex for key and base64 for value
impl From<cosmrs::proto::cosmwasm::wasm::v1::Model> for Model {
fn from(model: cosmrs::proto::cosmwasm::wasm::v1::Model) -> Self {
Model {
key: model.key,
value: model.value,
}
}
}
#[derive(Serialize)]
pub struct EmptyMsg {}
@@ -1,12 +1,24 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::nyxd::cosmwasm_client::logs::Log;
use crate::nyxd::TxResponse;
use cosmrs::tendermint::abci;
pub use abci::Event;
// Searches in events for an event of the given event type which contains an
// attribute for with the given key.
pub fn find_tx_attribute(tx: &TxResponse, event_type: &str, attribute_key: &str) -> Option<String> {
let event = tx.tx_result.events.iter().find(|e| e.kind == event_type)?;
find_event_attribute(&tx.tx_result.events, event_type, attribute_key)
}
pub fn find_event_attribute(
events: &[Event],
event_type: &str,
attribute_key: &str,
) -> Option<String> {
let event = events.iter().find(|e| e.kind == event_type)?;
let attribute = event.attributes.iter().find(|&attr| {
if let Ok(key_str) = attr.key_str() {
key_str == attribute_key
@@ -16,3 +28,23 @@ pub fn find_tx_attribute(tx: &TxResponse, event_type: &str, attribute_key: &str)
})?;
Some(attribute.value_str().ok().map(|str| str.to_string())).flatten()
}
pub fn find_attribute_value_in_logs_or_events(
logs: &[Log],
events: &[Event],
event_type: &str,
attribute_key: &str,
) -> Option<String> {
// if logs are empty, i.e. we're using post 0.50 code, parse the events instead
if !logs.is_empty() {
#[allow(deprecated)]
return crate::nyxd::cosmwasm_client::logs::find_attribute_in_logs(
logs,
event_type,
attribute_key,
)
.map(|attr| attr.value.clone());
}
find_event_attribute(events, event_type, attribute_key)
}
@@ -4,9 +4,11 @@
use crate::rpc::TendermintRpcClient;
use async_trait::async_trait;
use base64::Engine;
use cosmrs::tendermint;
use cosmrs::tendermint::{block::Height, evidence::Evidence, Hash};
use reqwest::header::HeaderMap;
use reqwest::{header, RequestBuilder};
use tendermint_rpc::dialect::{v0_34, v0_37, v0_38, LatestDialect};
use tendermint_rpc::{
client::CompatMode,
dialect::{self, Dialect},
@@ -21,8 +23,21 @@ macro_rules! perform_with_compat {
($self:expr, $request:expr) => {{
let request = $request;
match $self.compat {
CompatMode::V0_37 => $self.perform_v0_37(request).await,
CompatMode::V0_34 => $self.perform_v0_34(request).await,
CompatMode::V0_38 => {
$self
.perform_request_with_dialect(request, dialect::v0_38::Dialect)
.await
}
CompatMode::V0_37 => {
$self
.perform_request_with_dialect(request, dialect::v0_37::Dialect)
.await
}
CompatMode::V0_34 => {
$self
.perform_request_with_dialect(request, dialect::v0_34::Dialect)
.await
}
}
}};
}
@@ -70,7 +85,11 @@ impl ReqwestRpcClient {
.headers(headers)
}
async fn perform_request<R, S>(&self, request: R) -> Result<R::Output, Error>
async fn perform_request_with_dialect<R, S>(
&self,
request: R,
_dialect: S,
) -> Result<R::Output, Error>
where
R: SimpleRequest<S>,
S: Dialect,
@@ -81,26 +100,25 @@ impl ReqwestRpcClient {
.send()
.await
.map_err(TendermintRpcErrorMap::into_rpc_err)?;
let response_status = response.status();
let bytes = response
.bytes()
.await
.map_err(TendermintRpcErrorMap::into_rpc_err)?;
// Successful JSON-RPC requests are expected to return a 200 OK HTTP status.
// Otherwise, this means that the HTTP request failed as a whole,
// as opposed to the JSON-RPC request returning an error,
// and we cannot expect the response body to be a valid JSON-RPC response.
if response_status != reqwest::StatusCode::OK {
// hehe, that's so nasty but we have to somehow convert between different versions of the same lib
return Err(Error::http_request_failed(
response_status.as_u16().try_into().unwrap(),
));
}
R::Response::from_string(bytes).map(Into::into)
}
async fn perform_v0_34<R>(&self, request: R) -> Result<R::Output, Error>
where
R: SimpleRequest<dialect::v0_34::Dialect>,
{
self.perform_request(request).await
}
async fn perform_v0_37<R>(&self, request: R) -> Result<R::Output, Error>
where
R: SimpleRequest<dialect::v0_37::Dialect>,
{
self.perform_request(request).await
}
}
trait TendermintRpcErrorMap {
@@ -120,18 +138,50 @@ impl TendermintRpcClient for ReqwestRpcClient {
where
R: SimpleRequest,
{
self.perform_request(request).await
self.perform_request_with_dialect(request, LatestDialect)
.await
}
async fn block_results<H>(&self, height: H) -> Result<block_results::Response, Error>
async fn block<H>(&self, height: H) -> Result<endpoint::block::Response, Error>
where
H: Into<Height> + Send,
{
perform_with_compat!(self, block_results::Request::new(height.into()))
perform_with_compat!(self, endpoint::block::Request::new(height.into()))
}
async fn latest_block_results(&self) -> Result<block_results::Response, Error> {
perform_with_compat!(self, block_results::Request::default())
async fn block_by_hash(
&self,
hash: tendermint::Hash,
) -> Result<endpoint::block_by_hash::Response, Error> {
perform_with_compat!(self, endpoint::block_by_hash::Request::new(hash))
}
async fn latest_block(&self) -> Result<endpoint::block::Response, Error> {
perform_with_compat!(self, endpoint::block::Request::default())
}
async fn block_results<H>(&self, height: H) -> Result<endpoint::block_results::Response, Error>
where
H: Into<Height> + Send,
{
perform_with_compat!(self, endpoint::block_results::Request::new(height.into()))
}
async fn latest_block_results(&self) -> Result<endpoint::block_results::Response, Error> {
perform_with_compat!(self, endpoint::block_results::Request::default())
}
async fn block_search(
&self,
query: Query,
page: u32,
per_page: u8,
order: Order,
) -> Result<endpoint::block_search::Response, Error> {
perform_with_compat!(
self,
endpoint::block_search::Request::new(query, page, per_page, order)
)
}
async fn header<H>(&self, height: H) -> Result<endpoint::header::Response, Error>
@@ -140,11 +190,26 @@ impl TendermintRpcClient for ReqwestRpcClient {
{
let height = height.into();
match self.compat {
CompatMode::V0_37 => self.perform(endpoint::header::Request::new(height)).await,
CompatMode::V0_38 => {
self.perform_request_with_dialect(
endpoint::header::Request::new(height),
v0_38::Dialect,
)
.await
}
CompatMode::V0_37 => {
self.perform_request_with_dialect(
endpoint::header::Request::new(height),
v0_37::Dialect,
)
.await
}
CompatMode::V0_34 => {
// Back-fill with a request to /block endpoint and
// taking just the header from the response.
let resp = self.perform_v0_34(block::Request::new(height)).await?;
let resp = self
.perform_request_with_dialect(block::Request::new(height), v0_34::Dialect)
.await?;
Ok(resp.into())
}
}
@@ -152,12 +217,25 @@ impl TendermintRpcClient for ReqwestRpcClient {
async fn header_by_hash(&self, hash: Hash) -> Result<header_by_hash::Response, Error> {
match self.compat {
CompatMode::V0_37 => self.perform(header_by_hash::Request::new(hash)).await,
CompatMode::V0_38 => {
self.perform_request_with_dialect(
header_by_hash::Request::new(hash),
v0_38::Dialect,
)
.await
}
CompatMode::V0_37 => {
self.perform_request_with_dialect(
header_by_hash::Request::new(hash),
v0_37::Dialect,
)
.await
}
CompatMode::V0_34 => {
// Back-fill with a request to /block_by_hash endpoint and
// taking just the header from the response.
let resp = self
.perform_v0_34(block_by_hash::Request::new(hash))
.perform_request_with_dialect(block_by_hash::Request::new(hash), v0_34::Dialect)
.await?;
Ok(resp.into())
}
@@ -167,8 +245,18 @@ impl TendermintRpcClient for ReqwestRpcClient {
/// `/broadcast_evidence`: broadcast an evidence.
async fn broadcast_evidence(&self, e: Evidence) -> Result<evidence::Response, Error> {
match self.compat {
CompatMode::V0_37 => self.perform(evidence::Request::new(e)).await,
CompatMode::V0_34 => self.perform_v0_34(evidence::Request::new(e)).await,
CompatMode::V0_38 => {
self.perform_request_with_dialect(evidence::Request::new(e), v0_38::Dialect)
.await
}
CompatMode::V0_37 => {
self.perform_request_with_dialect(evidence::Request::new(e), v0_37::Dialect)
.await
}
CompatMode::V0_34 => {
self.perform_request_with_dialect(evidence::Request::new(e), v0_34::Dialect)
.await
}
}
}
+4 -2
View File
@@ -10,6 +10,7 @@ anyhow = { workspace = true }
base64 = { workspace = true }
bip39 = { workspace = true }
bs58 = { workspace = true }
colored = { workspace = true }
comfy-table = { workspace = true }
cfg-if = { workspace = true }
clap = { workspace = true, features = ["derive"] }
@@ -25,9 +26,10 @@ rand = { workspace = true, features = ["std"] }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
thiserror = { workspace = true }
tempfile = { workspace = true }
time = { workspace = true, features = ["parsing", "formatting"] }
tokio = { workspace = true, features = ["sync"]}
toml = "0.5.6"
tokio = { workspace = true, features = ["sync"] }
toml = { workspace = true }
url = { workspace = true }
tap = { workspace = true }
zeroize = { workspace = true }
@@ -1,64 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::utils::CommonConfigsWrapper;
use anyhow::bail;
use clap::ArgGroup;
use clap::Parser;
use nym_credential_storage::initialise_persistent_storage;
use nym_id::import_credential;
use std::fs;
use std::path::PathBuf;
fn parse_encoded_credential_data(raw: &str) -> bs58::decode::Result<Vec<u8>> {
bs58::decode(raw).into_vec()
}
#[derive(Debug, Parser)]
#[clap(group(ArgGroup::new("cred_data").required(true)))]
pub struct Args {
/// Config file of the client that is supposed to use the credential.
#[clap(long)]
pub(crate) client_config: PathBuf,
/// Explicitly provide the encoded credential data (as base58)
#[clap(long, group = "cred_data", value_parser = parse_encoded_credential_data)]
pub(crate) credential_data: Option<Vec<u8>>,
/// Specifies the path to file containing binary credential data
#[clap(long, group = "cred_data")]
pub(crate) credential_path: Option<PathBuf>,
// currently hidden as there exists only a single serialization standard
#[clap(long, hide = true)]
pub(crate) version: Option<u8>,
}
pub async fn execute(args: Args) -> anyhow::Result<()> {
let loaded = CommonConfigsWrapper::try_load(args.client_config)?;
if let Ok(id) = loaded.try_get_id() {
println!("loaded config file for client '{id}'");
}
let Ok(credentials_store) = loaded.try_get_credentials_store() else {
bail!("the loaded config does not have a credentials store information")
};
println!(
"using credentials store at '{}'",
credentials_store.display()
);
let credentials_store = initialise_persistent_storage(credentials_store).await;
let raw_credential = match args.credential_data {
Some(data) => data,
None => {
// SAFETY: one of those arguments must have been set
fs::read(args.credential_path.unwrap())?
}
};
import_credential(credentials_store, raw_credential, args.version).await?;
Ok(())
}
@@ -1,56 +0,0 @@
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::SigningClient;
use crate::utils::CommonConfigsWrapper;
use anyhow::bail;
use clap::Parser;
use nym_credential_storage::initialise_persistent_storage;
use nym_credential_utils::utils;
use nym_credentials_interface::TicketType;
use nym_crypto::asymmetric::identity;
use std::path::PathBuf;
#[derive(Debug, Parser)]
pub struct Args {
/// Specify which type of ticketbook should be issued
#[clap(long, default_value_t = TicketType::V1MixnetEntry)]
pub(crate) ticketbook_type: TicketType,
/// Config file of the client that is supposed to use the credential.
#[clap(long)]
pub(crate) client_config: PathBuf,
}
pub async fn execute(args: Args, client: SigningClient) -> anyhow::Result<()> {
let loaded = CommonConfigsWrapper::try_load(args.client_config)?;
if let Ok(id) = loaded.try_get_id() {
println!("loaded config file for client '{id}'");
}
let Ok(credentials_store) = loaded.try_get_credentials_store() else {
bail!("the loaded config does not have a credentials store information")
};
let Ok(private_id_key) = loaded.try_get_private_id_key() else {
bail!("the loaded config does not have a public id key information")
};
println!(
"using credentials store at '{}'",
credentials_store.display()
);
let persistent_storage = initialise_persistent_storage(credentials_store).await;
let private_id_key: identity::PrivateKey = nym_pemstore::load_key(private_id_key)?;
utils::issue_credential(
&client,
&persistent_storage,
&private_id_key.to_bytes(),
args.ticketbook_type,
)
.await?;
Ok(())
}
@@ -0,0 +1,178 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::utils::CommonConfigsWrapper;
use anyhow::{anyhow, bail};
use clap::Parser;
use colored::Colorize;
use comfy_table::Table;
use nym_credential_storage::initialise_persistent_storage;
use nym_credential_storage::storage::Storage;
use nym_credentials::ecash::bandwidth::serialiser::VersionedSerialise;
use std::path::PathBuf;
#[derive(Debug, Parser)]
pub struct Args {
/// Specify the index of the ticket to retrieve from the ticketbook.
/// By default, the current unspent value is used.
#[clap(long, group = "output")]
pub(crate) ticket_index: Option<u64>,
/// Specify whether we should display payments for ALL available tickets
#[clap(long, group = "output")]
pub(crate) full: bool,
/// Base58-encoded identity of the provider (must be 32 bytes long)
#[clap(long)]
pub(crate) provider: String,
/// Config file of the client that is supposed to use the credential.
#[clap(long, group = "source")]
pub(crate) client_config: Option<PathBuf>,
/// Path to the dedicated credential storage database
#[clap(long, group = "source")]
pub(crate) credential_storage: Option<PathBuf>,
}
pub async fn execute(args: Args) -> anyhow::Result<()> {
let credentials_store = if let Some(explicit) = args.credential_storage {
explicit
} else {
// SAFETY: at least one of them MUST HAVE been specified
let cfg = args.client_config.unwrap();
let loaded = CommonConfigsWrapper::try_load(cfg)?;
if let Ok(id) = loaded.try_get_id() {
println!("loaded config file for client '{id}'");
}
let Ok(credentials_store) = loaded.try_get_credentials_store() else {
bail!("the loaded config does not have a credentials store information")
};
credentials_store
};
let decoded_provider = bs58::decode(&args.provider).into_vec()?;
if decoded_provider.len() != 32 {
bail!("the provided provider information is malformed")
}
let provider_arr: [u8; 32] = decoded_provider.try_into().unwrap();
let persistent_storage = initialise_persistent_storage(&credentials_store).await;
let Some(mut next_ticketbook) = persistent_storage
.get_next_unspent_usable_ticketbook(0)
.await?
else {
bail!(
"there are no valid ticketbooks in the storage at {}",
credentials_store.display()
)
};
let epoch_id = next_ticketbook.ticketbook.epoch_id();
let expiration_date = next_ticketbook.ticketbook.expiration_date();
let verification_key = persistent_storage
.get_master_verification_key(epoch_id)
.await?
.ok_or_else(|| {
anyhow!("ticketbook got incorrectly imported - the master verification key is missing")
})?;
let expiration_signatures = persistent_storage
.get_expiration_date_signatures(expiration_date)
.await?
.ok_or_else(|| {
anyhow!(
"ticketbook got incorrectly imported - the expiration date signatures are missing"
)
})?;
let coin_indices_signatures = persistent_storage
.get_coin_index_signatures(epoch_id)
.await?
.ok_or_else(|| {
anyhow!("ticketbook got incorrectly imported - the coin index signatures are missing")
})?;
let ticketbook_data = next_ticketbook.ticketbook.pack();
let next_ticket = args
.ticket_index
.unwrap_or(next_ticketbook.ticketbook.spent_tickets());
let pay_info = next_ticketbook.ticketbook.generate_pay_info(provider_arr);
println!("{}", "TICKETBOOK DATA:".bold());
println!("{}", bs58::encode(&ticketbook_data.data).into_string());
println!();
// display it only for a single ticket
if !args.full {
println!("attempting to generate payment for ticket {next_ticket}...");
println!();
next_ticketbook.ticketbook.update_spent_tickets(next_ticket);
let req = next_ticketbook.ticketbook.prepare_for_spending(
&verification_key,
pay_info.into(),
&coin_indices_signatures,
&expiration_signatures,
1,
)?;
let payment = req.payment;
println!("{}", format!("PAYMENT FOR TICKET {next_ticket}: ").bold());
println!("{}", bs58::encode(&payment.to_bytes()).into_string());
return Ok(());
}
println!(
"generating payment information for {} tickets. this might take a while!...",
next_ticketbook.ticketbook.params_total_tickets()
);
// otherwise generate all the payments
let last_spent = next_ticketbook.ticketbook.spent_tickets();
let mut table = Table::new();
table.set_header(vec!["index", "binary data", "spend status"]);
for i in 0..next_ticketbook.ticketbook.params_total_tickets() {
let status = if i < last_spent {
"SPENT".red()
} else {
"NOT SPENT".green()
};
next_ticketbook.ticketbook.update_spent_tickets(i);
let req = next_ticketbook.ticketbook.prepare_for_spending(
&verification_key,
pay_info.into(),
&coin_indices_signatures,
&expiration_signatures,
1,
)?;
let payment = req.payment;
let payment_bytes = payment.to_bytes();
let len = payment_bytes.len();
let display_size = 100;
let remaining = len - display_size;
table.add_row(vec![
i.to_string(),
format!(
"{}{remaining}bytes remaining",
bs58::encode(&payment_bytes[..display_size]).into_string()
),
status.to_string(),
]);
}
println!("{}", "AVAILABLE TICKETS".bold());
println!("{table}");
Ok(())
}
@@ -0,0 +1,76 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::utils::CommonConfigsWrapper;
use anyhow::bail;
use clap::ArgGroup;
use clap::Parser;
use nym_credential_storage::initialise_persistent_storage;
use nym_id::import_credential::import_expiration_date_signatures;
use std::fs;
use std::path::PathBuf;
fn parse_encoded_signatures_data(raw: &str) -> bs58::decode::Result<Vec<u8>> {
bs58::decode(raw).into_vec()
}
#[derive(Debug, Parser)]
#[clap(
group(ArgGroup::new("sig_data").required(true)),
)]
pub struct Args {
/// Config file of the client that is supposed to use the signatures.
#[clap(long)]
pub(crate) client_config: PathBuf,
/// Explicitly provide the encoded signatures data (as base58)
#[clap(long, group = "sig_data", value_parser = parse_encoded_signatures_data)]
pub(crate) signatures_data: Option<Vec<u8>>,
/// Specifies the path to file containing binary signatures data
#[clap(long, group = "sig_data")]
pub(crate) signatures_path: Option<PathBuf>,
// currently hidden as there exists only a single serialization standard
#[clap(long, hide = true)]
pub(crate) version: Option<u8>,
}
impl Args {
fn signatures_data(self) -> anyhow::Result<Vec<u8>> {
let data = match self.signatures_data {
Some(data) => data,
None => {
// SAFETY: one of those arguments must have been set
#[allow(clippy::unwrap_used)]
fs::read(self.signatures_path.unwrap())?
}
};
Ok(data)
}
}
pub async fn execute(args: Args) -> anyhow::Result<()> {
let loaded = CommonConfigsWrapper::try_load(&args.client_config)?;
if let Ok(id) = loaded.try_get_id() {
println!("loaded config file for client '{id}'");
}
let Ok(credentials_store) = loaded.try_get_credentials_store() else {
bail!("the loaded config does not have a credentials store information")
};
println!(
"using credentials store at '{}'",
credentials_store.display()
);
let credentials_store = initialise_persistent_storage(credentials_store).await;
let version = args.version;
let raw_signatures = args.signatures_data()?;
import_expiration_date_signatures(credentials_store, raw_signatures, version).await?;
Ok(())
}
@@ -0,0 +1,76 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::utils::CommonConfigsWrapper;
use anyhow::bail;
use clap::ArgGroup;
use clap::Parser;
use nym_credential_storage::initialise_persistent_storage;
use nym_id::import_credential::import_expiration_date_signatures;
use std::fs;
use std::path::PathBuf;
fn parse_encoded_signatures_data(raw: &str) -> bs58::decode::Result<Vec<u8>> {
bs58::decode(raw).into_vec()
}
#[derive(Debug, Parser)]
#[clap(
group(ArgGroup::new("sig_data").required(true)),
)]
pub struct Args {
/// Config file of the client that is supposed to use the signatures.
#[clap(long)]
pub(crate) client_config: PathBuf,
/// Explicitly provide the encoded signatures data (as base58)
#[clap(long, group = "sig_data", value_parser = parse_encoded_signatures_data)]
pub(crate) signatures_data: Option<Vec<u8>>,
/// Specifies the path to file containing binary signatures data
#[clap(long, group = "sig_data")]
pub(crate) signatures_path: Option<PathBuf>,
// currently hidden as there exists only a single serialization standard
#[clap(long, hide = true)]
pub(crate) version: Option<u8>,
}
impl Args {
fn signatures_data(self) -> anyhow::Result<Vec<u8>> {
let data = match self.signatures_data {
Some(data) => data,
None => {
// SAFETY: one of those arguments must have been set
#[allow(clippy::unwrap_used)]
fs::read(self.signatures_path.unwrap())?
}
};
Ok(data)
}
}
pub async fn execute(args: Args) -> anyhow::Result<()> {
let loaded = CommonConfigsWrapper::try_load(&args.client_config)?;
if let Ok(id) = loaded.try_get_id() {
println!("loaded config file for client '{id}'");
}
let Ok(credentials_store) = loaded.try_get_credentials_store() else {
bail!("the loaded config does not have a credentials store information")
};
println!(
"using credentials store at '{}'",
credentials_store.display()
);
let credentials_store = initialise_persistent_storage(credentials_store).await;
let version = args.version;
let raw_signatures = args.signatures_data()?;
import_expiration_date_signatures(credentials_store, raw_signatures, version).await?;
Ok(())
}

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