Compare commits

..

4 Commits

Author SHA1 Message Date
benedetta davico 4c449797eb Update Cargo.toml 2025-08-27 08:03:16 +02:00
benedetta davico fcdf0fb580 Update push-node-status-api.yaml 2025-08-27 08:02:55 +02:00
benedettadavico 66d0296f47 update dockerfile to pg 2025-08-26 16:02:11 +02:00
benedettadavico 03bbbf44e9 ns api ci fix 2025-08-26 16:02:11 +02:00
209 changed files with 1650 additions and 10512 deletions
+9 -5
View File
@@ -34,18 +34,22 @@ jobs:
- name: Get version from cargo.toml
id: get_version
run: |
yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
VERSION=$(yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml)
echo "result=$VERSION" >> $GITHUB_OUTPUT
- name: Set GIT_TAG variable
run: echo "GIT_TAG=${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }}" >> $GITHUB_ENV
- name: Set RELEASE_TAG variable
- name: Initialise RELEASE_TAG
run: echo "RELEASE_TAG=" >> $GITHUB_ENV
- name: Set RELEASE_TAG for release
if: github.event.inputs.release_image == 'true'
run: echo "RELEASE_TAG=golden-" >> $GITHUB_ENV
- name: Set IMAGE_NAME_AND_TAGS variable
run: echo "IMAGE_NAME_AND_TAGS=${{ env.CONTAINER_NAME }}:${{ env.RELEASE_TAG }}${{ steps.get_version.outputs.result }}" >> $GITHUB_ENV
- name: New env vars
run: echo "RELEASE_TAG='$RELEASE_TAG' GIT_TAG='$GIT_TAG' IMAGE_NAME_AND_TAGS='$IMAGE_NAME_AND_TAGS'"
@@ -65,6 +69,6 @@ jobs:
- name: BuildAndPushImageOnHarbor
run: |
docker build -f ${{ env.WORKING_DIRECTORY }}/Dockerfile . -t harbor.nymte.ch/nym/${{ env.IMAGE_NAME_AND_TAGS }} -t harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }}:latest
docker build -f ${{ env.WORKING_DIRECTORY }}/Dockerfile-sqlite . -t harbor.nymte.ch/nym/${{ env.IMAGE_NAME_AND_TAGS }} -t harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }}:latest
docker push harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }} --all-tags
Generated
+101 -166
View File
@@ -2930,7 +2930,7 @@ dependencies = [
"libc",
"log",
"rustversion",
"windows",
"windows 0.61.3",
]
[[package]]
@@ -3600,7 +3600,7 @@ dependencies = [
"js-sys",
"log",
"wasm-bindgen",
"windows-core",
"windows-core 0.61.2",
]
[[package]]
@@ -4863,7 +4863,6 @@ dependencies = [
"nym-ecash-signer-check",
"nym-ecash-time",
"nym-gateway-client",
"nym-http-api-client",
"nym-http-api-common",
"nym-mixnet-contract-common",
"nym-node-requests",
@@ -4953,22 +4952,48 @@ dependencies = [
]
[[package]]
name = "nym-authenticator-client"
name = "nym-authenticator"
version = "0.1.0"
dependencies = [
"anyhow",
"bincode",
"bs58",
"bytes",
"clap",
"defguard_wireguard_rs",
"fastrand 2.3.0",
"futures",
"ipnetwork",
"log",
"mock_instant",
"nym-authenticator-requests",
"nym-bin-common",
"nym-client-core",
"nym-config",
"nym-credential-verification",
"nym-credentials-interface",
"nym-crypto",
"nym-gateway-requests",
"nym-gateway-storage",
"nym-id",
"nym-network-defaults",
"nym-sdk",
"nym-service-provider-requests-common",
"nym-service-providers-common",
"nym-sphinx",
"nym-task",
"nym-types",
"nym-wireguard",
"nym-wireguard-types",
"semver 1.0.26",
"rand 0.8.5",
"serde",
"serde_json",
"thiserror 2.0.12",
"time",
"tokio",
"tokio-stream",
"tokio-util",
"tracing",
"url",
]
[[package]]
@@ -5094,7 +5119,6 @@ dependencies = [
"nym-crypto",
"nym-ecash-contract-common",
"nym-ecash-time",
"nym-http-api-client",
"nym-id",
"nym-mixnet-contract-common",
"nym-multisig-contract-common",
@@ -5183,7 +5207,6 @@ dependencies = [
"nym-http-api-client",
"nym-id",
"nym-mixnet-client",
"nym-mixnet-contract-common",
"nym-network-defaults",
"nym-nonexhaustive-delayqueue",
"nym-pemstore",
@@ -5233,7 +5256,6 @@ dependencies = [
name = "nym-client-core-gateways-storage"
version = "0.1.0"
dependencies = [
"anyhow",
"async-trait",
"cosmrs",
"nym-crypto",
@@ -5252,7 +5274,6 @@ dependencies = [
name = "nym-client-core-surb-storage"
version = "0.1.0"
dependencies = [
"anyhow",
"async-trait",
"dashmap",
"nym-crypto",
@@ -5395,7 +5416,7 @@ dependencies = [
[[package]]
name = "nym-credential-proxy"
version = "0.1.8"
version = "0.1.7"
dependencies = [
"anyhow",
"axum 0.7.9",
@@ -5413,7 +5434,6 @@ dependencies = [
"nym-credentials-interface",
"nym-crypto",
"nym-ecash-contract-common",
"nym-ecash-signer-check",
"nym-http-api-common",
"nym-network-defaults",
"nym-validator-client",
@@ -5464,7 +5484,6 @@ dependencies = [
name = "nym-credential-storage"
version = "0.1.0"
dependencies = [
"anyhow",
"async-trait",
"bincode",
"log",
@@ -5536,7 +5555,6 @@ dependencies = [
"nym-crypto",
"nym-ecash-contract-common",
"nym-ecash-time",
"nym-http-api-client",
"nym-network-defaults",
"nym-serde-helpers",
"nym-validator-client",
@@ -5571,7 +5589,6 @@ dependencies = [
"aead",
"aes",
"aes-gcm-siv",
"base64 0.22.1",
"blake3",
"bs58",
"cipher",
@@ -5637,7 +5654,6 @@ version = "0.1.0"
dependencies = [
"futures",
"nym-ecash-signer-check-types",
"nym-http-api-client",
"nym-network-defaults",
"nym-validator-client",
"semver 1.0.26",
@@ -5712,18 +5728,14 @@ version = "1.1.36"
dependencies = [
"anyhow",
"async-trait",
"bincode",
"bip39",
"bs58",
"dashmap",
"defguard_wireguard_rs",
"fastrand 2.3.0",
"futures",
"ipnetwork",
"mock_instant",
"nym-api-requests",
"nym-authenticator-requests",
"nym-client-core",
"nym-authenticator",
"nym-credential-verification",
"nym-credentials",
"nym-credentials-interface",
@@ -5731,7 +5743,6 @@ dependencies = [
"nym-gateway-requests",
"nym-gateway-stats-storage",
"nym-gateway-storage",
"nym-id",
"nym-ip-packet-router",
"nym-mixnet-client",
"nym-mixnode-common",
@@ -5739,7 +5750,6 @@ dependencies = [
"nym-network-requester",
"nym-node-metrics",
"nym-sdk",
"nym-service-provider-requests-common",
"nym-sphinx",
"nym-statistics-common",
"nym-task",
@@ -5747,11 +5757,10 @@ dependencies = [
"nym-types",
"nym-validator-client",
"nym-wireguard",
"nym-wireguard-private-metadata-server",
"nym-wireguard-types",
"rand 0.8.5",
"serde",
"sha2 0.10.9",
"sqlx",
"thiserror 2.0.12",
"time",
"tokio",
@@ -5837,7 +5846,6 @@ dependencies = [
name = "nym-gateway-stats-storage"
version = "0.1.0"
dependencies = [
"anyhow",
"nym-node-metrics",
"nym-sphinx",
"nym-statistics-common",
@@ -5853,7 +5861,6 @@ dependencies = [
name = "nym-gateway-storage"
version = "0.1.0"
dependencies = [
"anyhow",
"async-trait",
"bincode",
"defguard_wireguard_rs",
@@ -5910,7 +5917,6 @@ dependencies = [
"mime",
"nym-bin-common",
"nym-http-api-common",
"nym-network-defaults",
"once_cell",
"reqwest 0.12.22",
"serde",
@@ -5979,20 +5985,6 @@ dependencies = [
"thiserror 2.0.12",
]
[[package]]
name = "nym-ip-packet-client"
version = "0.1.0"
dependencies = [
"bytes",
"futures",
"nym-ip-packet-requests",
"nym-sdk",
"thiserror 2.0.12",
"tokio",
"tokio-util",
"tracing",
]
[[package]]
name = "nym-ip-packet-requests"
version = "0.1.0"
@@ -6185,8 +6177,6 @@ dependencies = [
"nym-client-core",
"nym-crypto",
"nym-gateway-requests",
"nym-http-api-client",
"nym-mixnet-contract-common",
"nym-network-defaults",
"nym-sdk",
"nym-sphinx",
@@ -6286,6 +6276,7 @@ dependencies = [
"indicatif 0.17.11",
"ipnetwork",
"lioness",
"nym-authenticator",
"nym-bin-common",
"nym-client-core-config-types",
"nym-config",
@@ -6654,7 +6645,6 @@ dependencies = [
"nym-credentials-interface",
"nym-crypto",
"nym-gateway-requests",
"nym-http-api-client",
"nym-network-defaults",
"nym-ordered-buffer",
"nym-service-providers-common",
@@ -7228,7 +7218,6 @@ dependencies = [
"nym-credentials-interface",
"nym-crypto",
"nym-ecash-time",
"nym-http-api-client",
"nym-network-defaults",
"nym-pemstore",
"nym-serde-helpers",
@@ -7258,9 +7247,7 @@ dependencies = [
"bytes",
"futures",
"humantime",
"nym-api-requests",
"nym-crypto",
"nym-http-api-client",
"nym-task",
"nym-validator-client",
"rand 0.8.5",
@@ -7331,26 +7318,6 @@ dependencies = [
"ts-rs",
]
[[package]]
name = "nym-wg-gateway-client"
version = "0.1.0"
dependencies = [
"nym-authenticator-client",
"nym-authenticator-requests",
"nym-bandwidth-controller",
"nym-credentials-interface",
"nym-crypto",
"nym-node-requests",
"nym-pemstore",
"nym-sdk",
"nym-statistics-common",
"nym-validator-client",
"rand 0.8.5",
"thiserror 2.0.12",
"tracing",
"url",
]
[[package]]
name = "nym-wireguard"
version = "0.1.0"
@@ -7383,68 +7350,6 @@ dependencies = [
"x25519-dalek",
]
[[package]]
name = "nym-wireguard-private-metadata-client"
version = "1.0.0"
dependencies = [
"async-trait",
"nym-http-api-client",
"nym-wireguard-private-metadata-shared",
"tracing",
]
[[package]]
name = "nym-wireguard-private-metadata-server"
version = "1.0.0"
dependencies = [
"anyhow",
"async-trait",
"axum 0.7.9",
"futures",
"nym-credential-verification",
"nym-credentials-interface",
"nym-http-api-common",
"nym-wireguard",
"nym-wireguard-private-metadata-shared",
"tokio",
"tokio-util",
"tower-http 0.5.2",
"utoipa",
"utoipa-swagger-ui",
]
[[package]]
name = "nym-wireguard-private-metadata-shared"
version = "1.0.0"
dependencies = [
"axum 0.7.9",
"bincode",
"nym-credentials-interface",
"schemars 0.8.22",
"serde",
"thiserror 2.0.12",
"utoipa",
]
[[package]]
name = "nym-wireguard-private-metadata-tests"
version = "1.0.0"
dependencies = [
"async-trait",
"axum 0.7.9",
"nym-credential-verification",
"nym-credentials-interface",
"nym-http-api-client",
"nym-http-api-common",
"nym-wireguard",
"nym-wireguard-private-metadata-client",
"nym-wireguard-private-metadata-server",
"nym-wireguard-private-metadata-shared",
"tokio",
"tower-http 0.5.2",
"utoipa",
]
[[package]]
name = "nym-wireguard-types"
version = "0.1.0"
@@ -7525,7 +7430,6 @@ dependencies = [
name = "nyxd-scraper"
version = "0.1.0"
dependencies = [
"anyhow",
"async-trait",
"const_format",
"cosmrs",
@@ -7546,25 +7450,6 @@ dependencies = [
"url",
]
[[package]]
name = "objc2-core-foundation"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166"
dependencies = [
"bitflags 2.9.1",
]
[[package]]
name = "objc2-io-kit"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71c1c64d6120e51cd86033f67176b1cb66780c2efe34dec55176f77befd93c0a"
dependencies = [
"libc",
"objc2-core-foundation",
]
[[package]]
name = "object"
version = "0.36.7"
@@ -9828,7 +9713,7 @@ dependencies = [
"tokio",
"tracing",
"tracing-subscriber",
"windows",
"windows 0.61.3",
]
[[package]]
@@ -10058,16 +9943,16 @@ dependencies = [
[[package]]
name = "sysinfo"
version = "0.37.0"
version = "0.33.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07cec4dc2d2e357ca1e610cfb07de2fa7a10fc3e9fe89f72545f3d244ea87753"
checksum = "4fc858248ea01b66f19d8e8a6d55f41deaf91e9d495246fd01368d99935c6c01"
dependencies = [
"core-foundation-sys",
"libc",
"memchr",
"ntapi",
"objc2-core-foundation",
"objc2-io-kit",
"windows",
"rayon",
"windows 0.57.0",
]
[[package]]
@@ -10261,7 +10146,6 @@ dependencies = [
"nym-crypto",
"nym-ecash-contract-common",
"nym-group-contract-common",
"nym-http-api-client",
"nym-mixnet-contract-common",
"nym-multisig-contract-common",
"nym-pemstore",
@@ -11384,7 +11268,6 @@ dependencies = [
"clap",
"comfy-table",
"nym-bin-common",
"nym-http-api-client",
"nym-network-defaults",
"nym-validator-client",
"serde",
@@ -11615,7 +11498,6 @@ dependencies = [
"nym-credential-storage",
"nym-crypto",
"nym-gateway-client",
"nym-http-api-client",
"nym-sphinx",
"nym-sphinx-acknowledgements",
"nym-statistics-common",
@@ -11817,6 +11699,16 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows"
version = "0.57.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143"
dependencies = [
"windows-core 0.57.0",
"windows-targets 0.52.6",
]
[[package]]
name = "windows"
version = "0.61.3"
@@ -11824,7 +11716,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893"
dependencies = [
"windows-collections",
"windows-core",
"windows-core 0.61.2",
"windows-future",
"windows-link",
"windows-numerics",
@@ -11836,7 +11728,19 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8"
dependencies = [
"windows-core",
"windows-core 0.61.2",
]
[[package]]
name = "windows-core"
version = "0.57.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d"
dependencies = [
"windows-implement 0.57.0",
"windows-interface 0.57.0",
"windows-result 0.1.2",
"windows-targets 0.52.6",
]
[[package]]
@@ -11845,10 +11749,10 @@ version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3"
dependencies = [
"windows-implement",
"windows-interface",
"windows-implement 0.60.0",
"windows-interface 0.59.1",
"windows-link",
"windows-result",
"windows-result 0.3.4",
"windows-strings",
]
@@ -11858,11 +11762,22 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e"
dependencies = [
"windows-core",
"windows-core 0.61.2",
"windows-link",
"windows-threading",
]
[[package]]
name = "windows-implement"
version = "0.57.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.104",
]
[[package]]
name = "windows-implement"
version = "0.60.0"
@@ -11874,6 +11789,17 @@ dependencies = [
"syn 2.0.104",
]
[[package]]
name = "windows-interface"
version = "0.57.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.104",
]
[[package]]
name = "windows-interface"
version = "0.59.1"
@@ -11897,10 +11823,19 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1"
dependencies = [
"windows-core",
"windows-core 0.61.2",
"windows-link",
]
[[package]]
name = "windows-result"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8"
dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "windows-result"
version = "0.3.4"
+4 -9
View File
@@ -102,22 +102,16 @@ members = [
"common/wasm/storage",
"common/wasm/utils",
"common/wireguard",
"common/wireguard-private-metadata/client",
"common/wireguard-private-metadata/server",
"common/wireguard-private-metadata/shared",
"common/wireguard-private-metadata/tests",
"common/wireguard-types",
"common/zulip-client",
"documentation/autodoc",
"gateway",
"nym-api",
"nym-api/nym-api-requests",
"nym-authenticator-client",
"nym-browser-extension/storage",
"nym-credential-proxy/nym-credential-proxy",
"nym-credential-proxy/nym-credential-proxy-requests",
"nym-credential-proxy/vpn-api-lib-wasm",
"nym-ip-packet-client",
"nym-network-monitor",
"nym-node",
"nym-node-status-api/nym-node-status-agent",
@@ -128,12 +122,12 @@ members = [
"nym-outfox",
"nym-statistics-api",
"nym-validator-rewarder",
"nym-wg-gateway-client",
"nyx-chain-watcher",
"sdk/ffi/cpp",
"sdk/ffi/go",
"sdk/ffi/shared",
"sdk/rust/nym-sdk",
"service-providers/authenticator",
"service-providers/common",
"service-providers/ip-packet-router",
"service-providers/network-requester",
@@ -171,6 +165,7 @@ default-members = [
"nym-statistics-api",
"nym-validator-rewarder",
"nyx-chain-watcher",
"service-providers/authenticator",
"service-providers/ip-packet-router",
"service-providers/network-requester",
"tools/nymvisor",
@@ -185,7 +180,7 @@ homepage = "https://nymtech.net"
documentation = "https://nymtech.net"
edition = "2021"
license = "Apache-2.0"
rust-version = "1.81"
rust-version = "1.80"
readme = "README.md"
[workspace.dependencies]
@@ -330,7 +325,7 @@ strum = "0.27.2"
strum_macros = "0.27.2"
subtle-encoding = "0.5"
syn = "1"
sysinfo = "0.37.0"
sysinfo = "0.33.0"
tap = "1.0.1"
tar = "0.4.44"
tempfile = "3.20"
+1 -1
View File
@@ -13,7 +13,7 @@ use nym_credentials_interface::{
};
use nym_ecash_time::Date;
use nym_validator_client::coconut::all_ecash_api_clients;
use nym_validator_client::nym_api::{EpochId, NymApiClientExt};
use nym_validator_client::nym_api::EpochId;
use nym_validator_client::nyxd::contract_traits::DkgQueryClient;
use nym_validator_client::EcashApiClient;
use rand::prelude::SliceRandom;
-1
View File
@@ -53,7 +53,6 @@ nym-client-core-config-types = { path = "./config-types", features = [
nym-client-core-surb-storage = { path = "./surb-storage" }
nym-client-core-gateways-storage = { path = "./gateways-storage" }
nym-ecash-time = { path = "../ecash-time" }
nym-mixnet-contract-common = { path = "../cosmwasm-smart-contracts/mixnet-contract" }
[target."cfg(not(target_arch = \"wasm32\"))".dependencies]
nym-mixnet-client = { path = "../client-libs/mixnet-client", default-features = false }
@@ -3,7 +3,6 @@ name = "nym-client-core-gateways-storage"
version = "0.1.0"
edition = "2021"
license.workspace = true
rust-version.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -27,7 +26,6 @@ features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate", "time"]
optional = true
[build-dependencies]
anyhow = { workspace = true }
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
sqlx = { workspace = true, features = [
"runtime-tokio-rustls",
+4 -13
View File
@@ -2,30 +2,23 @@
// SPDX-License-Identifier: Apache-2.0
#[tokio::main]
async fn main() -> anyhow::Result<()> {
async fn main() {
#[cfg(feature = "fs-gateways-storage")]
{
use anyhow::Context;
use sqlx::{Connection, SqliteConnection};
use std::env;
let out_dir = env::var("OUT_DIR")?;
let out_dir = env::var("OUT_DIR").unwrap();
let database_path = format!("{out_dir}/gateways-storage-example.sqlite");
// remove the db file if it already existed from previous build
// in case it was from a different branch
if std::fs::exists(&database_path)? {
std::fs::remove_file(&database_path)?;
}
let mut conn = SqliteConnection::connect(&format!("sqlite://{database_path}?mode=rwc"))
.await
.context("Failed to create SQLx database connection")?;
.expect("Failed to create SQLx database connection");
sqlx::migrate!("./fs_gateways_migrations")
.run(&mut conn)
.await
.context("Failed to perform SQLx migrations")?;
.expect("Failed to perform SQLx migrations");
#[cfg(target_family = "unix")]
println!("cargo:rustc-env=DATABASE_URL=sqlite://{}", &database_path);
@@ -35,6 +28,4 @@ async fn main() -> anyhow::Result<()> {
// not a valid windows path... but hey, it works...
println!("cargo:rustc-env=DATABASE_URL=sqlite:///{}", &database_path);
}
Ok(())
}
@@ -57,7 +57,7 @@ use nym_task::{TaskClient, TaskHandle};
use nym_topology::provider_trait::TopologyProvider;
use nym_topology::HardcodedTopologyProvider;
use nym_validator_client::nym_api::NymApiClientExt;
use nym_validator_client::{nyxd::contract_traits::DkgQueryClient, UserAgent};
use nym_validator_client::{nyxd::contract_traits::DkgQueryClient, NymApiClient, UserAgent};
use rand::prelude::SliceRandom;
use rand::rngs::OsRng;
use rand::thread_rng;
@@ -566,7 +566,7 @@ where
custom_provider: Option<Box<dyn TopologyProvider + Send + Sync>>,
config_topology: config::Topology,
nym_api_urls: Vec<Url>,
nym_api_client: nym_http_api_client::Client,
nym_api_client: NymApiClient,
) -> Box<dyn TopologyProvider + Send + Sync> {
// if no custom provider was ... provided ..., create one using nym-api
custom_provider.unwrap_or_else(|| {
@@ -749,42 +749,21 @@ where
setup_gateway(setup_method, key_store, details_store).await
}
fn construct_nym_api_client(
config: &Config,
user_agent: Option<UserAgent>,
) -> Result<nym_http_api_client::Client, ClientCoreError> {
fn construct_nym_api_client(config: &Config, user_agent: Option<UserAgent>) -> NymApiClient {
let mut nym_api_urls = config.get_nym_api_endpoints();
nym_api_urls.shuffle(&mut thread_rng());
let mut builder = nym_http_api_client::Client::builder::<
_,
nym_validator_client::models::RequestError,
>(nym_api_urls[0].clone())
.map_err(|e| ClientCoreError::NymApiQueryFailure {
source: nym_validator_client::nym_api::error::NymAPIError::GenericRequestFailure(
e.to_string(),
),
})?;
if let Some(user_agent) = user_agent {
builder = builder.with_user_agent(user_agent);
NymApiClient::new_with_user_agent(nym_api_urls[0].clone(), user_agent)
} else {
NymApiClient::new(nym_api_urls[0].clone())
}
builder = builder.with_bincode();
builder
.build::<nym_validator_client::models::RequestError>()
.map_err(|e| ClientCoreError::NymApiQueryFailure {
source: nym_validator_client::nym_api::error::NymAPIError::GenericRequestFailure(
e.to_string(),
),
})
}
async fn determine_key_rotation_state(
client: &nym_http_api_client::Client,
client: &NymApiClient,
) -> Result<KeyRotationConfig, ClientCoreError> {
Ok(client.get_key_rotation_info().await?.into())
Ok(client.nym_api.get_key_rotation_info().await?.into())
}
pub async fn start_base(mut self) -> Result<BaseClient, ClientCoreError>
@@ -851,7 +830,7 @@ where
.dkg_query_client
.map(|client| BandwidthController::new(credential_store, client));
let nym_api_client = Self::construct_nym_api_client(&self.config, self.user_agent.clone())?;
let nym_api_client = Self::construct_nym_api_client(&self.config, self.user_agent.clone());
let key_rotation_config = Self::determine_key_rotation_state(&nym_api_client).await?;
let topology_provider = Self::setup_topology_provider(
@@ -2,10 +2,8 @@
// SPDX-License-Identifier: Apache-2.0
use async_trait::async_trait;
use nym_mixnet_contract_common::EpochRewardedSet;
use nym_topology::provider_trait::{ToTopologyMetadata, TopologyProvider};
use nym_topology::NymTopology;
use nym_validator_client::nym_api::NymApiClientExt;
use rand::prelude::SliceRandom;
use rand::thread_rng;
use std::cmp::min;
@@ -41,43 +39,30 @@ impl Config {
pub struct NymApiTopologyProvider {
config: Config,
validator_client: nym_http_api_client::Client,
validator_client: nym_validator_client::client::NymApiClient,
nym_api_urls: Vec<Url>,
currently_used_api: usize,
use_bincode: bool,
}
impl NymApiTopologyProvider {
pub fn new(
config: impl Into<Config>,
mut nym_api_urls: Vec<Url>,
validator_client: nym_http_api_client::Client,
mut validator_client: nym_validator_client::client::NymApiClient,
) -> Self {
nym_api_urls.shuffle(&mut thread_rng());
let mut provider = NymApiTopologyProvider {
validator_client.change_nym_api(nym_api_urls[0].clone());
NymApiTopologyProvider {
config: config.into(),
validator_client,
nym_api_urls,
currently_used_api: 0,
use_bincode: true,
};
// Set all API URLs - the client will try them in order with automatic failover
provider.validator_client.change_base_urls(
provider
.nym_api_urls
.iter()
.map(|u| u.clone().into())
.collect(),
);
provider
}
}
pub fn disable_bincode(&mut self) {
self.use_bincode = false;
// Note: The unified client doesn't support toggling bincode after creation.
// This would require recreating the client without bincode.
// For now, we'll track the preference but it won't take effect.
warn!("Disabling bincode on existing client is not currently supported");
self.validator_client.use_bincode = false;
}
fn use_next_nym_api(&mut self) {
@@ -87,19 +72,8 @@ impl NymApiTopologyProvider {
}
self.currently_used_api = (self.currently_used_api + 1) % self.nym_api_urls.len();
// Provide all URLs starting from the next one in rotation order
// This enables automatic failover to other endpoints
let rotated_urls: Vec<_> = self
.nym_api_urls
.iter()
.cycle()
.skip(self.currently_used_api)
.take(self.nym_api_urls.len())
.map(|u| u.clone().into())
.collect();
self.validator_client.change_base_urls(rotated_urls)
self.validator_client
.change_nym_api(self.nym_api_urls[self.currently_used_api].clone())
}
async fn get_current_compatible_topology(&mut self) -> Option<NymTopology> {
@@ -125,13 +99,8 @@ impl NymApiTopologyProvider {
.filter(|n| n.performance.round_to_integer() >= self.config.min_node_performance())
.collect::<Vec<_>>();
let epoch_rewarded_set: EpochRewardedSet = rewarded_set.into();
NymTopology::new(
metadata.to_topology_metadata(),
epoch_rewarded_set,
Vec::new(),
)
.with_skimmed_nodes(&nodes_filtered)
NymTopology::new(metadata.to_topology_metadata(), rewarded_set, Vec::new())
.with_skimmed_nodes(&nodes_filtered)
} else {
// if we're not using extended topology, we're only getting active set mixnodes and gateways
@@ -179,13 +148,8 @@ impl NymApiTopologyProvider {
}
}
let epoch_rewarded_set: EpochRewardedSet = rewarded_set.into();
NymTopology::new(
metadata.to_topology_metadata(),
epoch_rewarded_set,
Vec::new(),
)
.with_skimmed_nodes(&nodes)
NymTopology::new(metadata.to_topology_metadata(), rewarded_set, Vec::new())
.with_skimmed_nodes(&nodes)
};
if !topology.is_minimally_routable() {
+8 -67
View File
@@ -7,8 +7,7 @@ use futures::{SinkExt, StreamExt};
use nym_crypto::asymmetric::ed25519;
use nym_gateway_client::GatewayClient;
use nym_topology::node::RoutingNode;
use nym_validator_client::client::{IdentityKeyRef, NymApiClientExt};
use nym_validator_client::nym_nodes::SkimmedNodesWithMetadata;
use nym_validator_client::client::IdentityKeyRef;
use nym_validator_client::UserAgent;
use rand::{seq::SliceRandom, Rng};
#[cfg(unix)]
@@ -84,48 +83,6 @@ struct GatewayWithLatency<'a, G: ConnectableGateway> {
latency: Duration,
}
// Helper to collect all pages of entry nodes - replicates NymApiClient's convenience method
async fn get_all_basic_entry_nodes_with_metadata(
client: &nym_http_api_client::Client,
use_bincode: bool,
) -> Result<SkimmedNodesWithMetadata, ClientCoreError> {
// Get first page to obtain metadata
let mut page = 0;
let res = client
.get_basic_entry_assigned_nodes_v2(false, Some(page), None, use_bincode)
.await?;
let mut nodes = res.nodes.data;
let metadata = res.metadata;
if res.nodes.pagination.total == nodes.len() {
return Ok(SkimmedNodesWithMetadata::new(nodes, metadata));
}
page += 1;
// Collect remaining pages
loop {
let mut res = client
.get_basic_entry_assigned_nodes_v2(false, Some(page), None, use_bincode)
.await?;
if !metadata.consistency_check(&res.metadata) {
return Err(ClientCoreError::ValidatorClientError(
nym_validator_client::ValidatorClientError::InconsistentPagedMetadata,
));
}
nodes.append(&mut res.nodes.data);
if nodes.len() < res.nodes.pagination.total {
page += 1
} else {
break;
}
}
Ok(SkimmedNodesWithMetadata::new(nodes, metadata))
}
impl<'a, G: ConnectableGateway> GatewayWithLatency<'a, G> {
fn new(gateway: &'a G, latency: Duration) -> Self {
GatewayWithLatency { gateway, latency }
@@ -142,32 +99,16 @@ pub async fn gateways_for_init<R: Rng>(
let nym_api = nym_apis
.choose(rng)
.ok_or(ClientCoreError::ListOfNymApisIsEmpty)?;
// Use the unified HTTP client directly with optional user agent
let mut builder = nym_http_api_client::Client::builder(nym_api.clone())
.map_err(|e| {
ClientCoreError::ValidatorClientError(
nym_validator_client::ValidatorClientError::NymAPIError { source: e },
)
})?
.with_bincode(); // Use bincode for better performance
if let Some(user_agent) = user_agent {
builder = builder.with_user_agent(user_agent);
}
let client = builder
.build::<nym_validator_client::models::RequestError>()
.map_err(|e| {
ClientCoreError::ValidatorClientError(
nym_validator_client::ValidatorClientError::NymAPIError { source: e },
)
})?;
let client = if let Some(user_agent) = user_agent {
nym_validator_client::client::NymApiClient::new_with_user_agent(nym_api.clone(), user_agent)
} else {
nym_validator_client::client::NymApiClient::new(nym_api.clone())
};
tracing::debug!("Fetching list of gateways from: {nym_api}");
// Use our helper to handle pagination
let gateways = get_all_basic_entry_nodes_with_metadata(&client, true)
let gateways = client
.get_all_basic_entry_assigned_nodes_with_metadata()
.await?
.nodes;
info!("nym api reports {} gateways", gateways.len());
@@ -30,7 +30,6 @@ optional = true
path = "../../../sqlx-pool-guard"
[build-dependencies]
anyhow = { workspace = true }
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
sqlx = { workspace = true, features = [
"runtime-tokio-rustls",
+4 -7
View File
@@ -2,24 +2,23 @@
// SPDX-License-Identifier: Apache-2.0
#[tokio::main]
async fn main() -> anyhow::Result<()> {
async fn main() {
#[cfg(feature = "fs-surb-storage")]
{
use anyhow::Context;
use sqlx::{Connection, SqliteConnection};
use std::env;
let out_dir = env::var("OUT_DIR")?;
let out_dir = env::var("OUT_DIR").unwrap();
let database_path = format!("{out_dir}/fs-surbs-example.sqlite");
let mut conn = SqliteConnection::connect(&format!("sqlite://{database_path}?mode=rwc"))
.await
.context("Failed to create SQLx database connection")?;
.expect("Failed to create SQLx database connection");
sqlx::migrate!("./fs_surbs_migrations")
.run(&mut conn)
.await
.context("Failed to perform SQLx migrations")?;
.expect("Failed to perform SQLx migrations");
#[cfg(target_family = "unix")]
println!("cargo:rustc-env=DATABASE_URL=sqlite://{}", &database_path);
@@ -29,6 +28,4 @@ async fn main() -> anyhow::Result<()> {
// not a valid windows path... but hey, it works...
println!("cargo:rustc-env=DATABASE_URL=sqlite:///{}", &database_path);
}
Ok(())
}
@@ -5,8 +5,8 @@ use crate::nyxd::{self, NyxdClient};
use crate::signing::direct_wallet::DirectSecp256k1HdWallet;
use crate::signing::signer::{NoSigner, OfflineSigner};
use crate::{
DirectSigningReqwestRpcValidatorClient, QueryReqwestRpcValidatorClient, ReqwestRpcClient,
ValidatorClientError,
nym_api, DirectSigningReqwestRpcValidatorClient, QueryReqwestRpcValidatorClient,
ReqwestRpcClient, ValidatorClientError,
};
use nym_api_requests::ecash::models::{
AggregatedCoinIndicesSignatureResponse, AggregatedExpirationDateSignatureResponse,
@@ -153,7 +153,7 @@ impl Config {
pub struct Client<C, S = NoSigner> {
// ideally they would have been read-only, but unfortunately rust doesn't have such features
// #[deprecated(note = "please use `nym_api_client` instead")]
pub nym_api: nym_http_api_client::Client,
pub nym_api: nym_api::Client,
// pub nym_api_client: NymApiClient,
pub nyxd: NyxdClient<C, S>,
}
@@ -214,7 +214,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_http_api_client::Client::new(config.api_url.clone(), None);
let nym_api_client = nym_api::Client::new(config.api_url.clone(), None);
Client {
nym_api: nym_api_client,
@@ -228,7 +228,7 @@ impl<C, S> Client<C, S> {
where
S: OfflineSigner,
{
let nym_api_client = nym_http_api_client::Client::new(config.api_url.clone(), None);
let nym_api_client = nym_api::Client::new(config.api_url.clone(), None);
Client {
nym_api: nym_api_client,
@@ -385,25 +385,38 @@ impl<C, S> Client<C, S> {
}
}
/// DEPRECATED: Use nym_http_api_client::Client with from_network() or with_bincode() instead
#[deprecated(
since = "1.2.0",
note = "Use nym_http_api_client::Client::from_network() or ClientBuilder::with_bincode() instead"
)]
#[derive(Clone)]
pub struct NymApiClient {
pub use_bincode: bool,
pub nym_api: nym_http_api_client::Client,
pub nym_api: nym_api::Client,
// TODO: perhaps if we really need it at some (currently I don't see any reasons for it)
// we could re-implement the communication with the REST API on port 1317
}
impl From<nym_api::Client> for NymApiClient {
fn from(nym_api: nym_api::Client) -> Self {
NymApiClient {
use_bincode: false,
nym_api,
}
}
}
// we have to allow the use of deprecated method here as they're calling the deprecated trait methods
#[allow(deprecated)]
impl NymApiClient {
pub fn new(api_url: Url) -> Self {
let nym_api = nym_api::Client::new(api_url, None);
NymApiClient {
use_bincode: true,
nym_api,
}
}
#[cfg(not(target_arch = "wasm32"))]
pub fn new_with_timeout(api_url: Url, timeout: std::time::Duration) -> Self {
let nym_api = nym_http_api_client::Client::new(api_url, Some(timeout));
let nym_api = nym_api::Client::new(api_url, Some(timeout));
NymApiClient {
use_bincode: true,
@@ -418,7 +431,7 @@ impl NymApiClient {
}
pub fn new_with_user_agent(api_url: Url, user_agent: impl Into<UserAgent>) -> Self {
let nym_api = nym_http_api_client::Client::builder::<_, ValidatorClientError>(api_url)
let nym_api = nym_api::Client::builder::<_, ValidatorClientError>(api_url)
.expect("invalid api url")
.with_user_agent(user_agent.into())
.build::<ValidatorClientError>()
@@ -3,6 +3,7 @@
use crate::nyxd::contract_traits::{DkgQueryClient, PagedDkgQueryClient};
use crate::nyxd::error::NyxdError;
use crate::NymApiClient;
use nym_coconut_dkg_common::types::{EpochId, NodeIndex};
use nym_coconut_dkg_common::verification_key::ContractVKShare;
use nym_compact_ecash::error::CompactEcashError;
@@ -14,7 +15,7 @@ use url::Url;
// TODO: it really doesn't feel like this should live in this crate.
#[derive(Clone)]
pub struct EcashApiClient {
pub api_client: nym_http_api_client::Client,
pub api_client: NymApiClient,
pub verification_key: VerificationKeyAuth,
pub node_id: NodeIndex,
pub cosmos_address: cosmrs::AccountId,
@@ -24,10 +25,10 @@ impl Display for EcashApiClient {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"[id: {}] {} @ {:?}",
"[id: {}] {} @ {}",
self.node_id,
self.cosmos_address,
self.api_client.base_urls()
self.api_client.api_url()
)
}
}
@@ -59,9 +60,6 @@ pub enum EcashApiError {
source: CompactEcashError,
},
#[error("failed to create API client: {0}")]
ClientError(String),
#[error("the provided account address is malformed: {source}")]
MalformedAccountAddress {
#[from]
@@ -91,16 +89,8 @@ impl TryFrom<ContractVKShare> for EcashApiClient {
// In non-client applications this resolver can cause warning logs about H2 connection
// failure. This indicates that the long lived https connection was closed by the remote
// peer and the resolver will have to reconnect. It should not impact actual functionality
let api_client = nym_http_api_client::Client::builder::<
_,
nym_api_requests::models::RequestError,
>(url_address)
.map_err(|e| EcashApiError::ClientError(e.to_string()))?
.build::<nym_api_requests::models::RequestError>()
.map_err(|e| EcashApiError::ClientError(e.to_string()))?;
Ok(EcashApiClient {
api_client,
api_client: NymApiClient::new(url_address),
verification_key: VerificationKeyAuth::try_from_bs58(&share.share)?,
node_id: share.node_index,
cosmos_address: share.owner.as_str().parse()?,
@@ -1,8 +1,7 @@
use crate::nym_api::NymApiClientExt;
use crate::nyxd::contract_traits::MixnetQueryClient;
use crate::nyxd::error::NyxdError;
use crate::nyxd::Config as ClientConfig;
use crate::{QueryHttpRpcNyxdClient, ValidatorClientError};
use crate::{NymApiClient, QueryHttpRpcNyxdClient, ValidatorClientError};
use colored::Colorize;
use core::fmt;
use itertools::Itertools;
@@ -88,19 +87,8 @@ fn setup_connection_tests<H: BuildHasher + 'static>(
}
});
let api_connection_test_clients = api_urls.filter_map(|(network, url)| {
match nym_http_api_client::Client::builder(url.clone())
.and_then(|b| b.build::<nym_api_requests::models::RequestError>())
{
Ok(client) => Some(ClientForConnectionTest::Api(network, url, client)),
Err(err) => {
eprintln!(
"Failed to create API client for {}: {err}",
network.network_name
);
None
}
}
let api_connection_test_clients = api_urls.map(|(network, url)| {
ClientForConnectionTest::Api(network, url.clone(), NymApiClient::new(url))
});
nyxd_connection_test_clients.chain(api_connection_test_clients)
@@ -172,7 +160,7 @@ async fn test_nyxd_connection(
async fn test_nym_api_connection(
network: NymNetworkDetails,
url: &Url,
client: &nym_http_api_client::Client,
client: &NymApiClient,
) -> ConnectionResult {
let result = match timeout(
Duration::from_secs(CONNECTION_TEST_TIMEOUT_SEC),
@@ -198,7 +186,7 @@ async fn test_nym_api_connection(
enum ClientForConnectionTest {
Nyxd(NymNetworkDetails, Url, Box<QueryHttpRpcNyxdClient>),
Api(NymNetworkDetails, Url, nym_http_api_client::Client),
Api(NymNetworkDetails, Url, NymApiClient),
}
impl ClientForConnectionTest {
@@ -14,6 +14,7 @@ pub mod signing;
pub use crate::error::ValidatorClientError;
pub use crate::rpc::reqwest::ReqwestRpcClient;
pub use crate::signing::direct_wallet::DirectSecp256k1HdWallet;
pub use client::NymApiClient;
pub use client::{Client, Config, EcashApiClient};
pub use nym_api_requests::*;
pub use nym_http_api_client::UserAgent;
@@ -3,7 +3,6 @@
use crate::nym_api::error::NymAPIError;
use crate::nym_api::routes::{ecash, CORE_STATUS_COUNT, SINCE_ARG};
use crate::nym_nodes::SkimmedNodesWithMetadata;
use async_trait::async_trait;
use nym_api_requests::ecash::models::{
AggregatedCoinIndicesSignatureResponse, AggregatedExpirationDateSignatureResponse,
@@ -38,7 +37,7 @@ pub use nym_api_requests::{
MixnodeStatusResponse, MixnodeUptimeHistoryResponse, RewardEstimationResponse,
StakeSaturationResponse, UptimeResponse,
},
nym_nodes::{CachedNodesResponse, SemiSkimmedNode, SemiSkimmedNodesWithMetadata, SkimmedNode},
nym_nodes::{CachedNodesResponse, SemiSkimmedNode, SkimmedNode},
NymNetworkDetailsResponse,
};
use nym_contracts_common::IdentityKey;
@@ -50,8 +49,8 @@ use time::format_description::BorrowedFormatItem;
use time::Date;
use tracing::instrument;
use crate::ValidatorClientError;
pub use nym_coconut_dkg_common::types::EpochId;
pub use nym_http_api_client::Client;
pub mod error;
pub mod routes;
@@ -63,9 +62,6 @@ pub fn rfc_3339_date() -> Vec<BorrowedFormatItem<'static>> {
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
pub trait NymApiClientExt: ApiClient {
/// Get the current API URL being used by the client
fn api_url(&self) -> &url::Url;
async fn health(&self) -> Result<ApiHealthResponse, NymAPIError> {
self.get_json(
&[
@@ -245,162 +241,6 @@ pub trait NymApiClientExt: ApiClient {
.await
}
async fn get_current_rewarded_set(&self) -> Result<RewardedSetResponse, NymAPIError> {
self.get_rewarded_set().await
}
async fn get_all_basic_nodes_with_metadata(
&self,
) -> Result<SkimmedNodesWithMetadata, NymAPIError> {
// unroll first loop iteration in order to obtain the metadata
let mut page = 0;
let res = self
.get_basic_nodes_v2(false, Some(page), None, true)
.await?;
let mut nodes = res.nodes.data;
let metadata = res.metadata;
if res.nodes.pagination.total == nodes.len() {
return Ok(SkimmedNodesWithMetadata::new(nodes, metadata));
}
page += 1;
loop {
let mut res = self
.get_basic_nodes_v2(false, Some(page), None, true)
.await?;
if !metadata.consistency_check(&res.metadata) {
// Create a custom error for inconsistent metadata
return Err(NymAPIError::EndpointFailure {
status: reqwest::StatusCode::INTERNAL_SERVER_ERROR,
error: nym_api_requests::models::RequestError::new(
"Inconsistent paged metadata",
),
});
}
nodes.append(&mut res.nodes.data);
if nodes.len() >= res.nodes.pagination.total {
break;
} else {
page += 1
}
}
Ok(SkimmedNodesWithMetadata::new(nodes, metadata))
}
async fn get_all_basic_active_mixing_assigned_nodes_with_metadata(
&self,
) -> Result<SkimmedNodesWithMetadata, NymAPIError> {
// Get all mixing nodes that are in the active/rewarded set
let mut page = 0;
let res = self
.get_basic_active_mixing_assigned_nodes_v2(false, Some(page), None, false)
.await?;
let metadata = res.metadata;
let mut nodes = res.nodes.data;
if res.nodes.pagination.total == nodes.len() {
return Ok(SkimmedNodesWithMetadata::new(nodes, metadata));
}
page += 1;
loop {
let res = self
.get_basic_active_mixing_assigned_nodes_v2(false, Some(page), None, false)
.await?;
if !metadata.consistency_check(&res.metadata) {
return Err(NymAPIError::EndpointFailure {
status: reqwest::StatusCode::INTERNAL_SERVER_ERROR,
error: nym_api_requests::models::RequestError::new(
"Inconsistent paged metadata",
),
});
}
nodes.append(&mut res.nodes.data.clone());
// Check if we've got all nodes
if nodes.len() >= res.nodes.pagination.total {
break;
} else {
page += 1;
}
}
Ok(SkimmedNodesWithMetadata::new(nodes, metadata))
}
async fn get_all_basic_entry_assigned_nodes_with_metadata(
&self,
) -> Result<SkimmedNodesWithMetadata, NymAPIError> {
// Get all nodes that can act as entry gateways
let mut page = 0;
let res = self
.get_basic_entry_assigned_nodes_v2(false, Some(page), None, false)
.await?;
let metadata = res.metadata;
let mut nodes = res.nodes.data;
if res.nodes.pagination.total == nodes.len() {
return Ok(SkimmedNodesWithMetadata::new(nodes, metadata));
}
page += 1;
loop {
let res = self
.get_basic_entry_assigned_nodes_v2(false, Some(page), None, false)
.await?;
if !metadata.consistency_check(&res.metadata) {
return Err(NymAPIError::EndpointFailure {
status: reqwest::StatusCode::INTERNAL_SERVER_ERROR,
error: nym_api_requests::models::RequestError::new(
"Inconsistent paged metadata",
),
});
}
nodes.append(&mut res.nodes.data.clone());
// Check if we've got all nodes
if nodes.len() >= res.nodes.pagination.total {
break;
} else {
page += 1;
}
}
Ok(SkimmedNodesWithMetadata::new(nodes, metadata))
}
async fn get_all_described_nodes(&self) -> Result<Vec<NymNodeDescription>, NymAPIError> {
// TODO: deal with paging in macro or some helper function or something, because it's the same pattern everywhere
let mut page = 0;
let mut descriptions = Vec::new();
loop {
let mut res = self.get_nodes_described(Some(page), None).await?;
descriptions.append(&mut res.data);
if descriptions.len() < res.pagination.total {
page += 1
} else {
break;
}
}
Ok(descriptions)
}
#[tracing::instrument(level = "debug", skip_all)]
async fn get_nym_nodes(
&self,
@@ -428,25 +268,6 @@ pub trait NymApiClientExt: ApiClient {
.await
}
async fn get_all_bonded_nym_nodes(&self) -> Result<Vec<NymNodeDetails>, ValidatorClientError> {
// TODO: deal with paging in macro or some helper function or something, because it's the same pattern everywhere
let mut page = 0;
let mut bonds = Vec::new();
loop {
let mut res = self.get_nym_nodes(Some(page), None).await?;
bonds.append(&mut res.data);
if bonds.len() < res.pagination.total {
page += 1
} else {
break;
}
}
Ok(bonds)
}
#[deprecated]
#[tracing::instrument(level = "debug", skip_all)]
async fn get_basic_mixnodes(&self) -> Result<CachedNodesResponse<SkimmedNode>, NymAPIError> {
@@ -1550,49 +1371,8 @@ pub trait NymApiClientExt: ApiClient {
)
.await
}
/// Method to change the base API URLs being used by the client
fn change_base_urls(&mut self, urls: Vec<url::Url>);
/// Retrieve expanded information for all bonded nodes on the network
async fn get_all_expanded_nodes(&self) -> Result<SemiSkimmedNodesWithMetadata, NymAPIError> {
// Unroll the first iteration to get the metadata
let mut page = 0;
let res = self.get_expanded_nodes(false, Some(page), None).await?;
let mut nodes = res.nodes.data;
let metadata = res.metadata;
if res.nodes.pagination.total == nodes.len() {
return Ok(SemiSkimmedNodesWithMetadata::new(nodes, metadata));
}
page += 1;
loop {
let mut res = self.get_expanded_nodes(false, Some(page), None).await?;
nodes.append(&mut res.nodes.data);
if nodes.len() < res.nodes.pagination.total {
page += 1
} else {
break;
}
}
Ok(SemiSkimmedNodesWithMetadata::new(nodes, metadata))
}
}
// Client is already nym_http_api_client::Client (re-exported above), so just one impl needed
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl NymApiClientExt for nym_http_api_client::Client {
fn api_url(&self) -> &url::Url {
self.current_url().as_ref()
}
fn change_base_urls(&mut self, urls: Vec<url::Url>) {
self.change_base_urls(urls.into_iter().map(|u| u.into()).collect());
}
}
impl NymApiClientExt for Client {}
-1
View File
@@ -38,7 +38,6 @@ cosmrs = { workspace = true }
cosmwasm-std = { workspace = true }
nym-validator-client = { path = "../client-libs/validator-client" }
nym-http-api-client = { path = "../http-api-client" }
nym-bin-common = { path = "../../common/bin-common", features = ["output_format"] }
nym-crypto = { path = "../../common/crypto", features = ["asymmetric"] }
nym-network-defaults = { path = "../network-defaults" }
+1 -1
View File
@@ -2,12 +2,12 @@
// SPDX-License-Identifier: Apache-2.0
use crate::context::errors::ContextError;
pub use nym_http_api_client::Client as NymApiClient;
use nym_network_defaults::{
setup_env,
var_names::{MIXNET_CONTRACT_ADDRESS, NYM_API, NYXD, VESTING_CONTRACT_ADDRESS},
NymNetworkDetails,
};
pub use nym_validator_client::nym_api::Client as NymApiClient;
use nym_validator_client::nyxd::{self, AccountId, NyxdClient};
use nym_validator_client::{
DirectSigningHttpRpcNyxdClient, DirectSigningHttpRpcValidatorClient, QueryHttpRpcNyxdClient,
-3
View File
@@ -4,7 +4,6 @@
use clap::{Args, Subcommand};
pub mod ecash;
pub mod nyx;
#[derive(Debug, Args)]
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
@@ -17,6 +16,4 @@ pub struct Internal {
pub enum InternalCommands {
/// Ecash related internal commands
Ecash(ecash::InternalEcash),
Nyx(nyx::InternalNyx),
}
@@ -1,116 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::SigningClient;
use anyhow::bail;
use clap::Parser;
use nym_mixnet_contract_common::nym_node::Role;
use nym_mixnet_contract_common::reward_params::NodeRewardingParameters;
use nym_mixnet_contract_common::{
EpochRewardedSet, EpochState, NodeId, RewardingParams, RoleAssignment,
};
use nym_validator_client::nyxd::contract_traits::mixnet_query_client::MixnetQueryClientExt;
use nym_validator_client::nyxd::contract_traits::{MixnetQueryClient, MixnetSigningClient};
use rand::prelude::*;
use rand::thread_rng;
#[derive(Debug, Parser)]
pub struct Args {}
fn choose_new_nodes(
params: &RewardingParams,
rewarded_set: &EpochRewardedSet,
role: Role,
) -> Vec<NodeId> {
let mut rng = thread_rng();
match role {
Role::EntryGateway => rewarded_set
.assignment
.entry_gateways
.choose_multiple(&mut rng, params.rewarded_set.entry_gateways as usize)
.copied()
.collect(),
Role::Layer1 => rewarded_set
.assignment
.layer1
.choose_multiple(&mut rng, params.rewarded_set.mixnodes as usize / 3)
.copied()
.collect(),
Role::Layer2 => rewarded_set
.assignment
.layer2
.choose_multiple(&mut rng, params.rewarded_set.mixnodes as usize / 3)
.copied()
.collect(),
Role::Layer3 => rewarded_set
.assignment
.layer3
.choose_multiple(&mut rng, params.rewarded_set.mixnodes as usize / 3)
.copied()
.collect(),
Role::ExitGateway => rewarded_set
.assignment
.exit_gateways
.choose_multiple(&mut rng, params.rewarded_set.exit_gateways as usize)
.copied()
.collect(),
Role::Standby => rewarded_set
.assignment
.standby
.choose_multiple(&mut rng, params.rewarded_set.standby as usize)
.copied()
.collect(),
}
}
pub async fn force_advance_epoch(_: Args, client: SigningClient) -> anyhow::Result<()> {
let current_epoch = client.get_current_interval_details().await?;
let epoch_status = client.get_current_epoch_status().await?;
if epoch_status.being_advanced_by.as_str() != client.address().to_string() {
bail!(
"this client is not authorised to perform any epoch operations. we need {}",
client.address()
)
}
let rewarding_params = client.get_rewarding_parameters().await?;
let current_rewarded_set = client.get_rewarded_set().await?;
if !current_epoch.is_current_epoch_over {
println!("the current epoch is not over yet - there's nothing to do")
}
// is this most efficient? no. but it's simple
loop {
let epoch_status = client.get_current_epoch_status().await?;
match epoch_status.state {
EpochState::InProgress => break,
EpochState::Rewarding { final_node_id, .. } => {
println!("rewarding {final_node_id} with big fat 0...");
client
.reward_node(
final_node_id,
NodeRewardingParameters::new(Default::default(), Default::default()),
None,
)
.await?;
}
EpochState::ReconcilingEvents => {
println!("trying to reconcile events...");
client.reconcile_epoch_events(None, None).await?;
}
EpochState::RoleAssignment { next } => {
let nodes = choose_new_nodes(&rewarding_params, &current_rewarded_set, next);
println!("assigning {nodes:?} as {next}");
client
.assign_roles(RoleAssignment { role: next, nodes }, None)
.await?;
}
}
}
Ok(())
}
-19
View File
@@ -1,19 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::{Args, Subcommand};
pub mod force_advance_epoch;
#[derive(Debug, Args)]
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
pub struct InternalNyx {
#[clap(subcommand)]
pub command: InternalNyxCommands,
}
#[derive(Debug, Subcommand)]
pub enum InternalNyxCommands {
/// Attempt to force advance the current epoch
ForceAdvanceEpoch(force_advance_epoch::Args),
}
@@ -86,25 +86,6 @@ impl IntervalRewardParams {
pub fn to_inline_json(&self) -> String {
to_json_string(self).unwrap_or_else(|_| "serialisation failure".into())
}
pub fn active_node_work(&self, standby_node_work: Decimal) -> WorkFactor {
self.active_set_work_factor * standby_node_work
}
pub fn standby_node_work(
&self,
rewarded_set_size: Decimal,
standby_set_size: Decimal,
) -> WorkFactor {
let f = self.active_set_work_factor;
let k = rewarded_set_size;
let one = Decimal::one();
// nodes in reserve
let k_r = standby_set_size;
one / (f * k - (f - one) * k_r)
}
}
/// Parameters used for reward calculation.
@@ -128,15 +109,18 @@ pub struct RewardingParams {
impl RewardingParams {
pub fn active_node_work(&self) -> WorkFactor {
let standby_work = self.standby_node_work();
self.interval.active_node_work(standby_work)
self.interval.active_set_work_factor * self.standby_node_work()
}
pub fn standby_node_work(&self) -> WorkFactor {
let rewarded_set_size = self.dec_rewarded_set_size();
let standby_set_size = self.dec_standby_set_size();
self.interval
.standby_node_work(rewarded_set_size, standby_set_size)
let f = self.interval.active_set_work_factor;
let k = self.dec_rewarded_set_size();
let one = Decimal::one();
// nodes in reserve
let k_r = self.dec_standby_set_size();
one / (f * k - (f - one) * k_r)
}
pub fn rewarded_set_size(&self) -> u32 {
@@ -3,7 +3,6 @@
use crate::config_score::{ConfigScoreParams, OutdatedVersionWeights, VersionScoreFormulaParams};
use crate::nym_node::Role;
use crate::reward_params::RewardedSetParams;
use crate::EpochId;
use contracts_common::Percent;
use cosmwasm_schema::cw_serde;
@@ -86,11 +85,7 @@ impl RewardedSet {
}
pub fn rewarded_set_size(&self) -> usize {
self.active_set_size() + self.standby_set_size()
}
pub fn standby_set_size(&self) -> usize {
self.standby.len()
self.active_set_size() + self.standby.len()
}
pub fn get_role(&self, node_id: NodeId) -> Option<Role> {
@@ -115,13 +110,6 @@ impl RewardedSet {
}
None
}
pub fn matches_parameters(&self, params: RewardedSetParams) -> bool {
self.entry_gateways.len() <= params.entry_gateways as usize
&& self.exit_gateways.len() <= params.exit_gateways as usize
&& self.layer1.len() + self.layer2.len() + self.layer3.len() <= params.mixnodes as usize
&& self.standby.len() <= params.standby as usize
}
}
#[cw_serde]
-2
View File
@@ -3,7 +3,6 @@ name = "nym-credential-storage"
version = "0.1.0"
edition = "2021"
license.workspace = true
rust-version.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -34,7 +33,6 @@ features = ["rt-multi-thread", "net", "signal", "fs"]
[build-dependencies]
anyhow = { workspace = true }
sqlx = { workspace = true, features = [
"runtime-tokio-rustls",
"sqlite",
+4 -13
View File
@@ -3,29 +3,22 @@
* SPDX-License-Identifier: Apache-2.0
*/
use anyhow::Context;
use sqlx::{Connection, SqliteConnection};
use std::env;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let out_dir = env::var("OUT_DIR")?;
async fn main() {
let out_dir = env::var("OUT_DIR").unwrap();
let database_path = format!("{out_dir}/coconut-credential-example.sqlite");
// remove the db file if it already existed from previous build
// in case it was from a different branch
if std::fs::exists(&database_path)? {
std::fs::remove_file(&database_path)?;
}
let mut conn = SqliteConnection::connect(&format!("sqlite://{database_path}?mode=rwc"))
.await
.context("Failed to create SQLx database connection")?;
.expect("Failed to create SQLx database connection");
sqlx::migrate!("./migrations")
.run(&mut conn)
.await
.context("Failed to perform SQLx migrations")?;
.expect("Failed to perform SQLx migrations");
#[cfg(target_family = "unix")]
println!("cargo:rustc-env=DATABASE_URL=sqlite://{}", &database_path);
@@ -34,6 +27,4 @@ async fn main() -> anyhow::Result<()> {
// for some strange reason we need to add a leading `/` to the windows path even though it's
// not a valid windows path... but hey, it works...
println!("cargo:rustc-env=DATABASE_URL=sqlite:///{}", &database_path);
Ok(())
}
@@ -110,14 +110,14 @@ FROM ecash_ticketbook;
-- 6. finally swap out the old tables
-- drop old tables
DROP TABLE expiration_date_signatures;
DROP TABLE pending_issuance;
DROP TABLE ecash_ticketbook;
DROP TABLE expiration_date_signatures;
-- rename new tables
ALTER TABLE expiration_date_signatures_new
RENAME TO expiration_date_signatures;
ALTER TABLE pending_issuance_new
RENAME TO pending_issuance;
ALTER TABLE ecash_ticketbook_new
RENAME TO ecash_ticketbook;
ALTER TABLE expiration_date_signatures_new
RENAME TO expiration_date_signatures;
RENAME TO ecash_ticketbook;
@@ -14,7 +14,7 @@ use nym_api_requests::ecash::models::{BatchRedeemTicketsBody, VerifyEcashTicketB
use nym_credentials_interface::Bandwidth;
use nym_credentials_interface::{ClientTicket, TicketType};
use nym_validator_client::coconut::EcashApiError;
use nym_validator_client::nym_api::{EpochId, NymApiClientExt};
use nym_validator_client::nym_api::EpochId;
use nym_validator_client::nyxd::contract_traits::{
EcashSigningClient, MultisigQueryClient, MultisigSigningClient, PagedMultisigQueryClient,
};
@@ -354,7 +354,7 @@ impl CredentialHandler {
Err(err) => {
error!("failed to send ticket {ticket_id} for verification to ecash signer '{client}': {err}. if we don't reach quorum, we'll retry later");
Err(EcashTicketError::ApiFailure(EcashApiError::NymApi {
source: nym_validator_client::ValidatorClientError::NymAPIError { source: err },
source: err,
}))
}
}
-16
View File
@@ -2,7 +2,6 @@
// SPDX-License-Identifier: Apache-2.0
use crate::ecash::traits::EcashManager;
use async_trait::async_trait;
use bandwidth_storage_manager::BandwidthStorageManager;
use nym_credentials::ecash::utils::{cred_exp_date, ecash_today, EcashTime};
use nym_credentials_interface::{Bandwidth, ClientTicket, TicketType};
@@ -140,18 +139,3 @@ impl CredentialVerifier {
.await)
}
}
#[async_trait]
pub trait TicketVerifier {
/// Verify that the ticket is valid and cryptographically correct.
/// If the verification succeeds, also increase the bandwidth with the ticket's
/// amount and return the latest available bandwidth
async fn verify(&mut self) -> Result<i64>;
}
#[async_trait]
impl TicketVerifier for CredentialVerifier {
async fn verify(&mut self) -> Result<i64> {
self.verify().await
}
}
-1
View File
@@ -22,7 +22,6 @@ nym-ecash-time = { path = "../ecash-time", features = ["expiration"] }
nym-credentials-interface = { path = "../credentials-interface" }
nym-crypto = { path = "../crypto" }
nym-api-requests = { path = "../../nym-api/nym-api-requests" }
nym-http-api-client = { path = "../http-api-client" }
nym-validator-client = { path = "../client-libs/validator-client", default-features = false }
nym-ecash-contract-common = { path = "../cosmwasm-smart-contracts/ecash-contract" }
nym-network-defaults = { path = "../network-defaults" }
@@ -15,7 +15,7 @@ use nym_credentials_interface::{
use nym_crypto::asymmetric::ed25519;
use nym_ecash_contract_common::deposit::DepositId;
use nym_ecash_time::{ecash_default_expiration_date, ecash_today, EcashTime};
use nym_validator_client::nym_api::{EpochId, NymApiClientExt};
use nym_validator_client::nym_api::EpochId;
use serde::{Deserialize, Serialize};
use time::Date;
@@ -108,7 +108,7 @@ impl IssuanceTicketBook {
signing_request.withdrawal_request.clone(),
self.deposit_id,
request_signature,
signing_request.ecash_pub_key,
signing_request.ecash_pub_key.clone(),
signing_request.expiration_date,
signing_request.ticketbook_type,
)
@@ -116,7 +116,7 @@ impl IssuanceTicketBook {
pub async fn obtain_blinded_credential(
&self,
client: &nym_http_api_client::Client,
client: &nym_validator_client::client::NymApiClient,
request_body: &BlindSignRequestBody,
) -> Result<BlindedSignature, Error> {
let server_response = client.blind_sign(request_body).await?;
@@ -179,7 +179,7 @@ impl IssuanceTicketBook {
// ideally this would have been generic over credential type, but we really don't need secp256k1 keys for bandwidth vouchers
pub async fn obtain_partial_ticketbook_credential(
&self,
client: &nym_http_api_client::Client,
client: &nym_validator_client::client::NymApiClient,
signer_index: u64,
validator_vk: &VerificationKeyAuth,
signing_data: CredentialSigningData,
-1
View File
@@ -10,7 +10,6 @@ use nym_credentials_interface::{
VerificationKeyAuth, WalletSignatures,
};
use nym_validator_client::client::EcashApiClient;
use nym_validator_client::nym_api::NymApiClientExt;
// so we wouldn't break all the existing imports
pub use nym_ecash_time::{cred_exp_date, ecash_date_offset, ecash_today, EcashTime};
+1 -4
View File
@@ -4,7 +4,7 @@
use crate::ecash::bandwidth::issued::CURRENT_SERIALIZATION_REVISION;
use nym_credentials_interface::CompactEcashError;
use nym_crypto::asymmetric::x25519::KeyRecoveryError;
use nym_validator_client::{nym_api::error::NymAPIError, ValidatorClientError};
use nym_validator_client::ValidatorClientError;
use thiserror::Error;
#[derive(Debug, Error)]
@@ -37,9 +37,6 @@ pub enum Error {
#[error("Ran into a validator client error - {0}")]
ValidatorClientError(#[from] ValidatorClientError),
#[error("Nym API request failed - {0}")]
NymAPIError(#[from] NymAPIError),
#[error("Bandwidth operation overflowed. {0}")]
BandwidthOverflow(String),
-1
View File
@@ -11,7 +11,6 @@ repository = { workspace = true }
aes-gcm-siv = { workspace = true, optional = true }
aes = { workspace = true, optional = true }
aead = { workspace = true, optional = true }
base64.workspace = true
bs58 = { workspace = true }
blake3 = { workspace = true, features = ["traits-preview"], optional = true }
ctr = { workspace = true, optional = true }
@@ -1,7 +1,6 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use base64::Engine;
use nym_pemstore::traits::{PemStorableKey, PemStorableKeyPair};
use std::fmt::{self, Debug, Display, Formatter};
use std::str::FromStr;
@@ -159,15 +158,6 @@ impl PublicKey {
.map_err(|source| KeyRecoveryError::MalformedPublicKeyString { source })?;
Self::from_bytes(&bytes)
}
pub fn from_base64(s: &str) -> Option<Self> {
let bytes = base64::engine::general_purpose::STANDARD.decode(s).ok()?;
Self::from_bytes(&bytes).ok()
}
pub fn to_base64(&self) -> String {
base64::engine::general_purpose::STANDARD.encode(self.as_bytes())
}
}
impl FromStr for PublicKey {
-1
View File
@@ -22,7 +22,6 @@ url = { workspace = true }
nym-validator-client = { path = "../client-libs/validator-client" }
nym-network-defaults = { path = "../network-defaults" }
nym-ecash-signer-check-types = { path = "../ecash-signer-check-types" }
nym-http-api-client = { path = "../http-api-client" }
[lints]
workspace = true
+25 -31
View File
@@ -1,14 +1,15 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::{LocalChainStatus, SignerCheckError, SigningStatus, TypedSignerResult};
use crate::{LocalChainStatus, SigningStatus, TypedSignerResult};
use nym_ecash_signer_check_types::dealer_information::RawDealerInformation;
use nym_ecash_signer_check_types::status::{SignerStatus, SignerTestResult};
use nym_validator_client::client::NymApiClientExt;
use nym_validator_client::models::BinaryBuildInformationOwned;
use nym_validator_client::nym_api::NymApiClientExt;
use nym_validator_client::nyxd::contract_traits::dkg_query_client::{
ContractVKShare, DealerDetails,
};
use nym_validator_client::NymApiClient;
use std::time::Duration;
use tracing::{error, warn};
use url::Url;
@@ -31,38 +32,37 @@ pub(crate) mod signing_status {
}
struct ClientUnderTest {
api_client: nym_http_api_client::Client,
api_client: NymApiClient,
build_info: Option<BinaryBuildInformationOwned>,
}
impl ClientUnderTest {
pub(crate) fn new(api_url: &Url) -> Result<Self, SignerCheckError> {
// The builder should not fail with a valid URL that's already parsed
// If it does fail, it's an internal error that we can't recover from
let api_client = nym_http_api_client::Client::builder(api_url.clone())?.build()?;
Ok(ClientUnderTest {
api_client,
pub(crate) fn new(api_url: &Url) -> Self {
ClientUnderTest {
api_client: NymApiClient::new(api_url.clone()),
build_info: None,
})
}
}
pub(crate) async fn try_retrieve_build_information(&mut self) -> bool {
match tokio::time::timeout(Duration::from_secs(5), self.api_client.build_information())
.await
match tokio::time::timeout(
Duration::from_secs(5),
self.api_client.nym_api.build_information(),
)
.await
{
Ok(Ok(build_information)) => {
self.build_info = Some(build_information);
true
}
Ok(Err(err)) => {
warn!("{}: failed to retrieve build information: {err}. the signer is most likely down", self.api_client.current_url());
warn!("{}: failed to retrieve build information: {err}. the signer is most likely down", self.api_client.api_url());
false
}
Err(_timeout) => {
warn!(
"{}: timed out while attempting to retrieve build information",
self.api_client.current_url()
self.api_client.api_url()
);
false
}
@@ -77,7 +77,7 @@ impl ClientUnderTest {
.inspect_err(|err| {
error!(
"ecash signer '{}' reports invalid version {}: {err}",
self.api_client.current_url(),
self.api_client.api_url(),
build_info.build_version
)
})
@@ -121,14 +121,14 @@ impl ClientUnderTest {
// check if it supports the current query
if self.supports_chain_status_query() {
return match self.api_client.get_chain_blocks_status().await {
return match self.api_client.nym_api.get_chain_blocks_status().await {
Ok(status) => LocalChainStatus::Reachable {
response: Box::new(status),
},
Err(err) => {
warn!(
"{}: failed to retrieve local chain status: {err}",
self.api_client.current_url()
self.api_client.api_url()
);
LocalChainStatus::Unreachable
}
@@ -136,14 +136,14 @@ impl ClientUnderTest {
}
// fallback to the legacy query
match self.api_client.get_chain_status().await {
match self.api_client.nym_api.get_chain_status().await {
Ok(status) => LocalChainStatus::ReachableLegacy {
response: Box::new(status),
},
Err(err) => {
warn!(
"{}: failed to retrieve [legacy] local chain status: {err}",
self.api_client.current_url()
self.api_client.api_url()
);
LocalChainStatus::Unreachable
}
@@ -158,14 +158,14 @@ impl ClientUnderTest {
// check if it supports the current query
if self.supports_signing_status_query() {
return match self.api_client.get_signer_status().await {
return match self.api_client.nym_api.get_signer_status().await {
Ok(response) => SigningStatus::Reachable {
response: Box::new(response),
},
Err(err) => {
warn!(
"{}: failed to retrieve signer chain status: {err}",
self.api_client.current_url()
self.api_client.api_url()
);
SigningStatus::Unreachable
}
@@ -173,14 +173,14 @@ impl ClientUnderTest {
}
// fallback to the legacy query
match self.api_client.get_signer_information().await {
match self.api_client.nym_api.get_signer_information().await {
Ok(status) => SigningStatus::ReachableLegacy {
response: Box::new(status),
},
Err(err) => {
warn!(
"{}: failed to retrieve [legacy] signer chain status: {err}",
self.api_client.current_url()
self.api_client.api_url()
);
// NOTE: this might equally mean the signing is disabled
SigningStatus::Unreachable
@@ -201,13 +201,7 @@ pub(crate) async fn check_client(
return SignerStatus::ProvidedInvalidDetails.with_details(dealer_information, dkg_epoch);
};
let mut client = match ClientUnderTest::new(&parsed_information.announce_address) {
Ok(client) => client,
Err(err) => {
error!("failed to create client instance: {err}");
return SignerStatus::Unreachable.with_details(dealer_information, dkg_epoch);
}
};
let mut client = ClientUnderTest::new(&parsed_information.announce_address);
// 8. check basic connection status - can you retrieve build information?
if !client.try_retrieve_build_information().await {
-7
View File
@@ -1,7 +1,6 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use nym_http_api_client::HttpClientError;
use nym_validator_client::nyxd::error::NyxdError;
use thiserror::Error;
@@ -12,12 +11,6 @@ pub enum SignerCheckError {
#[error("failed to query the DKG contract: {source}")]
DKGContractQueryFailure { source: NyxdError },
#[error("failed to build client: {source}")]
HttpClient {
#[from]
source: HttpClientError,
},
}
impl SignerCheckError {
+4 -37
View File
@@ -15,9 +15,6 @@ use nym_validator_client::ecash::models::EcashSignerStatusResponse;
use nym_validator_client::models::{
ChainBlocksStatusResponse, ChainStatusResponse, SignerInformationResponse,
};
use nym_validator_client::nyxd::contract_traits::dkg_query_client::{
ContractVKShare, DealerDetails, Epoch,
};
mod client_check;
pub mod error;
@@ -51,22 +48,7 @@ pub async fn check_signers(
check_signers_with_client(&client).await
}
pub struct DkgDetails {
pub dkg_epoch: Epoch,
pub threshold: Option<u64>,
pub network_dealers: Vec<DealerDetails>,
pub submitted_shared: HashMap<u64, ContractVKShare>,
}
pub async fn check_signers_with_client<C>(client: &C) -> Result<SignersTestResult, SignerCheckError>
where
C: DkgQueryClient + Sync,
{
let dkg_details = dkg_details_with_client(client).await?;
check_known_dealers(dkg_details).await
}
pub async fn dkg_details_with_client<C>(client: &C) -> Result<DkgDetails, SignerCheckError>
where
C: DkgQueryClient + Sync,
{
@@ -97,31 +79,16 @@ where
.map(|share| (share.node_index, share))
.collect();
Ok(DkgDetails {
dkg_epoch,
threshold,
network_dealers: dealers,
submitted_shared: shares,
})
}
pub async fn check_known_dealers(
dkg_details: DkgDetails,
) -> Result<SignersTestResult, SignerCheckError> {
// 6. for each dealer attempt to perform the checks
let results = dkg_details
.network_dealers
let results = dealers
.into_iter()
.map(|d| {
let share = dkg_details.submitted_shared.get(&d.assigned_index);
check_client(d, dkg_details.dkg_epoch.epoch_id, share)
let share = shares.get(&d.assigned_index);
check_client(d, dkg_epoch.epoch_id, share)
})
.collect::<FuturesUnordered<_>>()
.collect::<Vec<_>>()
.await;
Ok(SignersTestResult {
threshold: dkg_details.threshold,
results,
})
Ok(SignersTestResult { threshold, results })
}
+1 -1
View File
@@ -89,7 +89,7 @@ mod tests {
.unwrap();
let blind_sig = issue(
keypair.secret_key(),
sig_req.ecash_pub_key,
sig_req.ecash_pub_key.clone(),
&sig_req.withdrawal_request,
expiration_date.ecash_unix_timestamp(),
issuance.ticketbook_type().encode(),
-2
View File
@@ -7,7 +7,6 @@ homepage.workspace = true
documentation.workspace = true
edition.workspace = true
license.workspace = true
rust-version.workspace = true
[dependencies]
sqlx = { workspace = true, features = [
@@ -28,7 +27,6 @@ nym-statistics-common = { path = "../statistics" }
[build-dependencies]
anyhow = { workspace = true }
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
sqlx = { workspace = true, features = [
"runtime-tokio-rustls",
+4 -13
View File
@@ -1,29 +1,22 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use anyhow::Context;
use sqlx::{Connection, SqliteConnection};
use std::env;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let out_dir = env::var("OUT_DIR")?;
async fn main() {
let out_dir = env::var("OUT_DIR").unwrap();
let database_path = format!("{out_dir}/gateway-stats-example.sqlite");
// remove the db file if it already existed from previous build
// in case it was from a different branch
if std::fs::exists(&database_path)? {
std::fs::remove_file(&database_path)?;
}
let mut conn = SqliteConnection::connect(&format!("sqlite://{database_path}?mode=rwc"))
.await
.context("Failed to create SQLx database connection")?;
.expect("Failed to create SQLx database connection");
sqlx::migrate!("./migrations")
.run(&mut conn)
.await
.context("Failed to perform SQLx migrations")?;
.expect("Failed to perform SQLx migrations");
#[cfg(target_family = "unix")]
println!("cargo:rustc-env=DATABASE_URL=sqlite://{}", &database_path);
@@ -32,6 +25,4 @@ async fn main() -> anyhow::Result<()> {
// for some strange reason we need to add a leading `/` to the windows path even though it's
// not a valid windows path... but hey, it works...
println!("cargo:rustc-env=DATABASE_URL=sqlite:///{}", &database_path);
Ok(())
}
-2
View File
@@ -7,7 +7,6 @@ homepage.workspace = true
documentation.workspace = true
edition.workspace = true
license.workspace = true
rust-version.workspace = true
[dependencies]
async-trait = { workspace = true }
@@ -32,7 +31,6 @@ nym-gateway-requests = { path = "../gateway-requests" }
nym-sphinx = { path = "../nymsphinx" }
[build-dependencies]
anyhow = { workspace = true }
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
sqlx = { workspace = true, features = [
"runtime-tokio-rustls",
+4 -13
View File
@@ -1,29 +1,22 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use anyhow::Context;
use sqlx::{Connection, SqliteConnection};
use std::env;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let out_dir = env::var("OUT_DIR")?;
async fn main() {
let out_dir = env::var("OUT_DIR").unwrap();
let database_path = format!("{out_dir}/gateway-example.sqlite");
// remove the db file if it already existed from previous build
// in case it was from a different branch
if std::fs::exists(&database_path)? {
std::fs::remove_file(&database_path)?;
}
let mut conn = SqliteConnection::connect(&format!("sqlite://{database_path}?mode=rwc"))
.await
.context("Failed to create SQLx database connection")?;
.expect("Failed to create SQLx database connection");
sqlx::migrate!("./migrations")
.run(&mut conn)
.await
.context("Failed to perform SQLx migrations")?;
.expect("Failed to perform SQLx migrations");
#[cfg(target_family = "unix")]
println!("cargo:rustc-env=DATABASE_URL=sqlite://{}", &database_path);
@@ -32,6 +25,4 @@ async fn main() -> anyhow::Result<()> {
// for some strange reason we need to add a leading `/` to the windows path even though it's
// not a valid windows path... but hey, it works...
println!("cargo:rustc-env=DATABASE_URL=sqlite:///{}", &database_path);
Ok(())
}
-2
View File
@@ -13,7 +13,6 @@ license.workspace = true
[features]
default=["tunneling"]
tunneling=[]
network-defaults = ["dep:nym-network-defaults"]
[dependencies]
async-trait = { workspace = true }
@@ -35,7 +34,6 @@ mime = { workspace = true }
nym-http-api-common = { path = "../http-api-common", default-features = false }
nym-bin-common = { path = "../bin-common" }
nym-network-defaults = { path = "../network-defaults", optional = true }
[target."cfg(not(target_arch = \"wasm32\"))".dependencies]
hickory-resolver = { workspace = true, features = ["https-ring", "tls-ring", "webpki-roots"] }
-4
View File
@@ -54,14 +54,10 @@ impl Front {
#[derive(Debug, Default, PartialEq, Clone)]
#[cfg(feature = "tunneling")]
/// Policy for when to use domain fronting for HTTP requests.
pub enum FrontPolicy {
/// Always use domain fronting for all requests.
Always,
/// Only use domain fronting when retrying failed requests.
OnRetry,
#[default]
/// Never use domain fronting.
Off,
}
+8 -105
View File
@@ -136,7 +136,6 @@
//! ```
#![warn(missing_docs)]
pub use reqwest::ClientBuilder as ReqwestClientBuilder;
pub use reqwest::StatusCode;
use crate::path::RequestPath;
@@ -162,8 +161,6 @@ use std::sync::Arc;
#[cfg(feature = "tunneling")]
mod fronted;
#[cfg(feature = "tunneling")]
pub use fronted::FrontPolicy;
mod url;
pub use url::{IntoUrl, Url};
mod user_agent;
@@ -194,15 +191,6 @@ pub type Params<'a, K, V> = &'a [(K, V)];
/// Empty collection of HTTP Request Parameters.
pub const NO_PARAMS: Params<'_, &'_ str, &'_ str> = &[];
/// Serialization format for API requests and responses
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SerializationFormat {
/// Use JSON serialization (default, always works)
Json,
/// Use bincode serialization (must be explicitly opted into)
Bincode,
}
/// The Errors that may occur when creating or using an HTTP client.
#[derive(Debug, Error)]
#[allow(missing_docs)]
@@ -382,7 +370,6 @@ pub struct ClientBuilder {
front: Option<fronted::Front>,
retry_limit: usize,
serialization: SerializationFormat,
}
impl ClientBuilder {
@@ -408,50 +395,6 @@ impl ClientBuilder {
}
}
/// Create a client builder from network details with sensible defaults
#[cfg(feature = "network-defaults")]
pub fn from_network(
network: &nym_network_defaults::NymNetworkDetails,
) -> Result<Self, HttpClientError> {
let urls = network
.nym_api_urls
.as_ref()
.ok_or_else(|| {
HttpClientError::GenericRequestFailure(
"No API URLs configured in network details".to_string(),
)
})?
.iter()
.map(|api_url| {
// Convert ApiUrl to our Url type with fronting support
let mut url = Url::parse(&api_url.url)?;
// Add fronting domains if available
#[cfg(feature = "tunneling")]
if let Some(ref front_hosts) = api_url.front_hosts {
let fronts: Vec<String> = front_hosts
.iter()
.map(|host| format!("https://{}", host))
.collect();
url = Url::new(api_url.url.clone(), Some(fronts))
.map_err(|e| HttpClientError::GenericRequestFailure(e.to_string()))?;
}
Ok(url)
})
.collect::<Result<Vec<_>, HttpClientError>>()?;
let mut builder = Self::new_with_urls(urls);
// Enable domain fronting by default (on retry)
#[cfg(feature = "tunneling")]
{
builder = builder.with_fronting(FrontPolicy::OnRetry);
}
Ok(builder)
}
/// Constructs a new http `ClientBuilder` from a valid url.
pub fn new_with_urls(urls: Vec<Url>) -> Self {
let urls = Self::check_urls(urls);
@@ -485,7 +428,6 @@ impl ClientBuilder {
front: None,
retry_limit: 0,
serialization: SerializationFormat::Json,
}
}
@@ -558,17 +500,6 @@ impl ClientBuilder {
self
}
/// Set the serialization format for API requests and responses
pub fn with_serialization(mut self, format: SerializationFormat) -> Self {
self.serialization = format;
self
}
/// Configure the client to use bincode serialization
pub fn with_bincode(self) -> Self {
self.with_serialization(SerializationFormat::Bincode)
}
/// Returns a Client that uses this ClientBuilder configuration.
pub fn build<E>(self) -> Result<Client, HttpClientError<E>>
where
@@ -610,7 +541,6 @@ impl ClientBuilder {
#[cfg(target_arch = "wasm32")]
request_timeout: self.timeout.unwrap_or(DEFAULT_TIMEOUT),
retry_limit: self.retry_limit,
serialization: self.serialization,
};
Ok(client)
@@ -631,7 +561,6 @@ pub struct Client {
request_timeout: Duration,
retry_limit: usize,
serialization: SerializationFormat,
}
impl Client {
@@ -689,7 +618,6 @@ impl Client {
#[cfg(target_arch = "wasm32")]
request_timeout: self.request_timeout,
serialization: self.serialization,
}
}
@@ -765,37 +693,26 @@ impl Client {
/// this method. For example, if the client is configured to rotate hosts after each error, this
/// method should be called after the host has been updated -- i.e. as part of the subsequent
/// send.
fn apply_hosts_to_req(&self, r: &mut reqwest::Request) -> (&str, Option<&str>) {
fn apply_hosts_to_req(&self, r: &mut reqwest::Request) {
let url = self.current_url();
r.url_mut().set_host(url.host_str()).unwrap();
#[cfg(feature = "tunneling")]
if let Some(ref front) = self.front {
if front.is_enabled() {
let front_host = url.front_str().unwrap_or("");
let actual_host = url.host_str().unwrap_or("");
tracing::debug!(
"Domain fronting enabled: routing via CDN {} to actual host {}",
front_host,
actual_host
);
// this should never fail as we are transplanting the host from one url to another
r.url_mut().set_host(Some(front_host)).unwrap();
r.url_mut().set_host(url.front_str()).unwrap();
let actual_host_header: HeaderValue =
actual_host.parse().unwrap_or(HeaderValue::from_static(""));
let actual_host: HeaderValue = url
.host_str()
.unwrap_or("")
.parse()
.unwrap_or(HeaderValue::from_static(""));
// If the map did have this key present, the new value is associated with the key
// and all previous values are removed. (reqwest HeaderMap docs)
_ = r
.headers_mut()
.insert(reqwest::header::HOST, actual_host_header);
return (url.as_str(), url.front_str());
_ = r.headers_mut().insert(reqwest::header::HOST, actual_host);
}
}
(url.as_str(), None)
}
}
@@ -825,13 +742,6 @@ impl ApiClientCore for Client {
let mut rb = RequestBuilder::from_parts(self.reqwest_client.clone(), req);
// Set Accept header based on serialization preference
let accept_header = match self.serialization {
SerializationFormat::Json => "application/json",
SerializationFormat::Bincode => "application/bincode",
};
rb = rb.header(reqwest::header::ACCEPT, accept_header);
if let Some(body) = json_body {
rb = rb.json(body);
}
@@ -880,14 +790,7 @@ impl ApiClientCore for Client {
if let Some(ref front) = self.front {
// If fronting is set to be enabled on error, enable domain fronting as we
// have encountered an error.
let was_enabled = front.is_enabled();
front.retry_enable();
if !was_enabled && front.is_enabled() {
tracing::info!(
"Domain fronting activated after connection failure: {}",
e
);
}
}
if attempts < self.retry_limit {
+1 -2
View File
@@ -47,8 +47,7 @@ pub mod nyx {
pub mod wireguard {
use std::net::{Ipv4Addr, Ipv6Addr};
pub const WG_TUNNEL_PORT: u16 = 51822;
pub const WG_METADATA_PORT: u16 = 51830;
pub const WG_PORT: u16 = 51822;
// The interface used to route traffic
pub const WG_TUN_BASE_NAME: &str = "nymwg";
+2 -9
View File
@@ -55,7 +55,6 @@ pub struct ApiUrl {
pub front_hosts: Option<Vec<String>>,
}
#[derive(Copy, Clone)]
pub struct ApiUrlConst<'a> {
pub url: &'a str,
pub front_hosts: Option<&'a [&'a str]>,
@@ -189,14 +188,8 @@ impl NymNetworkDetails {
),
},
nym_vpn_api_url: parse_optional_str(mainnet::NYM_VPN_API),
nym_api_urls: Some(mainnet::NYM_APIS.iter().copied().map(Into::into).collect()),
nym_vpn_api_urls: Some(
mainnet::NYM_VPN_APIS
.iter()
.copied()
.map(Into::into)
.collect(),
),
nym_api_urls: None,
nym_vpn_api_urls: None,
}
}
@@ -319,9 +319,9 @@ mod tests {
let sk = grp.random_scalar();
let sk_user = SecretKeyUser { sk };
let pk_user = sk_user.public_key();
public_keys.push(pk_user);
public_keys.push(pk_user.clone());
}
public_keys.push(user_keypair.public_key());
public_keys.push(user_keypair.public_key().clone());
let (req, req_info) =
withdrawal_request(user_keypair.secret_key(), expiration_date, t_type).unwrap();
@@ -462,9 +462,9 @@ mod tests {
let sk = grp.random_scalar();
let sk_user = SecretKeyUser { sk };
let pk_user = sk_user.public_key();
public_keys.push(pk_user);
public_keys.push(pk_user.clone());
}
public_keys.push(user_keypair.public_key());
public_keys.push(user_keypair.public_key().clone());
let (req, req_info) =
withdrawal_request(user_keypair.secret_key(), expiration_date, t_type).unwrap();
@@ -401,7 +401,7 @@ impl Bytable for SecretKeyUser {
impl Base58 for SecretKeyUser {}
#[derive(Debug, Eq, PartialEq, Clone, Copy, Serialize, Deserialize)]
#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
pub struct PublicKeyUser {
pub(crate) pk: G1Projective,
}
@@ -554,7 +554,7 @@ impl KeyPairUser {
}
pub fn public_key(&self) -> PublicKeyUser {
self.public_key
self.public_key.clone()
}
pub fn to_bytes(&self) -> Vec<u8> {
-2
View File
@@ -7,7 +7,6 @@ homepage.workspace = true
documentation.workspace = true
edition.workspace = true
license.workspace = true
rust-version.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -37,6 +36,5 @@ url.workspace = true
[build-dependencies]
anyhow = { workspace = true }
sqlx = { workspace = true, features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"] }
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
+4 -14
View File
@@ -1,30 +1,22 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use anyhow::Context;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
async fn main() {
use sqlx::{Connection, SqliteConnection};
use std::env;
let out_dir = env::var("OUT_DIR")?;
let out_dir = env::var("OUT_DIR").unwrap();
let database_path = format!("{out_dir}/scraper-example.sqlite");
// remove the db file if it already existed from previous build
// in case it was from a different branch
if std::fs::exists(&database_path)? {
std::fs::remove_file(&database_path)?;
}
let mut conn = SqliteConnection::connect(&format!("sqlite://{database_path}?mode=rwc"))
.await
.context("Failed to create SQLx database connection")?;
.expect("Failed to create SQLx database connection");
sqlx::migrate!("./sql_migrations")
.run(&mut conn)
.await
.context("Failed to perform SQLx migrations")?;
.expect("Failed to perform SQLx migrations");
#[cfg(target_family = "unix")]
println!("cargo:rustc-env=DATABASE_URL=sqlite://{}", &database_path);
@@ -33,6 +25,4 @@ async fn main() -> anyhow::Result<()> {
// for some strange reason we need to add a leading `/` to the windows path even though it's
// not a valid windows path... but hey, it works...
println!("cargo:rustc-env=DATABASE_URL=sqlite:///{}", &database_path);
Ok(())
}
-2
View File
@@ -25,5 +25,3 @@ url = { workspace = true }
nym-crypto = { path = "../crypto", features = ["asymmetric"] }
nym-task = { path = "../task" }
nym-validator-client = { path = "../client-libs/validator-client" }
nym-http-api-client = { path = "../http-api-client" }
nym-api-requests = { path = "../../nym-api/nym-api-requests" }
+5 -12
View File
@@ -10,7 +10,7 @@ use futures::StreamExt;
use nym_crypto::asymmetric::ed25519;
use nym_task::ShutdownToken;
use nym_validator_client::models::NymNodeDescription;
use nym_validator_client::nym_api::NymApiClientExt;
use nym_validator_client::NymApiClient;
use rand::prelude::SliceRandom;
use rand::thread_rng;
use std::net::SocketAddr;
@@ -135,17 +135,10 @@ impl VerlocMeasurer {
let mut api_endpoints = self.config.nym_api_urls.clone();
api_endpoints.shuffle(&mut thread_rng());
for api_endpoint in api_endpoints {
let client =
match nym_http_api_client::Client::builder(api_endpoint.clone()).and_then(|b| {
b.with_user_agent(self.config.user_agent.clone())
.build::<nym_api_requests::models::RequestError>()
}) {
Ok(c) => c,
Err(err) => {
warn!("failed to create client for {api_endpoint}: {err}");
continue;
}
};
let client = NymApiClient::new_with_user_agent(
api_endpoint.clone(),
self.config.user_agent.clone(),
);
match client.get_all_described_nodes().await {
Ok(res) => return Some(res),
Err(err) => {
+6 -1
View File
@@ -130,7 +130,12 @@ impl VerlocMeasurement {
let variance_micros = data
.iter()
.map(|&value| {
let diff = mean.abs_diff(value);
// make sure we don't underflow
let diff = if mean > value {
mean - value
} else {
value - mean
};
// we don't need nanos precision
let diff_micros = diff.as_micros();
diff_micros * diff_micros
-1
View File
@@ -34,7 +34,6 @@ nym-statistics-common = { path = "../../statistics" }
nym-task = { path = "../../task" }
nym-topology = { path = "../../topology", features = ["wasm-serde-types"] }
nym-validator-client = { path = "../../client-libs/validator-client", default-features = false }
nym-http-api-client = { path = "../../http-api-client" }
wasm-utils = { path = "../utils" }
wasm-storage = { path = "../storage" }
-6
View File
@@ -37,12 +37,6 @@ pub enum WasmCoreError {
source: ValidatorClientError,
},
#[error("failed to query nym api: {source}")]
NymApiQueryError {
#[from]
source: nym_validator_client::nym_api::error::NymAPIError,
},
#[error("The provided wasm topology was invalid: {source}")]
WasmTopologyError {
#[from]
+6 -23
View File
@@ -19,9 +19,9 @@ use nym_client_core::init::{
use nym_sphinx::addressing::clients::Recipient;
use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag;
use nym_topology::wasm_helpers::WasmFriendlyNymTopology;
use nym_topology::{EpochRewardedSet, NymTopology, RoutingNode};
use nym_topology::{NymTopology, RoutingNode};
use nym_validator_client::client::IdentityKey;
use nym_validator_client::{nym_api::NymApiClientExt, UserAgent};
use nym_validator_client::{NymApiClient, UserAgent};
use rand::thread_rng;
use url::Url;
use wasm_bindgen::prelude::wasm_bindgen;
@@ -72,19 +72,7 @@ pub async fn current_network_topology_async(
}
};
let api_client = nym_http_api_client::Client::builder::<
_,
nym_validator_client::models::RequestError,
>(url.clone())
.map_err(|_err| WasmCoreError::MalformedUrl {
raw: nym_api_url.to_string(),
source: url::ParseError::EmptyHost,
})?
.build::<nym_validator_client::models::RequestError>()
.map_err(|_err| WasmCoreError::MalformedUrl {
raw: nym_api_url.to_string(),
source: url::ParseError::EmptyHost,
})?;
let api_client = NymApiClient::new(url);
let rewarded_set = api_client.get_current_rewarded_set().await?;
let mixnodes_res = api_client
.get_all_basic_active_mixing_assigned_nodes_with_metadata()
@@ -102,14 +90,9 @@ pub async fn current_network_topology_async(
let gateways = gateways_res.nodes;
let epoch_rewarded_set: EpochRewardedSet = rewarded_set.into();
let topology = NymTopology::new(
metadata.to_topology_metadata(),
epoch_rewarded_set,
Vec::new(),
)
.with_skimmed_nodes(&mixnodes)
.with_skimmed_nodes(&gateways);
let topology = NymTopology::new(metadata.to_topology_metadata(), rewarded_set, Vec::new())
.with_skimmed_nodes(&mixnodes)
.with_skimmed_nodes(&gateways);
Ok(topology.into())
}
+1 -1
View File
@@ -21,7 +21,6 @@ pub use nym_client_core::{
pub use nym_gateway_client::{
error::GatewayClientError, GatewayClient, GatewayClientConfig, GatewayConfig,
};
pub use nym_http_api_client::Client as ApiClient;
pub use nym_sphinx::{
addressing::{clients::Recipient, nodes::NodeIdentity},
params::PacketType,
@@ -30,6 +29,7 @@ pub use nym_sphinx::{
pub use nym_statistics_common::clients::ClientStatsSender;
pub use nym_task;
pub use nym_topology::{HardcodedTopologyProvider, MixLayer, NymTopology, TopologyProvider};
pub use nym_validator_client::nym_api::Client as ApiClient;
pub use nym_validator_client::{DirectSigningReqwestRpcNyxdClient, QueryReqwestRpcNyxdClient};
// TODO: that's a very nasty import path. it should come from contracts instead!
pub use nym_validator_client::client::IdentityKey;
@@ -1,18 +0,0 @@
[package]
name = "nym-wireguard-private-metadata-client"
version = "1.0.0"
authors.workspace = true
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
edition.workspace = true
license.workspace = true
[dependencies]
async-trait = { workspace = true }
tracing = { workspace = true }
nym-http-api-client = { path = "../../http-api-client" }
nym-wireguard-private-metadata-shared = { path = "../shared" }
[lints]
workspace = true
@@ -1,58 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use async_trait::async_trait;
use tracing::instrument;
use nym_http_api_client::{ApiClient, Client, HttpClientError, NO_PARAMS};
use nym_wireguard_private_metadata_shared::{
routes, Version, {ErrorResponse, Request, Response},
};
pub type WireguardMetadataApiClientError = HttpClientError<ErrorResponse>;
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
pub trait WireguardMetadataApiClient: ApiClient {
#[instrument(level = "debug", skip(self))]
async fn version(&self) -> Result<Version, WireguardMetadataApiClientError> {
let version: u64 = self
.get_json(
&[routes::V1_API_VERSION, routes::BANDWIDTH, routes::VERSION],
NO_PARAMS,
)
.await?;
Ok(version.into())
}
#[instrument(level = "debug", skip(self))]
async fn available_bandwidth(
&self,
request_body: &Request,
) -> Result<Response, WireguardMetadataApiClientError> {
self.post_json(
&[routes::V1_API_VERSION, routes::BANDWIDTH, routes::AVAILABLE],
NO_PARAMS,
request_body,
)
.await
}
#[instrument(level = "debug", skip(self, request_body))]
async fn topup_bandwidth(
&self,
request_body: &Request,
) -> Result<Response, WireguardMetadataApiClientError> {
self.post_json(
&[routes::V1_API_VERSION, routes::BANDWIDTH, routes::TOPUP],
NO_PARAMS,
request_body,
)
.await
}
}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl WireguardMetadataApiClient for Client {}
@@ -1,43 +0,0 @@
[package]
name = "nym-wireguard-private-metadata-server"
version = "1.0.0"
authors.workspace = true
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
edition.workspace = true
license.workspace = true
[dependencies]
anyhow = { workspace = true }
axum = { workspace = true, features = ["tokio", "macros"] }
futures = { workspace = true }
tokio = { workspace = true, features = ["rt-multi-thread", "net", "io-util"] }
tokio-util = { workspace = true }
tower-http = { workspace = true, features = [
"cors",
"trace",
"compression-br",
"compression-deflate",
"compression-gzip",
"compression-zstd",
] }
utoipa = { workspace = true, features = ["axum_extras", "time"] }
utoipa-swagger-ui = { workspace = true, features = ["axum"] }
nym-credentials-interface = { path = "../../credentials-interface" }
nym-credential-verification = { path = "../../credential-verification" }
nym-http-api-common = { path = "../../http-api-common", features = [
"middleware",
"utoipa",
"output",
] }
nym-wireguard = { path = "../../wireguard" }
nym-wireguard-private-metadata-shared = { path = "../shared" }
[dev-dependencies]
async-trait = { workspace = true }
[lints]
workspace = true
@@ -1,46 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::sync::Arc;
use tokio::task::JoinHandle;
use tokio_util::sync::CancellationToken;
use nym_wireguard::WgApiWrapper;
pub(crate) mod openapi;
pub(crate) mod router;
pub(crate) mod state;
/// Shutdown goes 2 directions:
/// 1. signal background tasks to gracefully finish
/// 2. signal server itself
///
/// These are done through separate shutdown handles. Of course, shut down server
/// AFTER you have shut down BG tasks (or past their grace period).
#[allow(unused)]
pub struct ShutdownHandles {
axum_shutdown_button: CancellationToken,
/// Tokio JoinHandle for axum server's task
axum_join_handle: AxumJoinHandle,
/// Wireguard API for kernel interactions
wg_api: Arc<WgApiWrapper>,
}
impl ShutdownHandles {
/// Cancellation token is given to Axum server constructor. When the token
/// receives a shutdown signal, Axum server will shut down gracefully.
pub fn new(
axum_join_handle: AxumJoinHandle,
wg_api: Arc<WgApiWrapper>,
axum_shutdown_button: CancellationToken,
) -> Self {
Self {
axum_shutdown_button,
axum_join_handle,
wg_api,
}
}
}
type AxumJoinHandle = JoinHandle<std::io::Result<()>>;
@@ -1,14 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use utoipa::OpenApi;
use nym_wireguard_private_metadata_shared::{Request, Response};
#[derive(OpenApi)]
#[openapi(
info(title = "Nym Wireguard Private Metadata"),
tags(),
components(schemas(Request, Response))
)]
pub(crate) struct ApiDoc;
@@ -1,101 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use anyhow::anyhow;
use axum::response::Redirect;
use axum::routing::get;
use axum::Router;
use core::net::SocketAddr;
use nym_http_api_common::middleware::logging::log_request_info;
use tokio::net::TcpListener;
use tokio_util::sync::WaitForCancellationFutureOwned;
use tower_http::cors::CorsLayer;
use utoipa::OpenApi;
use utoipa_swagger_ui::SwaggerUi;
use crate::http::openapi::ApiDoc;
use crate::http::state::AppState;
use crate::network::bandwidth_routes;
/// Wrapper around `axum::Router` which ensures correct [order of layers][order].
/// Add new routes as if you were working directly with `axum`.
///
/// Why? Middleware like logger, CORS, TLS which need to handle request before other
/// layers should be added last. Using this builder pattern ensures that.
///
/// [order]: https://docs.rs/axum/latest/axum/middleware/index.html#ordering
pub struct RouterBuilder {
unfinished_router: Router<AppState>,
}
impl RouterBuilder {
/// All routes should be, if possible, added here. Exceptions are e.g.
/// routes which are added conditionally in other places based on some `if`.
pub fn with_default_routes() -> Self {
let default_routes = Router::new()
.merge(SwaggerUi::new("/swagger").url("/api-docs/openapi.json", ApiDoc::openapi()))
.route("/", get(|| async { Redirect::to("/swagger") }))
.nest("/v1", Router::new().nest("/bandwidth", bandwidth_routes()));
Self {
unfinished_router: default_routes,
}
}
/// Invoke this as late as possible before constructing HTTP server
/// (after all routes were added).
pub fn with_state(self, state: AppState) -> RouterWithState {
RouterWithState {
router: self.finalize_routes().with_state(state),
}
}
/// Middleware added here intercepts the request before it gets to other routes.
fn finalize_routes(self) -> Router<AppState> {
self.unfinished_router
.layer(setup_cors())
.layer(axum::middleware::from_fn(log_request_info))
}
}
fn setup_cors() -> CorsLayer {
CorsLayer::new()
.allow_origin(tower_http::cors::Any)
.allow_methods([axum::http::Method::GET, axum::http::Method::POST])
.allow_headers(tower_http::cors::Any)
.allow_credentials(false)
}
pub struct RouterWithState {
pub router: Router,
}
impl RouterWithState {
pub async fn build_server(self, bind_address: &SocketAddr) -> anyhow::Result<ApiHttpServer> {
let listener = tokio::net::TcpListener::bind(bind_address)
.await
.map_err(|err| anyhow!("Couldn't bind to address {} due to {}", bind_address, err))?;
Ok(ApiHttpServer {
router: self.router,
listener,
})
}
}
pub struct ApiHttpServer {
router: Router,
listener: TcpListener,
}
impl ApiHttpServer {
pub async fn run(self, receiver: WaitForCancellationFutureOwned) -> Result<(), std::io::Error> {
// into_make_service_with_connect_info allows us to see client ip address
axum::serve(
self.listener,
self.router
.into_make_service_with_connect_info::<SocketAddr>(),
)
.with_graceful_shutdown(receiver)
.await
}
}
@@ -1,35 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::net::IpAddr;
use nym_credentials_interface::CredentialSpendingData;
use crate::transceiver::PeerControllerTransceiver;
use nym_wireguard_private_metadata_shared::error::MetadataError;
#[derive(Clone, axum::extract::FromRef)]
pub struct AppState {
transceiver: PeerControllerTransceiver,
}
impl AppState {
pub fn new(transceiver: PeerControllerTransceiver) -> Self {
Self { transceiver }
}
pub async fn available_bandwidth(&self, ip: IpAddr) -> Result<i64, MetadataError> {
self.transceiver.query_bandwidth(ip).await
}
// Top up with a credential and return the afterwards available bandwidth
pub async fn topup_bandwidth(
&self,
ip: IpAddr,
credential: CredentialSpendingData,
) -> Result<i64, MetadataError> {
self.transceiver
.topup_bandwidth(ip, Box::new(credential))
.await
}
}
@@ -1,13 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
mod http;
mod network;
mod transceiver;
pub use http::{
router::{ApiHttpServer, RouterBuilder, RouterWithState},
state::AppState,
ShutdownHandles,
};
pub use transceiver::PeerControllerTransceiver;
@@ -1,111 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::net::SocketAddr;
use axum::{
extract::{ConnectInfo, Query, State},
Json, Router,
};
use nym_http_api_common::{FormattedResponse, OutputParams};
use nym_wireguard_private_metadata_shared::{
interface::{RequestData, ResponseData},
latest, AxumErrorResponse, AxumResult, Construct, Extract, Request, Response,
};
use tower_http::compression::CompressionLayer;
use crate::http::state::AppState;
pub(crate) fn bandwidth_routes() -> Router<AppState> {
Router::new()
.route("/version", axum::routing::get(version))
.route("/available", axum::routing::post(available_bandwidth))
.route("/topup", axum::routing::post(topup_bandwidth))
.layer(CompressionLayer::new())
}
#[utoipa::path(
tag = "bandwidth",
get,
path = "/v1/bandwidth/version",
responses(
(status = 200, content(
(Response = "application/bincode")
))
),
)]
async fn version(Query(output): Query<OutputParams>) -> AxumResult<FormattedResponse<u64>> {
let output = output.output.unwrap_or_default();
Ok(output.to_response(latest::VERSION.into()))
}
#[utoipa::path(
tag = "bandwidth",
post,
request_body = Request,
path = "/v1/bandwidth/available",
responses(
(status = 200, content(
(Response = "application/bincode")
))
),
)]
async fn available_bandwidth(
ConnectInfo(addr): ConnectInfo<SocketAddr>,
Query(output): Query<OutputParams>,
State(state): State<AppState>,
Json(request): Json<Request>,
) -> AxumResult<FormattedResponse<Response>> {
let output = output.output.unwrap_or_default();
let (RequestData::AvailableBandwidth(_), version) =
request.extract().map_err(AxumErrorResponse::bad_request)?
else {
return Err(AxumErrorResponse::bad_request("incorrect request type"));
};
let available_bandwidth = state
.available_bandwidth(addr.ip())
.await
.map_err(AxumErrorResponse::bad_request)?;
let response = Response::construct(
ResponseData::AvailableBandwidth(available_bandwidth),
version,
)
.map_err(AxumErrorResponse::bad_request)?;
Ok(output.to_response(response))
}
#[utoipa::path(
tag = "bandwidth",
post,
request_body = Request,
path = "/v1/bandwidth/topup",
responses(
(status = 200, content(
(Response = "application/bincode")
))
),
)]
async fn topup_bandwidth(
ConnectInfo(addr): ConnectInfo<SocketAddr>,
Query(output): Query<OutputParams>,
State(state): State<AppState>,
Json(request): Json<Request>,
) -> AxumResult<FormattedResponse<Response>> {
let output = output.output.unwrap_or_default();
let (RequestData::TopUpBandwidth(credential), version) =
request.extract().map_err(AxumErrorResponse::bad_request)?
else {
return Err(AxumErrorResponse::bad_request("incorrect request type"));
};
let available_bandwidth = state
.topup_bandwidth(addr.ip(), *credential)
.await
.map_err(AxumErrorResponse::bad_request)?;
let response = Response::construct(ResponseData::TopUpBandwidth(available_bandwidth), version)
.map_err(AxumErrorResponse::bad_request)?;
Ok(output.to_response(response))
}
@@ -1,307 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::net::IpAddr;
use futures::channel::oneshot;
use tokio::sync::mpsc;
use nym_credential_verification::ClientBandwidth;
use nym_credentials_interface::CredentialSpendingData;
use nym_wireguard::peer_controller::PeerControlRequest;
use nym_wireguard_private_metadata_shared::error::MetadataError;
#[derive(Clone)]
pub struct PeerControllerTransceiver {
request_tx: mpsc::Sender<PeerControlRequest>,
}
impl PeerControllerTransceiver {
pub fn new(request_tx: mpsc::Sender<PeerControlRequest>) -> Self {
Self { request_tx }
}
async fn get_client_bandwidth(&self, ip: IpAddr) -> Result<ClientBandwidth, MetadataError> {
let (response_tx, response_rx) = oneshot::channel();
let msg = PeerControlRequest::GetClientBandwidthByIp { ip, response_tx };
self.request_tx
.send(msg)
.await
.map_err(|_| MetadataError::PeerInteractionStopped)?;
response_rx
.await
.map_err(|_| MetadataError::NoResponse)?
.map_err(|err| MetadataError::Unsuccessful {
reason: err.to_string(),
})
}
pub(crate) async fn query_bandwidth(&self, ip: IpAddr) -> Result<i64, MetadataError> {
Ok(self.get_client_bandwidth(ip).await?.available().await)
}
// Top up with a credential and return the afterwards available bandwidth
pub(crate) async fn topup_bandwidth(
&self,
ip: IpAddr,
credential: Box<CredentialSpendingData>,
) -> Result<i64, MetadataError> {
let (response_tx, response_rx) = oneshot::channel();
let msg = PeerControlRequest::GetVerifierByIp {
ip,
credential,
response_tx,
};
self.request_tx
.send(msg)
.await
.map_err(|_| MetadataError::PeerInteractionStopped)?;
let mut verifier = response_rx
.await
.map_err(|_| MetadataError::NoResponse)?
.map_err(|err| MetadataError::Unsuccessful {
reason: err.to_string(),
})?;
let available_bandwidth =
verifier
.verify()
.await
.map_err(|err| MetadataError::CredentialVerification {
message: err.to_string(),
})?;
Ok(available_bandwidth)
}
}
#[cfg(test)]
pub(crate) mod tests {
use nym_credential_verification::TicketVerifier;
use nym_wireguard::CONTROL_CHANNEL_SIZE;
use super::*;
pub(crate) const CREDENTIAL_BYTES: [u8; 1245] = [
0, 0, 4, 133, 96, 179, 223, 185, 136, 23, 213, 166, 59, 203, 66, 69, 209, 181, 227, 254,
16, 102, 98, 237, 59, 119, 170, 111, 31, 194, 51, 59, 120, 17, 115, 229, 79, 91, 11, 139,
154, 2, 212, 23, 68, 70, 167, 3, 240, 54, 224, 171, 221, 1, 69, 48, 60, 118, 119, 249, 123,
35, 172, 227, 131, 96, 232, 209, 187, 123, 4, 197, 102, 90, 96, 45, 125, 135, 140, 99, 1,
151, 17, 131, 143, 157, 97, 107, 139, 232, 212, 87, 14, 115, 253, 255, 166, 167, 186, 43,
90, 96, 173, 105, 120, 40, 10, 163, 250, 224, 214, 200, 178, 4, 160, 16, 130, 59, 76, 193,
39, 240, 3, 101, 141, 209, 183, 226, 186, 207, 56, 210, 187, 7, 164, 240, 164, 205, 37, 81,
184, 214, 193, 195, 90, 205, 238, 225, 195, 104, 12, 123, 203, 57, 233, 243, 215, 145, 195,
196, 57, 38, 125, 172, 18, 47, 63, 165, 110, 219, 180, 40, 58, 116, 92, 254, 160, 98, 48,
92, 254, 232, 107, 184, 80, 234, 60, 160, 235, 249, 76, 41, 38, 165, 28, 40, 136, 74, 48,
166, 50, 245, 23, 201, 140, 101, 79, 93, 235, 128, 186, 146, 126, 180, 134, 43, 13, 186,
19, 195, 48, 168, 201, 29, 216, 95, 176, 198, 132, 188, 64, 39, 212, 150, 32, 52, 53, 38,
228, 199, 122, 226, 217, 75, 40, 191, 151, 48, 164, 242, 177, 79, 14, 122, 105, 151, 85,
88, 199, 162, 17, 96, 103, 83, 178, 128, 9, 24, 30, 74, 108, 241, 85, 240, 166, 97, 241,
85, 199, 11, 198, 226, 234, 70, 107, 145, 28, 208, 114, 51, 12, 234, 108, 101, 202, 112,
48, 185, 22, 159, 67, 109, 49, 27, 149, 90, 109, 32, 226, 112, 7, 201, 208, 209, 104, 31,
97, 134, 204, 145, 27, 181, 206, 181, 106, 32, 110, 136, 115, 249, 201, 111, 5, 245, 203,
71, 121, 169, 126, 151, 178, 236, 59, 221, 195, 48, 135, 115, 6, 50, 227, 74, 97, 107, 107,
213, 90, 2, 203, 154, 138, 47, 128, 52, 134, 128, 224, 51, 65, 240, 90, 8, 55, 175, 180,
178, 204, 206, 168, 110, 51, 57, 189, 169, 48, 169, 136, 121, 99, 51, 170, 178, 214, 74, 1,
96, 151, 167, 25, 173, 180, 171, 155, 10, 55, 142, 234, 190, 113, 90, 79, 80, 244, 71, 166,
30, 235, 113, 150, 133, 1, 218, 17, 109, 111, 223, 24, 216, 177, 41, 2, 204, 65, 221, 212,
207, 236, 144, 6, 65, 224, 55, 42, 1, 1, 161, 134, 118, 127, 111, 220, 110, 127, 240, 71,
223, 129, 12, 93, 20, 220, 60, 56, 71, 146, 184, 95, 132, 69, 28, 56, 53, 192, 213, 22,
119, 230, 152, 225, 182, 188, 163, 219, 37, 175, 247, 73, 14, 247, 38, 72, 243, 1, 48, 131,
59, 8, 13, 96, 143, 185, 127, 241, 161, 217, 24, 149, 193, 40, 16, 30, 202, 151, 28, 119,
240, 153, 101, 156, 61, 193, 72, 245, 199, 181, 12, 231, 65, 166, 67, 142, 121, 207, 202,
58, 197, 113, 188, 248, 42, 124, 105, 48, 161, 241, 55, 209, 36, 194, 27, 63, 233, 144,
189, 85, 117, 234, 9, 139, 46, 31, 206, 114, 95, 131, 29, 240, 13, 81, 142, 140, 133, 33,
30, 41, 141, 37, 80, 217, 95, 221, 76, 115, 86, 201, 165, 51, 252, 9, 28, 209, 1, 48, 150,
74, 248, 212, 187, 222, 66, 210, 3, 200, 19, 217, 171, 184, 42, 148, 53, 150, 57, 50, 6,
227, 227, 62, 49, 42, 148, 148, 157, 82, 191, 58, 24, 34, 56, 98, 120, 89, 105, 176, 85,
15, 253, 241, 41, 153, 195, 136, 1, 48, 142, 126, 213, 101, 223, 79, 133, 230, 105, 38,
161, 149, 2, 21, 136, 150, 42, 72, 218, 85, 146, 63, 223, 58, 108, 186, 183, 248, 62, 20,
47, 34, 113, 160, 177, 204, 181, 16, 24, 212, 224, 35, 84, 51, 168, 56, 136, 11, 1, 48,
135, 242, 62, 149, 230, 178, 32, 224, 119, 26, 234, 163, 237, 224, 114, 95, 112, 140, 170,
150, 96, 125, 136, 221, 180, 78, 18, 11, 12, 184, 2, 198, 217, 119, 43, 69, 4, 172, 109,
55, 183, 40, 131, 172, 161, 88, 183, 101, 1, 48, 173, 216, 22, 73, 42, 255, 211, 93, 249,
87, 159, 115, 61, 91, 55, 130, 17, 216, 60, 34, 122, 55, 8, 244, 244, 153, 151, 57, 5, 144,
178, 55, 249, 64, 211, 168, 34, 148, 56, 89, 92, 203, 70, 124, 219, 152, 253, 165, 0, 32,
203, 116, 63, 7, 240, 222, 82, 86, 11, 149, 167, 72, 224, 55, 190, 66, 201, 65, 168, 184,
96, 47, 194, 241, 168, 124, 7, 74, 214, 250, 37, 76, 32, 218, 69, 122, 103, 215, 145, 169,
24, 212, 229, 168, 106, 10, 144, 31, 13, 25, 178, 242, 250, 106, 159, 40, 48, 163, 165, 61,
130, 57, 146, 4, 73, 32, 254, 233, 125, 135, 212, 29, 111, 4, 177, 114, 15, 210, 170, 82,
108, 110, 62, 166, 81, 209, 106, 176, 156, 14, 133, 242, 60, 127, 120, 242, 28, 97, 0, 1,
32, 103, 93, 109, 89, 240, 91, 1, 84, 150, 50, 206, 157, 203, 49, 220, 120, 234, 175, 234,
150, 126, 225, 94, 163, 164, 199, 138, 114, 62, 99, 106, 112, 1, 32, 171, 40, 220, 82, 241,
203, 76, 146, 111, 139, 182, 179, 237, 182, 115, 75, 128, 201, 107, 43, 214, 0, 135, 217,
160, 68, 150, 232, 144, 114, 237, 98, 32, 30, 134, 232, 59, 93, 163, 253, 244, 13, 202, 52,
147, 168, 83, 121, 123, 95, 21, 210, 209, 225, 223, 143, 49, 10, 205, 238, 1, 22, 83, 81,
70, 1, 32, 26, 76, 6, 234, 160, 50, 139, 102, 161, 232, 155, 106, 130, 171, 226, 210, 233,
178, 85, 247, 71, 123, 55, 53, 46, 67, 148, 137, 156, 207, 208, 107, 1, 32, 102, 31, 4, 98,
110, 156, 144, 61, 229, 140, 198, 84, 196, 238, 128, 35, 131, 182, 137, 125, 241, 95, 69,
131, 170, 27, 2, 144, 75, 72, 242, 102, 3, 32, 121, 80, 45, 173, 56, 65, 218, 27, 40, 251,
197, 32, 169, 104, 123, 110, 90, 78, 153, 166, 38, 9, 129, 228, 99, 8, 1, 116, 142, 233,
162, 69, 32, 216, 169, 159, 116, 95, 12, 63, 176, 195, 6, 183, 123, 135, 75, 61, 112, 106,
83, 235, 176, 41, 27, 248, 48, 71, 165, 170, 12, 92, 103, 103, 81, 32, 58, 74, 75, 145,
192, 94, 153, 69, 80, 128, 241, 3, 16, 117, 192, 86, 161, 103, 44, 174, 211, 196, 182, 124,
55, 11, 107, 142, 49, 88, 6, 41, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 0, 37, 139, 240, 0, 0,
0, 0, 0, 0, 0, 1,
];
pub(crate) struct MockVerifier {
ret: i64,
}
impl MockVerifier {
pub(crate) fn new(ret: i64) -> MockVerifier {
Self { ret }
}
}
#[async_trait::async_trait]
impl TicketVerifier for MockVerifier {
async fn verify(&mut self) -> nym_credential_verification::Result<i64> {
Ok(self.ret)
}
}
#[tokio::test]
async fn get_bandwidth() {
let (request_tx, mut request_rx) = mpsc::channel(CONTROL_CHANNEL_SIZE);
let transceiver = PeerControllerTransceiver::new(request_tx);
tokio::spawn(async move {
match request_rx.recv().await.unwrap() {
PeerControlRequest::GetClientBandwidthByIp { ip: _, response_tx } => {
response_tx
.send(Ok(ClientBandwidth::new(Default::default())))
.ok();
}
_ => panic!("Not expected"),
}
});
let bw = transceiver
.query_bandwidth("10.0.0.42".parse().unwrap())
.await
.unwrap();
assert_eq!(bw, 0);
}
#[tokio::test]
async fn stop_peer() {
let (request_tx, request_rx) = mpsc::channel(CONTROL_CHANNEL_SIZE);
let transceiver = PeerControllerTransceiver::new(request_tx);
drop(request_rx);
let err = transceiver
.query_bandwidth("10.0.0.42".parse().unwrap())
.await
.unwrap_err();
assert_eq!(err, MetadataError::PeerInteractionStopped);
}
#[tokio::test]
async fn unresponsive_peer() {
let (request_tx, mut request_rx) = mpsc::channel(CONTROL_CHANNEL_SIZE);
let transceiver = PeerControllerTransceiver::new(request_tx);
tokio::spawn(async move {
match request_rx.recv().await.unwrap() {
PeerControlRequest::GetClientBandwidthByIp {
ip: _,
response_tx: _,
} => {}
_ => panic!("Not expected"),
}
});
let err = transceiver
.query_bandwidth("10.0.0.42".parse().unwrap())
.await
.unwrap_err();
assert_eq!(err, MetadataError::NoResponse);
}
#[tokio::test]
async fn unsuccessful_query_bandwidth() {
let (request_tx, mut request_rx) = mpsc::channel(CONTROL_CHANNEL_SIZE);
let transceiver = PeerControllerTransceiver::new(request_tx);
tokio::spawn(async move {
match request_rx.recv().await.unwrap() {
PeerControlRequest::GetClientBandwidthByIp { ip: _, response_tx } => {
response_tx
.send(Err(nym_wireguard::error::Error::Internal(
"testing".to_owned(),
)))
.ok();
}
_ => panic!("Not expected"),
}
});
let ret = transceiver
.query_bandwidth("10.0.0.42".parse().unwrap())
.await;
assert!(ret.is_err());
}
#[tokio::test]
async fn topup() {
let (request_tx, mut request_rx) = mpsc::channel(CONTROL_CHANNEL_SIZE);
let transceiver = PeerControllerTransceiver::new(request_tx);
let credential = CredentialSpendingData::try_from_bytes(&CREDENTIAL_BYTES).unwrap();
let verifier_bw = 42;
tokio::spawn(async move {
match request_rx.recv().await.unwrap() {
PeerControlRequest::GetVerifierByIp {
ip: _,
credential: _,
response_tx,
} => {
response_tx
.send(Ok(Box::new(MockVerifier::new(verifier_bw))))
.ok();
}
_ => panic!("Not expected"),
}
});
let bw = transceiver
.topup_bandwidth("10.0.0.42".parse().unwrap(), Box::new(credential))
.await
.unwrap();
assert_eq!(bw, verifier_bw);
}
#[tokio::test]
async fn unsuccessful_topup() {
let (request_tx, mut request_rx) = mpsc::channel(CONTROL_CHANNEL_SIZE);
let transceiver = PeerControllerTransceiver::new(request_tx);
let credential = CredentialSpendingData::try_from_bytes(&CREDENTIAL_BYTES).unwrap();
tokio::spawn(async move {
match request_rx.recv().await.unwrap() {
PeerControlRequest::GetVerifierByIp {
ip: _,
credential: _,
response_tx,
} => {
response_tx
.send(Err(nym_wireguard::error::Error::Internal(
"testing".to_owned(),
)))
.ok();
}
_ => panic!("Not expected"),
}
});
let ret = transceiver
.topup_bandwidth("10.0.0.42".parse().unwrap(), Box::new(credential))
.await;
assert!(ret.is_err());
}
}
@@ -1,25 +0,0 @@
[package]
name = "nym-wireguard-private-metadata-shared"
version = "1.0.0"
authors.workspace = true
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
edition.workspace = true
license.workspace = true
[dependencies]
axum = { workspace = true }
bincode = { workspace = true }
schemars = { workspace = true, features = ["preserve_order"] }
serde = { workspace = true }
thiserror = { workspace = true }
utoipa = { workspace = true }
nym-credentials-interface = { path = "../../credentials-interface" }
[features]
testing = []
[lints]
workspace = true
@@ -1,28 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
pub enum MetadataError {
#[error("peers can't be interacted with anymore")]
PeerInteractionStopped,
#[error("no response received")]
NoResponse,
#[error("query was not successful: {reason}")]
Unsuccessful { reason: String },
#[error("Models error: {message}")]
Models { message: String },
#[error("Credential verification error: {message}")]
CredentialVerification { message: String },
}
impl From<crate::models::error::Error> for MetadataError {
fn from(value: crate::models::error::Error) -> Self {
Self::Models {
message: value.to_string(),
}
}
}
@@ -1,20 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub mod error;
mod models;
pub mod routes;
#[cfg(feature = "testing")]
pub use models::v0;
pub use models::{
error::Error as ModelError, interface, latest, v1, AxumErrorResponse, AxumResult, Construct,
ErrorResponse, Extract, Request, Response, Version,
};
fn make_bincode_serializer() -> impl bincode::Options {
use bincode::Options;
bincode::DefaultOptions::new()
.with_big_endian()
.with_varint_encoding()
}
@@ -1,30 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::models::Version;
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error(transparent)]
Bincode(#[from] bincode::Error),
#[error("trying to deserialize from version {source_version:?} into {target_version:?}")]
InvalidVersion {
source_version: Version,
target_version: Version,
},
#[error(
"trying to deserialize from query type {source_query_type} query type {target_query_type}"
)]
InvalidQueryType {
source_query_type: String,
target_query_type: String,
},
#[error("update not possible from {from:?} to {to:?}")]
UpdateNotPossible { from: Version, to: Version },
#[error("downgrade not possible from {from:?} to {to:?}")]
DowngradeNotPossible { from: Version, to: Version },
}
@@ -1,145 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use nym_credentials_interface::CredentialSpendingData;
#[cfg(feature = "testing")]
use crate::models::v0;
use crate::models::{v1, Construct, Extract, Request, Response, Version};
pub enum RequestData {
AvailableBandwidth(()),
TopUpBandwidth(Box<CredentialSpendingData>),
}
impl From<super::latest::interface::RequestData> for RequestData {
fn from(value: super::latest::interface::RequestData) -> Self {
match value {
super::latest::interface::RequestData::AvailableBandwidth(inner) => {
Self::AvailableBandwidth(inner)
}
super::latest::interface::RequestData::TopUpBandwidth(credential_spending_data) => {
Self::TopUpBandwidth(credential_spending_data)
}
}
}
}
impl From<RequestData> for super::latest::interface::RequestData {
fn from(value: RequestData) -> Self {
match value {
RequestData::AvailableBandwidth(inner) => Self::AvailableBandwidth(inner),
RequestData::TopUpBandwidth(credential_spending_data) => {
Self::TopUpBandwidth(credential_spending_data)
}
}
}
}
impl From<super::latest::interface::ResponseData> for ResponseData {
fn from(value: super::latest::interface::ResponseData) -> Self {
match value {
super::latest::interface::ResponseData::AvailableBandwidth(inner) => {
Self::AvailableBandwidth(inner)
}
super::latest::interface::ResponseData::TopUpBandwidth(credential_spending_data) => {
Self::TopUpBandwidth(credential_spending_data)
}
}
}
}
impl From<ResponseData> for super::latest::interface::ResponseData {
fn from(value: ResponseData) -> Self {
match value {
ResponseData::AvailableBandwidth(inner) => Self::AvailableBandwidth(inner),
ResponseData::TopUpBandwidth(credential_spending_data) => {
Self::TopUpBandwidth(credential_spending_data)
}
}
}
}
impl Construct<RequestData> for Request {
fn construct(info: RequestData, version: Version) -> Result<Self, super::error::Error> {
match version {
#[cfg(feature = "testing")]
Version::V0 => {
let translate_info = super::latest::interface::RequestData::from(info);
let downgrade_info = v0::interface::RequestData::try_from(translate_info)?;
let versioned_request = v0::VersionedRequest::construct(downgrade_info, version)?;
Ok(versioned_request.try_into()?)
}
Version::V1 => {
let versioned_request = v1::VersionedRequest::construct(info.into(), version)?;
Ok(versioned_request.try_into()?)
}
}
}
}
impl Extract<RequestData> for Request {
fn extract(&self) -> Result<(RequestData, Version), crate::models::Error> {
match self.version {
#[cfg(feature = "testing")]
super::Version::V0 => {
let versioned_request = v0::VersionedRequest::try_from(self.clone())?;
let (request, version) = versioned_request.extract()?;
let upgrade_request = super::latest::interface::RequestData::try_from(request)?;
Ok((upgrade_request.into(), version))
}
super::Version::V1 => {
let versioned_request = v1::VersionedRequest::try_from(self.clone())?;
let (extracted, version) = versioned_request.extract()?;
Ok((extracted.into(), version))
}
}
}
}
pub enum ResponseData {
AvailableBandwidth(i64),
TopUpBandwidth(i64),
}
impl Construct<ResponseData> for Response {
fn construct(info: ResponseData, version: Version) -> Result<Self, super::error::Error> {
match version {
#[cfg(feature = "testing")]
super::Version::V0 => {
let translate_response = super::latest::interface::ResponseData::from(info);
let downgrade_response = v0::interface::ResponseData::try_from(translate_response)?;
let versioned_response =
v0::VersionedResponse::construct(downgrade_response, version)?;
Ok(versioned_response.try_into()?)
}
Version::V1 => {
let versioned_response = v1::VersionedResponse::construct(info.into(), version)?;
Ok(versioned_response.try_into()?)
}
}
}
}
impl Extract<ResponseData> for Response {
fn extract(&self) -> Result<(ResponseData, Version), super::error::Error> {
match self.version {
#[cfg(feature = "testing")]
super::Version::V0 => {
let versioned_response = v0::VersionedResponse::try_from(self.clone())?;
let (response, version) = versioned_response.extract()?;
let upgrade_response = super::latest::interface::ResponseData::try_from(response)?;
Ok((upgrade_response.into(), version))
}
super::Version::V1 => {
let versioned_response = v1::VersionedResponse::try_from(self.clone())?;
let (extracted, version) = versioned_response.extract()?;
Ok((extracted.into(), version))
}
}
}
}
@@ -1,175 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::fmt::{Display, Formatter};
use axum::http::StatusCode;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use utoipa::ToSchema;
pub(crate) mod error;
pub mod interface;
#[cfg(feature = "testing")]
pub mod v0; // dummy version, only for filling boilerplate code for update/downgrade and testing
pub mod v1;
pub use v1 as latest;
use crate::models::error::Error;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema, ToSchema)]
pub enum Version {
#[cfg(feature = "testing")]
/// only used for testing purposes, don't include it in your matching arms
V0,
V1,
}
impl From<u64> for Version {
fn from(value: u64) -> Self {
#[cfg(feature = "testing")]
let zero_version = Version::V0;
#[cfg(not(feature = "testing"))]
let zero_version = latest::VERSION;
match value {
0 => zero_version,
1 => Version::V1,
_ => latest::VERSION, // if unknown, it means we're behind, so we can use the latest we know about
}
}
}
impl From<Version> for u64 {
fn from(value: Version) -> Self {
// remember to modify the above match if you're bumping the version
match value {
#[cfg(feature = "testing")]
Version::V0 => 0,
Version::V1 => 1,
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, ToSchema)]
pub struct Request {
pub version: Version,
pub(crate) inner: Vec<u8>,
}
#[derive(Clone, Serialize, Deserialize, ToSchema)]
pub struct Response {
pub version: Version,
pub(crate) inner: Vec<u8>,
}
pub trait Extract<T> {
fn extract(&self) -> Result<(T, Version), Error>;
}
pub trait Construct<T>: Sized {
fn construct(info: T, version: Version) -> Result<Self, Error>;
}
pub type AxumResult<T> = Result<T, AxumErrorResponse>;
#[derive(Serialize, Deserialize, Debug, Clone, JsonSchema)]
pub struct ErrorResponse {
pub message: String,
}
impl Display for ErrorResponse {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.message.fmt(f)
}
}
pub struct AxumErrorResponse {
message: ErrorResponse,
status: StatusCode,
}
impl AxumErrorResponse {
pub fn bad_request(msg: impl Display) -> Self {
Self {
message: ErrorResponse {
message: msg.to_string(),
},
status: StatusCode::BAD_REQUEST,
}
}
}
impl axum::response::IntoResponse for AxumErrorResponse {
fn into_response(self) -> axum::response::Response {
(self.status, self.message.message.to_string()).into_response()
}
}
mod tests {
#[allow(dead_code)]
pub(crate) const CREDENTIAL_BYTES: [u8; 1245] = [
0, 0, 4, 133, 96, 179, 223, 185, 136, 23, 213, 166, 59, 203, 66, 69, 209, 181, 227, 254,
16, 102, 98, 237, 59, 119, 170, 111, 31, 194, 51, 59, 120, 17, 115, 229, 79, 91, 11, 139,
154, 2, 212, 23, 68, 70, 167, 3, 240, 54, 224, 171, 221, 1, 69, 48, 60, 118, 119, 249, 123,
35, 172, 227, 131, 96, 232, 209, 187, 123, 4, 197, 102, 90, 96, 45, 125, 135, 140, 99, 1,
151, 17, 131, 143, 157, 97, 107, 139, 232, 212, 87, 14, 115, 253, 255, 166, 167, 186, 43,
90, 96, 173, 105, 120, 40, 10, 163, 250, 224, 214, 200, 178, 4, 160, 16, 130, 59, 76, 193,
39, 240, 3, 101, 141, 209, 183, 226, 186, 207, 56, 210, 187, 7, 164, 240, 164, 205, 37, 81,
184, 214, 193, 195, 90, 205, 238, 225, 195, 104, 12, 123, 203, 57, 233, 243, 215, 145, 195,
196, 57, 38, 125, 172, 18, 47, 63, 165, 110, 219, 180, 40, 58, 116, 92, 254, 160, 98, 48,
92, 254, 232, 107, 184, 80, 234, 60, 160, 235, 249, 76, 41, 38, 165, 28, 40, 136, 74, 48,
166, 50, 245, 23, 201, 140, 101, 79, 93, 235, 128, 186, 146, 126, 180, 134, 43, 13, 186,
19, 195, 48, 168, 201, 29, 216, 95, 176, 198, 132, 188, 64, 39, 212, 150, 32, 52, 53, 38,
228, 199, 122, 226, 217, 75, 40, 191, 151, 48, 164, 242, 177, 79, 14, 122, 105, 151, 85,
88, 199, 162, 17, 96, 103, 83, 178, 128, 9, 24, 30, 74, 108, 241, 85, 240, 166, 97, 241,
85, 199, 11, 198, 226, 234, 70, 107, 145, 28, 208, 114, 51, 12, 234, 108, 101, 202, 112,
48, 185, 22, 159, 67, 109, 49, 27, 149, 90, 109, 32, 226, 112, 7, 201, 208, 209, 104, 31,
97, 134, 204, 145, 27, 181, 206, 181, 106, 32, 110, 136, 115, 249, 201, 111, 5, 245, 203,
71, 121, 169, 126, 151, 178, 236, 59, 221, 195, 48, 135, 115, 6, 50, 227, 74, 97, 107, 107,
213, 90, 2, 203, 154, 138, 47, 128, 52, 134, 128, 224, 51, 65, 240, 90, 8, 55, 175, 180,
178, 204, 206, 168, 110, 51, 57, 189, 169, 48, 169, 136, 121, 99, 51, 170, 178, 214, 74, 1,
96, 151, 167, 25, 173, 180, 171, 155, 10, 55, 142, 234, 190, 113, 90, 79, 80, 244, 71, 166,
30, 235, 113, 150, 133, 1, 218, 17, 109, 111, 223, 24, 216, 177, 41, 2, 204, 65, 221, 212,
207, 236, 144, 6, 65, 224, 55, 42, 1, 1, 161, 134, 118, 127, 111, 220, 110, 127, 240, 71,
223, 129, 12, 93, 20, 220, 60, 56, 71, 146, 184, 95, 132, 69, 28, 56, 53, 192, 213, 22,
119, 230, 152, 225, 182, 188, 163, 219, 37, 175, 247, 73, 14, 247, 38, 72, 243, 1, 48, 131,
59, 8, 13, 96, 143, 185, 127, 241, 161, 217, 24, 149, 193, 40, 16, 30, 202, 151, 28, 119,
240, 153, 101, 156, 61, 193, 72, 245, 199, 181, 12, 231, 65, 166, 67, 142, 121, 207, 202,
58, 197, 113, 188, 248, 42, 124, 105, 48, 161, 241, 55, 209, 36, 194, 27, 63, 233, 144,
189, 85, 117, 234, 9, 139, 46, 31, 206, 114, 95, 131, 29, 240, 13, 81, 142, 140, 133, 33,
30, 41, 141, 37, 80, 217, 95, 221, 76, 115, 86, 201, 165, 51, 252, 9, 28, 209, 1, 48, 150,
74, 248, 212, 187, 222, 66, 210, 3, 200, 19, 217, 171, 184, 42, 148, 53, 150, 57, 50, 6,
227, 227, 62, 49, 42, 148, 148, 157, 82, 191, 58, 24, 34, 56, 98, 120, 89, 105, 176, 85,
15, 253, 241, 41, 153, 195, 136, 1, 48, 142, 126, 213, 101, 223, 79, 133, 230, 105, 38,
161, 149, 2, 21, 136, 150, 42, 72, 218, 85, 146, 63, 223, 58, 108, 186, 183, 248, 62, 20,
47, 34, 113, 160, 177, 204, 181, 16, 24, 212, 224, 35, 84, 51, 168, 56, 136, 11, 1, 48,
135, 242, 62, 149, 230, 178, 32, 224, 119, 26, 234, 163, 237, 224, 114, 95, 112, 140, 170,
150, 96, 125, 136, 221, 180, 78, 18, 11, 12, 184, 2, 198, 217, 119, 43, 69, 4, 172, 109,
55, 183, 40, 131, 172, 161, 88, 183, 101, 1, 48, 173, 216, 22, 73, 42, 255, 211, 93, 249,
87, 159, 115, 61, 91, 55, 130, 17, 216, 60, 34, 122, 55, 8, 244, 244, 153, 151, 57, 5, 144,
178, 55, 249, 64, 211, 168, 34, 148, 56, 89, 92, 203, 70, 124, 219, 152, 253, 165, 0, 32,
203, 116, 63, 7, 240, 222, 82, 86, 11, 149, 167, 72, 224, 55, 190, 66, 201, 65, 168, 184,
96, 47, 194, 241, 168, 124, 7, 74, 214, 250, 37, 76, 32, 218, 69, 122, 103, 215, 145, 169,
24, 212, 229, 168, 106, 10, 144, 31, 13, 25, 178, 242, 250, 106, 159, 40, 48, 163, 165, 61,
130, 57, 146, 4, 73, 32, 254, 233, 125, 135, 212, 29, 111, 4, 177, 114, 15, 210, 170, 82,
108, 110, 62, 166, 81, 209, 106, 176, 156, 14, 133, 242, 60, 127, 120, 242, 28, 97, 0, 1,
32, 103, 93, 109, 89, 240, 91, 1, 84, 150, 50, 206, 157, 203, 49, 220, 120, 234, 175, 234,
150, 126, 225, 94, 163, 164, 199, 138, 114, 62, 99, 106, 112, 1, 32, 171, 40, 220, 82, 241,
203, 76, 146, 111, 139, 182, 179, 237, 182, 115, 75, 128, 201, 107, 43, 214, 0, 135, 217,
160, 68, 150, 232, 144, 114, 237, 98, 32, 30, 134, 232, 59, 93, 163, 253, 244, 13, 202, 52,
147, 168, 83, 121, 123, 95, 21, 210, 209, 225, 223, 143, 49, 10, 205, 238, 1, 22, 83, 81,
70, 1, 32, 26, 76, 6, 234, 160, 50, 139, 102, 161, 232, 155, 106, 130, 171, 226, 210, 233,
178, 85, 247, 71, 123, 55, 53, 46, 67, 148, 137, 156, 207, 208, 107, 1, 32, 102, 31, 4, 98,
110, 156, 144, 61, 229, 140, 198, 84, 196, 238, 128, 35, 131, 182, 137, 125, 241, 95, 69,
131, 170, 27, 2, 144, 75, 72, 242, 102, 3, 32, 121, 80, 45, 173, 56, 65, 218, 27, 40, 251,
197, 32, 169, 104, 123, 110, 90, 78, 153, 166, 38, 9, 129, 228, 99, 8, 1, 116, 142, 233,
162, 69, 32, 216, 169, 159, 116, 95, 12, 63, 176, 195, 6, 183, 123, 135, 75, 61, 112, 106,
83, 235, 176, 41, 27, 248, 48, 71, 165, 170, 12, 92, 103, 103, 81, 32, 58, 74, 75, 145,
192, 94, 153, 69, 80, 128, 241, 3, 16, 117, 192, 86, 161, 103, 44, 174, 211, 196, 182, 124,
55, 11, 107, 142, 49, 88, 6, 41, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 0, 37, 139, 240, 0, 0,
0, 0, 0, 0, 0, 1,
];
}
@@ -1,5 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub mod request;
pub mod response;
@@ -1,86 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use bincode::Options;
use serde::{Deserialize, Serialize};
use crate::{make_bincode_serializer, models::Request};
use super::super::{Error, QueryType, VersionedRequest};
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct InnerAvailableBandwidthRequest {}
impl TryFrom<VersionedRequest> for InnerAvailableBandwidthRequest {
type Error = Error;
fn try_from(value: VersionedRequest) -> Result<Self, Self::Error> {
match value.query_type {
QueryType::AvailableBandwidth => {
Ok(make_bincode_serializer().deserialize(&value.inner)?)
}
QueryType::TopupBandwidth => Err(Error::InvalidQueryType {
source_query_type: value.query_type.to_string(),
target_query_type: QueryType::AvailableBandwidth.to_string(),
}),
}
}
}
impl TryFrom<InnerAvailableBandwidthRequest> for VersionedRequest {
type Error = Error;
fn try_from(value: InnerAvailableBandwidthRequest) -> Result<Self, Self::Error> {
Ok(Self {
query_type: QueryType::AvailableBandwidth,
inner: make_bincode_serializer().serialize(&value)?,
})
}
}
impl TryFrom<Request> for InnerAvailableBandwidthRequest {
type Error = crate::error::MetadataError;
fn try_from(value: Request) -> Result<Self, Self::Error> {
VersionedRequest::try_from(value)?
.try_into()
.map_err(|err: Error| crate::error::MetadataError::Models {
message: err.to_string(),
})
}
}
impl TryFrom<InnerAvailableBandwidthRequest> for Request {
type Error = crate::error::MetadataError;
fn try_from(value: InnerAvailableBandwidthRequest) -> Result<Self, Self::Error> {
VersionedRequest::try_from(value)?
.try_into()
.map_err(|err: Error| crate::error::MetadataError::Models {
message: err.to_string(),
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn serde() {
let req = InnerAvailableBandwidthRequest {};
let ser = VersionedRequest::try_from(req).unwrap();
assert_eq!(QueryType::AvailableBandwidth, ser.query_type);
let de = InnerAvailableBandwidthRequest::try_from(ser).unwrap();
assert_eq!(req, de);
}
#[test]
fn empty_content() {
let future_req = VersionedRequest {
query_type: QueryType::AvailableBandwidth,
inner: vec![],
};
assert!(InnerAvailableBandwidthRequest::try_from(future_req).is_ok());
}
}
@@ -1,86 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use bincode::Options;
use serde::{Deserialize, Serialize};
use crate::{make_bincode_serializer, models::Response};
use super::super::{Error, QueryType, VersionedResponse};
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct InnerAvailableBandwidthResponse {}
impl TryFrom<VersionedResponse> for InnerAvailableBandwidthResponse {
type Error = Error;
fn try_from(value: VersionedResponse) -> Result<Self, Self::Error> {
match value.query_type {
QueryType::AvailableBandwidth => {
Ok(make_bincode_serializer().deserialize(&value.inner)?)
}
QueryType::TopupBandwidth => Err(Error::InvalidQueryType {
source_query_type: value.query_type.to_string(),
target_query_type: QueryType::AvailableBandwidth.to_string(),
}),
}
}
}
impl TryFrom<InnerAvailableBandwidthResponse> for VersionedResponse {
type Error = Error;
fn try_from(value: InnerAvailableBandwidthResponse) -> Result<Self, Self::Error> {
Ok(Self {
query_type: QueryType::AvailableBandwidth,
inner: make_bincode_serializer().serialize(&value)?,
})
}
}
impl TryFrom<Response> for InnerAvailableBandwidthResponse {
type Error = crate::error::MetadataError;
fn try_from(value: Response) -> Result<Self, Self::Error> {
VersionedResponse::try_from(value)?
.try_into()
.map_err(|err: Error| crate::error::MetadataError::Models {
message: err.to_string(),
})
}
}
impl TryFrom<InnerAvailableBandwidthResponse> for Response {
type Error = crate::error::MetadataError;
fn try_from(value: InnerAvailableBandwidthResponse) -> Result<Self, Self::Error> {
VersionedResponse::try_from(value)?
.try_into()
.map_err(|err: Error| crate::error::MetadataError::Models {
message: err.to_string(),
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn serde() {
let resp = InnerAvailableBandwidthResponse {};
let ser = VersionedResponse::try_from(resp).unwrap();
assert_eq!(QueryType::AvailableBandwidth, ser.query_type);
let de = InnerAvailableBandwidthResponse::try_from(ser).unwrap();
assert_eq!(resp, de);
}
#[test]
fn empty_content() {
let future_resp = VersionedResponse {
query_type: QueryType::AvailableBandwidth,
inner: vec![],
};
assert!(InnerAvailableBandwidthResponse::try_from(future_resp).is_ok());
}
}
@@ -1,73 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use super::{
available_bandwidth::{
request::InnerAvailableBandwidthRequest, response::InnerAvailableBandwidthResponse,
},
topup_bandwidth::{request::InnerTopUpRequest, response::InnerTopUpResponse},
QueryType, VersionedRequest, VersionedResponse, VERSION,
};
use crate::models::{error::Error, Construct, Extract, Version};
#[derive(Debug, Clone, PartialEq)]
pub enum RequestData {
AvailableBandwidth(()),
TopUpBandwidth(()),
}
#[derive(Debug, Clone, PartialEq)]
pub enum ResponseData {
AvailableBandwidth(()),
TopUpBandwidth(()),
}
impl Construct<RequestData> for VersionedRequest {
fn construct(info: RequestData, _version: Version) -> Result<Self, Error> {
match info {
RequestData::AvailableBandwidth(_) => Ok(InnerAvailableBandwidthRequest {}.try_into()?),
RequestData::TopUpBandwidth(_) => Ok(InnerTopUpRequest {}.try_into()?),
}
}
}
impl Extract<RequestData> for VersionedRequest {
fn extract(&self) -> Result<(RequestData, Version), Error> {
match self.query_type {
QueryType::AvailableBandwidth => {
let _req = InnerAvailableBandwidthRequest::try_from(self.clone())?;
Ok((RequestData::AvailableBandwidth(()), VERSION))
}
QueryType::TopupBandwidth => {
let _req = InnerTopUpRequest::try_from(self.clone())?;
Ok((RequestData::TopUpBandwidth(()), VERSION))
}
}
}
}
impl Construct<ResponseData> for VersionedResponse {
fn construct(info: ResponseData, _version: Version) -> Result<Self, Error> {
match info {
ResponseData::AvailableBandwidth(()) => {
Ok(InnerAvailableBandwidthResponse {}.try_into()?)
}
ResponseData::TopUpBandwidth(()) => Ok(InnerTopUpResponse {}.try_into()?),
}
}
}
impl Extract<ResponseData> for VersionedResponse {
fn extract(&self) -> Result<(ResponseData, Version), Error> {
match self.query_type {
QueryType::AvailableBandwidth => {
let _resp = InnerAvailableBandwidthResponse::try_from(self.clone())?;
Ok((ResponseData::AvailableBandwidth(()), VERSION))
}
QueryType::TopupBandwidth => {
let _resp = InnerTopUpResponse::try_from(self.clone())?;
Ok((ResponseData::TopUpBandwidth(()), VERSION))
}
}
}
}
@@ -1,175 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::fmt::Display;
use bincode::Options;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use utoipa::ToSchema;
use super::error::Error;
use crate::{
make_bincode_serializer,
models::{Request, Response, Version},
};
pub(crate) mod available_bandwidth;
pub mod interface;
pub(crate) mod topup_bandwidth;
pub const VERSION: Version = Version::V0;
pub use available_bandwidth::{
request::InnerAvailableBandwidthRequest as AvailableBandwidthRequest,
response::InnerAvailableBandwidthResponse as AvailableBandwidthResponse,
};
pub use topup_bandwidth::{
request::InnerTopUpRequest as TopUpRequest, response::InnerTopUpResponse as TopUpResponse,
};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema, ToSchema)]
pub enum QueryType {
AvailableBandwidth,
TopupBandwidth,
}
impl Display for QueryType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{self:?}")
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, ToSchema)]
pub struct VersionedRequest {
query_type: QueryType,
inner: Vec<u8>,
}
impl TryFrom<VersionedRequest> for Request {
type Error = Error;
fn try_from(value: VersionedRequest) -> Result<Self, Self::Error> {
Ok(Request {
version: VERSION,
inner: make_bincode_serializer().serialize(&value)?,
})
}
}
impl TryFrom<Request> for VersionedRequest {
type Error = Error;
fn try_from(value: Request) -> Result<Self, Self::Error> {
if value.version != VERSION {
return Err(Error::InvalidVersion {
source_version: value.version,
target_version: VERSION,
});
}
Ok(make_bincode_serializer().deserialize(&value.inner)?)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, ToSchema)]
pub struct VersionedResponse {
query_type: QueryType,
inner: Vec<u8>,
}
impl TryFrom<VersionedResponse> for Response {
type Error = Error;
fn try_from(value: VersionedResponse) -> Result<Self, Self::Error> {
Ok(Response {
version: VERSION,
inner: make_bincode_serializer().serialize(&value)?,
})
}
}
impl TryFrom<Response> for VersionedResponse {
type Error = Error;
fn try_from(value: Response) -> Result<Self, Self::Error> {
if value.version != VERSION {
return Err(Error::InvalidVersion {
source_version: value.version,
target_version: VERSION,
});
}
Ok(make_bincode_serializer().deserialize(&value.inner)?)
}
}
#[cfg(test)]
mod tests {
use self::{
available_bandwidth::{
request::InnerAvailableBandwidthRequest, response::InnerAvailableBandwidthResponse,
},
topup_bandwidth::{request::InnerTopUpRequest, response::InnerTopUpResponse},
};
use super::*;
#[test]
fn serde_request_av_bw() {
let req = VersionedRequest {
query_type: QueryType::AvailableBandwidth,
inner: make_bincode_serializer()
.serialize(&InnerAvailableBandwidthRequest {})
.unwrap(),
};
let ser = Request::try_from(req.clone()).unwrap();
assert_eq!(VERSION, ser.version);
let de = VersionedRequest::try_from(ser).unwrap();
assert_eq!(req, de);
}
#[test]
fn serde_response_av_bw() {
let resp = VersionedResponse {
query_type: QueryType::AvailableBandwidth,
inner: make_bincode_serializer()
.serialize(&InnerAvailableBandwidthResponse {})
.unwrap(),
};
let ser = Response::try_from(resp.clone()).unwrap();
assert_eq!(VERSION, ser.version);
let de = VersionedResponse::try_from(ser).unwrap();
assert_eq!(resp, de);
}
#[test]
fn serde_request_topup() {
let req = VersionedRequest {
query_type: QueryType::TopupBandwidth,
inner: make_bincode_serializer()
.serialize(&InnerTopUpRequest {})
.unwrap(),
};
let ser = Request::try_from(req.clone()).unwrap();
assert_eq!(VERSION, ser.version);
let de = VersionedRequest::try_from(ser).unwrap();
assert_eq!(req, de);
}
#[test]
fn serde_response_topup() {
let resp = VersionedResponse {
query_type: QueryType::TopupBandwidth,
inner: make_bincode_serializer()
.serialize(&InnerTopUpResponse {})
.unwrap(),
};
let ser = Response::try_from(resp.clone()).unwrap();
assert_eq!(VERSION, ser.version);
let de = VersionedResponse::try_from(ser).unwrap();
assert_eq!(resp, de);
}
}
@@ -1,5 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub mod request;
pub mod response;
@@ -1,84 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use bincode::Options;
use serde::{Deserialize, Serialize};
use crate::{make_bincode_serializer, models::Request};
use super::super::{Error, QueryType, VersionedRequest};
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct InnerTopUpRequest {}
impl TryFrom<VersionedRequest> for InnerTopUpRequest {
type Error = Error;
fn try_from(value: VersionedRequest) -> Result<Self, Self::Error> {
match value.query_type {
QueryType::TopupBandwidth => Ok(make_bincode_serializer().deserialize(&value.inner)?),
QueryType::AvailableBandwidth => Err(Error::InvalidQueryType {
source_query_type: value.query_type.to_string(),
target_query_type: QueryType::TopupBandwidth.to_string(),
}),
}
}
}
impl TryFrom<InnerTopUpRequest> for VersionedRequest {
type Error = Error;
fn try_from(value: InnerTopUpRequest) -> Result<Self, Self::Error> {
Ok(Self {
query_type: QueryType::TopupBandwidth,
inner: make_bincode_serializer().serialize(&value)?,
})
}
}
impl TryFrom<Request> for InnerTopUpRequest {
type Error = crate::error::MetadataError;
fn try_from(value: Request) -> Result<Self, Self::Error> {
VersionedRequest::try_from(value)?
.try_into()
.map_err(|err: Error| crate::error::MetadataError::Models {
message: err.to_string(),
})
}
}
impl TryFrom<InnerTopUpRequest> for Request {
type Error = crate::error::MetadataError;
fn try_from(value: InnerTopUpRequest) -> Result<Self, Self::Error> {
VersionedRequest::try_from(value)?
.try_into()
.map_err(|err: Error| crate::error::MetadataError::Models {
message: err.to_string(),
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn serde() {
let req = InnerTopUpRequest {};
let ser = VersionedRequest::try_from(req.clone()).unwrap();
assert_eq!(QueryType::TopupBandwidth, ser.query_type);
let de = InnerTopUpRequest::try_from(ser).unwrap();
assert_eq!(req, de);
}
#[test]
fn empty_content() {
let future_req = VersionedRequest {
query_type: QueryType::TopupBandwidth,
inner: vec![],
};
assert!(InnerTopUpRequest::try_from(future_req).is_ok());
}
}
@@ -1,84 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use bincode::Options;
use serde::{Deserialize, Serialize};
use crate::{make_bincode_serializer, models::Response};
use super::super::{Error, QueryType, VersionedResponse};
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct InnerTopUpResponse {}
impl TryFrom<VersionedResponse> for InnerTopUpResponse {
type Error = Error;
fn try_from(value: VersionedResponse) -> Result<Self, Self::Error> {
match value.query_type {
QueryType::TopupBandwidth => Ok(make_bincode_serializer().deserialize(&value.inner)?),
QueryType::AvailableBandwidth => Err(Error::InvalidQueryType {
source_query_type: value.query_type.to_string(),
target_query_type: QueryType::TopupBandwidth.to_string(),
}),
}
}
}
impl TryFrom<InnerTopUpResponse> for VersionedResponse {
type Error = Error;
fn try_from(value: InnerTopUpResponse) -> Result<Self, Self::Error> {
Ok(Self {
query_type: QueryType::TopupBandwidth,
inner: make_bincode_serializer().serialize(&value)?,
})
}
}
impl TryFrom<Response> for InnerTopUpResponse {
type Error = crate::error::MetadataError;
fn try_from(value: Response) -> Result<Self, Self::Error> {
VersionedResponse::try_from(value)?
.try_into()
.map_err(|err: Error| crate::error::MetadataError::Models {
message: err.to_string(),
})
}
}
impl TryFrom<InnerTopUpResponse> for Response {
type Error = crate::error::MetadataError;
fn try_from(value: InnerTopUpResponse) -> Result<Self, Self::Error> {
VersionedResponse::try_from(value)?
.try_into()
.map_err(|err: Error| crate::error::MetadataError::Models {
message: err.to_string(),
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn serde() {
let resp = InnerTopUpResponse {};
let ser = VersionedResponse::try_from(resp.clone()).unwrap();
assert_eq!(QueryType::TopupBandwidth, ser.query_type);
let de = InnerTopUpResponse::try_from(ser).unwrap();
assert_eq!(resp, de);
}
#[test]
fn empty_content() {
let future_resp = VersionedResponse {
query_type: QueryType::TopupBandwidth,
inner: vec![],
};
assert!(InnerTopUpResponse::try_from(future_resp).is_ok());
}
}
@@ -1,5 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub mod request;
pub mod response;
@@ -1,86 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use bincode::Options;
use serde::{Deserialize, Serialize};
use crate::{make_bincode_serializer, models::Request};
use super::super::{Error, QueryType, VersionedRequest};
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct InnerAvailableBandwidthRequest {}
impl TryFrom<VersionedRequest> for InnerAvailableBandwidthRequest {
type Error = Error;
fn try_from(value: VersionedRequest) -> Result<Self, Self::Error> {
match value.query_type {
QueryType::AvailableBandwidth => {
Ok(make_bincode_serializer().deserialize(&value.inner)?)
}
QueryType::TopupBandwidth => Err(Error::InvalidQueryType {
source_query_type: value.query_type.to_string(),
target_query_type: QueryType::AvailableBandwidth.to_string(),
}),
}
}
}
impl TryFrom<InnerAvailableBandwidthRequest> for VersionedRequest {
type Error = Error;
fn try_from(value: InnerAvailableBandwidthRequest) -> Result<Self, Self::Error> {
Ok(Self {
query_type: QueryType::AvailableBandwidth,
inner: make_bincode_serializer().serialize(&value)?,
})
}
}
impl TryFrom<Request> for InnerAvailableBandwidthRequest {
type Error = crate::error::MetadataError;
fn try_from(value: Request) -> Result<Self, Self::Error> {
VersionedRequest::try_from(value)?
.try_into()
.map_err(|err: Error| crate::error::MetadataError::Models {
message: err.to_string(),
})
}
}
impl TryFrom<InnerAvailableBandwidthRequest> for Request {
type Error = crate::error::MetadataError;
fn try_from(value: InnerAvailableBandwidthRequest) -> Result<Self, Self::Error> {
VersionedRequest::try_from(value)?
.try_into()
.map_err(|err: Error| crate::error::MetadataError::Models {
message: err.to_string(),
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn serde() {
let req = InnerAvailableBandwidthRequest {};
let ser = VersionedRequest::try_from(req).unwrap();
assert_eq!(QueryType::AvailableBandwidth, ser.query_type);
let de = InnerAvailableBandwidthRequest::try_from(ser).unwrap();
assert_eq!(req, de);
}
#[test]
fn empty_content() {
let future_req = VersionedRequest {
query_type: QueryType::AvailableBandwidth,
inner: vec![],
};
assert!(InnerAvailableBandwidthRequest::try_from(future_req).is_ok());
}
}
@@ -1,90 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use bincode::Options;
use serde::{Deserialize, Serialize};
use crate::{make_bincode_serializer, models::Response};
use super::super::{Error, QueryType, VersionedResponse};
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct InnerAvailableBandwidthResponse {
pub available_bandwidth: i64,
}
impl TryFrom<VersionedResponse> for InnerAvailableBandwidthResponse {
type Error = Error;
fn try_from(value: VersionedResponse) -> Result<Self, Self::Error> {
match value.query_type {
QueryType::AvailableBandwidth => {
Ok(make_bincode_serializer().deserialize(&value.inner)?)
}
QueryType::TopupBandwidth => Err(Error::InvalidQueryType {
source_query_type: value.query_type.to_string(),
target_query_type: QueryType::AvailableBandwidth.to_string(),
}),
}
}
}
impl TryFrom<InnerAvailableBandwidthResponse> for VersionedResponse {
type Error = Error;
fn try_from(value: InnerAvailableBandwidthResponse) -> Result<Self, Self::Error> {
Ok(Self {
query_type: QueryType::AvailableBandwidth,
inner: make_bincode_serializer().serialize(&value)?,
})
}
}
impl TryFrom<Response> for InnerAvailableBandwidthResponse {
type Error = crate::error::MetadataError;
fn try_from(value: Response) -> Result<Self, Self::Error> {
VersionedResponse::try_from(value)?
.try_into()
.map_err(|err: Error| crate::error::MetadataError::Models {
message: err.to_string(),
})
}
}
impl TryFrom<InnerAvailableBandwidthResponse> for Response {
type Error = crate::error::MetadataError;
fn try_from(value: InnerAvailableBandwidthResponse) -> Result<Self, Self::Error> {
VersionedResponse::try_from(value)?
.try_into()
.map_err(|err: Error| crate::error::MetadataError::Models {
message: err.to_string(),
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn serde() {
let resp = InnerAvailableBandwidthResponse {
available_bandwidth: 42,
};
let ser = VersionedResponse::try_from(resp).unwrap();
assert_eq!(QueryType::AvailableBandwidth, ser.query_type);
let de = InnerAvailableBandwidthResponse::try_from(ser).unwrap();
assert_eq!(resp, de);
}
#[test]
fn invalid_content() {
let future_resp = VersionedResponse {
query_type: QueryType::AvailableBandwidth,
inner: vec![],
};
assert!(InnerAvailableBandwidthResponse::try_from(future_resp).is_err());
}
}
@@ -1,224 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use nym_credentials_interface::CredentialSpendingData;
#[cfg(feature = "testing")]
use super::super::v0 as previous;
use super::{
available_bandwidth::{
request::InnerAvailableBandwidthRequest, response::InnerAvailableBandwidthResponse,
},
topup_bandwidth::{request::InnerTopUpRequest, response::InnerTopUpResponse},
QueryType, VersionedRequest, VersionedResponse, VERSION,
};
use crate::models::{error::Error, Construct, Extract, Version};
#[derive(Debug, Clone, PartialEq)]
pub enum RequestData {
AvailableBandwidth(()),
TopUpBandwidth(Box<CredentialSpendingData>),
}
#[derive(Debug, Clone, PartialEq)]
pub enum ResponseData {
AvailableBandwidth(i64),
TopUpBandwidth(i64),
}
impl Construct<RequestData> for VersionedRequest {
fn construct(info: RequestData, _version: Version) -> Result<Self, Error> {
match info {
RequestData::AvailableBandwidth(_) => Ok(InnerAvailableBandwidthRequest {}.try_into()?),
RequestData::TopUpBandwidth(credential) => Ok(InnerTopUpRequest {
credential: *credential,
}
.try_into()?),
}
}
}
impl Extract<RequestData> for VersionedRequest {
fn extract(&self) -> Result<(RequestData, Version), Error> {
match self.query_type {
QueryType::AvailableBandwidth => {
let _req = InnerAvailableBandwidthRequest::try_from(self.clone())?;
Ok((RequestData::AvailableBandwidth(()), VERSION))
}
QueryType::TopupBandwidth => {
let req = InnerTopUpRequest::try_from(self.clone())?;
Ok((
RequestData::TopUpBandwidth(Box::new(req.credential)),
VERSION,
))
}
}
}
}
impl Construct<ResponseData> for VersionedResponse {
fn construct(info: ResponseData, _version: Version) -> Result<Self, Error> {
match info {
ResponseData::AvailableBandwidth(available_bandwidth) => {
Ok(InnerAvailableBandwidthResponse {
available_bandwidth,
}
.try_into()?)
}
ResponseData::TopUpBandwidth(available_bandwidth) => Ok(InnerTopUpResponse {
available_bandwidth,
}
.try_into()?),
}
}
}
impl Extract<ResponseData> for VersionedResponse {
fn extract(&self) -> Result<(ResponseData, Version), Error> {
match self.query_type {
QueryType::AvailableBandwidth => {
let resp = InnerAvailableBandwidthResponse::try_from(self.clone())?;
Ok((
ResponseData::AvailableBandwidth(resp.available_bandwidth),
VERSION,
))
}
QueryType::TopupBandwidth => {
let resp = InnerTopUpResponse::try_from(self.clone())?;
Ok((
ResponseData::TopUpBandwidth(resp.available_bandwidth),
VERSION,
))
}
}
}
}
// this should be with #[cfg(feature = "testing")] only coming from v0, don't copy this for future versions
#[cfg(feature = "testing")]
impl TryFrom<previous::interface::RequestData> for RequestData {
type Error = super::Error;
fn try_from(value: previous::interface::RequestData) -> Result<Self, Self::Error> {
match value {
previous::interface::RequestData::AvailableBandwidth(inner) => {
Ok(Self::AvailableBandwidth(inner))
}
previous::interface::RequestData::TopUpBandwidth(_) => {
Err(super::Error::UpdateNotPossible {
from: previous::VERSION,
to: VERSION,
})
}
}
}
}
// this should be with #[cfg(feature = "testing")] only coming from v0, don't copy this for future versions
#[cfg(feature = "testing")]
impl TryFrom<RequestData> for previous::interface::RequestData {
type Error = super::Error;
fn try_from(value: RequestData) -> Result<Self, Self::Error> {
match value {
RequestData::AvailableBandwidth(inner) => Ok(Self::AvailableBandwidth(inner)),
RequestData::TopUpBandwidth(_) => Ok(Self::TopUpBandwidth(())),
}
}
}
// this should be with #[cfg(feature = "testing")] only coming from v0, don't copy this for future versions
#[cfg(feature = "testing")]
impl TryFrom<previous::interface::ResponseData> for ResponseData {
type Error = super::Error;
fn try_from(value: previous::interface::ResponseData) -> Result<Self, Self::Error> {
match value {
previous::interface::ResponseData::AvailableBandwidth(_) => {
Err(super::Error::UpdateNotPossible {
from: previous::VERSION,
to: VERSION,
})
}
previous::interface::ResponseData::TopUpBandwidth(_) => {
Err(super::Error::UpdateNotPossible {
from: previous::VERSION,
to: VERSION,
})
}
}
}
}
// this should be with #[cfg(feature = "testing")] only coming from v0, don't copy this for future versions
#[cfg(feature = "testing")]
impl TryFrom<ResponseData> for previous::interface::ResponseData {
type Error = super::Error;
fn try_from(value: ResponseData) -> Result<Self, Self::Error> {
match value {
ResponseData::AvailableBandwidth(_) => Ok(Self::AvailableBandwidth(())),
ResponseData::TopUpBandwidth(_) => Ok(Self::TopUpBandwidth(())),
}
}
}
#[cfg(test)]
mod test {
use crate::models::tests::CREDENTIAL_BYTES;
use super::*;
#[test]
fn request_upgrade() {
assert_eq!(
RequestData::try_from(previous::interface::RequestData::AvailableBandwidth(()))
.unwrap(),
RequestData::AvailableBandwidth(())
);
assert!(
RequestData::try_from(previous::interface::RequestData::TopUpBandwidth(())).is_err(),
);
}
#[test]
fn response_upgrade() {
assert!(
ResponseData::try_from(previous::interface::ResponseData::AvailableBandwidth(()))
.is_err()
);
assert!(
ResponseData::try_from(previous::interface::ResponseData::TopUpBandwidth(())).is_err()
);
}
#[test]
fn request_downgrade() {
assert_eq!(
previous::interface::RequestData::try_from(RequestData::AvailableBandwidth(()))
.unwrap(),
previous::interface::RequestData::AvailableBandwidth(())
);
assert_eq!(
previous::interface::RequestData::try_from(RequestData::TopUpBandwidth(Box::new(
CredentialSpendingData::try_from_bytes(&CREDENTIAL_BYTES).unwrap()
)))
.unwrap(),
previous::interface::RequestData::TopUpBandwidth(())
);
}
#[test]
fn response_downgrade() {
assert_eq!(
previous::interface::ResponseData::try_from(ResponseData::AvailableBandwidth(42))
.unwrap(),
previous::interface::ResponseData::AvailableBandwidth(())
);
assert_eq!(
previous::interface::ResponseData::try_from(ResponseData::TopUpBandwidth(42)).unwrap(),
previous::interface::ResponseData::TopUpBandwidth(())
);
}
}
@@ -1,224 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::fmt::Display;
use bincode::Options;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use utoipa::ToSchema;
use super::error::Error;
use crate::{
make_bincode_serializer,
models::{Request, Response, Version},
};
pub use available_bandwidth::{
request::InnerAvailableBandwidthRequest as AvailableBandwidthRequest,
response::InnerAvailableBandwidthResponse as AvailableBandwidthResponse,
};
pub use topup_bandwidth::{
request::InnerTopUpRequest as TopUpRequest, response::InnerTopUpResponse as TopUpResponse,
};
pub(crate) mod available_bandwidth;
pub mod interface;
pub(crate) mod topup_bandwidth;
pub const VERSION: Version = Version::V1;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema, ToSchema)]
pub enum QueryType {
AvailableBandwidth,
TopupBandwidth,
}
impl Display for QueryType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{self:?}")
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, ToSchema)]
pub struct VersionedRequest {
query_type: QueryType,
inner: Vec<u8>,
}
impl TryFrom<VersionedRequest> for Request {
type Error = Error;
fn try_from(value: VersionedRequest) -> Result<Self, Self::Error> {
Ok(Request {
version: VERSION,
inner: make_bincode_serializer().serialize(&value)?,
})
}
}
impl TryFrom<Request> for VersionedRequest {
type Error = Error;
fn try_from(value: Request) -> Result<Self, Self::Error> {
if value.version != VERSION {
return Err(Error::InvalidVersion {
source_version: value.version,
target_version: VERSION,
});
}
Ok(make_bincode_serializer().deserialize(&value.inner)?)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, ToSchema)]
pub struct VersionedResponse {
query_type: QueryType,
inner: Vec<u8>,
}
impl TryFrom<VersionedResponse> for Response {
type Error = Error;
fn try_from(value: VersionedResponse) -> Result<Self, Self::Error> {
Ok(Response {
version: VERSION,
inner: make_bincode_serializer().serialize(&value)?,
})
}
}
impl TryFrom<Response> for VersionedResponse {
type Error = Error;
fn try_from(value: Response) -> Result<Self, Self::Error> {
if value.version != VERSION {
return Err(Error::InvalidVersion {
source_version: value.version,
target_version: VERSION,
});
}
Ok(make_bincode_serializer().deserialize(&value.inner)?)
}
}
#[cfg(test)]
mod tests {
use nym_credentials_interface::CredentialSpendingData;
use crate::models::tests::CREDENTIAL_BYTES;
use self::{
available_bandwidth::{
request::InnerAvailableBandwidthRequest, response::InnerAvailableBandwidthResponse,
},
topup_bandwidth::{request::InnerTopUpRequest, response::InnerTopUpResponse},
};
use super::*;
#[test]
fn mismatched_request_version() {
let version = Version::V0;
let future_bw = Request {
version,
inner: vec![],
};
if let Err(Error::InvalidVersion {
source_version,
target_version,
}) = VersionedRequest::try_from(future_bw)
{
assert_eq!(source_version, version);
assert_eq!(target_version, VERSION);
} else {
panic!("failed");
};
}
#[test]
fn mismatched_response_version() {
let version = Version::V0;
let future_bw = Response {
version,
inner: vec![],
};
if let Err(Error::InvalidVersion {
source_version,
target_version,
}) = VersionedResponse::try_from(future_bw)
{
assert_eq!(source_version, version);
assert_eq!(target_version, VERSION);
} else {
panic!("failed");
};
}
#[test]
fn serde_request_av_bw() {
let req = VersionedRequest {
query_type: QueryType::AvailableBandwidth,
inner: make_bincode_serializer()
.serialize(&InnerAvailableBandwidthResponse {
available_bandwidth: 42,
})
.unwrap(),
};
let ser = Request::try_from(req.clone()).unwrap();
assert_eq!(VERSION, ser.version);
let de = VersionedRequest::try_from(ser).unwrap();
assert_eq!(req, de);
}
#[test]
fn serde_response_av_bw() {
let resp = VersionedResponse {
query_type: QueryType::AvailableBandwidth,
inner: make_bincode_serializer()
.serialize(&InnerAvailableBandwidthRequest {})
.unwrap(),
};
let ser = Response::try_from(resp.clone()).unwrap();
assert_eq!(VERSION, ser.version);
let de = VersionedResponse::try_from(ser).unwrap();
assert_eq!(resp, de);
}
#[test]
fn serde_request_topup() {
let req = VersionedRequest {
query_type: QueryType::TopupBandwidth,
inner: make_bincode_serializer()
.serialize(&InnerTopUpRequest {
credential: CredentialSpendingData::try_from_bytes(&CREDENTIAL_BYTES).unwrap(),
})
.unwrap(),
};
let ser = Request::try_from(req.clone()).unwrap();
assert_eq!(VERSION, ser.version);
let de = VersionedRequest::try_from(ser).unwrap();
assert_eq!(req, de);
}
#[test]
fn serde_response_topup() {
let resp = VersionedResponse {
query_type: QueryType::TopupBandwidth,
inner: make_bincode_serializer()
.serialize(&InnerTopUpResponse {
available_bandwidth: 42,
})
.unwrap(),
};
let ser = Response::try_from(resp.clone()).unwrap();
assert_eq!(VERSION, ser.version);
let de = VersionedResponse::try_from(ser).unwrap();
assert_eq!(resp, de);
}
}
@@ -1,5 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub mod request;
pub mod response;
@@ -1,92 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use bincode::Options;
use nym_credentials_interface::CredentialSpendingData;
use serde::{Deserialize, Serialize};
use crate::{make_bincode_serializer, models::Request};
use super::super::{Error, QueryType, VersionedRequest};
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct InnerTopUpRequest {
/// Ecash credential
pub credential: CredentialSpendingData,
}
impl TryFrom<VersionedRequest> for InnerTopUpRequest {
type Error = Error;
fn try_from(value: VersionedRequest) -> Result<Self, Self::Error> {
match value.query_type {
QueryType::TopupBandwidth => Ok(make_bincode_serializer().deserialize(&value.inner)?),
QueryType::AvailableBandwidth => Err(Error::InvalidQueryType {
source_query_type: value.query_type.to_string(),
target_query_type: QueryType::TopupBandwidth.to_string(),
}),
}
}
}
impl TryFrom<InnerTopUpRequest> for VersionedRequest {
type Error = Error;
fn try_from(value: InnerTopUpRequest) -> Result<Self, Self::Error> {
Ok(Self {
query_type: QueryType::TopupBandwidth,
inner: make_bincode_serializer().serialize(&value)?,
})
}
}
impl TryFrom<Request> for InnerTopUpRequest {
type Error = crate::error::MetadataError;
fn try_from(value: Request) -> Result<Self, Self::Error> {
VersionedRequest::try_from(value)?
.try_into()
.map_err(|err: Error| crate::error::MetadataError::Models {
message: err.to_string(),
})
}
}
impl TryFrom<InnerTopUpRequest> for Request {
type Error = crate::error::MetadataError;
fn try_from(value: InnerTopUpRequest) -> Result<Self, Self::Error> {
VersionedRequest::try_from(value)?
.try_into()
.map_err(|err: Error| crate::error::MetadataError::Models {
message: err.to_string(),
})
}
}
#[cfg(test)]
mod tests {
use crate::models::tests::CREDENTIAL_BYTES;
use super::*;
#[test]
fn serde() {
let req = InnerTopUpRequest {
credential: CredentialSpendingData::try_from_bytes(&CREDENTIAL_BYTES).unwrap(),
};
let ser = VersionedRequest::try_from(req.clone()).unwrap();
assert_eq!(QueryType::TopupBandwidth, ser.query_type);
let de = InnerTopUpRequest::try_from(ser).unwrap();
assert_eq!(req, de);
}
#[test]
fn invalid_content() {
let future_req = VersionedRequest {
query_type: QueryType::TopupBandwidth,
inner: vec![],
};
assert!(InnerTopUpRequest::try_from(future_req).is_err());
}
}
@@ -1,88 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use bincode::Options;
use serde::{Deserialize, Serialize};
use crate::{make_bincode_serializer, models::Response};
use super::super::{Error, QueryType, VersionedResponse};
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct InnerTopUpResponse {
pub available_bandwidth: i64,
}
impl TryFrom<VersionedResponse> for InnerTopUpResponse {
type Error = Error;
fn try_from(value: VersionedResponse) -> Result<Self, Self::Error> {
match value.query_type {
QueryType::TopupBandwidth => Ok(make_bincode_serializer().deserialize(&value.inner)?),
QueryType::AvailableBandwidth => Err(Error::InvalidQueryType {
source_query_type: value.query_type.to_string(),
target_query_type: QueryType::TopupBandwidth.to_string(),
}),
}
}
}
impl TryFrom<InnerTopUpResponse> for VersionedResponse {
type Error = Error;
fn try_from(value: InnerTopUpResponse) -> Result<Self, Self::Error> {
Ok(Self {
query_type: QueryType::TopupBandwidth,
inner: make_bincode_serializer().serialize(&value)?,
})
}
}
impl TryFrom<Response> for InnerTopUpResponse {
type Error = crate::error::MetadataError;
fn try_from(value: Response) -> Result<Self, Self::Error> {
VersionedResponse::try_from(value)?
.try_into()
.map_err(|err: Error| crate::error::MetadataError::Models {
message: err.to_string(),
})
}
}
impl TryFrom<InnerTopUpResponse> for Response {
type Error = crate::error::MetadataError;
fn try_from(value: InnerTopUpResponse) -> Result<Self, Self::Error> {
VersionedResponse::try_from(value)?
.try_into()
.map_err(|err: Error| crate::error::MetadataError::Models {
message: err.to_string(),
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn serde() {
let resp = InnerTopUpResponse {
available_bandwidth: 42,
};
let ser = VersionedResponse::try_from(resp.clone()).unwrap();
assert_eq!(QueryType::TopupBandwidth, ser.query_type);
let de = InnerTopUpResponse::try_from(ser).unwrap();
assert_eq!(resp, de);
}
#[test]
fn invalid_content() {
let future_resp = VersionedResponse {
query_type: QueryType::TopupBandwidth,
inner: vec![],
};
assert!(InnerTopUpResponse::try_from(future_resp).is_err());
}
}
@@ -1,10 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub const V1_API_VERSION: &str = "v1";
pub const BANDWIDTH: &str = "bandwidth";
pub const VERSION: &str = "version";
pub const AVAILABLE: &str = "available";
pub const TOPUP: &str = "topup";
@@ -1,41 +0,0 @@
[package]
name = "nym-wireguard-private-metadata-tests"
version = "1.0.0"
authors.workspace = true
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
edition.workspace = true
license.workspace = true
[dependencies]
async-trait = { workspace = true }
axum = { workspace = true, features = ["tokio", "macros"] }
nym-credential-verification = { path = "../../credential-verification" }
nym-credentials-interface = { path = "../../credentials-interface" }
nym-http-api-client = { path = "../../http-api-client" }
nym-http-api-common = { path = "../../http-api-common", features = [
"middleware",
"utoipa",
"output",
] }
nym-wireguard = { path = "../../wireguard" }
tokio = { workspace = true, features = ["rt-multi-thread", "net", "io-util"] }
tower-http = { workspace = true, features = [
"cors",
"trace",
"compression-br",
"compression-deflate",
"compression-gzip",
"compression-zstd",
] }
utoipa = { workspace = true, features = ["axum_extras", "time"] }
nym-wireguard-private-metadata-client = { path = "../client" }
nym-wireguard-private-metadata-shared = { path = "../shared", features = [
"testing",
] }
nym-wireguard-private-metadata-server = { path = "../server" }
[lints]
workspace = true
@@ -1,217 +0,0 @@
#[cfg(test)]
mod v0;
#[cfg(test)]
mod tests {
use std::net::SocketAddr;
use nym_credential_verification::{ClientBandwidth, TicketVerifier};
use nym_credentials_interface::CredentialSpendingData;
use nym_http_api_client::Client;
use nym_wireguard::{peer_controller::PeerControlRequest, CONTROL_CHANNEL_SIZE};
use nym_wireguard_private_metadata_client::WireguardMetadataApiClient;
use nym_wireguard_private_metadata_server::{
AppState, PeerControllerTransceiver, RouterBuilder,
};
use nym_wireguard_private_metadata_shared::{latest, v0, v1, ErrorResponse};
use tokio::{net::TcpListener, sync::mpsc};
pub(crate) const VERIFIER_AVAILABLE_BANDWIDTH: i64 = 42;
pub(crate) const CREDENTIAL_BYTES: [u8; 1245] = [
0, 0, 4, 133, 96, 179, 223, 185, 136, 23, 213, 166, 59, 203, 66, 69, 209, 181, 227, 254,
16, 102, 98, 237, 59, 119, 170, 111, 31, 194, 51, 59, 120, 17, 115, 229, 79, 91, 11, 139,
154, 2, 212, 23, 68, 70, 167, 3, 240, 54, 224, 171, 221, 1, 69, 48, 60, 118, 119, 249, 123,
35, 172, 227, 131, 96, 232, 209, 187, 123, 4, 197, 102, 90, 96, 45, 125, 135, 140, 99, 1,
151, 17, 131, 143, 157, 97, 107, 139, 232, 212, 87, 14, 115, 253, 255, 166, 167, 186, 43,
90, 96, 173, 105, 120, 40, 10, 163, 250, 224, 214, 200, 178, 4, 160, 16, 130, 59, 76, 193,
39, 240, 3, 101, 141, 209, 183, 226, 186, 207, 56, 210, 187, 7, 164, 240, 164, 205, 37, 81,
184, 214, 193, 195, 90, 205, 238, 225, 195, 104, 12, 123, 203, 57, 233, 243, 215, 145, 195,
196, 57, 38, 125, 172, 18, 47, 63, 165, 110, 219, 180, 40, 58, 116, 92, 254, 160, 98, 48,
92, 254, 232, 107, 184, 80, 234, 60, 160, 235, 249, 76, 41, 38, 165, 28, 40, 136, 74, 48,
166, 50, 245, 23, 201, 140, 101, 79, 93, 235, 128, 186, 146, 126, 180, 134, 43, 13, 186,
19, 195, 48, 168, 201, 29, 216, 95, 176, 198, 132, 188, 64, 39, 212, 150, 32, 52, 53, 38,
228, 199, 122, 226, 217, 75, 40, 191, 151, 48, 164, 242, 177, 79, 14, 122, 105, 151, 85,
88, 199, 162, 17, 96, 103, 83, 178, 128, 9, 24, 30, 74, 108, 241, 85, 240, 166, 97, 241,
85, 199, 11, 198, 226, 234, 70, 107, 145, 28, 208, 114, 51, 12, 234, 108, 101, 202, 112,
48, 185, 22, 159, 67, 109, 49, 27, 149, 90, 109, 32, 226, 112, 7, 201, 208, 209, 104, 31,
97, 134, 204, 145, 27, 181, 206, 181, 106, 32, 110, 136, 115, 249, 201, 111, 5, 245, 203,
71, 121, 169, 126, 151, 178, 236, 59, 221, 195, 48, 135, 115, 6, 50, 227, 74, 97, 107, 107,
213, 90, 2, 203, 154, 138, 47, 128, 52, 134, 128, 224, 51, 65, 240, 90, 8, 55, 175, 180,
178, 204, 206, 168, 110, 51, 57, 189, 169, 48, 169, 136, 121, 99, 51, 170, 178, 214, 74, 1,
96, 151, 167, 25, 173, 180, 171, 155, 10, 55, 142, 234, 190, 113, 90, 79, 80, 244, 71, 166,
30, 235, 113, 150, 133, 1, 218, 17, 109, 111, 223, 24, 216, 177, 41, 2, 204, 65, 221, 212,
207, 236, 144, 6, 65, 224, 55, 42, 1, 1, 161, 134, 118, 127, 111, 220, 110, 127, 240, 71,
223, 129, 12, 93, 20, 220, 60, 56, 71, 146, 184, 95, 132, 69, 28, 56, 53, 192, 213, 22,
119, 230, 152, 225, 182, 188, 163, 219, 37, 175, 247, 73, 14, 247, 38, 72, 243, 1, 48, 131,
59, 8, 13, 96, 143, 185, 127, 241, 161, 217, 24, 149, 193, 40, 16, 30, 202, 151, 28, 119,
240, 153, 101, 156, 61, 193, 72, 245, 199, 181, 12, 231, 65, 166, 67, 142, 121, 207, 202,
58, 197, 113, 188, 248, 42, 124, 105, 48, 161, 241, 55, 209, 36, 194, 27, 63, 233, 144,
189, 85, 117, 234, 9, 139, 46, 31, 206, 114, 95, 131, 29, 240, 13, 81, 142, 140, 133, 33,
30, 41, 141, 37, 80, 217, 95, 221, 76, 115, 86, 201, 165, 51, 252, 9, 28, 209, 1, 48, 150,
74, 248, 212, 187, 222, 66, 210, 3, 200, 19, 217, 171, 184, 42, 148, 53, 150, 57, 50, 6,
227, 227, 62, 49, 42, 148, 148, 157, 82, 191, 58, 24, 34, 56, 98, 120, 89, 105, 176, 85,
15, 253, 241, 41, 153, 195, 136, 1, 48, 142, 126, 213, 101, 223, 79, 133, 230, 105, 38,
161, 149, 2, 21, 136, 150, 42, 72, 218, 85, 146, 63, 223, 58, 108, 186, 183, 248, 62, 20,
47, 34, 113, 160, 177, 204, 181, 16, 24, 212, 224, 35, 84, 51, 168, 56, 136, 11, 1, 48,
135, 242, 62, 149, 230, 178, 32, 224, 119, 26, 234, 163, 237, 224, 114, 95, 112, 140, 170,
150, 96, 125, 136, 221, 180, 78, 18, 11, 12, 184, 2, 198, 217, 119, 43, 69, 4, 172, 109,
55, 183, 40, 131, 172, 161, 88, 183, 101, 1, 48, 173, 216, 22, 73, 42, 255, 211, 93, 249,
87, 159, 115, 61, 91, 55, 130, 17, 216, 60, 34, 122, 55, 8, 244, 244, 153, 151, 57, 5, 144,
178, 55, 249, 64, 211, 168, 34, 148, 56, 89, 92, 203, 70, 124, 219, 152, 253, 165, 0, 32,
203, 116, 63, 7, 240, 222, 82, 86, 11, 149, 167, 72, 224, 55, 190, 66, 201, 65, 168, 184,
96, 47, 194, 241, 168, 124, 7, 74, 214, 250, 37, 76, 32, 218, 69, 122, 103, 215, 145, 169,
24, 212, 229, 168, 106, 10, 144, 31, 13, 25, 178, 242, 250, 106, 159, 40, 48, 163, 165, 61,
130, 57, 146, 4, 73, 32, 254, 233, 125, 135, 212, 29, 111, 4, 177, 114, 15, 210, 170, 82,
108, 110, 62, 166, 81, 209, 106, 176, 156, 14, 133, 242, 60, 127, 120, 242, 28, 97, 0, 1,
32, 103, 93, 109, 89, 240, 91, 1, 84, 150, 50, 206, 157, 203, 49, 220, 120, 234, 175, 234,
150, 126, 225, 94, 163, 164, 199, 138, 114, 62, 99, 106, 112, 1, 32, 171, 40, 220, 82, 241,
203, 76, 146, 111, 139, 182, 179, 237, 182, 115, 75, 128, 201, 107, 43, 214, 0, 135, 217,
160, 68, 150, 232, 144, 114, 237, 98, 32, 30, 134, 232, 59, 93, 163, 253, 244, 13, 202, 52,
147, 168, 83, 121, 123, 95, 21, 210, 209, 225, 223, 143, 49, 10, 205, 238, 1, 22, 83, 81,
70, 1, 32, 26, 76, 6, 234, 160, 50, 139, 102, 161, 232, 155, 106, 130, 171, 226, 210, 233,
178, 85, 247, 71, 123, 55, 53, 46, 67, 148, 137, 156, 207, 208, 107, 1, 32, 102, 31, 4, 98,
110, 156, 144, 61, 229, 140, 198, 84, 196, 238, 128, 35, 131, 182, 137, 125, 241, 95, 69,
131, 170, 27, 2, 144, 75, 72, 242, 102, 3, 32, 121, 80, 45, 173, 56, 65, 218, 27, 40, 251,
197, 32, 169, 104, 123, 110, 90, 78, 153, 166, 38, 9, 129, 228, 99, 8, 1, 116, 142, 233,
162, 69, 32, 216, 169, 159, 116, 95, 12, 63, 176, 195, 6, 183, 123, 135, 75, 61, 112, 106,
83, 235, 176, 41, 27, 248, 48, 71, 165, 170, 12, 92, 103, 103, 81, 32, 58, 74, 75, 145,
192, 94, 153, 69, 80, 128, 241, 3, 16, 117, 192, 86, 161, 103, 44, 174, 211, 196, 182, 124,
55, 11, 107, 142, 49, 88, 6, 41, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 0, 37, 139, 240, 0, 0,
0, 0, 0, 0, 0, 1,
];
pub(crate) struct MockVerifier {
ret: i64,
}
impl MockVerifier {
pub(crate) fn new(ret: i64) -> MockVerifier {
Self { ret }
}
}
#[async_trait::async_trait]
impl TicketVerifier for MockVerifier {
async fn verify(&mut self) -> nym_credential_verification::Result<i64> {
Ok(self.ret)
}
}
pub(crate) async fn spawn_server_and_create_client() -> Client {
let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
let addr = listener.local_addr().unwrap();
let (request_tx, mut request_rx) = mpsc::channel(CONTROL_CHANNEL_SIZE);
let router = RouterBuilder::with_default_routes()
.with_state(AppState::new(PeerControllerTransceiver::new(request_tx)))
.router;
tokio::spawn(async move {
loop {
match request_rx.recv().await {
Some(PeerControlRequest::GetClientBandwidthByIp { ip: _, response_tx }) => {
response_tx
.send(Ok(ClientBandwidth::new(Default::default())))
.ok();
}
Some(PeerControlRequest::GetVerifierByIp {
ip: _,
credential: _,
response_tx,
}) => {
response_tx
.send(Ok(Box::new(MockVerifier::new(
VERIFIER_AVAILABLE_BANDWIDTH,
))))
.ok();
}
None => break,
_ => panic!("Not expected"),
}
}
});
tokio::spawn(async move {
axum::serve(
listener,
router.into_make_service_with_connect_info::<SocketAddr>(),
)
.await
.unwrap();
});
Client::new_url::<_, ErrorResponse>(addr.to_string(), None).unwrap()
}
#[tokio::test]
async fn query_latest_version() {
let client = spawn_server_and_create_client().await;
let version = client.version().await.unwrap();
assert_eq!(version, latest::VERSION);
}
#[tokio::test]
async fn query_against_server_v0() {
let client = super::v0::network::test::spawn_server_and_create_client().await;
// version check
let version = client.version().await.unwrap();
assert_eq!(version, v0::VERSION);
// v0 reqwests
let request = v0::AvailableBandwidthRequest {}.try_into().unwrap();
let response = client.available_bandwidth(&request).await.unwrap();
v0::AvailableBandwidthResponse::try_from(response).unwrap();
let request = v0::TopUpRequest {}.try_into().unwrap();
let response = client.topup_bandwidth(&request).await.unwrap();
v0::TopUpResponse::try_from(response).unwrap();
// v1 reqwests
let request = v1::AvailableBandwidthRequest {}.try_into().unwrap();
assert!(client.available_bandwidth(&request).await.is_err());
let request = v1::TopUpRequest {
credential: CredentialSpendingData::try_from_bytes(&CREDENTIAL_BYTES).unwrap(),
}
.try_into()
.unwrap();
assert!(client.topup_bandwidth(&request).await.is_err());
}
#[tokio::test]
async fn query_against_server_v1() {
let client = spawn_server_and_create_client().await;
// version check
let version = client.version().await.unwrap();
assert_eq!(version, v1::VERSION);
// v0 reqwests
let request = v0::AvailableBandwidthRequest {}.try_into().unwrap();
let response = client.available_bandwidth(&request).await.unwrap();
v0::AvailableBandwidthResponse::try_from(response).unwrap();
let request = v0::TopUpRequest {}.try_into().unwrap();
assert!(client.topup_bandwidth(&request).await.is_err());
// v1 reqwests
let request = v1::AvailableBandwidthRequest {}.try_into().unwrap();
let response = client.available_bandwidth(&request).await.unwrap();
let available_bandwidth = v1::AvailableBandwidthResponse::try_from(response)
.unwrap()
.available_bandwidth;
assert_eq!(available_bandwidth, 0);
let request = v1::TopUpRequest {
credential: CredentialSpendingData::try_from_bytes(&CREDENTIAL_BYTES).unwrap(),
}
.try_into()
.unwrap();
let response = client.topup_bandwidth(&request).await.unwrap();
let available_bandwidth = v1::TopUpResponse::try_from(response)
.unwrap()
.available_bandwidth;
assert_eq!(available_bandwidth, VERIFIER_AVAILABLE_BANDWIDTH);
}
}
@@ -1,150 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use nym_wireguard_private_metadata_shared::{
v0 as latest, Construct, Extract, Request, Response, Version,
};
pub enum RequestData {
AvailableBandwidth(()),
TopUpBandwidth(()),
}
impl From<latest::interface::RequestData> for RequestData {
fn from(value: latest::interface::RequestData) -> Self {
match value {
latest::interface::RequestData::AvailableBandwidth(inner) => {
Self::AvailableBandwidth(inner)
}
latest::interface::RequestData::TopUpBandwidth(credential_spending_data) => {
Self::TopUpBandwidth(credential_spending_data)
}
}
}
}
impl From<RequestData> for latest::interface::RequestData {
fn from(value: RequestData) -> Self {
match value {
RequestData::AvailableBandwidth(inner) => Self::AvailableBandwidth(inner),
RequestData::TopUpBandwidth(credential_spending_data) => {
Self::TopUpBandwidth(credential_spending_data)
}
}
}
}
impl From<latest::interface::ResponseData> for ResponseData {
fn from(value: latest::interface::ResponseData) -> Self {
match value {
latest::interface::ResponseData::AvailableBandwidth(inner) => {
Self::AvailableBandwidth(inner)
}
latest::interface::ResponseData::TopUpBandwidth(credential_spending_data) => {
Self::TopUpBandwidth(credential_spending_data)
}
}
}
}
impl From<ResponseData> for latest::interface::ResponseData {
fn from(value: ResponseData) -> Self {
match value {
ResponseData::AvailableBandwidth(inner) => Self::AvailableBandwidth(inner),
ResponseData::TopUpBandwidth(credential_spending_data) => {
Self::TopUpBandwidth(credential_spending_data)
}
}
}
}
impl Construct<RequestData> for Request {
fn construct(
info: RequestData,
version: Version,
) -> Result<Self, nym_wireguard_private_metadata_shared::ModelError> {
match version {
Version::V0 => {
let translate_info = latest::interface::RequestData::from(info);
let versioned_request =
latest::VersionedRequest::construct(translate_info, latest::VERSION)?;
Ok(versioned_request.try_into()?)
}
_ => Err(
nym_wireguard_private_metadata_shared::ModelError::DowngradeNotPossible {
from: version,
to: Version::V0,
},
),
}
}
}
impl Extract<RequestData> for Request {
fn extract(
&self,
) -> Result<(RequestData, Version), nym_wireguard_private_metadata_shared::ModelError> {
match self.version {
Version::V0 => {
let versioned_request = latest::VersionedRequest::try_from(self.clone())?;
let (request, version) = versioned_request.extract()?;
Ok((request.into(), version))
}
_ => Err(
nym_wireguard_private_metadata_shared::ModelError::UpdateNotPossible {
from: self.version,
to: Version::V0,
},
),
}
}
}
pub enum ResponseData {
AvailableBandwidth(()),
TopUpBandwidth(()),
}
impl Construct<ResponseData> for Response {
fn construct(
info: ResponseData,
version: Version,
) -> Result<Self, nym_wireguard_private_metadata_shared::ModelError> {
match version {
Version::V0 => {
let translate_response = latest::interface::ResponseData::from(info);
let versioned_response =
latest::VersionedResponse::construct(translate_response, version)?;
Ok(versioned_response.try_into()?)
}
_ => Err(
nym_wireguard_private_metadata_shared::ModelError::DowngradeNotPossible {
from: version,
to: Version::V0,
},
),
}
}
}
impl Extract<ResponseData> for Response {
fn extract(
&self,
) -> Result<(ResponseData, Version), nym_wireguard_private_metadata_shared::ModelError> {
match self.version {
Version::V0 => {
let versioned_response = latest::VersionedResponse::try_from(self.clone())?;
let (response, version) = versioned_response.extract()?;
Ok((response.into(), version))
}
_ => Err(
nym_wireguard_private_metadata_shared::ModelError::UpdateNotPossible {
from: self.version,
to: Version::V0,
},
),
}
}
}
@@ -1,2 +0,0 @@
pub(crate) mod interface;
pub(crate) mod network;
@@ -1,146 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
#[cfg(test)]
pub(crate) mod test {
use std::net::SocketAddr;
use crate::{
tests::{MockVerifier, VERIFIER_AVAILABLE_BANDWIDTH},
v0::interface::{RequestData, ResponseData},
};
use axum::{extract::Query, Json, Router};
use nym_credential_verification::ClientBandwidth;
use nym_http_api_client::Client;
use nym_http_api_common::{FormattedResponse, OutputParams};
use nym_wireguard::{peer_controller::PeerControlRequest, CONTROL_CHANNEL_SIZE};
use nym_wireguard_private_metadata_server::PeerControllerTransceiver;
use nym_wireguard_private_metadata_shared::ErrorResponse;
use nym_wireguard_private_metadata_shared::{
v0 as latest, AxumErrorResponse, AxumResult, Construct, Extract, Request, Response,
};
use tokio::{net::TcpListener, sync::mpsc};
use tower_http::compression::CompressionLayer;
use nym_wireguard_private_metadata_server::AppState;
fn bandwidth_routes() -> Router<AppState> {
Router::new()
.route("/version", axum::routing::get(version))
.route("/available", axum::routing::post(available_bandwidth))
.route("/topup", axum::routing::post(topup_bandwidth))
.layer(CompressionLayer::new())
}
#[utoipa::path(
tag = "bandwidth",
get,
path = "/v1/bandwidth/version",
responses(
(status = 200, content(
(Response = "application/bincode")
))
),
)]
async fn version(Query(output): Query<OutputParams>) -> AxumResult<FormattedResponse<u64>> {
let output = output.output.unwrap_or_default();
Ok(output.to_response(latest::VERSION.into()))
}
#[utoipa::path(
tag = "bandwidth",
post,
request_body = Request,
path = "/v1/bandwidth/available",
responses(
(status = 200, content(
(Response = "application/bincode")
))
),
)]
async fn available_bandwidth(
Query(output): Query<OutputParams>,
Json(request): Json<Request>,
) -> AxumResult<FormattedResponse<Response>> {
let output = output.output.unwrap_or_default();
let (RequestData::AvailableBandwidth(_), version) =
request.extract().map_err(AxumErrorResponse::bad_request)?
else {
return Err(AxumErrorResponse::bad_request("incorrect request type"));
};
let response = Response::construct(ResponseData::AvailableBandwidth(()), version)
.map_err(AxumErrorResponse::bad_request)?;
Ok(output.to_response(response))
}
#[utoipa::path(
tag = "bandwidth",
post,
request_body = Request,
path = "/v1/bandwidth/topup",
responses(
(status = 200, content(
(Response = "application/bincode")
))
),
)]
async fn topup_bandwidth(
Query(output): Query<OutputParams>,
Json(request): Json<Request>,
) -> AxumResult<FormattedResponse<Response>> {
let output = output.output.unwrap_or_default();
let (RequestData::TopUpBandwidth(_), version) =
request.extract().map_err(AxumErrorResponse::bad_request)?
else {
return Err(AxumErrorResponse::bad_request("incorrect request type"));
};
let response = Response::construct(ResponseData::TopUpBandwidth(()), version)
.map_err(AxumErrorResponse::bad_request)?;
Ok(output.to_response(response))
}
pub(crate) async fn spawn_server_and_create_client() -> Client {
let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
let addr = listener.local_addr().unwrap();
let (request_tx, mut request_rx) = mpsc::channel(CONTROL_CHANNEL_SIZE);
let router = Router::new()
.nest("/v1", Router::new().nest("/bandwidth", bandwidth_routes()))
.with_state(AppState::new(PeerControllerTransceiver::new(request_tx)));
tokio::spawn(async move {
match request_rx.recv().await.unwrap() {
PeerControlRequest::GetClientBandwidthByIp { ip: _, response_tx } => {
response_tx
.send(Ok(ClientBandwidth::new(Default::default())))
.ok();
}
PeerControlRequest::GetVerifierByIp {
ip: _,
credential: _,
response_tx,
} => {
response_tx
.send(Ok(Box::new(MockVerifier::new(
VERIFIER_AVAILABLE_BANDWIDTH,
))))
.ok();
}
_ => panic!("Not expected"),
}
});
tokio::spawn(async move {
axum::serve(
listener,
router.into_make_service_with_connect_info::<SocketAddr>(),
)
.await
.unwrap();
});
Client::new_url::<_, ErrorResponse>(addr.to_string(), None).unwrap()
}
}
+2 -6
View File
@@ -17,13 +17,9 @@ pub struct Config {
/// default: `fc01::1`
pub private_ipv6: Ipv6Addr,
/// Tunnel port announced to external clients wishing to connect to the wireguard interface.
/// Port announced to external clients wishing to connect to the wireguard interface.
/// Useful in the instances where the node is behind a proxy.
pub announced_tunnel_port: u16,
/// Metadata port announced to external clients wishing to connect to the endpoint.
/// Useful in the instances where the node is behind a proxy.
pub announced_metadata_port: u16,
pub announced_port: u16,
/// The prefix denoting the maximum number of the clients that can be connected via Wireguard using IPv4.
/// The maximum value for IPv4 is 32
+6 -15
View File
@@ -18,13 +18,11 @@ use tokio::sync::mpsc::{self, Receiver, Sender};
#[cfg(target_os = "linux")]
use nym_network_defaults::constants::WG_TUN_BASE_NAME;
pub mod error;
pub(crate) mod error;
pub mod peer_controller;
pub mod peer_handle;
pub mod peer_storage_manager;
pub const CONTROL_CHANNEL_SIZE: usize = 256;
pub struct WgApiWrapper {
inner: WGApi,
}
@@ -128,7 +126,7 @@ pub struct WireguardGatewayData {
impl WireguardGatewayData {
pub fn new(config: Config, keypair: Arc<KeyPair>) -> (Self, Receiver<PeerControlRequest>) {
let (peer_tx, peer_rx) = mpsc::channel(CONTROL_CHANNEL_SIZE);
let (peer_tx, peer_rx) = mpsc::channel(1);
(
WireguardGatewayData {
config,
@@ -180,16 +178,10 @@ pub async fn start_wireguard(
let mut peer_bandwidth_managers = HashMap::with_capacity(peers.len());
for peer in peers.iter() {
let bandwidth_manager = peer_handle::SharedBandwidthStorageManager::new(
Arc::new(RwLock::new(
PeerController::generate_bandwidth_manager(
ecash_manager.storage(),
&peer.public_key,
)
let bandwidth_manager = Arc::new(RwLock::new(
PeerController::generate_bandwidth_manager(ecash_manager.storage(), &peer.public_key)
.await?,
)),
peer.allowed_ips.clone(),
);
));
peer_bandwidth_managers.insert(peer.public_key.clone(), (bandwidth_manager, peer.clone()));
}
@@ -198,7 +190,7 @@ pub async fn start_wireguard(
name: ifname.clone(),
prvkey: BASE64_STANDARD.encode(wireguard_data.inner.keypair().private_key().to_bytes()),
address: wireguard_data.inner.config().private_ipv4.to_string(),
port: wireguard_data.inner.config().announced_tunnel_port as u32,
port: wireguard_data.inner.config().announced_port as u32,
peers,
mtu: None,
};
@@ -241,7 +233,6 @@ pub async fn start_wireguard(
let host = wg_api.read_interface_data()?;
let wg_api = std::sync::Arc::new(WgApiWrapper::new(wg_api));
let mut controller = PeerController::new(
ecash_manager,
metrics,

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