Compare commits

..

53 Commits

Author SHA1 Message Date
Jędrzej Stuczyński 9f766549e8 extra logs 2023-10-17 09:59:50 +01:00
Jędrzej Stuczyński 98131d1277 restored and revamped 'force_tls' flag to filter all gateways that support the wss protocol 2023-10-16 10:29:06 +01:00
Jędrzej Stuczyński 42c2d4de7a corrected health endpoint description 2023-10-13 14:58:58 +01:00
Jędrzej Stuczyński 688ffaf59a using camelcase for node status 2023-10-13 14:22:29 +01:00
Jędrzej Stuczyński 270d065661 nym-api trying multiple ports for the client 2023-10-13 13:22:29 +01:00
Jędrzej Stuczyński 25d66b5241 added health endpoint 2023-10-13 13:03:09 +01:00
Jędrzej Stuczyński a5d8a5071f nym-api using new default port for queries 2023-10-13 12:16:12 +01:00
Jędrzej Stuczyński 6c51696cfd changed default http port from 80 to 8080 2023-10-13 10:06:54 +01:00
Jędrzej Stuczyński f2196366e1 reduced severity of gateway details lookup failure 2023-10-12 14:04:38 +01:00
Jędrzej Stuczyński ac22a9d4d9 added default user-agent to http-api-client 2023-10-12 13:55:53 +01:00
Jędrzej Stuczyński d77bcbb241 fixed build and lints of other crates 2023-10-12 11:30:50 +01:00
Jędrzej Stuczyński a6f0fbff3d increased configurability 2023-10-12 11:00:04 +01:00
Jędrzej Stuczyński 07e6d7750a landing page configurability 2023-10-12 10:07:03 +01:00
Jędrzej Stuczyński fcce6b435c fixed the registration test 2023-10-12 10:03:36 +01:00
Jędrzej Stuczyński c7a66fc451 using concrete error type for nym node request error 2023-10-12 09:39:42 +01:00
Jędrzej Stuczyński 6dc7fc92bd output type support for wg routes 2023-10-11 17:52:48 +01:00
Jędrzej Stuczyński 215f927f0d turns out swagger can be happy with strongly typed requests 2023-10-11 17:34:28 +01:00
Jędrzej Stuczyński 7477337807 basic swagger support for wg endpoints 2023-10-11 16:47:00 +01:00
Jędrzej Stuczyński 44db2b8d71 added ErrorResponse for wireguard routes 2023-10-11 16:03:04 +01:00
Jędrzej Stuczyński 3260ba84fe brought in wg routes into nym-node router 2023-10-11 15:30:28 +01:00
Jędrzej Stuczyński 89cddc62cc cargo fmt 2023-10-10 17:11:00 +01:00
Jędrzej Stuczyński fe3b47f103 post rebase fixes 2023-10-10 17:11:00 +01:00
Jędrzej Stuczyński 636bbd9d12 fixed wasm builds 2023-10-10 17:11:00 +01:00
Jędrzej Stuczyński 2a338458b9 fixed request timeouts for wasm 2023-10-10 17:10:59 +01:00
Jędrzej Stuczyński ee2026e53d clients using self-described gateway information 2023-10-10 17:10:54 +01:00
Jędrzej Stuczyński 8f2cb95ffa caching self reported host info 2023-10-10 17:10:54 +01:00
Jędrzej Stuczyński 79ef88dd49 temporarily wired up NymContractCache to NodeDescriptionProvider 2023-10-10 17:10:54 +01:00
Jędrzej Stuczyński 216b32811c removed old cache type 2023-10-10 17:10:54 +01:00
Jędrzej Stuczyński baf7178dc8 nym-api caching node self described information 2023-10-10 17:10:52 +01:00
Jędrzej Stuczyński 00cdc5010a new generic cache and refresher 2023-10-10 17:10:48 +01:00
Jędrzej Stuczyński 04bf153701 routes unification + node api client 2023-10-10 17:10:46 +01:00
Jędrzej Stuczyński e6eb83f350 unified http api client 2023-10-10 17:10:41 +01:00
Jędrzej Stuczyński e8eb031fbd moved all models to separate crate 2023-10-10 17:10:40 +01:00
Jędrzej Stuczyński 0ce5ba2668 signing host information 2023-10-10 17:10:30 +01:00
Jędrzej Stuczyński e8d601df67 expanded on gateway-related endpoints 2023-10-10 17:10:30 +01:00
Jędrzej Stuczyński ca6b09ce8d initial host information endpoint 2023-10-10 17:10:28 +01:00
Jędrzej Stuczyński b8e27c67d9 refactored routes structures 2023-10-10 17:10:23 +01:00
Jędrzej Stuczyński 8dbb72296b fixing some version/feature clashes 2023-10-10 17:10:22 +01:00
Jędrzej Stuczyński c2893e5ca0 running the api as a proper task in gateway 'run' 2023-10-10 17:09:37 +01:00
Jędrzej Stuczyński 0489e5aa65 missing swagger data + using closure capture for immutable state 2023-10-10 17:08:06 +01:00
Jędrzej Stuczyński eda8b6bf85 wip: populating rest of swagger 2023-10-10 17:08:06 +01:00
Jędrzej Stuczyński a1aff09b91 populated build-info endpoint 2023-10-10 17:08:06 +01:00
Jędrzej Stuczyński 5de218bca9 initial empty openapi/swagger 2023-10-10 17:08:05 +01:00
Jędrzej Stuczyński 50960e7bf3 started expanding the API 2023-10-10 17:08:05 +01:00
Jędrzej Stuczyński 723e729f3b initial router 2023-10-10 17:08:03 +01:00
serinko 3913e3e56b [DOC]: edit minor syntax - smoosh-faq.md 2023-10-10 13:08:44 +00:00
Tommy Verrall 5f3f97f3be Merge pull request #3975 from nymtech/bugfix/3973
Add packet type to NE mixnet response listener
2023-10-10 12:27:33 +02:00
Jon Häggblad fb2b7d3480 Fix rust-analyzer warnings (#3974)
* Fix rust-analyzer warnings

Fix some warnings that rust-analyzer emits due to enabling all features.
These are annoying when you try to list all warnings in the entire
workspace.

* Revert change to signing client

* Instead add nested feature
2023-10-10 12:02:00 +02:00
Jon Häggblad 7577ec9cb2 [NC-43] Route packets back to WG peer (#3965)
* Initial work on reverse nat

* wip

* Refine key gen

* Rename to wg_tunnel

* Forward packet to peer

* Remove source_addr

* Check if allowed to write to tunnel

* Extract out network_table

* Move map struc definitions to udp_listener

* Delegate ip network table calls

* Fix mac compilation

* Add TunTaskTx type
2023-10-10 11:01:03 +02:00
Drazen Urch 1e900c32df Gateway client registry and api routes (#3955)
* Add HTTP API and Client registry to Gateway

* Update CHANGELOG

* Smooshify structure

* Reify x25519 public key

* Hmac message verification

* Add lightweight handshake with replay protection

* Tidy up, move registartion to its own file

* Test for the registration flow

* Fix nonce loop hole
2023-10-10 10:55:26 +02:00
Drazen cb4eaddc9f Add packet type to NE mixnet response listener 2023-10-10 10:36:28 +02:00
Tommy Verrall 52151c5fb4 Merge pull request #3971 from nymtech/move_builds_to_ubuntu_lts
Github Actions: Fix release process
2023-10-09 11:27:26 +02:00
Raphaël Walther 24fe628e15 Github Actions: Fix release process 2023-10-09 07:24:18 +02:00
232 changed files with 7447 additions and 1398 deletions
@@ -14,7 +14,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [custom-linux, macos-latest, windows10]
os: [custom-ubuntu-20.04, macos-latest, windows10]
runs-on: ${{ matrix.os }}
env:
CARGO_TERM_COLOR: always
+1 -1
View File
@@ -20,7 +20,7 @@ jobs:
strategy:
fail-fast: false
matrix:
platform: [custom-runner-linux]
platform: [custom-ubuntu-20.04]
runs-on: ${{ matrix.platform }}
outputs:
@@ -14,7 +14,7 @@ jobs:
strategy:
fail-fast: false
matrix:
platform: [custom-runner-linux]
platform: [custom-ubuntu-20.04]
runs-on: ${{ matrix.platform }}
outputs:
+1 -1
View File
@@ -7,7 +7,7 @@ on:
jobs:
build:
if: ${{ (startsWith(github.ref, 'refs/tags/nym-contracts-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
runs-on: [self-hosted, custom-runner-linux]
runs-on: [self-hosted, custom-ubuntu-20.04]
steps:
- uses: actions/checkout@v2
@@ -14,7 +14,7 @@ jobs:
strategy:
fail-fast: false
matrix:
platform: [custom-runner-linux]
platform: [custom-ubuntu-20.04]
runs-on: ${{ matrix.platform }}
outputs:
@@ -12,7 +12,7 @@ on:
jobs:
build:
name: Build APK
runs-on: custom-runner-linux
runs-on: custom-ubuntu-20.04
env:
ANDROID_HOME: ${{ github.workspace }}/android-sdk
NDK_VERSION: 25.2.9519653
+1 -1
View File
@@ -4,7 +4,7 @@ on:
jobs:
publish:
runs-on: [custom-runner-linux]
runs-on: [custom-ubuntu-20.04]
steps:
- uses: actions/checkout@v2
+3
View File
@@ -3,6 +3,9 @@
Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
- add client registry to Gateway ([#3955])
- add HTTP API to Gateway ([#3955])
- add `/client/<pub-key>`, `clients` and `register` routes to the gateway ([#3955])
## [2023.1-milka] (2023-09-24)
Generated
+254 -82
View File
@@ -766,7 +766,11 @@ dependencies = [
"pin-project-lite 0.2.12",
"rustversion",
"serde",
"serde_json",
"serde_path_to_error",
"serde_urlencoded",
"sync_wrapper",
"tokio",
"tower",
"tower-layer",
"tower-service",
@@ -789,6 +793,18 @@ dependencies = [
"tower-service",
]
[[package]]
name = "axum-macros"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdca6a10ecad987bda04e95606ef85a5417dcaac1a78455242d72e031e2b6b62"
dependencies = [
"heck 0.4.1",
"proc-macro2",
"quote",
"syn 2.0.28",
]
[[package]]
name = "backtrace"
version = "0.3.68"
@@ -868,7 +884,7 @@ dependencies = [
"pbkdf2",
"rand_core 0.6.4",
"ripemd",
"sha2 0.10.7",
"sha2 0.10.8",
"subtle 2.4.1",
"zeroize",
]
@@ -1076,7 +1092,7 @@ version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5353f36341f7451062466f0b755b96ac3a9547e4d7f6b70d603fc721a7d7896"
dependencies = [
"sha2 0.10.7",
"sha2 0.10.8",
]
[[package]]
@@ -1739,7 +1755,7 @@ dependencies = [
"schemars",
"serde",
"serde-json-wasm",
"sha2 0.10.7",
"sha2 0.10.8",
"thiserror",
]
@@ -2095,6 +2111,7 @@ dependencies = [
"cfg-if",
"cpufeatures",
"curve25519-dalek-derive",
"digest 0.10.7",
"fiat-crypto",
"platforms",
"rustc_version 0.4.0",
@@ -2690,6 +2707,20 @@ dependencies = [
"zeroize",
]
[[package]]
name = "ed25519-dalek"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7277392b266383ef8396db7fdeb1e77b6c52fed775f5df15bb24f35b72156980"
dependencies = [
"curve25519-dalek 4.1.0",
"ed25519 2.2.2",
"rand_core 0.6.4",
"serde",
"sha2 0.10.8",
"zeroize",
]
[[package]]
name = "ed25519-zebra"
version = "3.1.0"
@@ -2852,7 +2883,7 @@ dependencies = [
"libp2p 0.51.3",
"libp2p-identity",
"log",
"lru 0.10.0",
"lru 0.10.1",
"nym-config",
"nym-ephemera-common",
"nym-task",
@@ -2975,7 +3006,7 @@ dependencies = [
[[package]]
name = "extension-storage"
version = "1.2.0"
version = "1.2.0-rc.10"
dependencies = [
"bip39",
"console_error_panic_hook",
@@ -3831,6 +3862,20 @@ dependencies = [
"itoa",
]
[[package]]
name = "http-api-client"
version = "0.1.0"
dependencies = [
"async-trait",
"reqwest",
"serde",
"serde_json",
"thiserror",
"tracing",
"url",
"wasmtimer",
]
[[package]]
name = "http-body"
version = "0.4.5"
@@ -3842,6 +3887,12 @@ dependencies = [
"pin-project-lite 0.2.12",
]
[[package]]
name = "http-range-header"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "add0ab9360ddbd88cfeb3bd9574a1d85cfdfa14db10b3e21d3700dbc4328758f"
[[package]]
name = "httparse"
version = "1.8.0"
@@ -4093,6 +4144,7 @@ checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d"
dependencies = [
"equivalent",
"hashbrown 0.14.0",
"serde",
]
[[package]]
@@ -4228,7 +4280,7 @@ dependencies = [
"socket2 0.4.9",
"widestring",
"winapi",
"winreg",
"winreg 0.10.1",
]
[[package]]
@@ -4365,7 +4417,7 @@ dependencies = [
"cfg-if",
"ecdsa 0.14.8",
"elliptic-curve 0.12.3",
"sha2 0.10.7",
"sha2 0.10.8",
]
[[package]]
@@ -4378,7 +4430,7 @@ dependencies = [
"ecdsa 0.16.8",
"elliptic-curve 0.13.5",
"once_cell",
"sha2 0.10.7",
"sha2 0.10.8",
"signature 2.1.0",
]
@@ -4594,7 +4646,7 @@ source = "git+https://github.com/ChainSafe/rust-libp2p.git?rev=e3440d25681df380c
dependencies = [
"asn1_der",
"bs58 0.4.0",
"ed25519-dalek",
"ed25519-dalek 1.0.1",
"either",
"fnv",
"futures",
@@ -4612,7 +4664,7 @@ dependencies = [
"rand 0.8.5",
"rw-stream-sink 0.3.0 (git+https://github.com/ChainSafe/rust-libp2p.git?rev=e3440d25681df380c9f0f8cfdcfd5ecc0a4f2fb6)",
"sec1 0.3.0",
"sha2 0.10.7",
"sha2 0.10.8",
"smallvec",
"thiserror",
"unsigned-varint",
@@ -4698,7 +4750,7 @@ dependencies = [
"prost-codec",
"rand 0.8.5",
"regex",
"sha2 0.10.7",
"sha2 0.10.8",
"smallvec",
"thiserror",
"unsigned-varint",
@@ -4730,7 +4782,7 @@ dependencies = [
"rand 0.8.5",
"regex",
"serde",
"sha2 0.10.7",
"sha2 0.10.8",
"smallvec",
"thiserror",
"unsigned-varint",
@@ -4761,19 +4813,19 @@ dependencies = [
[[package]]
name = "libp2p-identity"
version = "0.1.2"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e2d584751cecb2aabaa56106be6be91338a60a0f4e420cf2af639204f596fc1"
checksum = "276bb57e7af15d8f100d3c11cbdd32c6752b7eef4ba7a18ecf464972c07abcce"
dependencies = [
"bs58 0.4.0",
"ed25519-dalek",
"ed25519-dalek 2.0.0",
"log",
"multiaddr 0.17.1",
"multihash",
"quick-protobuf",
"rand 0.8.5",
"serde",
"sha2 0.10.7",
"sha2 0.10.8",
"thiserror",
"zeroize",
]
@@ -4799,7 +4851,7 @@ dependencies = [
"quick-protobuf",
"rand 0.8.5",
"serde",
"sha2 0.10.7",
"sha2 0.10.8",
"smallvec",
"thiserror",
"uint",
@@ -4904,7 +4956,7 @@ dependencies = [
"prost",
"prost-build",
"rand 0.8.5",
"sha2 0.10.7",
"sha2 0.10.8",
"snow",
"static_assertions",
"thiserror",
@@ -4927,7 +4979,7 @@ dependencies = [
"once_cell",
"quick-protobuf",
"rand 0.8.5",
"sha2 0.10.7",
"sha2 0.10.8",
"snow",
"static_assertions",
"thiserror",
@@ -5368,9 +5420,9 @@ dependencies = [
[[package]]
name = "lru"
version = "0.10.0"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03f1160296536f10c833a82dca22267d5486734230d47bf00bf435885814ba1e"
checksum = "718e8fae447df0c7e1ba7f5189829e63fd536945c8988d61444c19039f16b670"
dependencies = [
"hashbrown 0.13.2",
]
@@ -5517,15 +5569,16 @@ dependencies = [
[[package]]
name = "mix-fetch-wasm"
version = "1.2.0"
version = "1.2.0-rc.10"
dependencies = [
"async-trait",
"futures",
"http-api-client",
"js-sys",
"nym-ordered-buffer",
"nym-service-providers-common",
"nym-socks5-requests",
"rand 0.7.3",
"reqwest",
"serde",
"serde-wasm-bindgen",
"thiserror",
@@ -5616,7 +5669,7 @@ dependencies = [
"multihash-derive",
"serde",
"serde-big-array",
"sha2 0.10.7",
"sha2 0.10.8",
"unsigned-varint",
]
@@ -5952,6 +6005,7 @@ dependencies = [
"nym-mixnet-contract-common",
"nym-multisig-contract-common",
"nym-name-service-common",
"nym-node-requests",
"nym-node-tester-utils",
"nym-pemstore",
"nym-service-provider-directory-common",
@@ -5962,7 +6016,6 @@ dependencies = [
"nym-vesting-contract-common",
"okapi",
"pin-project",
"pretty_env_logger",
"rand 0.7.3",
"rand 0.8.5",
"reqwest",
@@ -5996,6 +6049,7 @@ dependencies = [
"getset",
"nym-coconut-interface",
"nym-mixnet-contract-common",
"nym-node-requests",
"schemars",
"serde",
"ts-rs",
@@ -6029,12 +6083,14 @@ dependencies = [
"opentelemetry",
"opentelemetry-jaeger",
"pretty_env_logger",
"schemars",
"semver 0.11.0",
"serde",
"serde_json",
"tracing-opentelemetry",
"tracing-subscriber",
"tracing-tree",
"utoipa",
"vergen",
]
@@ -6193,7 +6249,7 @@ dependencies = [
"reqwest",
"serde",
"serde_json",
"sha2 0.10.7",
"sha2 0.10.8",
"sqlx 0.6.3",
"tap",
"tempfile",
@@ -6213,7 +6269,7 @@ dependencies = [
[[package]]
name = "nym-client-wasm"
version = "1.2.0"
version = "1.2.0-rc.10"
dependencies = [
"anyhow",
"futures",
@@ -6374,7 +6430,7 @@ dependencies = [
"cipher 0.4.4",
"ctr 0.9.2",
"digest 0.10.7",
"ed25519-dalek",
"ed25519-dalek 1.0.1",
"generic-array 0.14.7",
"hkdf 0.12.3",
"hmac 0.12.1",
@@ -6463,6 +6519,9 @@ dependencies = [
"anyhow",
"async-trait",
"atty",
"axum",
"axum-macros",
"base64 0.21.4",
"bip39",
"bs58 0.4.0",
"clap 4.3.21",
@@ -6470,8 +6529,11 @@ dependencies = [
"dashmap",
"dirs 4.0.0",
"dotenvy",
"fastrand 2.0.0",
"futures",
"hmac 0.12.1",
"humantime-serde",
"hyper",
"lazy_static",
"log",
"nym-api-requests",
@@ -6485,6 +6547,7 @@ dependencies = [
"nym-mixnode-common",
"nym-network-defaults",
"nym-network-requester",
"nym-node",
"nym-pemstore",
"nym-sphinx",
"nym-statistics-common",
@@ -6495,8 +6558,10 @@ dependencies = [
"once_cell",
"pretty_env_logger",
"rand 0.7.3",
"rand 0.8.5",
"serde",
"serde_json",
"sha2 0.10.8",
"sqlx 0.5.11",
"subtle-encoding",
"thiserror",
@@ -6504,7 +6569,9 @@ dependencies = [
"tokio-stream",
"tokio-tungstenite",
"tokio-util",
"tower",
"url",
"x25519-dalek 2.0.0",
"zeroize",
]
@@ -6572,21 +6639,6 @@ dependencies = [
"serde",
]
[[package]]
name = "nym-http-requests"
version = "0.1.0"
dependencies = [
"bytecodec",
"bytes",
"http",
"httpcodec",
"nym-ordered-buffer",
"nym-service-providers-common",
"nym-socks5-requests",
"thiserror",
"url",
]
[[package]]
name = "nym-inclusion-probability"
version = "0.1.0"
@@ -6811,6 +6863,57 @@ dependencies = [
"tokio",
]
[[package]]
name = "nym-node"
version = "0.1.0"
dependencies = [
"anyhow",
"axum",
"bytes",
"colored",
"fastrand 2.0.0",
"hmac 0.12.1",
"hyper",
"mime",
"nym-config",
"nym-crypto",
"nym-node-requests",
"nym-task",
"rand 0.7.3",
"serde",
"serde_json",
"serde_yaml",
"thiserror",
"tokio",
"tower",
"tower-http",
"tracing",
"utoipa",
"utoipa-swagger-ui",
"x25519-dalek 2.0.0",
]
[[package]]
name = "nym-node-requests"
version = "0.1.0"
dependencies = [
"async-trait",
"base64 0.21.4",
"hmac 0.12.1",
"http-api-client",
"nym-bin-common",
"nym-crypto",
"rand 0.7.3",
"schemars",
"serde",
"serde_json",
"sha2 0.10.8",
"thiserror",
"tokio",
"utoipa",
"x25519-dalek 2.0.0",
]
[[package]]
name = "nym-node-tester-utils"
version = "0.1.0"
@@ -6832,7 +6935,7 @@ dependencies = [
[[package]]
name = "nym-node-tester-wasm"
version = "1.2.0"
version = "1.2.0-rc.10"
dependencies = [
"futures",
"js-sys",
@@ -7300,6 +7403,7 @@ dependencies = [
"async-trait",
"bs58 0.4.0",
"log",
"nym-api-requests",
"nym-bin-common",
"nym-config",
"nym-crypto",
@@ -7360,6 +7464,7 @@ dependencies = [
"eyre",
"flate2",
"futures",
"http-api-client",
"itertools",
"log",
"nym-api-requests",
@@ -7426,7 +7531,7 @@ dependencies = [
[[package]]
name = "nym-wasm-sdk"
version = "1.2.0"
version = "1.2.0-rc.10"
dependencies = [
"mix-fetch-wasm",
"nym-client-wasm",
@@ -7444,6 +7549,8 @@ dependencies = [
"dashmap",
"etherparse",
"futures",
"ip_network",
"ip_network_table",
"log",
"nym-task",
"tap",
@@ -7694,7 +7801,7 @@ checksum = "51f44edd08f51e2ade572f141051021c5af22677e42b7dd28a88155151c33594"
dependencies = [
"ecdsa 0.14.8",
"elliptic-curve 0.12.3",
"sha2 0.10.7",
"sha2 0.10.8",
]
[[package]]
@@ -7705,7 +7812,7 @@ checksum = "dfc8c5bf642dde52bb9e87c0ecd8ca5a76faac2eeed98dedb7c717997e1080aa"
dependencies = [
"ecdsa 0.14.8",
"elliptic-curve 0.12.3",
"sha2 0.10.7",
"sha2 0.10.8",
]
[[package]]
@@ -7939,7 +8046,7 @@ checksum = "56af0a30af74d0445c0bf6d9d051c979b516a1a5af790d251daee76005420a48"
dependencies = [
"once_cell",
"pest",
"sha2 0.10.7",
"sha2 0.10.8",
]
[[package]]
@@ -8684,9 +8791,9 @@ dependencies = [
[[package]]
name = "refinery"
version = "0.8.10"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb0436d0dd7bd8d4fce1e828751fa79742b08e35f27cfea7546f8a322b5ef24"
checksum = "529664dbccc0a296947615c997a857912d72d1c44be1fafb7bae54ecfa7a8c24"
dependencies = [
"refinery-core",
"refinery-macros",
@@ -8694,9 +8801,9 @@ dependencies = [
[[package]]
name = "refinery-core"
version = "0.8.10"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19206547cd047e8f4dfa6b20c30d3ecaf24be05841b6aa0aa926a47a3d0662bb"
checksum = "e895cb870cf06e92318cbbeb701f274d022d5ca87a16fa8244e291cd035ef954"
dependencies = [
"async-trait",
"cfg-if",
@@ -8715,9 +8822,9 @@ dependencies = [
[[package]]
name = "refinery-macros"
version = "0.8.10"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d94d4b9241859ba19eaa5c04c86e782eb3aa0aae2c5868e0cfa90c856e58a174"
checksum = "123e8b80f8010c3ae38330c81e76938fc7adf6cdbfbaad20295bb8c22718b4f1"
dependencies = [
"proc-macro2",
"quote",
@@ -8772,9 +8879,9 @@ checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2"
[[package]]
name = "reqwest"
version = "0.11.18"
version = "0.11.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55"
checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b"
dependencies = [
"base64 0.21.4",
"bytes",
@@ -8797,6 +8904,7 @@ dependencies = [
"serde",
"serde_json",
"serde_urlencoded",
"system-configuration",
"tokio",
"tokio-native-tls",
"tokio-socks",
@@ -8805,7 +8913,7 @@ dependencies = [
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"winreg",
"winreg 0.50.0",
]
[[package]]
@@ -9075,7 +9183,7 @@ version = "7.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "512b0ab6853f7e14e3c8754acb43d6f748bb9ced66aa5915a6553ac8213f7731"
dependencies = [
"sha2 0.10.7",
"sha2 0.10.8",
"walkdir",
]
@@ -9569,6 +9677,16 @@ dependencies = [
"serde",
]
[[package]]
name = "serde_path_to_error"
version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4beec8bce849d58d06238cb50db2e1c417cfeafa4c63f692b15c82b7c80f8335"
dependencies = [
"itoa",
"serde",
]
[[package]]
name = "serde_repr"
version = "0.1.16"
@@ -9601,6 +9719,19 @@ dependencies = [
"serde",
]
[[package]]
name = "serde_yaml"
version = "0.9.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a49e178e4452f45cb61d0cd8cebc1b0fafd3e41929e996cef79aa3aca91f574"
dependencies = [
"indexmap 2.0.0",
"itoa",
"ryu",
"serde",
"unsafe-libyaml",
]
[[package]]
name = "sha-1"
version = "0.9.8"
@@ -9640,9 +9771,9 @@ dependencies = [
[[package]]
name = "sha2"
version = "0.10.7"
version = "0.10.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8"
checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
dependencies = [
"cfg-if",
"cpufeatures",
@@ -9719,9 +9850,9 @@ dependencies = [
[[package]]
name = "siphasher"
version = "0.3.10"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de"
checksum = "54ac45299ccbd390721be55b412d41931911f654fa99e2cb8bfb57184b2061fe"
[[package]]
name = "slab"
@@ -9796,7 +9927,7 @@ dependencies = [
"rand_core 0.6.4",
"ring",
"rustc_version 0.4.0",
"sha2 0.10.7",
"sha2 0.10.8",
"subtle 2.4.1",
]
@@ -9982,7 +10113,7 @@ dependencies = [
"paste",
"percent-encoding",
"rustls 0.19.1",
"sha2 0.10.7",
"sha2 0.10.8",
"smallvec",
"sqlformat 0.1.8",
"sqlx-rt 0.5.13",
@@ -10030,7 +10161,7 @@ dependencies = [
"percent-encoding",
"rustls 0.20.8",
"rustls-pemfile",
"sha2 0.10.7",
"sha2 0.10.8",
"smallvec",
"sqlformat 0.2.1",
"sqlx-rt 0.6.3",
@@ -10053,7 +10184,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
"sha2 0.10.7",
"sha2 0.10.8",
"sqlx-core 0.5.13",
"sqlx-rt 0.5.13",
"syn 1.0.109",
@@ -10072,7 +10203,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
"sha2 0.10.7",
"sha2 0.10.8",
"sqlx-core 0.6.3",
"sqlx-rt 0.6.3",
"syn 1.0.109",
@@ -10374,7 +10505,7 @@ dependencies = [
"serde_bytes",
"serde_json",
"serde_repr",
"sha2 0.10.7",
"sha2 0.10.8",
"signature 2.1.0",
"subtle 2.4.1",
"subtle-encoding",
@@ -10477,18 +10608,18 @@ checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d"
[[package]]
name = "thiserror"
version = "1.0.44"
version = "1.0.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "611040a08a0439f8248d1990b111c95baa9c704c805fa1f62104b39655fd7f90"
checksum = "9d6d7a740b8a666a7e828dd00da9c0dc290dff53154ea77ac109281de90589b7"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.44"
version = "1.0.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96"
checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35"
dependencies = [
"proc-macro2",
"quote",
@@ -10847,6 +10978,31 @@ dependencies = [
"tracing",
]
[[package]]
name = "tower-http"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61c5bb1d698276a2443e5ecfabc1008bf15a36c12e6a7176e7bf089ea9131140"
dependencies = [
"bitflags 2.4.0",
"bytes",
"futures-core",
"futures-util",
"http",
"http-body",
"http-range-header",
"httpdate",
"mime",
"mime_guess",
"percent-encoding",
"pin-project-lite 0.2.12",
"tokio",
"tokio-util",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
name = "tower-layer"
version = "0.3.2"
@@ -11278,6 +11434,12 @@ dependencies = [
"subtle 2.4.1",
]
[[package]]
name = "unsafe-libyaml"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f28467d3e1d3c6586d8f25fa243f544f5800fec42d97032474e17222c2b75cfa"
[[package]]
name = "unsigned-varint"
version = "0.7.1"
@@ -11357,11 +11519,11 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
[[package]]
name = "utoipa"
version = "3.3.0"
version = "3.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68ae74ef183fae36d650f063ae7bde1cacbe1cd7e72b617cbe1e985551878b98"
checksum = "d82b1bc5417102a73e8464c686eef947bdfb99fcdfc0a4f228e81afa9526470a"
dependencies = [
"indexmap 1.9.3",
"indexmap 2.0.0",
"serde",
"serde_json",
"utoipa-gen",
@@ -11369,11 +11531,10 @@ dependencies = [
[[package]]
name = "utoipa-gen"
version = "3.3.0"
version = "3.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ea8ac818da7e746a63285594cce8a96f5e00ee31994e655bd827569cb8b137b"
checksum = "05d96dcd6fc96f3df9b3280ef480770af1b7c5d14bc55192baa9b067976d920c"
dependencies = [
"lazy_static",
"proc-macro-error",
"proc-macro2",
"quote",
@@ -11383,11 +11544,12 @@ dependencies = [
[[package]]
name = "utoipa-swagger-ui"
version = "3.1.3"
version = "3.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "062bba5a3568e126ac72049a63254f4cb1da2eb713db0c1ab2a4c76be191db8c"
checksum = "84614caa239fb25b2bb373a52859ffd94605ceb256eeb1d63436325cf81e3653"
dependencies = [
"actix-web",
"axum",
"mime_guess",
"regex",
"rust-embed",
@@ -11762,7 +11924,7 @@ dependencies = [
"sdp",
"serde",
"serde_json",
"sha2 0.10.7",
"sha2 0.10.8",
"stun",
"thiserror",
"time",
@@ -11825,7 +11987,7 @@ dependencies = [
"sec1 0.3.0",
"serde",
"sha1",
"sha2 0.10.7",
"sha2 0.10.8",
"signature 1.6.4",
"subtle 2.4.1",
"thiserror",
@@ -12198,6 +12360,16 @@ dependencies = [
"winapi",
]
[[package]]
name = "winreg"
version = "0.50.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1"
dependencies = [
"cfg-if",
"windows-sys 0.48.0",
]
[[package]]
name = "with_builtin_macros"
version = "0.0.3"
+12 -3
View File
@@ -46,7 +46,7 @@ members = [
"common/crypto",
"common/dkg",
"common/execute",
"common/http-requests",
"common/http-api-client",
"common/inclusion-probability",
"common/ledger",
"common/mixnode-common",
@@ -93,6 +93,8 @@ members = [
"nym-api",
"nym-browser-extension/storage",
"nym-api/nym-api-requests",
"nym-node",
"nym-node/nym-node-requests",
"nym-outfox",
"tools/internal/ssl-inject",
"tools/internal/sdk-version-bump",
@@ -129,6 +131,8 @@ license = "Apache-2.0"
[workspace.dependencies]
anyhow = "1.0.71"
async-trait = "0.1.68"
axum = "0.6.20"
base64 = "0.21.4"
bip39 = { version = "2.0.0", features = ["zeroize"] }
cfg-if = "1.0.0"
cosmwasm-derive = "=1.3.0"
@@ -151,22 +155,27 @@ dotenvy = "0.15.6"
futures = "0.3.28"
generic-array = "0.14.7"
getrandom = "0.2.10"
hyper = "0.14.27"
k256 = "0.13"
lazy_static = "1.4.0"
log = "0.4"
once_cell = "1.7.2"
parking_lot = "0.12.1"
rand = "0.8.5"
reqwest = "0.11.18"
reqwest = "0.11.22"
schemars = "0.8.1"
serde = "1.0.152"
serde_json = "1.0.91"
tap = "1.0.1"
tendermint-rpc = "0.32" # same version as used by cosmrs
thiserror = "1.0.38"
thiserror = "1.0.48"
tokio = "1.24.1"
tokio-tungstenite = "0.20.1"
tracing = "0.1.37"
tungstenite = { version = "0.20.1", default-features = false }
ts-rs = "7.0.0"
utoipa = "3.5.0"
utoipa-swagger-ui = "3.1.5"
url = "2.4"
zeroize = "1.6.0"
+1 -1
View File
@@ -8,7 +8,7 @@ edition = "2021"
[dependencies]
bip39 = { workspace = true }
rand = "0.7.3"
thiserror = "1.0"
thiserror = { workspace = true }
url = { workspace = true }
nym-coconut-interface = { path = "../coconut-interface" }
+4
View File
@@ -15,6 +15,7 @@ clap_complete_fig = "4.0"
log = { workspace = true }
pretty_env_logger = "0.4.0"
semver = "0.11"
schemars = { workspace = true, features = ["preserve_order"], optional = true }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true, optional = true }
@@ -29,6 +30,7 @@ opentelemetry-jaeger = { version = "0.18.0", optional = true, features = [
"isahc_collector_client",
] }
tracing-opentelemetry = { version = "0.19.0", optional = true }
utoipa = { workspace = true, optional = true }
opentelemetry = { version = "0.19.0", optional = true, features = ["rt-tokio"] }
@@ -42,7 +44,9 @@ vergen = { version = "=7.4.3", default-features = false, features = [
[features]
default = []
openapi = ["utoipa"]
output_format = ["serde_json"]
bin_info_schema = ["schemars"]
tracing = [
"tracing-subscriber",
"tracing-tree",
@@ -81,6 +81,8 @@ impl BinaryBuildInformation {
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "bin_info_schema", derive(schemars::JsonSchema))]
pub struct BinaryBuildInformationOwned {
/// Provides the name of the binary, i.e. the content of `CARGO_PKG_NAME` environmental variable.
pub binary_name: String,
@@ -69,7 +69,7 @@ impl NymApiTopologyProvider {
Ok(mixes) => mixes,
};
let gateways = match self.validator_client.get_cached_gateways().await {
let gateways = match self.validator_client.get_cached_described_gateways().await {
Err(err) => {
error!("failed to get network gateways - {err}");
return None;
+14 -19
View File
@@ -280,29 +280,24 @@ impl GatewayEndpointConfig {
.map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)
}
pub fn from_node(node: nym_topology::gateway::Node, use_tls: bool) -> Self {
// TODO: in the future this shall return a Result and explicit `use_tls` will be removed in favour of the tls info being available on the struct
if use_tls {
Self::from_topology_node_tls(node)
pub fn from_node(
node: nym_topology::gateway::Node,
must_use_tls: bool,
) -> Result<Self, ClientCoreError> {
let gateway_listener = if must_use_tls {
node.clients_address_tls()
.ok_or(ClientCoreError::UnsupportedWssProtocol {
gateway: node.identity_key.to_base58_string(),
})?
} else {
Self::from_topology_node_no_tls(node)
}
}
node.clients_address()
};
pub fn from_topology_node_no_tls(node: nym_topology::gateway::Node) -> Self {
GatewayEndpointConfig {
Ok(GatewayEndpointConfig {
gateway_id: node.identity_key.to_base58_string(),
gateway_listener: node.clients_address(),
gateway_listener,
gateway_owner: node.owner,
}
}
pub fn from_topology_node_tls(node: nym_topology::gateway::Node) -> Self {
GatewayEndpointConfig {
gateway_id: node.identity_key.to_base58_string(),
gateway_listener: node.clients_address_tls(),
gateway_owner: node.owner,
}
})
}
}
+6
View File
@@ -126,6 +126,12 @@ pub enum ClientCoreError {
#[error("this client has performed gateway initialisation in another session")]
NoInitClientPresent,
#[error("there are no gateways supporting the wss protocol available")]
NoWssGateways,
#[error("the specified gateway '{gateway}' does not support the wss protocol")]
UnsupportedWssProtocol { gateway: String },
}
/// Set of messages that the client can send to listeners via the task manager
+37 -5
View File
@@ -174,7 +174,10 @@ async fn measure_latency(gateway: &gateway::Node) -> Result<GatewayWithLatency,
pub async fn choose_gateway_by_latency<R: Rng>(
rng: &mut R,
gateways: &[gateway::Node],
must_use_tls: bool,
) -> Result<gateway::Node, ClientCoreError> {
let gateways = filter_by_tls(gateways, must_use_tls)?;
info!(
"choosing gateway by latency, pinging {} gateways ...",
gateways.len()
@@ -210,28 +213,57 @@ pub async fn choose_gateway_by_latency<R: Rng>(
Ok(chosen.gateway.clone())
}
fn filter_by_tls(
gateways: &[gateway::Node],
must_use_tls: bool,
) -> Result<Vec<&gateway::Node>, ClientCoreError> {
if must_use_tls {
let filtered = gateways
.iter()
.filter(|g| g.clients_wss_port.is_some())
.collect::<Vec<_>>();
if filtered.is_empty() {
return Err(ClientCoreError::NoWssGateways);
}
Ok(filtered)
} else {
Ok(gateways.iter().collect())
}
}
pub(super) fn uniformly_random_gateway<R: Rng>(
rng: &mut R,
gateways: &[gateway::Node],
must_use_tls: bool,
) -> Result<gateway::Node, ClientCoreError> {
gateways
filter_by_tls(gateways, must_use_tls)?
.choose(rng)
.ok_or(ClientCoreError::NoGatewaysOnNetwork)
.cloned()
.map(|&r| r.clone())
}
pub(super) fn get_specified_gateway(
gateway_identity: IdentityKeyRef,
gateways: &[gateway::Node],
must_use_tls: bool,
) -> Result<gateway::Node, ClientCoreError> {
let user_gateway = identity::PublicKey::from_base58_string(gateway_identity)
.map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)?;
gateways
let gateway = gateways
.iter()
.find(|gateway| gateway.identity_key == user_gateway)
.ok_or_else(|| ClientCoreError::NoGatewayWithId(gateway_identity.to_string()))
.cloned()
.ok_or_else(|| ClientCoreError::NoGatewayWithId(gateway_identity.to_string()))?;
if must_use_tls && gateway.clients_wss_port.is_none() {
return Err(ClientCoreError::UnsupportedWssProtocol {
gateway: gateway_identity.to_string(),
});
}
Ok(gateway.clone())
}
pub(super) async fn register_with_gateway(
+7 -6
View File
@@ -108,19 +108,20 @@ where
let gateway_details = match selection_specification {
GatewaySelectionSpecification::UniformRemote { must_use_tls } => {
let gateway = uniformly_random_gateway(&mut rng, &available_gateways)?;
GatewayDetails::Configured(GatewayEndpointConfig::from_node(gateway, must_use_tls))
let gateway = uniformly_random_gateway(&mut rng, &available_gateways, must_use_tls)?;
GatewayDetails::Configured(GatewayEndpointConfig::from_node(gateway, must_use_tls)?)
}
GatewaySelectionSpecification::RemoteByLatency { must_use_tls } => {
let gateway = choose_gateway_by_latency(&mut rng, &available_gateways).await?;
GatewayDetails::Configured(GatewayEndpointConfig::from_node(gateway, must_use_tls))
let gateway =
choose_gateway_by_latency(&mut rng, &available_gateways, must_use_tls).await?;
GatewayDetails::Configured(GatewayEndpointConfig::from_node(gateway, must_use_tls)?)
}
GatewaySelectionSpecification::Specified {
must_use_tls,
identity,
} => {
let gateway = get_specified_gateway(&identity, &available_gateways)?;
GatewayDetails::Configured(GatewayEndpointConfig::from_node(gateway, must_use_tls))
let gateway = get_specified_gateway(&identity, &available_gateways, must_use_tls)?;
GatewayDetails::Configured(GatewayEndpointConfig::from_node(gateway, must_use_tls)?)
}
GatewaySelectionSpecification::Custom {
gateway_identity,
@@ -24,6 +24,7 @@ nym-service-provider-directory-common = { path = "../../cosmwasm-smart-contracts
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
reqwest = { workspace = true, features = ["json"] }
http-api-client = { path = "../../../common/http-api-client"}
thiserror = { workspace = true }
log = { workspace = true }
url = { workspace = true, features = ["serde"] }
@@ -86,4 +87,5 @@ required-features = ["http-client"]
default = ["http-client"]
http-client = ["cosmrs/rpc", "openssl"]
generate-ts = []
contract-testing = ["nym-mixnet-contract-common/contract-testing"]
@@ -11,7 +11,7 @@ use crate::{
use nym_api_requests::coconut::{
BlindSignRequestBody, BlindedSignatureResponse, VerifyCredentialBody, VerifyCredentialResponse,
};
use nym_api_requests::models::MixNodeBondAnnotated;
use nym_api_requests::models::{DescribedGateway, MixNodeBondAnnotated};
use nym_api_requests::models::{
GatewayCoreStatusResponse, MixnodeCoreStatusResponse, MixnodeStatusResponse,
RewardEstimationResponse, StakeSaturationResponse,
@@ -19,6 +19,7 @@ use nym_api_requests::models::{
use nym_network_defaults::NymNetworkDetails;
use url::Url;
pub use crate::nym_api::NymApiClientExt;
pub use nym_mixnet_contract_common::{
mixnode::MixNodeDetails, GatewayBond, IdentityKey, IdentityKeyRef, MixId,
};
@@ -147,7 +148,7 @@ impl Client<ReqwestRpcClient> {
impl<C> Client<C> {
pub fn new_with_rpc_client(config: Config, rpc_client: C) -> Self {
let nym_api_client = nym_api::Client::new(config.api_url.clone());
let nym_api_client = nym_api::Client::new(config.api_url.clone(), None);
Client {
nym_api: nym_api_client,
@@ -161,7 +162,7 @@ impl<C, S> Client<C, S> {
where
S: OfflineSigner,
{
let nym_api_client = nym_api::Client::new(config.api_url.clone());
let nym_api_client = nym_api::Client::new(config.api_url.clone(), None);
Client {
nym_api: nym_api_client,
@@ -177,7 +178,7 @@ impl<C, S> Client<C, S> {
}
pub fn change_nym_api(&mut self, new_endpoint: Url) {
self.nym_api.change_url(new_endpoint)
self.nym_api.change_base_url(new_endpoint)
}
pub async fn get_cached_mixnodes(&self) -> Result<Vec<MixNodeDetails>, ValidatorClientError> {
@@ -241,7 +242,7 @@ pub struct NymApiClient {
impl NymApiClient {
pub fn new(api_url: Url) -> Self {
let nym_api = nym_api::Client::new(api_url);
let nym_api = nym_api::Client::new(api_url, None);
NymApiClient { nym_api }
}
@@ -251,7 +252,7 @@ impl NymApiClient {
}
pub fn change_nym_api(&mut self, new_endpoint: Url) {
self.nym_api.change_url(new_endpoint);
self.nym_api.change_base_url(new_endpoint);
}
pub async fn get_cached_active_mixnodes(
@@ -274,6 +275,12 @@ impl NymApiClient {
Ok(self.nym_api.get_gateways().await?)
}
pub async fn get_cached_described_gateways(
&self,
) -> Result<Vec<DescribedGateway>, ValidatorClientError> {
Ok(self.nym_api.get_gateways_described().await?)
}
pub async fn get_gateway_core_status_count(
&self,
identity: IdentityKeyRef<'_>,
@@ -1,20 +1,7 @@
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use http_api_client::HttpClientError;
use nym_api_requests::models::RequestError;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum NymAPIError {
#[error("There was an issue with the REST request - {source}")]
ReqwestClientError {
#[from]
source: reqwest::Error,
},
#[error("Not found")]
NotFound,
#[error("Request failed with error message - {0}")]
GenericRequestFailure(String),
#[error("The nym API has failed to resolve our request. It returned status code {status} and additional error message: {}", error.message())]
ApiRequestFailure { status: u16, error: RequestError },
}
pub type NymAPIError = HttpClientError<RequestError>;
@@ -3,140 +3,38 @@
use crate::nym_api::error::NymAPIError;
use crate::nym_api::routes::{CORE_STATUS_COUNT, SINCE_ARG};
use async_trait::async_trait;
use http_api_client::{ApiClient, NO_PARAMS};
use nym_api_requests::coconut::{
BlindSignRequestBody, BlindedSignatureResponse, VerifyCredentialBody, VerifyCredentialResponse,
};
use nym_api_requests::models::{
ComputeRewardEstParam, GatewayBondAnnotated, GatewayCoreStatusResponse,
ComputeRewardEstParam, DescribedGateway, GatewayBondAnnotated, GatewayCoreStatusResponse,
GatewayStatusReportResponse, GatewayUptimeHistoryResponse, InclusionProbabilityResponse,
MixNodeBondAnnotated, MixnodeCoreStatusResponse, MixnodeStatusReportResponse,
MixnodeStatusResponse, MixnodeUptimeHistoryResponse, RequestError, RewardEstimationResponse,
MixnodeStatusResponse, MixnodeUptimeHistoryResponse, RewardEstimationResponse,
StakeSaturationResponse, UptimeResponse,
};
use nym_mixnet_contract_common::mixnode::MixNodeDetails;
use nym_mixnet_contract_common::{GatewayBond, IdentityKeyRef, MixId};
use nym_name_service_common::response::NamesListResponse;
use nym_service_provider_directory_common::response::ServicesListResponse;
use reqwest::{Response, StatusCode};
use serde::{Deserialize, Serialize};
use url::Url;
pub mod error;
pub mod routes;
type PathSegments<'a> = &'a [&'a str];
type Params<'a, K, V> = &'a [(K, V)];
pub use http_api_client::Client;
const NO_PARAMS: Params<'_, &'_ str, &'_ str> = &[];
#[derive(Clone)]
pub struct Client {
url: Url,
reqwest_client: reqwest::Client,
}
impl Client {
pub fn new(url: Url) -> Self {
let reqwest_client = reqwest::Client::new();
Self {
url,
reqwest_client,
}
}
pub fn change_url(&mut self, new_url: Url) {
self.url = new_url
}
pub fn current_url(&self) -> &Url {
&self.url
}
async fn send_get_request<K, V>(
&self,
path: PathSegments<'_>,
params: Params<'_, K, V>,
) -> Result<Response, NymAPIError>
where
K: AsRef<str>,
V: AsRef<str>,
{
let url = create_api_url(&self.url, path, params);
Ok(self.reqwest_client.get(url).send().await?)
}
async fn query_nym_api<T, K, V>(
&self,
path: PathSegments<'_>,
params: Params<'_, K, V>,
) -> Result<T, NymAPIError>
where
for<'a> T: Deserialize<'a>,
K: AsRef<str>,
V: AsRef<str>,
{
let res = self.send_get_request(path, params).await?;
if res.status().is_success() {
Ok(res.json().await?)
} else if res.status() == StatusCode::NOT_FOUND {
Err(NymAPIError::NotFound)
} else {
Err(NymAPIError::GenericRequestFailure(res.text().await?))
}
}
// This works for endpoints returning Result<Json<T>, ErrorResponse>
async fn query_nym_api_fallible<T, K, V>(
&self,
path: PathSegments<'_>,
params: Params<'_, K, V>,
) -> Result<T, NymAPIError>
where
for<'a> T: Deserialize<'a>,
K: AsRef<str>,
V: AsRef<str>,
{
let res = self.send_get_request(path, params).await?;
let status = res.status();
if res.status().is_success() {
Ok(res.json().await?)
} else {
let request_error: RequestError = res.json().await?;
Err(NymAPIError::ApiRequestFailure {
status: status.as_u16(),
error: request_error,
})
}
}
async fn post_nym_api<B, T, K, V>(
&self,
path: PathSegments<'_>,
params: Params<'_, K, V>,
json_body: &B,
) -> Result<T, NymAPIError>
where
B: Serialize + ?Sized,
for<'a> T: Deserialize<'a>,
K: AsRef<str>,
V: AsRef<str>,
{
let url = create_api_url(&self.url, path, params);
let response = self.reqwest_client.post(url).json(json_body).send().await?;
if response.status().is_success() {
Ok(response.json().await?)
} else {
Err(NymAPIError::GenericRequestFailure(response.text().await?))
}
}
pub async fn get_mixnodes(&self) -> Result<Vec<MixNodeDetails>, NymAPIError> {
self.query_nym_api(&[routes::API_VERSION, routes::MIXNODES], NO_PARAMS)
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
pub trait NymApiClientExt: ApiClient {
async fn get_mixnodes(&self) -> Result<Vec<MixNodeDetails>, NymAPIError> {
self.get_json(&[routes::API_VERSION, routes::MIXNODES], NO_PARAMS)
.await
}
pub async fn get_mixnodes_detailed(&self) -> Result<Vec<MixNodeBondAnnotated>, NymAPIError> {
self.query_nym_api(
async fn get_mixnodes_detailed(&self) -> Result<Vec<MixNodeBondAnnotated>, NymAPIError> {
self.get_json(
&[
routes::API_VERSION,
routes::STATUS,
@@ -148,8 +46,8 @@ impl Client {
.await
}
pub async fn get_gateways_detailed(&self) -> Result<Vec<GatewayBondAnnotated>, NymAPIError> {
self.query_nym_api(
async fn get_gateways_detailed(&self) -> Result<Vec<GatewayBondAnnotated>, NymAPIError> {
self.get_json(
&[
routes::API_VERSION,
routes::STATUS,
@@ -161,10 +59,10 @@ impl Client {
.await
}
pub async fn get_mixnodes_detailed_unfiltered(
async fn get_mixnodes_detailed_unfiltered(
&self,
) -> Result<Vec<MixNodeBondAnnotated>, NymAPIError> {
self.query_nym_api(
self.get_json(
&[
routes::API_VERSION,
routes::STATUS,
@@ -176,23 +74,29 @@ impl Client {
.await
}
pub async fn get_gateways(&self) -> Result<Vec<GatewayBond>, NymAPIError> {
self.query_nym_api(&[routes::API_VERSION, routes::GATEWAYS], NO_PARAMS)
async fn get_gateways(&self) -> Result<Vec<GatewayBond>, NymAPIError> {
self.get_json(&[routes::API_VERSION, routes::GATEWAYS], NO_PARAMS)
.await
}
pub async fn get_active_mixnodes(&self) -> Result<Vec<MixNodeDetails>, NymAPIError> {
self.query_nym_api(
async fn get_gateways_described(&self) -> Result<Vec<DescribedGateway>, NymAPIError> {
self.get_json(
&[routes::API_VERSION, routes::GATEWAYS, routes::DESCRIBED],
NO_PARAMS,
)
.await
}
async fn get_active_mixnodes(&self) -> Result<Vec<MixNodeDetails>, NymAPIError> {
self.get_json(
&[routes::API_VERSION, routes::MIXNODES, routes::ACTIVE],
NO_PARAMS,
)
.await
}
pub async fn get_active_mixnodes_detailed(
&self,
) -> Result<Vec<MixNodeBondAnnotated>, NymAPIError> {
self.query_nym_api(
async fn get_active_mixnodes_detailed(&self) -> Result<Vec<MixNodeBondAnnotated>, NymAPIError> {
self.get_json(
&[
routes::API_VERSION,
routes::STATUS,
@@ -205,19 +109,19 @@ impl Client {
.await
}
pub async fn get_rewarded_mixnodes(&self) -> Result<Vec<MixNodeDetails>, NymAPIError> {
self.query_nym_api(
async fn get_rewarded_mixnodes(&self) -> Result<Vec<MixNodeDetails>, NymAPIError> {
self.get_json(
&[routes::API_VERSION, routes::MIXNODES, routes::REWARDED],
NO_PARAMS,
)
.await
}
pub async fn get_mixnode_report(
async fn get_mixnode_report(
&self,
mix_id: MixId,
) -> Result<MixnodeStatusReportResponse, NymAPIError> {
self.query_nym_api(
self.get_json(
&[
routes::API_VERSION,
routes::STATUS,
@@ -230,11 +134,11 @@ impl Client {
.await
}
pub async fn get_gateway_report(
async fn get_gateway_report(
&self,
identity: IdentityKeyRef<'_>,
) -> Result<GatewayStatusReportResponse, NymAPIError> {
self.query_nym_api(
self.get_json(
&[
routes::API_VERSION,
routes::STATUS,
@@ -247,11 +151,11 @@ impl Client {
.await
}
pub async fn get_mixnode_history(
async fn get_mixnode_history(
&self,
mix_id: MixId,
) -> Result<MixnodeUptimeHistoryResponse, NymAPIError> {
self.query_nym_api(
self.get_json(
&[
routes::API_VERSION,
routes::STATUS,
@@ -264,11 +168,11 @@ impl Client {
.await
}
pub async fn get_gateway_history(
async fn get_gateway_history(
&self,
identity: IdentityKeyRef<'_>,
) -> Result<GatewayUptimeHistoryResponse, NymAPIError> {
self.query_nym_api(
self.get_json(
&[
routes::API_VERSION,
routes::STATUS,
@@ -281,10 +185,10 @@ impl Client {
.await
}
pub async fn get_rewarded_mixnodes_detailed(
async fn get_rewarded_mixnodes_detailed(
&self,
) -> Result<Vec<MixNodeBondAnnotated>, NymAPIError> {
self.query_nym_api(
self.get_json(
&[
routes::API_VERSION,
routes::STATUS,
@@ -297,13 +201,13 @@ impl Client {
.await
}
pub async fn get_gateway_core_status_count(
async fn get_gateway_core_status_count(
&self,
identity: IdentityKeyRef<'_>,
since: Option<i64>,
) -> Result<GatewayCoreStatusResponse, NymAPIError> {
if let Some(since) = since {
self.query_nym_api(
self.get_json(
&[
routes::API_VERSION,
routes::STATUS_ROUTES,
@@ -315,7 +219,7 @@ impl Client {
)
.await
} else {
self.query_nym_api(
self.get_json(
&[
routes::API_VERSION,
routes::STATUS_ROUTES,
@@ -328,13 +232,13 @@ impl Client {
}
}
pub async fn get_mixnode_core_status_count(
async fn get_mixnode_core_status_count(
&self,
mix_id: MixId,
since: Option<i64>,
) -> Result<MixnodeCoreStatusResponse, NymAPIError> {
if let Some(since) = since {
self.query_nym_api(
self.get_json(
&[
routes::API_VERSION,
routes::STATUS_ROUTES,
@@ -346,7 +250,7 @@ impl Client {
)
.await
} else {
self.query_nym_api(
self.get_json(
&[
routes::API_VERSION,
routes::STATUS_ROUTES,
@@ -360,11 +264,11 @@ impl Client {
}
}
pub async fn get_mixnode_status(
async fn get_mixnode_status(
&self,
mix_id: MixId,
) -> Result<MixnodeStatusResponse, NymAPIError> {
self.query_nym_api(
self.get_json(
&[
routes::API_VERSION,
routes::STATUS_ROUTES,
@@ -377,11 +281,11 @@ impl Client {
.await
}
pub async fn get_mixnode_reward_estimation(
async fn get_mixnode_reward_estimation(
&self,
mix_id: MixId,
) -> Result<RewardEstimationResponse, NymAPIError> {
self.query_nym_api_fallible(
self.get_json(
&[
routes::API_VERSION,
routes::STATUS_ROUTES,
@@ -394,12 +298,12 @@ impl Client {
.await
}
pub async fn compute_mixnode_reward_estimation(
async fn compute_mixnode_reward_estimation(
&self,
mix_id: MixId,
request_body: &ComputeRewardEstParam,
) -> Result<RewardEstimationResponse, NymAPIError> {
self.post_nym_api(
self.post_json(
&[
routes::API_VERSION,
routes::STATUS_ROUTES,
@@ -413,11 +317,11 @@ impl Client {
.await
}
pub async fn get_mixnode_stake_saturation(
async fn get_mixnode_stake_saturation(
&self,
mix_id: MixId,
) -> Result<StakeSaturationResponse, NymAPIError> {
self.query_nym_api_fallible(
self.get_json(
&[
routes::API_VERSION,
routes::STATUS_ROUTES,
@@ -430,11 +334,11 @@ impl Client {
.await
}
pub async fn get_mixnode_inclusion_probability(
async fn get_mixnode_inclusion_probability(
&self,
mix_id: MixId,
) -> Result<InclusionProbabilityResponse, NymAPIError> {
self.query_nym_api_fallible(
self.get_json(
&[
routes::API_VERSION,
routes::STATUS_ROUTES,
@@ -447,11 +351,8 @@ impl Client {
.await
}
pub async fn get_mixnode_avg_uptime(
&self,
mix_id: MixId,
) -> Result<UptimeResponse, NymAPIError> {
self.query_nym_api_fallible(
async fn get_mixnode_avg_uptime(&self, mix_id: MixId) -> Result<UptimeResponse, NymAPIError> {
self.get_json(
&[
routes::API_VERSION,
routes::STATUS_ROUTES,
@@ -464,11 +365,11 @@ impl Client {
.await
}
pub async fn blind_sign(
async fn blind_sign(
&self,
request_body: &BlindSignRequestBody,
) -> Result<BlindedSignatureResponse, NymAPIError> {
self.post_nym_api(
self.post_json(
&[
routes::API_VERSION,
routes::COCONUT_ROUTES,
@@ -481,11 +382,11 @@ impl Client {
.await
}
pub async fn verify_bandwidth_credential(
async fn verify_bandwidth_credential(
&self,
request_body: &VerifyCredentialBody,
) -> Result<VerifyCredentialResponse, NymAPIError> {
self.post_nym_api(
self.post_json(
&[
routes::API_VERSION,
routes::COCONUT_ROUTES,
@@ -498,118 +399,20 @@ impl Client {
.await
}
pub async fn get_service_providers(&self) -> Result<ServicesListResponse, NymAPIError> {
async fn get_service_providers(&self) -> Result<ServicesListResponse, NymAPIError> {
log::trace!("Getting service providers");
self.query_nym_api(&[routes::API_VERSION, routes::SERVICE_PROVIDERS], NO_PARAMS)
self.get_json(&[routes::API_VERSION, routes::SERVICE_PROVIDERS], NO_PARAMS)
.await
}
//pub async fn get_registered_names(&self) -> Result<Vec<NameEntry>, NymAPIError> {
pub async fn get_registered_names(&self) -> Result<NamesListResponse, NymAPIError> {
//async fn get_registered_names(&self) -> Result<Vec<NameEntry>, NymAPIError> {
async fn get_registered_names(&self) -> Result<NamesListResponse, NymAPIError> {
log::trace!("Getting registered names");
self.query_nym_api(&[routes::API_VERSION, routes::REGISTERED_NAMES], NO_PARAMS)
self.get_json(&[routes::API_VERSION, routes::REGISTERED_NAMES], NO_PARAMS)
.await
}
}
// utility function that should solve the double slash problem in validator API forever.
fn create_api_url<K: AsRef<str>, V: AsRef<str>>(
base: &Url,
segments: PathSegments<'_>,
params: Params<'_, K, V>,
) -> Url {
let mut url = base.clone();
let mut path_segments = url
.path_segments_mut()
.expect("provided validator url does not have a base!");
for segment in segments {
let segment = segment.strip_prefix('/').unwrap_or(segment);
let segment = segment.strip_suffix('/').unwrap_or(segment);
path_segments.push(segment);
}
// I don't understand why compiler couldn't figure out that it's no longer used
// and can be dropped
drop(path_segments);
if !params.is_empty() {
url.query_pairs_mut().extend_pairs(params);
}
url
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn creating_api_path() {
let base_url: Url = "http://foomp.com".parse().unwrap();
// works with 1 segment
assert_eq!(
"http://foomp.com/foo",
create_api_url(&base_url, &["foo"], NO_PARAMS).as_str()
);
// works with 2 segments
assert_eq!(
"http://foomp.com/foo/bar",
create_api_url(&base_url, &["foo", "bar"], NO_PARAMS).as_str()
);
// works with leading slash
assert_eq!(
"http://foomp.com/foo",
create_api_url(&base_url, &["/foo"], NO_PARAMS).as_str()
);
assert_eq!(
"http://foomp.com/foo/bar",
create_api_url(&base_url, &["/foo", "bar"], NO_PARAMS).as_str()
);
assert_eq!(
"http://foomp.com/foo/bar",
create_api_url(&base_url, &["foo", "/bar"], NO_PARAMS).as_str()
);
// works with trailing slash
assert_eq!(
"http://foomp.com/foo",
create_api_url(&base_url, &["foo/"], NO_PARAMS).as_str()
);
assert_eq!(
"http://foomp.com/foo/bar",
create_api_url(&base_url, &["foo/", "bar"], NO_PARAMS).as_str()
);
assert_eq!(
"http://foomp.com/foo/bar",
create_api_url(&base_url, &["foo", "bar/"], NO_PARAMS).as_str()
);
// works with both leading and trailing slash
assert_eq!(
"http://foomp.com/foo",
create_api_url(&base_url, &["/foo/"], NO_PARAMS).as_str()
);
assert_eq!(
"http://foomp.com/foo/bar",
create_api_url(&base_url, &["/foo/", "/bar/"], NO_PARAMS).as_str()
);
// adds params
assert_eq!(
"http://foomp.com/foo/bar?foomp=baz",
create_api_url(&base_url, &["foo", "bar"], &[("foomp", "baz")]).as_str()
);
assert_eq!(
"http://foomp.com/foo/bar?arg1=val1&arg2=val2",
create_api_url(
&base_url,
&["/foo/", "/bar/"],
&[("arg1", "val1"), ("arg2", "val2")]
)
.as_str()
);
}
}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl NymApiClientExt for Client {}
@@ -6,6 +6,7 @@ use nym_network_defaults::NYM_API_VERSION;
pub const API_VERSION: &str = NYM_API_VERSION;
pub const MIXNODES: &str = "mixnodes";
pub const GATEWAYS: &str = "gateways";
pub const DESCRIBED: &str = "described";
pub const DETAILED: &str = "detailed";
pub const DETAILED_UNFILTERED: &str = "detailed-unfiltered";
@@ -683,13 +683,14 @@ pub trait MixnetSigningClient {
.await
}
#[cfg(feature = "nym_mixnet_contract_common/contract-testing")]
#[cfg(feature = "contract-testing")]
async fn testing_resolve_all_pending_events(
&self,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_mixnet_contract(
fee,
MixnetExecuteMsg::TestingResolveAllPendingEvents {},
MixnetExecuteMsg::TestingResolveAllPendingEvents { limit: None },
vec![],
)
.await
@@ -928,8 +929,8 @@ mod tests {
.withdraw_delegator_reward_on_behalf(owner.parse().unwrap(), mix_id, None)
.ignore(),
#[cfg(feature = "nym_mixnet_contract_common/contract-testing")]
MixnetExecuteMsg::TestingResolveAllPendingEvents {} => {
#[cfg(feature = "contract-testing")]
MixnetExecuteMsg::TestingResolveAllPendingEvents { .. } => {
client.testing_resolve_all_pending_events(None).ignore()
}
};
+1 -1
View File
@@ -8,6 +8,6 @@ description = "Crutch library until there is proper SerDe support for coconut st
bs58 = "0.4.0"
getset = "0.1.1"
serde = { workspace = true, features = ["derive"] }
thiserror = "1"
thiserror = { workspace = true }
nym-coconut = {path = "../nymcoconut" }
@@ -1,11 +1,11 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::Parser;
use comfy_table::Table;
use crate::context::QueryClientWithNyxd;
use crate::utils::{pretty_cosmwasm_coin, show_error};
use clap::Parser;
use comfy_table::Table;
use nym_validator_client::client::NymApiClientExt;
#[derive(Debug, Parser)]
pub struct Args {
@@ -1,11 +1,11 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::Parser;
use comfy_table::Table;
use crate::context::QueryClientWithNyxd;
use crate::utils::{pretty_decimal_with_denom, show_error};
use clap::Parser;
use comfy_table::Table;
use nym_validator_client::client::NymApiClientExt;
#[derive(Debug, Parser)]
pub struct Args {
@@ -1,12 +1,12 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::Parser;
use comfy_table::Table;
use nym_validator_client::nym_api::error::NymAPIError;
use crate::context::QueryClientWithNyxd;
use crate::utils::show_error;
use clap::Parser;
use comfy_table::Table;
use nym_validator_client::client::NymApiClientExt;
use nym_validator_client::nym_api::error::NymAPIError;
#[derive(Debug, Parser)]
pub struct Args {
@@ -1,12 +1,12 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::Parser;
use comfy_table::Table;
use nym_validator_client::nym_api::error::NymAPIError;
use crate::context::QueryClientWithNyxd;
use crate::utils::show_error;
use clap::Parser;
use comfy_table::Table;
use nym_validator_client::client::NymApiClientExt;
use nym_validator_client::nym_api::error::NymAPIError;
#[derive(Debug, Parser)]
pub struct Args {
@@ -18,7 +18,7 @@ serde_repr = "0.1"
# we still have to preserve that import for `JsonSchema` for `Layer` type (since we can't use cw_serde macro due to custom serde impl)
schemars = "0.8"
thiserror = "1.0"
thiserror = { workspace = true }
contracts-common = { path = "../contracts-common", package = "nym-contracts-common", version = "0.5.0" }
serde-json-wasm = { workspace = true }
humantime-serde = "1.1.1"
@@ -14,4 +14,4 @@ cosmwasm-schema = { workspace = true }
cosmwasm-std = { workspace = true }
schemars = "0.8"
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
thiserror = { version = "1.0.23" }
thiserror = { workspace = true }
@@ -14,7 +14,7 @@ cw2 = { workspace = true, optional = true }
mixnet-contract-common = { path = "../mixnet-contract", package = "nym-mixnet-contract-common", version = "0.6.0" }
contracts-common = { path = "../contracts-common", package = "nym-contracts-common", version = "0.5.0" }
serde = { version = "1.0", features = ["derive"] }
thiserror = "1.0"
thiserror = { workspace = true }
ts-rs = { workspace = true, optional = true}
[features]
+1 -1
View File
@@ -9,7 +9,7 @@ edition = "2021"
async-trait = { workspace = true }
log = { workspace = true }
thiserror = "1.0"
thiserror = { workspace = true }
tokio = { version = "1.24.1", features = ["sync"]}
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.sqlx]
+1 -1
View File
@@ -23,7 +23,7 @@ rand = { version = "0.7.3", features = ["wasm-bindgen"], optional = true }
serde_bytes = { version = "0.11.6", optional = true }
serde_crate = { version = "1.0", optional = true, default_features = false, features = ["derive"], package = "serde" }
subtle-encoding = { version = "0.5", features = ["bech32-preview"]}
thiserror = "1.0.37"
thiserror = { workspace = true }
zeroize = { workspace = true, optional = true, features = ["zeroize_derive"] }
# internal
+9 -5
View File
@@ -145,8 +145,12 @@ impl PublicKey {
Self::from_bytes(&bytes)
}
pub fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), SignatureError> {
self.0.verify(message, &signature.0)
pub fn verify<M: AsRef<[u8]>>(
&self,
message: M,
signature: &Signature,
) -> Result<(), SignatureError> {
self.0.verify(message.as_ref(), &signature.0)
}
}
@@ -239,16 +243,16 @@ impl PrivateKey {
Self::from_bytes(&bytes)
}
pub fn sign(&self, message: &[u8]) -> Signature {
pub fn sign<M: AsRef<[u8]>>(&self, message: M) -> Signature {
let expanded_secret_key = ed25519_dalek::ExpandedSecretKey::from(&self.0);
let public_key: PublicKey = self.into();
let sig = expanded_secret_key.sign(message, &public_key.0);
let sig = expanded_secret_key.sign(message.as_ref(), &public_key.0);
Signature(sig)
}
/// Signs text with the provided Ed25519 private key, returning a base58 signature
pub fn sign_text(&self, text: &str) -> String {
let signature_bytes = self.sign(text.as_ref()).to_bytes();
let signature_bytes = self.sign(text).to_bytes();
bs58::encode(signature_bytes).into_string()
}
}
+3 -3
View File
@@ -21,10 +21,10 @@ rand = { version = "0.8.5", default-features = false}
rand_chacha = "0.3"
rand_core = "0.6.3"
sha2 = "0.9"
serde = "1.0"
serde = { workspace = true }
serde_derive = "1.0"
thiserror = "1.0"
zeroize = { version = "1.4", features = ["zeroize_derive"] }
thiserror = { workspace = true }
zeroize = { workspace = true, features = ["zeroize_derive"] }
nym-pemstore = { path = "../pemstore" }
+25
View File
@@ -0,0 +1,25 @@
[package]
name = "http-api-client"
version = "0.1.0"
authors.workspace = true
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
edition.workspace = true
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
async-trait = { workspace = true }
reqwest = { workspace = true, features = ["json"] }
url = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
thiserror = { workspace = true }
tracing = { workspace = true }
# for request timeout until https://github.com/seanmonstar/reqwest/issues/1135 is fixed
[target."cfg(target_arch = \"wasm32\")".dependencies.wasmtimer]
workspace = true
features = ["tokio"]
+512
View File
@@ -0,0 +1,512 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use async_trait::async_trait;
use reqwest::{IntoUrl, Response, StatusCode};
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use std::fmt::Display;
use std::time::Duration;
use thiserror::Error;
use tracing::warn;
use url::Url;
pub const DEFAULT_TIMEOUT: Duration = Duration::from_secs(3);
pub type PathSegments<'a> = &'a [&'a str];
pub type Params<'a, K, V> = &'a [(K, V)];
pub const NO_PARAMS: Params<'_, &'_ str, &'_ str> = &[];
#[derive(Debug, Error)]
pub enum HttpClientError<E: Display = String> {
#[error("there was an issue with the REST request: {source}")]
ReqwestClientError {
#[from]
source: reqwest::Error,
},
#[error("provided url is malformed: {source}")]
MalformedUrl {
#[from]
source: url::ParseError,
},
#[error("the requested resource could not be found")]
NotFound,
#[error("request failed with error message: {0}")]
GenericRequestFailure(String),
#[error("the request failed with status '{status}'. no additional error message provided")]
RequestFailure { status: StatusCode },
#[error("the returned response was empty. status: '{status}'")]
EmptyResponse { status: StatusCode },
#[error("failed to resolve request. status: '{status}', additional error message: {error}")]
EndpointFailure { status: StatusCode, error: E },
#[cfg(target_arch = "wasm32")]
#[error("the request has timed out")]
RequestTimeout,
}
/// A simple extendable client wrapper for http request with extra url sanitization.
#[derive(Debug, Clone)]
pub struct Client {
base_url: Url,
reqwest_client: reqwest::Client,
#[cfg(target_arch = "wasm32")]
request_timeout: Duration,
}
impl Client {
// no timeout until https://github.com/seanmonstar/reqwest/issues/1135 is fixed
pub fn new(base_url: Url, timeout: Option<Duration>) -> Self {
#[cfg(target_arch = "wasm32")]
let reqwest_client = reqwest::Client::new();
// TODO: we should probably be propagating the error rather than panicking,
// but that'd break bunch of things due to type changes
#[cfg(not(target_arch = "wasm32"))]
let reqwest_client = reqwest::ClientBuilder::new()
.timeout(timeout.unwrap_or(DEFAULT_TIMEOUT))
.user_agent(format!("nym-http-api-client/{}", env!("CARGO_PKG_VERSION")))
.build()
.expect("Client::new()");
Client {
base_url,
reqwest_client,
#[cfg(target_arch = "wasm32")]
request_timeout: timeout.unwrap_or(DEFAULT_TIMEOUT),
}
}
pub fn new_url<U, E>(url: U, timeout: Option<Duration>) -> Result<Self, HttpClientError<E>>
where
U: IntoUrl,
E: Display,
{
// a naive check: if the provided URL does not start with http(s), add that scheme
let str_url = url.as_str();
if !str_url.starts_with("http") {
let alt = format!("http://{str_url}");
warn!("the provided url ('{str_url}') does not contain scheme information. Changing it to '{alt}' ...");
// TODO: or should we maybe default to https?
Self::new_url(alt, timeout)
} else {
Ok(Self::new(url.into_url()?, timeout))
}
}
pub fn change_base_url(&mut self, new_url: Url) {
self.base_url = new_url
}
pub fn current_url(&self) -> &Url {
&self.base_url
}
async fn send_get_request<K, V, E>(
&self,
path: PathSegments<'_>,
params: Params<'_, K, V>,
) -> Result<Response, HttpClientError<E>>
where
K: AsRef<str>,
V: AsRef<str>,
E: Display,
{
let url = sanitize_url(&self.base_url, path, params);
#[cfg(target_arch = "wasm32")]
{
Ok(
wasmtimer::tokio::timeout(
self.request_timeout,
self.reqwest_client.get(url).send(),
)
.await
.map_err(|_timeout| HttpClientError::RequestTimeout)??,
)
}
#[cfg(not(target_arch = "wasm32"))]
{
Ok(self.reqwest_client.get(url).send().await?)
}
}
async fn send_post_request<B, K, V, E>(
&self,
path: PathSegments<'_>,
params: Params<'_, K, V>,
json_body: &B,
) -> Result<Response, HttpClientError<E>>
where
B: Serialize + ?Sized,
K: AsRef<str>,
V: AsRef<str>,
E: Display,
{
let url = sanitize_url(&self.base_url, path, params);
#[cfg(target_arch = "wasm32")]
{
Ok(wasmtimer::tokio::timeout(
self.request_timeout,
self.reqwest_client.post(url).json(json_body).send(),
)
.await
.map_err(|_timeout| HttpClientError::RequestTimeout)??)
}
#[cfg(not(target_arch = "wasm32"))]
{
Ok(self.reqwest_client.post(url).json(json_body).send().await?)
}
}
pub async fn get_json<T, K, V, E>(
&self,
path: PathSegments<'_>,
params: Params<'_, K, V>,
) -> Result<T, HttpClientError<E>>
where
for<'a> T: Deserialize<'a>,
K: AsRef<str>,
V: AsRef<str>,
E: Display + DeserializeOwned,
{
let res = self.send_get_request(path, params).await?;
parse_response(res, false).await
}
pub async fn post_json<B, T, K, V, E>(
&self,
path: PathSegments<'_>,
params: Params<'_, K, V>,
json_body: &B,
) -> Result<T, HttpClientError<E>>
where
B: Serialize + ?Sized,
for<'a> T: Deserialize<'a>,
K: AsRef<str>,
V: AsRef<str>,
E: Display + DeserializeOwned,
{
let res = self.send_post_request(path, params, json_body).await?;
parse_response(res, true).await
}
pub async fn get_json_endpoint<T, S, E>(&self, endpoint: S) -> Result<T, HttpClientError<E>>
where
for<'a> T: Deserialize<'a>,
E: Display + DeserializeOwned,
S: AsRef<str>,
{
#[cfg(target_arch = "wasm32")]
let res = {
wasmtimer::tokio::timeout(
self.request_timeout,
self.reqwest_client
.get(self.base_url.join(endpoint.as_ref())?)
.send(),
)
.await
.map_err(|_timeout| HttpClientError::RequestTimeout)??
};
#[cfg(not(target_arch = "wasm32"))]
let res = {
self.reqwest_client
.get(self.base_url.join(endpoint.as_ref())?)
.send()
.await?
};
parse_response(res, false).await
}
pub async fn post_json_endpoint<B, T, S, E>(
&self,
endpoint: S,
json_body: &B,
) -> Result<T, HttpClientError<E>>
where
B: Serialize + ?Sized,
for<'a> T: Deserialize<'a>,
E: Display + DeserializeOwned,
S: AsRef<str>,
{
#[cfg(target_arch = "wasm32")]
let res = {
wasmtimer::tokio::timeout(
self.request_timeout,
self.reqwest_client
.post(self.base_url.join(endpoint.as_ref())?)
.json(json_body)
.send(),
)
.await
.map_err(|_timeout| HttpClientError::RequestTimeout)??
};
#[cfg(not(target_arch = "wasm32"))]
let res = {
self.reqwest_client
.post(self.base_url.join(endpoint.as_ref())?)
.json(json_body)
.send()
.await?
};
parse_response(res, true).await
}
}
// define those methods on the trait for nicer extensions (and not having to type the thing twice)
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
pub trait ApiClient {
/// 'get' json data from the segment-defined path, i.e. for example `["api", "v1", "mixnodes"]`,
/// with tuple defined key-value parameters, i.e. for example `[("since", "12345")]`
async fn get_json<T, K, V, E>(
&self,
path: PathSegments<'_>,
params: Params<'_, K, V>,
) -> Result<T, HttpClientError<E>>
where
for<'a> T: Deserialize<'a>,
K: AsRef<str> + Sync,
V: AsRef<str> + Sync,
E: Display + DeserializeOwned;
async fn post_json<B, T, K, V, E>(
&self,
path: PathSegments<'_>,
params: Params<'_, K, V>,
json_body: &B,
) -> Result<T, HttpClientError<E>>
where
B: Serialize + ?Sized + Sync,
for<'a> T: Deserialize<'a>,
K: AsRef<str> + Sync,
V: AsRef<str> + Sync,
E: Display + DeserializeOwned;
/// `get` json data from the provided absolute endpoint, i.e. for example `"/api/v1/mixnodes?since=12345"`
async fn get_json_from<T, S, E>(&self, endpoint: S) -> Result<T, HttpClientError<E>>
where
for<'a> T: Deserialize<'a>,
E: Display + DeserializeOwned,
S: AsRef<str> + Sync + Send;
async fn post_json_data_to<B, T, S, E>(
&self,
endpoint: S,
json_body: &B,
) -> Result<T, HttpClientError<E>>
where
B: Serialize + ?Sized + Sync,
for<'a> T: Deserialize<'a>,
E: Display + DeserializeOwned,
S: AsRef<str> + Sync + Send;
}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl ApiClient for Client {
async fn get_json<T, K, V, E>(
&self,
path: PathSegments<'_>,
params: Params<'_, K, V>,
) -> Result<T, HttpClientError<E>>
where
for<'a> T: Deserialize<'a>,
K: AsRef<str> + Sync,
V: AsRef<str> + Sync,
E: Display + DeserializeOwned,
{
self.get_json(path, params).await
}
async fn post_json<B, T, K, V, E>(
&self,
path: PathSegments<'_>,
params: Params<'_, K, V>,
json_body: &B,
) -> Result<T, HttpClientError<E>>
where
B: Serialize + ?Sized + Sync,
for<'a> T: Deserialize<'a>,
K: AsRef<str> + Sync,
V: AsRef<str> + Sync,
E: Display + DeserializeOwned,
{
self.post_json(path, params, json_body).await
}
async fn get_json_from<T, S, E>(&self, endpoint: S) -> Result<T, HttpClientError<E>>
where
for<'a> T: Deserialize<'a>,
E: Display + DeserializeOwned,
S: AsRef<str> + Sync + Send,
{
self.get_json_endpoint(endpoint).await
}
async fn post_json_data_to<B, T, S, E>(
&self,
endpoint: S,
json_body: &B,
) -> Result<T, HttpClientError<E>>
where
B: Serialize + ?Sized + Sync,
for<'a> T: Deserialize<'a>,
E: Display + DeserializeOwned,
S: AsRef<str> + Sync + Send,
{
self.post_json_endpoint(endpoint, json_body).await
}
}
// utility function that should solve the double slash problem in API urls forever.
pub fn sanitize_url<K: AsRef<str>, V: AsRef<str>>(
base: &Url,
segments: PathSegments<'_>,
params: Params<'_, K, V>,
) -> Url {
let mut url = base.clone();
let mut path_segments = url
.path_segments_mut()
.expect("provided validator url does not have a base!");
for segment in segments {
let segment = segment.strip_prefix('/').unwrap_or(segment);
let segment = segment.strip_suffix('/').unwrap_or(segment);
path_segments.push(segment);
}
// I don't understand why compiler couldn't figure out that it's no longer used
// and can be dropped
drop(path_segments);
if !params.is_empty() {
url.query_pairs_mut().extend_pairs(params);
}
url
}
async fn parse_response<T, E>(res: Response, allow_empty: bool) -> Result<T, HttpClientError<E>>
where
T: DeserializeOwned,
E: DeserializeOwned + Display,
{
let status = res.status();
if !allow_empty {
if let Some(0) = res.content_length() {
return Err(HttpClientError::EmptyResponse { status });
}
}
if res.status().is_success() {
Ok(res.json().await?)
} else if res.status() == StatusCode::NOT_FOUND {
Err(HttpClientError::NotFound)
} else {
let Ok(plaintext) = res.text().await else {
return Err(HttpClientError::RequestFailure { status });
};
if let Ok(request_error) = serde_json::from_str(&plaintext) {
Err(HttpClientError::EndpointFailure {
status,
error: request_error,
})
} else {
Err(HttpClientError::GenericRequestFailure(plaintext))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn sanitizing_urls() {
let base_url: Url = "http://foomp.com".parse().unwrap();
// works with 1 segment
assert_eq!(
"http://foomp.com/foo",
sanitize_url(&base_url, &["foo"], NO_PARAMS).as_str()
);
// works with 2 segments
assert_eq!(
"http://foomp.com/foo/bar",
sanitize_url(&base_url, &["foo", "bar"], NO_PARAMS).as_str()
);
// works with leading slash
assert_eq!(
"http://foomp.com/foo",
sanitize_url(&base_url, &["/foo"], NO_PARAMS).as_str()
);
assert_eq!(
"http://foomp.com/foo/bar",
sanitize_url(&base_url, &["/foo", "bar"], NO_PARAMS).as_str()
);
assert_eq!(
"http://foomp.com/foo/bar",
sanitize_url(&base_url, &["foo", "/bar"], NO_PARAMS).as_str()
);
// works with trailing slash
assert_eq!(
"http://foomp.com/foo",
sanitize_url(&base_url, &["foo/"], NO_PARAMS).as_str()
);
assert_eq!(
"http://foomp.com/foo/bar",
sanitize_url(&base_url, &["foo/", "bar"], NO_PARAMS).as_str()
);
assert_eq!(
"http://foomp.com/foo/bar",
sanitize_url(&base_url, &["foo", "bar/"], NO_PARAMS).as_str()
);
// works with both leading and trailing slash
assert_eq!(
"http://foomp.com/foo",
sanitize_url(&base_url, &["/foo/"], NO_PARAMS).as_str()
);
assert_eq!(
"http://foomp.com/foo/bar",
sanitize_url(&base_url, &["/foo/", "/bar/"], NO_PARAMS).as_str()
);
// adds params
assert_eq!(
"http://foomp.com/foo/bar?foomp=baz",
sanitize_url(&base_url, &["foo", "bar"], &[("foomp", "baz")]).as_str()
);
assert_eq!(
"http://foomp.com/foo/bar?arg1=val1&arg2=val2",
sanitize_url(
&base_url,
&["/foo/", "/bar/"],
&[("arg1", "val1"), ("arg2", "val2")]
)
.as_str()
);
}
}
-19
View File
@@ -1,19 +0,0 @@
[package]
name = "nym-http-requests"
version = "0.1.0"
description = "Helper library for sending HTTP requesters over the Nym mixnet"
edition = { workspace = true }
authors = { workspace = true }
license = { workspace = true }
repository = { workspace = true }
[dependencies]
nym-socks5-requests = { path = "../socks5/requests" }
nym-ordered-buffer = { path = "../socks5/ordered-buffer" }
nym-service-providers-common = { path = "../../service-providers/common" }
bytecodec = "0.4.15"
httpcodec = "0.2.3"
bytes = "1"
http = "0.2.9"
thiserror = "1"
url = "2"
-23
View File
@@ -1,23 +0,0 @@
use std::io;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum MixHttpRequestError {
#[error("invalid Socks5 response")]
InvalidSocks5Response,
#[error("the received Socks5 response was empty")]
EmptySocks5Response,
#[error("bytecodec Error: {0}")]
ByteCodecError(#[from] bytecodec::Error),
#[error("Url parse error: {0}")]
UrlParseError(#[from] url::ParseError),
#[error("could not resolve socket address from the provided URL")]
SocketAddrResolveError {
#[source]
source: io::Error,
},
}
-63
View File
@@ -1,63 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use bytecodec::bytes::BytesEncoder;
use bytecodec::io::IoEncodeExt;
use bytecodec::Encode;
use httpcodec::{BodyEncoder, Request, RequestEncoder};
pub mod error;
pub mod socks;
pub fn encode_http_request_as_string(
request: Request<Vec<u8>>,
) -> Result<String, error::MixHttpRequestError> {
// Encode HTTP request as bytes
let mut encoder = RequestEncoder::new(BodyEncoder::new(BytesEncoder::new()));
encoder.start_encoding(request)?;
let mut buf = Vec::new();
encoder.encode_all(&mut buf)?;
Ok(String::from_utf8_lossy(&buf).to_string())
}
#[cfg(test)]
mod http_requests_tests {
use super::*;
use httpcodec::{HeaderField, HttpVersion, Method, RequestTarget};
fn create_http_get_request() -> Request<Vec<u8>> {
let mut request = Request::new(
Method::new("GET").unwrap(),
RequestTarget::new("/.wellknown/wallet/validators.json").unwrap(),
HttpVersion::V1_1,
b"".to_vec(),
);
let mut headers = request.header_mut();
headers.add_field(HeaderField::new("Host", "nymtech.net").unwrap());
request
}
#[test]
fn http_request_ok() {
// Encode HTTP request as bytes
let request = create_http_get_request();
let mut encoder = RequestEncoder::new(BodyEncoder::new(BytesEncoder::new()));
encoder.start_encoding(request).unwrap();
let mut buf = Vec::new();
encoder.encode_all(&mut buf).unwrap();
let body_as_string = String::from_utf8(buf).unwrap();
// replace newlines with \r\n
let expected = r"GET /.wellknown/wallet/validators.json HTTP/1.1
Host: nymtech.net
Content-Length: 0
"
.replace('\n', "\r\n");
assert_eq!(expected, body_as_string);
}
}
-214
View File
@@ -1,214 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::error;
use bytecodec::bytes::BytesEncoder;
use bytecodec::bytes::RemainingBytesDecoder;
use bytecodec::io::IoEncodeExt;
use bytecodec::{DecodeExt, Encode};
use httpcodec::{BodyDecoder, ResponseDecoder};
use httpcodec::{BodyEncoder, Request, RequestEncoder};
use nym_service_providers_common::interface::ProviderInterfaceVersion;
use nym_socks5_requests::{SocketData, Socks5ProtocolVersion, Socks5ProviderRequest};
pub fn encode_http_request_as_socks_send_request(
provider_interface: ProviderInterfaceVersion,
socks5_protocol: Socks5ProtocolVersion,
conn_id: u64,
request: Request<Vec<u8>>,
seq: Option<u64>,
local_closed: bool,
) -> Result<nym_socks5_requests::Socks5ProviderRequest, error::MixHttpRequestError> {
// Encode HTTP request as bytes
let mut encoder = RequestEncoder::new(BodyEncoder::new(BytesEncoder::new()));
encoder.start_encoding(request)?;
let mut buf = Vec::new();
encoder.encode_all(&mut buf)?;
// Wrap it as SOCKS send request
let request_content = nym_socks5_requests::request::Socks5Request::new_send(
socks5_protocol,
SocketData::new(seq.unwrap_or_default(), conn_id, local_closed, buf),
);
// and wrap it in provider request
Ok(Socks5ProviderRequest::new_provider_data(
provider_interface,
request_content,
))
}
#[derive(Debug)]
pub struct MixHttpResponse {
// pub connection_id: u64,
// #[deprecated]
// pub is_closed: bool,
pub http_response: httpcodec::Response<Vec<u8>>,
// #[deprecated]
// pub seq: u64,
}
impl MixHttpResponse {
pub fn try_from_bytes(b: &[u8]) -> Result<MixHttpResponse, error::MixHttpRequestError> {
if b.is_empty() {
Err(error::MixHttpRequestError::EmptySocks5Response)
} else {
let mut decoder = ResponseDecoder::<BodyDecoder<RemainingBytesDecoder>>::default();
let http_response = decoder.decode_from_bytes(b)?;
Ok(MixHttpResponse { http_response })
}
}
}
// impl TryFrom<Socks5Response> for MixHttpResponse {
// type Error = error::MixHttpRequestError;
//
// fn try_from(value: Socks5Response) -> Result<Self, Self::Error> {
// if let Socks5ResponseContent::NetworkData { content } = value.content {
// content.try_into()
// } else {
// Err(error::MixHttpRequestError::InvalidSocks5Response)
// }
// }
// }
//
// impl TryFrom<SocketData> for MixHttpResponse {
// type Error = error::MixHttpRequestError;
//
// fn try_from(value: SocketData) -> Result<Self, Self::Error> {
// if value.data.is_empty() {
// Err(error::MixHttpRequestError::EmptySocks5Response)
// } else {
// let mut decoder = ResponseDecoder::<BodyDecoder<RemainingBytesDecoder>>::default();
// let http_response = decoder.decode_from_bytes(value.data.as_ref())?;
//
// Ok(MixHttpResponse {
// connection_id: value.header.connection_id,
// is_closed: value.header.local_socket_closed,
// http_response,
// seq: value.header.seq,
// })
// }
// }
// }
// pub fn decode_socks_response_as_http_response(
// socks5_response: Socks5Response,
// ) -> Result<MixHttpResponse, error::MixHttpRequestError> {
// socks5_response.try_into()
// }
//
// #[cfg(test)]
// mod http_requests_tests {
// use super::*;
// use httpcodec::{HeaderField, HttpVersion, Method, RequestTarget};
// use nym_service_providers_common::interface::Serializable;
// use nym_socks5_requests::Socks5Response;
//
// fn create_http_get_request() -> Request<Vec<u8>> {
// let mut request = Request::new(
// Method::new("GET").unwrap(),
// RequestTarget::new("/.wellknown/wallet/validators.json").unwrap(),
// HttpVersion::V1_1,
// b"".to_vec(),
// );
// let mut headers = request.header_mut();
// headers.add_field(HeaderField::new("Host", "nymtech.net").unwrap());
//
// request
// }
//
// fn create_socks5_request_buffer() -> Vec<u8> {
// let request = create_http_get_request();
// let socks5_request = encode_http_request_as_socks_send_request(
// ProviderInterfaceVersion::new_current(),
// Socks5ProtocolVersion::new_current(),
// 99u64,
// request,
// Some(42u64),
// true,
// )
// .unwrap();
// socks5_request.into_bytes()
// }
//
// #[test]
// fn request_http_request_content_ok() {
// let buffer = create_socks5_request_buffer();
//
// // HTTP request string content is as expected
// assert_eq!(
// [71u8, 69u8, 84u8, 32u8, 47u8, 46u8, 119u8, 101u8],
// buffer[19..27]
// );
// }
//
// /// This test will fail if the framing of the request buffer changes, e.g. when OrderedMessage
// /// changes to have the `index` value as a field, instead of packed with the `data`
// #[test]
// fn request_size_as_expected_ok() {
// let buffer = create_socks5_request_buffer();
// // println!("{:?}", buffer) // uncomment and run `cargo test -- --nocapture` to view
//
// assert_eq!(108, buffer.len()); // version set to SOCKS5
// }
//
// #[test]
// fn request_socks5_headers_ok() {
// let buffer = create_socks5_request_buffer();
//
// assert_eq!(5u8, buffer[0]); // version set to SOCKS5
// assert_eq!(1u8, buffer[1]); // type is SEND
// assert_eq!(99u8, buffer[9]); // ConnectionId is correct
// assert_eq!(1u8, buffer[10]); // local_closed is true
// }
//
// #[test]
// fn request_ordered_message_ok() {
// let buffer = create_socks5_request_buffer();
//
// // OrderedMessage index is correct
// assert_eq!(42u8, buffer[18]);
// }
//
// fn create_socks_response() -> Socks5Response {
// // HTTP response is just a string
// let http_response_string = "HTTP/1.1 200 OK\r\nServer: foo/0.0.1\r\n\r\n";
//
// let data = http_response_string.as_bytes().to_vec();
//
// // wrap in `NetworkData`, then Socks5Response
// Socks5Response::new(
// Socks5ProtocolVersion::new_current(),
// Socks5ResponseContent::NetworkData {
// content: SocketData::new(42, 99u64, false, data),
// },
// )
// }
//
// /// This test will fail is anything in the framing of the socks5_response byte
// /// representation changes
// #[test]
// fn response_byte_size_is_as_expected() {
// let socks5_response = create_socks_response();
// let buf = socks5_response.into_bytes();
//
// assert_eq!(57, buf.len());
// }
//
// #[test]
// fn response_parses() {
// unimplemented!()
// // let socks5_response = create_socks_response();
// // let response = decode_socks_response_as_http_response(socks5_response).unwrap();
// //
// // assert_eq!(42u64, response.seq); // OrderedMessage index as expected
// // assert_eq!(HttpVersion::V1_1, response.http_response.http_version());
// // assert_eq!(200u16, response.http_response.status_code().as_u16());
// // assert_eq!(
// // "foo/0.0.1",
// // response.http_response.header().get_field("Server").unwrap()
// // );
// }
// }
+1 -1
View File
@@ -10,4 +10,4 @@ bip32 = "0.5.1"
k256 = { workspace = true }
ledger-transport = "0.10.0"
ledger-transport-hid = "0.10.0"
thiserror = "1"
thiserror = { workspace = true }
+2 -2
View File
@@ -23,7 +23,7 @@ impl EchoPacket {
.chain(keys.public_key().to_bytes().iter().cloned())
.collect::<Vec<_>>();
let signature = keys.private_key().sign(&bytes_to_sign);
let signature = keys.private_key().sign(bytes_to_sign);
EchoPacket {
sequence_number,
@@ -67,7 +67,7 @@ impl EchoPacket {
pub(crate) fn construct_reply(self, private_key: &identity::PrivateKey) -> ReplyPacket {
let bytes = self.to_bytes();
let signature = private_key.sign(&bytes);
let signature = private_key.sign(bytes);
ReplyPacket {
base_packet: self,
signature,
+1 -1
View File
@@ -7,5 +7,5 @@ edition = "2021"
[dependencies]
async-trait = { workspace = true }
thiserror = "1.0"
thiserror = { workspace = true }
+1 -1
View File
@@ -12,7 +12,7 @@ cfg-if = { workspace = true }
dotenvy = { workspace = true }
hex-literal = "0.3.3"
once_cell = { workspace = true }
schemars = { version = "0.8", features = ["preserve_order"] }
schemars = { workspace = true, features = ["preserve_order"] }
serde = { workspace = true, features = ["derive"]}
thiserror = { workspace = true }
url = { workspace = true }
+3
View File
@@ -462,6 +462,9 @@ pub const DEFAULT_NYM_API_PORT: u16 = 8080;
pub const NYM_API_VERSION: &str = "v1";
// NYM-NODE
pub const DEFAULT_NYM_NODE_HTTP_PORT: u16 = 8080;
// REWARDING
/// We'll be assuming a few more things, profit margin and cost function. Since we don't have reliable package measurement, we'll be using uptime. We'll also set the value of 1 Nym to 1 $, to be able to translate interval costs to Nyms. We'll also assume a cost of 40$ per interval(month), converting that to Nym at our 1$ rate translates to 40_000_000 uNyms
+2 -2
View File
@@ -11,8 +11,8 @@ bls12_381 = { git = "https://github.com/jstuczyn/bls12_381", branch ="gt-seriali
itertools = "0.10"
digest = "0.9"
rand = "0.8"
thiserror = "1.0"
serde = "1.0"
thiserror = { workspace = true }
serde = { workspace = true }
serde_derive = "1.0"
bs58 = "0.4.0"
sha2 = "0.9"
+1 -1
View File
@@ -11,7 +11,7 @@ repository = { workspace = true }
nym-crypto = { path = "../../crypto", features = ["asymmetric"] } # all addresses are expressed in terms on their crypto keys
nym-sphinx-types = { path = "../types", features = ["sphinx"] } # we need to be able to refer to some types defined inside sphinx crate
serde = "1.0" # implementing serialization/deserialization for some types, like `Recipient`
thiserror = "1.0.37"
thiserror = { workspace = true }
[dev-dependencies]
rand = "0.7"
@@ -10,8 +10,8 @@ repository = { workspace = true }
[dependencies]
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
bs58 = "0.4"
serde = "1.0"
thiserror = "1"
serde = { workspace = true }
thiserror = { workspace = true }
nym-crypto = { path = "../../crypto", features = ["symmetric", "rand"] }
nym-sphinx-addressing = { path = "../addressing" }
+1 -1
View File
@@ -12,7 +12,7 @@ repository = { workspace = true }
[dependencies]
log = { workspace = true }
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
thiserror = "1.0.37"
thiserror = { workspace = true }
nym-sphinx-addressing = { path = "../addressing" }
nym-sphinx-params = { path = "../params" }
+1 -1
View File
@@ -9,7 +9,7 @@ repository = { workspace = true }
[dependencies]
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
thiserror = "1.0.37"
thiserror = { workspace = true }
nym-crypto = { path = "../../crypto" }
nym-sphinx-acknowledgements = { path = "../acknowledgements" }
+1 -1
View File
@@ -12,4 +12,4 @@ nym-sphinx-addressing = { path = "../addressing" }
nym-sphinx-params = { path = "../params" }
nym-sphinx-types = { path = "../types", features = ["sphinx", "outfox"] }
nym-outfox = { path = "../../../nym-outfox" }
thiserror = "1"
thiserror = { workspace = true }
+1 -1
View File
@@ -10,7 +10,7 @@ repository = { workspace = true }
[dependencies]
bytes = "1.0"
tokio-util = { version = "0.7.4", features = ["codec"] }
thiserror = "1.0.37"
thiserror = { workspace = true }
nym-sphinx-types = { path = "../types", features = ["sphinx", "outfox"] }
nym-sphinx-params = { path = "../params", features = ["sphinx", "outfox"] }
+1 -1
View File
@@ -8,7 +8,7 @@ license = { workspace = true }
repository = { workspace = true }
[dependencies]
thiserror = "1.0.37"
thiserror = { workspace = true }
serde = { workspace = true, features = ["derive"] }
nym-crypto = { path = "../../crypto", features = ["hashing", "symmetric"] }
+2 -1
View File
@@ -294,7 +294,8 @@ mod message_receiver {
owner: "foomp4".to_string(),
host: "1.2.3.4".parse().unwrap(),
mix_host: "1.2.3.4:1789".parse().unwrap(),
clients_port: 9000,
clients_ws_port: 9000,
clients_wss_port: None,
identity_key: identity::PublicKey::from_base58_string(
"FioFa8nMmPpQnYi7JyojoTuwGLeyNS8BF4ChPr29zUML",
)
+1 -1
View File
@@ -10,7 +10,7 @@ repository = { workspace = true }
[dependencies]
sphinx-packet = { version = "0.1.0", optional = true }
nym-outfox = { path = "../../../nym-outfox", optional = true }
thiserror = "1"
thiserror = { workspace = true }
[features]
default = ["sphinx"]
+5 -1
View File
@@ -51,7 +51,11 @@ where
if T::pem_type() != key_pem.tag {
return Err(io::Error::new(
io::ErrorKind::Other,
"unexpected key pem tag",
format!(
"unexpected key pem tag. Got '{}', expected: '{}'",
key_pem.tag,
T::pem_type()
),
));
}
+2 -2
View File
@@ -13,10 +13,10 @@ log = { workspace = true }
pin-project = "1.0"
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
reqwest = { workspace = true }
schemars = { version = "0.8", features = ["preserve_order"] }
schemars = { workspace = true, features = ["preserve_order"] }
serde = { workspace = true, features = ["derive"] } # for config serialization/deserialization
tap = "1.0.1"
thiserror = "1.0.34"
thiserror = { workspace = true }
tokio = { version = "1.24.1", features = ["rt-multi-thread", "net", "signal"] }
url = { workspace = true }
+1 -1
View File
@@ -8,4 +8,4 @@ edition = "2021"
[dependencies]
log = { workspace = true }
thiserror = "1.0.37"
thiserror = { workspace = true }
+1 -1
View File
@@ -15,5 +15,5 @@ reqwest = { workspace = true, features = ["json"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1"
sqlx = { version = "0.5", features = ["runtime-tokio-rustls", "chrono"]}
thiserror = "1"
thiserror = { workspace = true }
tokio = { version = "1.24.1", features = [ "time" ] }
+5 -1
View File
@@ -15,7 +15,7 @@ documentation = { workspace = true }
bs58 = "0.4"
log = { workspace = true }
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
thiserror = "1.0.37"
thiserror = { workspace = true }
async-trait = { workspace = true, optional = true }
semver = "0.11"
@@ -35,6 +35,10 @@ nym-sphinx-types = { path = "../nymsphinx/types", features = ["sphinx", "outfox"
nym-sphinx-routing = { path = "../nymsphinx/routing" }
nym-bin-common = { path = "../bin-common" }
# I'm not sure how to feel about pulling in this dependency here...
nym-api-requests = { path = "../../nym-api/nym-api-requests" }
# 'serializable' feature
nym-config = { path = "../config", optional = true }
+86 -6
View File
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
use crate::{filter, NetworkAddress, NodeVersion};
use nym_api_requests::models::DescribedGateway;
use nym_crypto::asymmetric::{encryption, identity};
use nym_mixnet_contract_common::GatewayBond;
use nym_sphinx_addressing::nodes::{NodeIdentity, NymNodeRoutingAddress};
@@ -10,6 +11,7 @@ use std::convert::{TryFrom, TryInto};
use std::fmt;
use std::fmt::Formatter;
use std::io;
use std::net::AddrParseError;
use std::net::SocketAddr;
use thiserror::Error;
@@ -27,6 +29,17 @@ pub enum GatewayConversionError {
#[source]
source: io::Error,
},
#[error("'{gateway}' has not provided any valid ip addresses")]
NoIpAddressesProvided { gateway: String },
#[error("'{gateway}' has provided a malformed ip address: {err}")]
MalformedIpAddress {
gateway: String,
#[source]
err: AddrParseError,
},
}
#[derive(Clone)]
@@ -36,7 +49,13 @@ pub struct Node {
// we're keeping this as separate resolved field since we do not want to be resolving the potential
// hostname every time we want to construct a path via this node
pub mix_host: SocketAddr,
pub clients_port: u16,
// #[serde(alias = "clients_port")]
pub clients_ws_port: u16,
// #[serde(default)]
pub clients_wss_port: Option<u16>,
pub identity_key: identity::PublicKey,
pub sphinx_key: encryption::PublicKey, // TODO: or nymsphinx::PublicKey? both are x25519
pub version: NodeVersion,
@@ -48,7 +67,8 @@ impl std::fmt::Debug for Node {
.field("host", &self.host)
.field("owner", &self.owner)
.field("mix_host", &self.mix_host)
.field("clients_port", &self.clients_port)
.field("clients_ws_port", &self.clients_ws_port)
.field("clients_wss_port", &self.clients_wss_port)
.field("identity_key", &self.identity_key.to_base58_string())
.field("sphinx_key", &self.sphinx_key.to_base58_string())
.field("version", &self.version)
@@ -82,11 +102,17 @@ impl Node {
}
pub fn clients_address(&self) -> String {
format!("ws://{}:{}", self.host, self.clients_port)
self.clients_address_tls()
.unwrap_or_else(|| self.clients_address_no_tls())
}
pub fn clients_address_tls(&self) -> String {
format!("wss://{}:443", self.host)
pub fn clients_address_no_tls(&self) -> String {
format!("ws://{}:{}", self.host, self.clients_ws_port)
}
pub fn clients_address_tls(&self) -> Option<String> {
self.clients_wss_port
.map(|p| format!("wss://{}:{p}", self.host))
}
}
@@ -131,7 +157,8 @@ impl<'a> TryFrom<&'a GatewayBond> for Node {
owner: bond.owner.as_str().to_owned(),
host,
mix_host,
clients_port: bond.gateway.clients_port,
clients_ws_port: bond.gateway.clients_port,
clients_wss_port: None,
identity_key: identity::PublicKey::from_base58_string(&bond.gateway.identity_key)?,
sphinx_key: encryption::PublicKey::from_base58_string(&bond.gateway.sphinx_key)?,
version: bond.gateway.version.as_str().into(),
@@ -146,3 +173,56 @@ impl TryFrom<GatewayBond> for Node {
Node::try_from(&bond)
}
}
impl<'a> TryFrom<&'a DescribedGateway> for Node {
type Error = GatewayConversionError;
fn try_from(value: &'a DescribedGateway) -> Result<Self, Self::Error> {
let Some(ref self_described) = value.self_described else {
return (&value.bond).try_into();
};
let ips = &self_described.host_information.ip_address;
if ips.is_empty() {
return Err(GatewayConversionError::NoIpAddressesProvided {
gateway: value.bond.gateway.identity_key.clone(),
});
}
let host = match &self_described.host_information.hostname {
None => NetworkAddress::IpAddr(ips[0]),
Some(hostname) => NetworkAddress::Hostname(hostname.clone()),
};
// get ip from the self-reported values so we wouldn't need to do any hostname resolution
// (which doesn't really work in wasm)
let mix_host = SocketAddr::new(ips[0], value.bond.gateway.mix_port);
Ok(Node {
owner: value.bond.owner.as_str().to_owned(),
host,
mix_host,
clients_ws_port: self_described.mixnet_websockets.ws_port,
clients_wss_port: self_described.mixnet_websockets.wss_port,
identity_key: identity::PublicKey::from_base58_string(
&self_described.host_information.keys.ed25519,
)?,
sphinx_key: encryption::PublicKey::from_base58_string(
&self_described.host_information.keys.x25519,
)?,
version: self_described
.build_information
.build_version
.as_str()
.into(),
})
}
}
impl TryFrom<DescribedGateway> for Node {
type Error = GatewayConversionError;
fn try_from(value: DescribedGateway) -> Result<Self, Self::Error> {
Node::try_from(&value)
}
}
+28 -4
View File
@@ -19,6 +19,7 @@ use std::str::FromStr;
#[cfg(feature = "serializable")]
use ::serde::{Deserialize, Deserializer, Serialize, Serializer};
use nym_api_requests::models::DescribedGateway;
pub mod error;
pub mod filter;
@@ -403,10 +404,33 @@ impl<'de> Deserialize<'de> for NymTopology {
}
}
pub fn nym_topology_from_detailed(
pub trait IntoGatewayNode: TryInto<gateway::Node>
where
<Self as TryInto<gateway::Node>>::Error: Display,
{
fn identity(&self) -> IdentityKeyRef;
}
impl IntoGatewayNode for GatewayBond {
fn identity(&self) -> IdentityKeyRef {
&self.gateway.identity_key
}
}
impl IntoGatewayNode for DescribedGateway {
fn identity(&self) -> IdentityKeyRef {
&self.bond.gateway.identity_key
}
}
pub fn nym_topology_from_detailed<G>(
mix_details: Vec<MixNodeDetails>,
gateway_bonds: Vec<GatewayBond>,
) -> NymTopology {
gateway_bonds: Vec<G>,
) -> NymTopology
where
G: IntoGatewayNode,
<G as TryInto<gateway::Node>>::Error: Display,
{
let mut mixes = BTreeMap::new();
for bond in mix_details
.into_iter()
@@ -435,7 +459,7 @@ pub fn nym_topology_from_detailed(
let mut gateways = Vec::with_capacity(gateway_bonds.len());
for bond in gateway_bonds.into_iter() {
let gate_id = bond.gateway.identity_key.clone();
let gate_id = bond.identity().to_owned();
match bond.try_into() {
Ok(gate) => gateways.push(gate),
Err(err) => {
+13 -4
View File
@@ -190,7 +190,12 @@ pub struct SerializableGateway {
#[cfg_attr(feature = "wasm-serde-types", tsify(optional))]
#[serde(alias = "clients_port")]
pub clients_port: Option<u16>,
#[serde(alias = "clients_ws_port")]
pub clients_ws_port: Option<u16>,
#[cfg_attr(feature = "wasm-serde-types", tsify(optional))]
#[serde(alias = "clients_wss_port")]
pub clients_wss_port: Option<u16>,
#[serde(alias = "identity_key")]
pub identity_key: String,
@@ -209,7 +214,9 @@ impl TryFrom<SerializableGateway> for gateway::Node {
let host = gateway::Node::parse_host(&value.host)?;
let mix_port = value.mix_port.unwrap_or(DEFAULT_MIX_LISTENING_PORT);
let clients_port = value.clients_port.unwrap_or(DEFAULT_CLIENT_LISTENING_PORT);
let clients_ws_port = value
.clients_ws_port
.unwrap_or(DEFAULT_CLIENT_LISTENING_PORT);
let version = value.version.map(|v| v.as_str().into()).unwrap_or_default();
// try to completely resolve the host in the mix situation to avoid doing it every
@@ -224,7 +231,8 @@ impl TryFrom<SerializableGateway> for gateway::Node {
owner: value.owner,
host,
mix_host,
clients_port,
clients_ws_port,
clients_wss_port: value.clients_wss_port,
identity_key: identity::PublicKey::from_base58_string(&value.identity_key)
.map_err(GatewayConversionError::from)?,
sphinx_key: encryption::PublicKey::from_base58_string(&value.sphinx_key)
@@ -241,7 +249,8 @@ impl<'a> From<&'a gateway::Node> for SerializableGateway {
host: value.host.to_string(),
explicit_ip: Some(value.mix_host.ip()),
mix_port: Some(value.mix_host.port()),
clients_port: Some(value.clients_port),
clients_ws_port: Some(value.clients_ws_port),
clients_wss_port: value.clients_wss_port,
identity_key: value.identity_key.to_base58_string(),
sphinx_key: value.sphinx_key.to_base58_string(),
version: Some(value.version.to_string()),
+1 -1
View File
@@ -14,7 +14,7 @@ js-sys = { workspace = true }
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
serde = { workspace = true, features = ["derive"] }
serde-wasm-bindgen = { workspace = true }
thiserror = { workspace = true }
thiserror = { workspace = true }
tsify = { workspace = true, features = ["js"] }
url = { workspace = true }
wasm-bindgen = { workspace = true }
+2
View File
@@ -22,6 +22,8 @@ bytes = "1.5.0"
dashmap = "5.5.3"
etherparse = "0.13.0"
futures = "0.3.28"
ip_network = "0.4.1"
ip_network_table = "0.2.0"
log.workspace = true
nym-task = { path = "../task" }
tap.workspace = true
+18 -10
View File
@@ -1,40 +1,48 @@
#![cfg_attr(not(target_os = "linux"), allow(dead_code))]
use nym_task::TaskClient;
mod error;
mod event;
mod network_table;
mod platform;
mod setup;
mod tun;
mod udp_listener;
mod wg_tunnel;
// Currently the module related to setting up the virtual network device is platform specific.
#[cfg(target_os = "linux")]
use platform::linux::tun_device;
type ActivePeers =
dashmap::DashMap<std::net::SocketAddr, tokio::sync::mpsc::UnboundedSender<crate::event::Event>>;
#[derive(Clone)]
struct TunTaskTx(tokio::sync::mpsc::UnboundedSender<Vec<u8>>);
impl TunTaskTx {
fn send(&self, packet: Vec<u8>) -> Result<(), tokio::sync::mpsc::error::SendError<Vec<u8>>> {
self.0.send(packet)
}
}
#[cfg(target_os = "linux")]
pub async fn start_wireguard(
task_client: TaskClient,
task_client: nym_task::TaskClient,
) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
use std::sync::Arc;
// The set of active tunnels indexed by the peer's address
let active_peers = std::sync::Arc::new(ActivePeers::new());
let active_peers = Arc::new(udp_listener::ActivePeers::new());
let peers_by_ip = Arc::new(std::sync::Mutex::new(network_table::NetworkTable::new()));
// Start the tun device that is used to relay traffic outbound
let tun_task_tx = tun_device::start_tun_device(active_peers.clone());
let tun_task_tx = tun_device::start_tun_device(peers_by_ip.clone());
// Start the UDP listener that clients connect to
udp_listener::start_udp_listener(tun_task_tx, active_peers, task_client).await?;
udp_listener::start_udp_listener(tun_task_tx, active_peers, peers_by_ip, task_client).await?;
Ok(())
}
#[cfg(not(target_os = "linux"))]
pub async fn start_wireguard(
_task_client: TaskClient,
_task_client: nym_task::TaskClient,
) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
todo!("WireGuard is currently only supported on Linux")
}
+25
View File
@@ -0,0 +1,25 @@
use std::net::IpAddr;
use ip_network::IpNetwork;
use ip_network_table::IpNetworkTable;
#[derive(Default)]
pub(crate) struct NetworkTable<T> {
ips: IpNetworkTable<T>,
}
impl<T> NetworkTable<T> {
pub(crate) fn new() -> Self {
Self {
ips: IpNetworkTable::new(),
}
}
pub fn insert<N: Into<IpNetwork>>(&mut self, network: N, data: T) -> Option<T> {
self.ips.insert(network, data)
}
pub fn longest_match<I: Into<IpAddr>>(&self, ip: I) -> Option<(IpNetwork, &T)> {
self.ips.longest_match(ip)
}
}
@@ -1,14 +1,17 @@
use std::{net::Ipv4Addr, sync::Arc};
use etherparse::{InternetSlice, SlicedPacket};
use tap::TapFallible;
use tokio::{
io::{AsyncReadExt, AsyncWriteExt},
sync::mpsc::{self, UnboundedSender},
sync::mpsc::{self},
};
use crate::{
event::Event,
setup::{TUN_BASE_NAME, TUN_DEVICE_ADDRESS, TUN_DEVICE_NETMASK},
ActivePeers,
udp_listener::PeersByIp,
TunTaskTx,
};
fn setup_tokio_tun_device(name: &str, address: Ipv4Addr, netmask: Ipv4Addr) -> tokio_tun::Tun {
@@ -25,7 +28,7 @@ fn setup_tokio_tun_device(name: &str, address: Ipv4Addr, netmask: Ipv4Addr) -> t
.expect("Failed to setup tun device, do you have permission?")
}
pub fn start_tun_device(_active_peers: Arc<ActivePeers>) -> UnboundedSender<Vec<u8>> {
pub(crate) fn start_tun_device(peers_by_ip: Arc<std::sync::Mutex<PeersByIp>>) -> TunTaskTx {
let tun = setup_tokio_tun_device(
format!("{}%d", TUN_BASE_NAME).as_str(),
TUN_DEVICE_ADDRESS.parse().unwrap(),
@@ -37,6 +40,7 @@ pub fn start_tun_device(_active_peers: Arc<ActivePeers>) -> UnboundedSender<Vec<
// Channels to communicate with the other tasks
let (tun_task_tx, mut tun_task_rx) = mpsc::unbounded_channel::<Vec<u8>>();
let tun_task_tx = TunTaskTx(tun_task_tx);
tokio::spawn(async move {
let mut buf = [0u8; 1024];
@@ -55,8 +59,16 @@ pub fn start_tun_device(_active_peers: Arc<ActivePeers>) -> UnboundedSender<Vec<
};
log::info!("iface: read Packet({src_addr} -> {dst_addr}, {len} bytes)");
// TODO: route packet to the correct peer.
log::info!("...forward packet to the correct peer (NOT YET IMPLEMENTED)");
// Route packet to the correct peer.
if let Some(peer_tx) = peers_by_ip.lock().unwrap().longest_match(dst_addr).map(|(_, tx)| tx) {
log::info!("Forward packet to wg tunnel");
peer_tx
.send(Event::IpPacket(packet.to_vec().into()))
.tap_err(|err| log::error!("{err}"))
.unwrap();
} else {
log::info!("No peer found, packet dropped");
}
},
Err(err) => {
log::info!("iface: read error: {err}");
+36 -22
View File
@@ -1,3 +1,5 @@
use std::net::IpAddr;
use base64::{engine::general_purpose, Engine as _};
use boringtun::x25519;
use log::info;
@@ -15,35 +17,47 @@ pub const TUN_DEVICE_NETMASK: &str = "255.255.255.0";
// Corresponding public key: "WM8s8bYegwMa0TJ+xIwhk+dImk2IpDUKslDBCZPizlE="
const PRIVATE_KEY: &str = "AEqXrLFT4qjYq3wmX0456iv94uM6nDj5ugp6Jedcflg=";
// The public keys of the registered peers (clients)
const PEERS: &[&str; 1] = &[
// Corresponding private key: "ILeN6gEh6vJ3Ju8RJ3HVswz+sPgkcKtAYTqzQRhTtlo="
"NCIhkgiqxFx1ckKl3Zuh595DzIFl8mxju1Vg995EZhI=",
// Another key
// "mxV/mw7WZTe+0Msa0kvJHMHERDA/cSskiZWQce+TdEs=",
];
// The public keys of the registered peer (clients)
// Corresponding private key: "ILeN6gEh6vJ3Ju8RJ3HVswz+sPgkcKtAYTqzQRhTtlo="
const PEER: &str = "NCIhkgiqxFx1ckKl3Zuh595DzIFl8mxju1Vg995EZhI=";
pub fn init_static_dev_keys() -> (x25519::StaticSecret, x25519::PublicKey) {
// TODO: this is a temporary solution for development
let static_private_bytes: [u8; 32] = general_purpose::STANDARD
.decode(PRIVATE_KEY)
// The AllowedIPs for the connected peer, which is one a single IP and the same as the IP that the
// peer has configured on their side.
const ALLOWED_IPS: &str = "10.0.0.2";
fn decode_base64_key(base64_key: &str) -> [u8; 32] {
general_purpose::STANDARD
.decode(base64_key)
.unwrap()
.try_into()
.unwrap();
.unwrap()
}
pub fn server_static_private_key() -> x25519::StaticSecret {
// TODO: this is a temporary solution for development
let static_private_bytes: [u8; 32] = decode_base64_key(PRIVATE_KEY);
let static_private = x25519::StaticSecret::try_from(static_private_bytes).unwrap();
let static_public = x25519::PublicKey::from(&static_private);
info!(
"wg public key: {}",
general_purpose::STANDARD.encode(static_public)
);
// TODO: A single static public key is used for all peers during development
let peer_static_public_bytes: [u8; 32] = general_purpose::STANDARD
.decode(PEERS[0])
.unwrap()
.try_into()
.unwrap();
let peer_static_public = x25519::PublicKey::try_from(peer_static_public_bytes).unwrap();
(static_private, peer_static_public)
static_private
}
pub fn peer_static_public_key() -> x25519::PublicKey {
// A single static public key is used during development
let peer_static_public_bytes: [u8; 32] = decode_base64_key(PEER);
let peer_static_public = x25519::PublicKey::try_from(peer_static_public_bytes).unwrap();
info!(
"peer public key: {}",
general_purpose::STANDARD.encode(peer_static_public)
);
peer_static_public
}
pub fn peer_allowed_ips() -> ip_network::IpNetwork {
let key: IpAddr = ALLOWED_IPS.parse().unwrap();
let cidr = 0u8;
ip_network::IpNetwork::new_truncate(key, cidr).unwrap()
}
+28 -7
View File
@@ -1,22 +1,31 @@
use std::{net::SocketAddr, sync::Arc};
use dashmap::DashMap;
use futures::StreamExt;
use log::error;
use nym_task::TaskClient;
use tap::TapFallible;
use tokio::{net::UdpSocket, sync::mpsc::UnboundedSender};
use tokio::{
net::UdpSocket,
sync::mpsc::{self},
};
use crate::{
event::Event,
setup::{WG_ADDRESS, WG_PORT},
ActivePeers,
network_table::NetworkTable,
setup::{self, WG_ADDRESS, WG_PORT},
TunTaskTx,
};
const MAX_PACKET: usize = 65535;
pub async fn start_udp_listener(
tun_task_tx: UnboundedSender<Vec<u8>>,
pub(crate) type ActivePeers = DashMap<SocketAddr, mpsc::UnboundedSender<Event>>;
pub(crate) type PeersByIp = NetworkTable<mpsc::UnboundedSender<Event>>;
pub(crate) async fn start_udp_listener(
tun_task_tx: TunTaskTx,
active_peers: Arc<ActivePeers>,
peers_by_ip: Arc<std::sync::Mutex<PeersByIp>>,
mut task_client: TaskClient,
) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
let wg_address = SocketAddr::new(WG_ADDRESS.parse().unwrap(), WG_PORT);
@@ -24,7 +33,9 @@ pub async fn start_udp_listener(
let udp_socket = Arc::new(UdpSocket::bind(wg_address).await?);
// Setup some static keys for development
let (static_private, peer_static_public) = crate::setup::init_static_dev_keys();
let static_private = setup::server_static_private_key();
let peer_static_public = setup::peer_static_public_key();
let peer_allowed_ips = setup::peer_allowed_ips();
tokio::spawn(async move {
// Each tunnel is run in its own task, and the task handle is stored here so we can remove
@@ -44,6 +55,7 @@ pub async fn start_udp_listener(
Ok(addr) => {
log::info!("Removing peer: {addr:?}");
active_peers.remove(&addr);
// TODO: remove from peers_by_ip
}
Err(err) => {
error!("WireGuard UDP listener: error receiving shutdown from peer: {err}");
@@ -61,13 +73,22 @@ pub async fn start_udp_listener(
.unwrap();
} else {
log::info!("udp: received {len} bytes from {addr} from unknown peer, starting tunnel");
let (join_handle, peer_tx) = crate::tun::start_wg_tunnel(
// TODO: this is a temporary solution for development since this
// assumes we know the peer_static_public this corresponds to.
// TODO: rework this before production! This is likely not secure!
log::warn!("Assuming peer_static_public is known");
log::warn!("SECURITY: Rework me to do proper handshake before creating the tunnel!");
let (join_handle, peer_tx) = crate::wg_tunnel::start_wg_tunnel(
addr,
udp_socket.clone(),
static_private.clone(),
peer_static_public,
peer_allowed_ips,
tun_task_tx.clone(),
);
peers_by_ip.lock().unwrap().insert(peer_allowed_ips, peer_tx.clone());
peer_tx.send(Event::WgPacket(buf[..len].to_vec().into()))
.tap_err(|err| log::error!("{err}"))
.unwrap();
@@ -6,7 +6,6 @@ use boringtun::{
x25519,
};
use bytes::Bytes;
use etherparse::{InternetSlice, SlicedPacket};
use log::{debug, error, info, warn};
use tap::TapFallible;
use tokio::{
@@ -15,7 +14,7 @@ use tokio::{
time::timeout,
};
use crate::{error::WgError, event::Event};
use crate::{error::WgError, event::Event, network_table::NetworkTable, TunTaskTx};
const MAX_PACKET: usize = 65535;
@@ -27,10 +26,10 @@ pub struct WireGuardTunnel {
udp: Arc<UdpSocket>,
// Peer endpoint
endpoint: SocketAddr,
endpoint: Arc<tokio::sync::RwLock<SocketAddr>>,
// The source address of the last packet received from the peer
source_addr: Arc<std::sync::RwLock<Option<std::net::Ipv4Addr>>>,
// AllowedIPs for this peer
allowed_ips: NetworkTable<()>,
// `boringtun` tunnel, used for crypto & WG protocol
wg_tunnel: Arc<tokio::sync::Mutex<Tunn>>,
@@ -40,7 +39,7 @@ pub struct WireGuardTunnel {
close_rx: broadcast::Receiver<()>,
// Send data to the task that handles sending data through the tun device
tun_task_tx: mpsc::UnboundedSender<Vec<u8>>,
tun_task_tx: TunTaskTx,
}
impl Drop for WireGuardTunnel {
@@ -51,12 +50,13 @@ impl Drop for WireGuardTunnel {
}
impl WireGuardTunnel {
pub fn new(
pub(crate) fn new(
udp: Arc<UdpSocket>,
endpoint: SocketAddr,
static_private: x25519::StaticSecret,
peer_static_public: x25519::PublicKey,
tunnel_tx: mpsc::UnboundedSender<Vec<u8>>,
peer_allowed_ips: ip_network::IpNetwork,
tunnel_tx: TunTaskTx,
) -> (Self, mpsc::UnboundedSender<Event>) {
let local_addr = udp.local_addr().unwrap();
let peer_addr = udp.peer_addr();
@@ -85,11 +85,14 @@ impl WireGuardTunnel {
// Signal close tunnel
let (close_tx, close_rx) = broadcast::channel(1);
let mut allowed_ips = NetworkTable::new();
allowed_ips.insert(peer_allowed_ips, ());
let tunnel = WireGuardTunnel {
peer_rx,
udp,
endpoint,
source_addr: Default::default(),
endpoint: Arc::new(tokio::sync::RwLock::new(endpoint)),
allowed_ips,
wg_tunnel,
close_tx,
close_rx,
@@ -134,7 +137,7 @@ impl WireGuardTunnel {
},
}
}
info!("WireGuard tunnel ({}): closed", self.endpoint);
info!("WireGuard tunnel ({}): closed", self.endpoint.read().await);
}
async fn wg_tunnel_lock(&self) -> Result<tokio::sync::MutexGuard<'_, Tunn>, WgError> {
@@ -143,16 +146,11 @@ impl WireGuardTunnel {
.map_err(|_| WgError::UnableToGetTunnel)
}
fn set_source_addr(&self, source_addr: std::net::Ipv4Addr) {
let to_update = {
let stored_source_addr = self.source_addr.read().unwrap();
stored_source_addr
.map(|sa| sa != source_addr)
.unwrap_or(true)
};
if to_update {
log::info!("wg tunnel set_source_addr: {source_addr}");
*self.source_addr.write().unwrap() = Some(source_addr);
#[allow(unused)]
async fn set_endpoint(&self, addr: SocketAddr) {
if *self.endpoint.read().await != addr {
log::info!("wg tunnel update endpoint: {addr}");
*self.endpoint.write().await = addr;
}
}
@@ -161,8 +159,9 @@ impl WireGuardTunnel {
let mut tunnel = self.wg_tunnel_lock().await?;
match tunnel.decapsulate(None, data, &mut send_buf) {
TunnResult::WriteToNetwork(packet) => {
log::info!("udp: send {} bytes to {}", packet.len(), self.endpoint);
if let Err(err) = self.udp.send_to(packet, self.endpoint).await {
let endpoint = self.endpoint.read().await;
log::info!("udp: send {} bytes to {}", packet.len(), *endpoint);
if let Err(err) = self.udp.send_to(packet, *endpoint).await {
error!("Failed to send decapsulation-instructed packet to WireGuard endpoint: {err:?}");
};
// Flush pending queue
@@ -170,8 +169,8 @@ impl WireGuardTunnel {
let mut send_buf = [0u8; MAX_PACKET];
match tunnel.decapsulate(None, &[], &mut send_buf) {
TunnResult::WriteToNetwork(packet) => {
log::info!("udp: send {} bytes to {}", packet.len(), self.endpoint);
if let Err(err) = self.udp.send_to(packet, self.endpoint).await {
log::info!("udp: send {} bytes to {}", packet.len(), *endpoint);
if let Err(err) = self.udp.send_to(packet, *endpoint).await {
error!("Failed to send decapsulation-instructed packet to WireGuard endpoint: {err:?}");
break;
};
@@ -182,14 +181,23 @@ impl WireGuardTunnel {
}
}
}
TunnResult::WriteToTunnelV4(packet, _) | TunnResult::WriteToTunnelV6(packet, _) => {
let headers = SlicedPacket::from_ip(packet).unwrap();
let (source_addr, _destination_addr) = match headers.ip.unwrap() {
InternetSlice::Ipv4(ip, _) => (ip.source_addr(), ip.destination_addr()),
InternetSlice::Ipv6(_, _) => unimplemented!(),
};
self.set_source_addr(source_addr);
self.tun_task_tx.send(packet.to_vec()).unwrap();
TunnResult::WriteToTunnelV4(packet, addr) => {
// TODO: once the flow is redone, we should add updating the endpoint dynamically
// self.set_endpoint(addr);
if self.allowed_ips.longest_match(addr).is_some() {
self.tun_task_tx.send(packet.to_vec()).unwrap();
} else {
warn!("Packet from {addr} not in allowed_ips");
}
}
TunnResult::WriteToTunnelV6(packet, addr) => {
// TODO: once the flow is redone, we should add updating the endpoint dynamically
// self.set_endpoint(addr);
if self.allowed_ips.longest_match(addr).is_some() {
self.tun_task_tx.send(packet.to_vec()).unwrap();
} else {
warn!("Packet (v6) from {addr} not in allowed_ips");
}
}
TunnResult::Done => {
debug!("WireGuard: decapsulate done");
@@ -209,9 +217,10 @@ impl WireGuardTunnel {
encapsulated_packet.len()
);
info!("consume_eth: send to {}: {}", self.endpoint, data.len());
let endpoint = self.endpoint.read().await;
info!("consume_eth: send to {}: {}", *endpoint, data.len());
self.udp
.send_to(&encapsulated_packet, self.endpoint)
.send_to(&encapsulated_packet, *endpoint)
.await
.unwrap();
}
@@ -244,12 +253,9 @@ impl WireGuardTunnel {
async fn handle_routine_tun_result<'a: 'async_recursion>(&self, result: TunnResult<'a>) {
match result {
TunnResult::WriteToNetwork(packet) => {
log::info!(
"routine: write to network: {}: {}",
self.endpoint,
packet.len()
);
if let Err(err) = self.udp.send_to(packet, self.endpoint).await {
let endpoint = self.endpoint.read().await;
log::info!("routine: write to network: {}: {}", endpoint, packet.len());
if let Err(err) = self.udp.send_to(packet, *endpoint).await {
error!("routine: failed to send packet: {err:?}");
};
}
@@ -276,18 +282,25 @@ impl WireGuardTunnel {
}
}
pub fn start_wg_tunnel(
pub(crate) fn start_wg_tunnel(
endpoint: SocketAddr,
udp: Arc<UdpSocket>,
static_private: x25519::StaticSecret,
peer_static_public: x25519::PublicKey,
tunnel_tx: mpsc::UnboundedSender<Vec<u8>>,
peer_allowed_ips: ip_network::IpNetwork,
tunnel_tx: TunTaskTx,
) -> (
tokio::task::JoinHandle<SocketAddr>,
mpsc::UnboundedSender<Event>,
) {
let (mut tunnel, peer_tx) =
WireGuardTunnel::new(udp, endpoint, static_private, peer_static_public, tunnel_tx);
let (mut tunnel, peer_tx) = WireGuardTunnel::new(
udp,
endpoint,
static_private,
peer_static_public,
peer_allowed_ips,
tunnel_tx,
);
let join_handle = tokio::spawn(async move {
tunnel.spin_off().await;
endpoint
+13 -13
View File
@@ -810,7 +810,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.12",
"syn 2.0.32",
]
[[package]]
@@ -1628,9 +1628,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
[[package]]
name = "quote"
version = "1.0.26"
version = "1.0.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc"
checksum = "5907a1b7c277254a8b15170f6e7c97cfa60ee7872a3217663bb81151e48184bb"
dependencies = [
"proc-macro2",
]
@@ -1894,7 +1894,7 @@ checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.12",
"syn 2.0.32",
]
[[package]]
@@ -1927,7 +1927,7 @@ checksum = "bcec881020c684085e55a25f7fd888954d56609ef363479dc5a1305eb0d40cab"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.12",
"syn 2.0.32",
]
[[package]]
@@ -2041,9 +2041,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.12"
version = "2.0.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79d9531f94112cfc3e4c8f5f02cb2b58f72c97b7efd85f70203cc6d8efda5927"
checksum = "239814284fd6f1a4ffe4ca893952cdd93c224b6a1571c9a9eadd670295c0c9e2"
dependencies = [
"proc-macro2",
"quote",
@@ -2065,22 +2065,22 @@ dependencies = [
[[package]]
name = "thiserror"
version = "1.0.40"
version = "1.0.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac"
checksum = "9d6d7a740b8a666a7e828dd00da9c0dc290dff53154ea77ac109281de90589b7"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.40"
version = "1.0.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.12",
"syn 2.0.32",
]
[[package]]
@@ -2427,5 +2427,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.12",
"syn 2.0.32",
]
+2
View File
@@ -48,3 +48,5 @@ cw3 = "=1.1.0"
cw3-fixed-multisig = "=1.1.0"
cw4 = "=1.1.0"
cw20 = "=1.1.0"
thiserror = "1.0.48"
+1 -1
View File
@@ -23,7 +23,7 @@ cw-storage-plus = { workspace = true }
cw-controllers = { workspace = true }
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
thiserror = "1.0.23"
thiserror = { workspace = true }
[features]
schema-gen = ["nym-coconut-bandwidth-contract-common/schema", "cosmwasm-schema"]
+1 -1
View File
@@ -22,7 +22,7 @@ cw-storage-plus = { workspace = true }
cw-controllers = { workspace = true }
cw4 = { workspace = true }
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
thiserror = "1.0.23"
thiserror = { workspace = true }
[dev-dependencies]
cw-multi-test = { workspace = true }
+1 -1
View File
@@ -22,7 +22,7 @@ cw-utils = { workspace = true }
schemars = "0.8"
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
thiserror = "1.0.23"
thiserror = { workspace = true }
nym-coconut-bandwidth = { path = "../coconut-bandwidth" }
nym-coconut-dkg = { path = "../coconut-dkg" }
+1 -1
View File
@@ -23,7 +23,7 @@ cw-controllers = { workspace = true }
cw4 = { workspace = true }
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
thiserror = "1.0.23"
thiserror = { workspace = true }
[dev-dependencies]
cw-multi-test = { workspace = true }
@@ -243,7 +243,7 @@ impl TestSetup {
ContractMessageContent::new(Addr::unchecked(owner), proxy, vec![stake], payload);
let sign_payload = SignableMixNodeBondingMsg::new(signing_nonce, content);
let plaintext = sign_payload.to_plaintext().unwrap();
let signature = keypair.private_key().sign(&plaintext);
let signature = keypair.private_key().sign(plaintext);
let msg_signature = MessageSignature::from(signature.to_bytes().as_ref());
(mixnode, msg_signature)
+1 -1
View File
@@ -39,7 +39,7 @@ cw-storage-plus = { workspace = true }
bs58 = "0.4.0"
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
thiserror = { version = "1.0.23" }
thiserror = { workspace = true }
time = { version = "0.3", features = ["macros"] }
semver = { version = "1.0.16", default-features = false }
+2 -2
View File
@@ -206,7 +206,7 @@ pub mod test_helpers {
let sig_bytes = family_owner_keys
.private_key()
.sign(&msg.to_plaintext().unwrap())
.sign(msg.to_plaintext().unwrap())
.to_bytes();
MessageSignature::from(sig_bytes.as_ref())
}
@@ -956,7 +956,7 @@ pub mod test_helpers {
match message.algorithm {
SigningAlgorithm::Ed25519 => {
let plaintext = message.to_plaintext().unwrap();
let signature = private_key.sign(&plaintext);
let signature = private_key.sign(plaintext);
MessageSignature::from(signature.to_bytes().as_ref())
}
SigningAlgorithm::Secp256k1 => {
+1 -1
View File
@@ -41,5 +41,5 @@ cosmwasm-schema = { workspace = true }
cosmwasm-std = { workspace = true }
schemars = "0.8.1"
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
thiserror = { version = "1.0.23" }
thiserror = { workspace = true }
+1 -1
View File
@@ -22,7 +22,7 @@ nym-contracts-common = { path = "../../common/cosmwasm-smart-contracts/contracts
nym-name-service-common = { path = "../../common/cosmwasm-smart-contracts/name-service" }
semver = { version = "1.0.16", default-features = false }
serde = { version = "1.0.155", default-features = false, features = ["derive"] }
thiserror = "1.0.39"
thiserror = { workspace = true }
[build-dependencies]
vergen = { version = "=7.4.3", default-features = false, features = ["build", "git", "rustc"] }
@@ -29,7 +29,7 @@ pub fn ed25519_sign_message<T: Serialize + SigningPurpose>(
match message.algorithm {
SigningAlgorithm::Ed25519 => {
let plaintext = message.to_plaintext().unwrap();
let signature = private_key.sign(&plaintext);
let signature = private_key.sign(plaintext);
MessageSignature::from(signature.to_bytes().as_ref())
}
SigningAlgorithm::Secp256k1 => {
@@ -22,7 +22,7 @@ nym-contracts-common = { path = "../../common/cosmwasm-smart-contracts/contracts
nym-service-provider-directory-common = { path = "../../common/cosmwasm-smart-contracts/service-provider-directory" }
semver = { version = "1.0.16", default-features = false }
serde = { version = "1.0.155", default-features = false, features = ["derive"] }
thiserror = "1.0.39"
thiserror = { workspace = true }
[build-dependencies]
vergen = { version = "=7.4.3", default-features = false, features = ["build", "git", "rustc"] }
@@ -31,7 +31,7 @@ pub fn ed25519_sign_message<T: Serialize + SigningPurpose>(
match message.algorithm {
SigningAlgorithm::Ed25519 => {
let plaintext = message.to_plaintext().unwrap();
let signature = private_key.sign(&plaintext);
let signature = private_key.sign(plaintext);
MessageSignature::from(signature.to_bytes().as_ref())
}
SigningAlgorithm::Secp256k1 => {
+1 -1
View File
@@ -35,7 +35,7 @@ cw2 = { workspace = true }
cw-storage-plus = { workspace = true, features = ["iterator"] }
serde = { version = "1.0", default-features = false, features = ["derive"] }
thiserror = { version = "1.0" }
thiserror ={ workspace = true }
semver = { version = "1.0.16", default-features = false }
[dev-dependencies]
@@ -40,7 +40,7 @@ Yes, to run a mix node only is an option. However it will be less rewarded as no
### What are the incentives for the node operator?
In the original setup there were no incentives to run a `network-requester`. After the transition all the users will buy multiple tickets of zkNyms credentials and use those as [anonymous e-cash](https://arxiv.org/abs/2303.08221) to pay for their data traffic (`[Nym API](https://github.com/nymtech/nym/tree/master/nym-api)` will do the do cryptographical checks to prevent double-spending). All collected fees get distributed to all active nodes proportionally to their work by the end of each epoch.
In the original setup there were no incentives to run a `network-requester`. After the transition all the users will buy multiple tickets of zkNyms credentials and use those as [anonymous e-cash](https://arxiv.org/abs/2303.08221) to pay for their data traffic ([`Nym API`](https://github.com/nymtech/nym/tree/master/nym-api) will do the do cryptographical checks to prevent double-spending). All collected fees get distributed to all active nodes proportionally to their work by the end of each epoch.
### How does this change the token economics?
+4 -4
View File
@@ -36,7 +36,7 @@ nym-config = { path = "../common/config" }
nym-ephemera-common = { path = "../common/cosmwasm-smart-contracts/ephemera" }
pretty_env_logger = "0.4"
refinery = { version = "0.8.7", features = ["rusqlite"], optional = true }
reqwest = { version = "0.11.6", features = ["json"] }
reqwest = { version = "0.11.22", features = ["json"] }
# Rocksdb kills compilation times and we're not currently using it. The reason
# we comment it out is that rust-analyzer runs with --all-features
#rocksdb = { version = "0.21.0", optional = true }
@@ -44,14 +44,14 @@ rusqlite = { version = "0.27.0", features = ["bundled"], optional = true }
serde = { version = "1.0", features = ["derive"] }
serde_derive = "1.0.149"
serde_json = "1.0.91"
thiserror = "1.0.37"
thiserror = { workspace = true }
tokio = { version = "1", features = ["macros", "net","rt-multi-thread"] }
tokio-tungstenite = { workspace = true }
tokio-util = { version = "0.7.4", features = ["full"] }
toml = "0.7.0"
unsigned-varint = "0.7.1"
utoipa = { version = "3.0.1", features = ["actix_extras"] }
utoipa-swagger-ui = { version = "3.0.2", features = ["actix-web"] }
utoipa = { workspace = true, features = ["actix_extras"] }
utoipa-swagger-ui = { workspace = true, features = ["actix-web"] }
uuid = { version = "1.2.2", features = ["v4"] }
# Temporary fix to https://github.com/bluejekyll/trust-dns/issues/1946
+4 -4
View File
@@ -24,10 +24,10 @@ reqwest = { workspace = true }
rocket = { version = "0.5.0-rc.2", features = ["json"] }
rocket_cors = { git="https://github.com/lawliet89/rocket_cors", rev="dfd3662c49e2f6fc37df35091cb94d82f7fb5915" }
rocket_okapi = { version = "0.8.0-rc.2", features = ["swagger"] }
schemars = { version = "0.8", features = ["preserve_order"] }
serde = "1.0.126"
serde_json = "1.0.66"
thiserror = "1.0.29"
schemars = { workspace = true, features = ["preserve_order"] }
serde = { workspace = true }
serde_json = { workspace = true }
thiserror = { workspace = true }
tokio = {version = "1.21.2", features = ["full"] }
nym-bin-common = { path = "../common/bin-common"}
@@ -7,6 +7,6 @@ edition = "2021"
nym-contracts-common = { path = "../../common/cosmwasm-smart-contracts/contracts-common" }
nym-mixnet-contract-common = { path = "../../common/cosmwasm-smart-contracts/mixnet-contract" }
nym-api-requests = { path = "../../nym-api/nym-api-requests" }
schemars = { version = "0.8", features = ["preserve_order"] }
schemars = { workspace = true, features = ["preserve_order"] }
serde = { version = "1.0", features = ["derive"] }
ts-rs = { workspace = true, optional = true }
+1
View File
@@ -6,6 +6,7 @@ use crate::helpers::best_effort_small_dec_to_f64;
use crate::mix_node::models::EconomicDynamicsStats;
use nym_contracts_common::truncate_decimal;
use nym_mixnet_contract_common::MixId;
use nym_validator_client::client::NymApiClientExt;
pub(crate) async fn retrieve_mixnode_econ_stats(
client: &ThreadsafeValidatorClient,
+30 -3
View File
@@ -15,6 +15,15 @@ rust-version = "1.56"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
axum = "0.6.20"
sha2 = "0.10.8"
hmac = "0.12.1"
axum-macros = "0.3.8" # Useful for debugging axum Handler trait errors
fastrand = "2"
x25519-dalek = { version = "2.0.0", features = ["static_secrets"] }
base64 = "0.21.4"
anyhow = { workspace = true }
async-trait = { workspace = true }
atty = "0.2"
@@ -34,10 +43,21 @@ pretty_env_logger = "0.4"
rand = "0.7"
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
sqlx = { version = "0.5", features = [ "runtime-tokio-rustls", "sqlite", "macros", "migrate", ] }
sqlx = { version = "0.5", features = [
"runtime-tokio-rustls",
"sqlite",
"macros",
"migrate",
] }
subtle-encoding = { version = "0.5", features = ["bech32-preview"] }
thiserror = "1"
tokio = { workspace = true, features = [ "rt-multi-thread", "net", "signal", "fs", "time" ] }
thiserror = { workspace = true }
tokio = { workspace = true, features = [
"rt-multi-thread",
"net",
"signal",
"fs",
"time",
] }
tokio-stream = { version = "0.1.11", features = ["fs"] }
tokio-tungstenite = { version = "0.20.1" }
tokio-util = { version = "0.7.4", features = ["codec"] }
@@ -45,6 +65,8 @@ url = { version = "2.2", features = ["serde"] }
zeroize = { workspace = true }
# internal
nym-node = { path = "../nym-node" }
nym-api-requests = { path = "../nym-api/nym-api-requests" }
nym-bin-common = { path = "../common/bin-common", features = ["output_format"] }
nym-coconut-interface = { path = "../common/coconut-interface" }
@@ -64,6 +86,11 @@ nym-types = { path = "../common/types" }
nym-validator-client = { path = "../common/client-libs/validator-client" }
nym-wireguard = { path = "../common/wireguard", optional = true }
[dev-dependencies]
tower = "0.4.13"
rand = "0.8.5"
hyper = "0.14.27"
[build-dependencies]
tokio = { version = "1.24.1", features = ["rt-multi-thread", "macros"] }
sqlx = { version = "0.5", features = [
+1 -1
View File
@@ -17,7 +17,7 @@ log = { workspace = true }
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
thiserror = "1.0"
thiserror = { workspace = true }
zeroize = { workspace = true }
nym-crypto = { path = "../../common/crypto" }
@@ -137,7 +137,7 @@ impl<'a, S> State<'a, S> {
.chain(remote_ephemeral_key.to_bytes().iter().cloned())
.collect();
let signature = self.identity.private_key().sign(&message);
let signature = self.identity.private_key().sign(message);
let zero_iv = stream_cipher::zero_iv::<GatewayEncryptionAlgorithm>();
stream_cipher::encrypt::<GatewayEncryptionAlgorithm>(
self.derived_shared_keys.as_ref().unwrap().encryption_key(),
@@ -191,7 +191,7 @@ impl<'a, S> State<'a, S> {
self.remote_pubkey
.as_ref()
.unwrap()
.verify(&signed_payload, &signature)
.verify(signed_payload, &signature)
.map_err(|_| HandshakeError::InvalidSignature)
}
+1 -1
View File
@@ -41,7 +41,7 @@ pub(crate) struct OverrideConfig {
impl OverrideConfig {
pub(crate) fn do_override(self, mut config: Config) -> Result<Config, GatewayError> {
config = config
.with_optional(Config::with_listening_address, self.host)
.with_optional(Config::with_host, self.host)
.with_optional(Config::with_mix_port, self.mix_port)
.with_optional(Config::with_clients_port, self.clients_port)
.with_optional_custom_env(
+29 -1
View File
@@ -3,6 +3,7 @@
use crate::config::old_config_v1_1_20::ConfigV1_1_20;
use crate::config::old_config_v1_1_28::ConfigV1_1_28;
use crate::config::old_config_v1_1_29::ConfigV1_1_29;
use crate::config::{default_config_filepath, Config};
use crate::error::GatewayError;
use log::info;
@@ -20,7 +21,8 @@ fn try_upgrade_v1_1_20_config(id: &str) -> Result<bool, GatewayError> {
info!("It is going to get updated to the current specification.");
let updated_step1: ConfigV1_1_28 = old_config.into();
let updated: Config = updated_step1.into();
let updated_step2: ConfigV1_1_29 = updated_step1.into();
let updated: Config = updated_step2.into();
updated
.save_to_default_location()
.map_err(|err| GatewayError::ConfigSaveFailure {
@@ -42,6 +44,29 @@ fn try_upgrade_v1_1_28_config(id: &str) -> Result<bool, GatewayError> {
info!("It seems the gateway is using <= v1.1.28 config template.");
info!("It is going to get updated to the current specification.");
let updated_step1: ConfigV1_1_29 = old_config.into();
let updated: Config = updated_step1.into();
updated
.save_to_default_location()
.map_err(|err| GatewayError::ConfigSaveFailure {
path: default_config_filepath(id),
id: id.to_string(),
source: err,
})?;
Ok(true)
}
fn try_upgrade_v1_1_29_config(id: &str) -> Result<bool, GatewayError> {
// explicitly load it as v1.1.29 (which is incompatible with the current, i.e. 1.1.30+)
let Ok(old_config) = ConfigV1_1_29::read_from_default_path(id) else {
// if we failed to load it, there might have been nothing to upgrade
// or maybe it was an even older file. in either way. just ignore it and carry on with our day
return Ok(false);
};
info!("It seems the gateway is using <= v1.1.29 config template.");
info!("It is going to get updated to the current specification.");
let updated: Config = old_config.into();
updated
.save_to_default_location()
@@ -61,6 +86,9 @@ pub(crate) fn try_upgrade_config(id: &str) -> Result<(), GatewayError> {
if try_upgrade_v1_1_28_config(id)? {
return Ok(());
}
if try_upgrade_v1_1_29_config(id)? {
return Ok(());
}
Ok(())
}
+48 -4
View File
@@ -12,9 +12,10 @@ use nym_config::{
DEFAULT_CONFIG_DIR, DEFAULT_CONFIG_FILENAME, DEFAULT_DATA_DIR, NYM_DIR,
};
use nym_network_defaults::mainnet;
use serde::{Deserialize, Serialize};
use nym_node::config;
use serde::{Deserialize, Deserializer, Serialize};
use std::io;
use std::net::IpAddr;
use std::net::{IpAddr, SocketAddr};
use std::path::{Path, PathBuf};
use std::time::Duration;
use url::Url;
@@ -22,6 +23,7 @@ use zeroize::{Zeroize, ZeroizeOnDrop};
pub(crate) mod old_config_v1_1_20;
pub(crate) mod old_config_v1_1_28;
pub(crate) mod old_config_v1_1_29;
pub mod persistence;
mod template;
@@ -38,6 +40,18 @@ const DEFAULT_MAXIMUM_CONNECTION_BUFFER_SIZE: usize = 2000;
const DEFAULT_STORED_MESSAGE_FILENAME_LENGTH: u16 = 16;
const DEFAULT_MESSAGE_RETRIEVAL_LIMIT: i64 = 100;
fn de_maybe_port<'de, D>(deserializer: D) -> Result<Option<u16>, D::Error>
where
D: Deserializer<'de>,
{
let port = u16::deserialize(deserializer)?;
if port == 0 {
Ok(None)
} else {
Ok(Some(port))
}
}
/// Derive default path to gateway's config directory.
/// It should get resolved to `$HOME/.nym/gateways/<id>/config`
pub fn default_config_directory<P: AsRef<Path>>(id: P) -> PathBuf {
@@ -71,8 +85,17 @@ pub struct Config {
#[serde(skip)]
pub(crate) save_path: Option<PathBuf>,
pub host: config::Host,
#[serde(default)]
pub http: config::Http,
pub gateway: Gateway,
#[serde(default)]
// currently not really used for anything useful
pub wireguard: config::Wireguard,
pub storage_paths: GatewayPaths,
pub network_requester: NetworkRequester,
@@ -92,9 +115,16 @@ impl NymConfigTemplate for Config {
impl Config {
pub fn new<S: AsRef<str>>(id: S) -> Self {
let default_gateway = Gateway::new_default(id.as_ref());
Config {
save_path: None,
gateway: Gateway::new_default(id.as_ref()),
host: config::Host {
public_ips: vec![default_gateway.listening_address],
..Default::default()
},
http: Default::default(),
gateway: default_gateway,
wireguard: Default::default(),
storage_paths: GatewayPaths::new_default(id.as_ref()),
network_requester: Default::default(),
logging: Default::default(),
@@ -182,8 +212,16 @@ impl Config {
self
}
pub fn with_listening_address(mut self, listening_address: IpAddr) -> Self {
pub fn with_host(mut self, listening_address: IpAddr) -> Self {
self.gateway.listening_address = listening_address;
// temporary workaround
self.host.public_ips = vec![listening_address];
let http_port = self.http.bind_address.port();
self.http.bind_address = SocketAddr::new(listening_address, http_port);
let wg_port = self.wireguard.bind_address.port();
self.wireguard.bind_address = SocketAddr::new(listening_address, wg_port);
self
}
@@ -245,6 +283,11 @@ pub struct Gateway {
/// (default: 9000)
pub clients_port: u16,
/// If applicable, announced port for listening for secure websocket client traffic.
/// (default: None)
#[serde(deserialize_with = "de_maybe_port")]
pub clients_wss_port: Option<u16>,
/// Whether gateway collects and sends anonymized statistics
pub enabled_statistics: bool,
@@ -279,6 +322,7 @@ impl Gateway {
listening_address: inaddr_any(),
mix_port: DEFAULT_MIX_LISTENING_PORT,
clients_port: DEFAULT_CLIENT_LISTENING_PORT,
clients_wss_port: None,
enabled_statistics: false,
statistics_service_url: mainnet::STATISTICS_SERVICE_DOMAIN_ADDRESS
.parse()
+19 -10
View File
@@ -1,9 +1,10 @@
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::persistence::paths::{GatewayPaths, KeysPaths};
use crate::config::{Config, Debug, Gateway};
use nym_bin_common::logging::LoggingSettings;
use crate::config::old_config_v1_1_29::{
ConfigV1_1_29, DebugV1_1_29, GatewayPathsV1_1_29, GatewayV1_1_29, KeysPathsV1_1_29,
LoggingSettingsV1_1_29,
};
use nym_config::{
must_get_home, read_config_from_toml_file, DEFAULT_CONFIG_DIR, DEFAULT_CONFIG_FILENAME, NYM_DIR,
};
@@ -84,11 +85,13 @@ impl ConfigV1_1_28 {
}
}
impl From<ConfigV1_1_28> for Config {
impl From<ConfigV1_1_28> for ConfigV1_1_29 {
fn from(value: ConfigV1_1_28) -> Self {
Config {
ConfigV1_1_29 {
// \/ ADDED
save_path: None,
gateway: Gateway {
// /\ ADDED
gateway: GatewayV1_1_29 {
version: value.gateway.version,
id: value.gateway.id,
only_coconut_credentials: value.gateway.only_coconut_credentials,
@@ -101,19 +104,25 @@ impl From<ConfigV1_1_28> for Config {
statistics_service_url: value.gateway.statistics_service_url,
cosmos_mnemonic: value.gateway.cosmos_mnemonic,
},
storage_paths: GatewayPaths {
keys: KeysPaths {
storage_paths: GatewayPathsV1_1_29 {
keys: KeysPathsV1_1_29 {
private_identity_key_file: value.storage_paths.keys.private_identity_key_file,
public_identity_key_file: value.storage_paths.keys.public_identity_key_file,
private_sphinx_key_file: value.storage_paths.keys.private_sphinx_key_file,
public_sphinx_key_file: value.storage_paths.keys.public_sphinx_key_file,
},
clients_storage: value.storage_paths.clients_storage,
// \/ ADDED
network_requester_config: None,
// /\ ADDED
},
// \/ ADDED
network_requester: Default::default(),
logging: LoggingSettings {},
debug: Debug {
// /\ ADDED
logging: LoggingSettingsV1_1_29 {},
debug: DebugV1_1_29 {
packet_forwarding_initial_backoff: value.debug.packet_forwarding_initial_backoff,
packet_forwarding_maximum_backoff: value.debug.packet_forwarding_maximum_backoff,
initial_connection_timeout: value.debug.initial_connection_timeout,

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