Compare commits

..

8 Commits

Author SHA1 Message Date
dynco-nym f4785099c2 Add nodes/delegations endpoint (#5733)
* WIP

* Add /delegations endpoint

* Bump package version

* Remove node_id field
2025-04-28 23:59:40 +02:00
Jędrzej Stuczyński b04d3ba376 add reserved byte to reply surb serialisation (#5731) 2025-04-25 10:05:38 +01:00
Jędrzej Stuczyński b2dfdda210 NET-271: bugfix: use node saturation instead of its stake for selection weight (#5717) 2025-04-24 15:39:14 +01:00
dependabot[bot] 41ef3a26f5 build(deps-dev): bump http-proxy-middleware in /wasm/client/internal-dev (#5719)
Bumps [http-proxy-middleware](https://github.com/chimurai/http-proxy-middleware) from 2.0.6 to 2.0.9.
- [Release notes](https://github.com/chimurai/http-proxy-middleware/releases)
- [Changelog](https://github.com/chimurai/http-proxy-middleware/blob/v2.0.9/CHANGELOG.md)
- [Commits](https://github.com/chimurai/http-proxy-middleware/compare/v2.0.6...v2.0.9)

---
updated-dependencies:
- dependency-name: http-proxy-middleware
  dependency-version: 2.0.9
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-24 10:27:12 +01:00
dependabot[bot] bae1b488de build(deps): bump golang.org/x/net in /wasm/mix-fetch/go-mix-conn (#5720)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.36.0 to 0.38.0.
- [Commits](https://github.com/golang/net/compare/v0.36.0...v0.38.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-version: 0.38.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-24 10:26:52 +01:00
dependabot[bot] 40cf2c441a build(deps): bump clap from 4.5.36 to 4.5.37 in the patch-updates group (#5722)
Bumps the patch-updates group with 1 update: [clap](https://github.com/clap-rs/clap).


Updates `clap` from 4.5.36 to 4.5.37
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.36...clap_complete-v4.5.37)

---
updated-dependencies:
- dependency-name: clap
  dependency-version: 4.5.37
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-updates
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-24 10:26:32 +01:00
windy-ux 34871b14b3 / change landing page tab title to "Nym docs" (#5729) 2025-04-23 15:32:34 +00:00
dynco-nym c14b010f9e Eliminate duplicate node_ids from endpoint (#5728)
* Improve swagger definitions

* Sort data in DB

* Improve logging

* Store gw description to nym nodes table

* Move explorer related path to /explorer

* Bump package version
2025-04-23 15:19:15 +02:00
39 changed files with 747 additions and 300 deletions
-34
View File
@@ -4,40 +4,6 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
## [Unreleased]
## [2025.8-tourist] (2025-04-29)
- add reserved byte to reply surb serialisation ([#5731])
- Remove inactive peers ([#5721])
- Update Hickory DNS "0.24.4" to "0.25" ([#5709])
- build(deps): bump the patch-updates group across 1 directory with 7 updates ([#5708])
- Peer handle should die more gracefully ([#5704])
- build(deps): bump crossbeam-channel from 0.5.14 to 0.5.15 ([#5702])
- build(deps): bump actions/checkout from 3 to 4 ([#5700])
- Feature/updated sphinx payload keys ([#5698])
- Bump the nym-vpn deb metapackage to 1.0 ([#5697])
- Make mix hops optional for Mixnet Client ([#5696])
- build(deps): bump tokio from 1.44.1 to 1.44.2 ([#5693])
- Feature/replay protection ([#5682])
- Adding fresh nym-api tests and workflow ([#5659])
- build(deps): bump next from 14.2.21 to 14.2.25 ([#5655])
- build(deps): bump pnpm/action-setup from 4.0.0 to 4.1.0 ([#5436])
[#5731]: https://github.com/nymtech/nym/pull/5731
[#5721]: https://github.com/nymtech/nym/pull/5721
[#5709]: https://github.com/nymtech/nym/pull/5709
[#5708]: https://github.com/nymtech/nym/pull/5708
[#5704]: https://github.com/nymtech/nym/pull/5704
[#5702]: https://github.com/nymtech/nym/pull/5702
[#5700]: https://github.com/nymtech/nym/pull/5700
[#5698]: https://github.com/nymtech/nym/pull/5698
[#5697]: https://github.com/nymtech/nym/pull/5697
[#5696]: https://github.com/nymtech/nym/pull/5696
[#5693]: https://github.com/nymtech/nym/pull/5693
[#5682]: https://github.com/nymtech/nym/pull/5682
[#5659]: https://github.com/nymtech/nym/pull/5659
[#5655]: https://github.com/nymtech/nym/pull/5655
[#5436]: https://github.com/nymtech/nym/pull/5436
## [2025.7-tex] (2025-04-14)
- Expand /v3/nym-nodes with geodata ([#5686])
Generated
+14 -12
View File
@@ -1211,9 +1211,9 @@ dependencies = [
[[package]]
name = "clap"
version = "4.5.36"
version = "4.5.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2df961d8c8a0d08aa9945718ccf584145eee3f3aa06cddbeac12933781102e04"
checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071"
dependencies = [
"clap_builder",
"clap_derive",
@@ -1221,9 +1221,9 @@ dependencies = [
[[package]]
name = "clap_builder"
version = "4.5.36"
version = "4.5.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "132dbda40fb6753878316a489d5a1242a8ef2f0d9e47ba01c951ea8aa7d013a5"
checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2"
dependencies = [
"anstream",
"anstyle",
@@ -4733,7 +4733,7 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
[[package]]
name = "nym-api"
version = "1.1.56"
version = "1.1.55"
dependencies = [
"anyhow",
"async-trait",
@@ -4987,7 +4987,7 @@ dependencies = [
[[package]]
name = "nym-cli"
version = "1.1.54"
version = "1.1.53"
dependencies = [
"anyhow",
"base64 0.22.1",
@@ -5070,7 +5070,7 @@ dependencies = [
[[package]]
name = "nym-client"
version = "1.1.54"
version = "1.1.53"
dependencies = [
"bs58",
"clap",
@@ -6068,7 +6068,7 @@ dependencies = [
[[package]]
name = "nym-network-requester"
version = "1.1.55"
version = "1.1.54"
dependencies = [
"addr",
"anyhow",
@@ -6119,7 +6119,7 @@ dependencies = [
[[package]]
name = "nym-node"
version = "1.10.0"
version = "1.9.0"
dependencies = [
"anyhow",
"arc-swap",
@@ -6252,7 +6252,7 @@ dependencies = [
[[package]]
name = "nym-node-status-api"
version = "2.1.0"
version = "2.3.0"
dependencies = [
"ammonia",
"anyhow",
@@ -6269,6 +6269,8 @@ dependencies = [
"nym-contracts-common",
"nym-crypto",
"nym-http-api-client",
"nym-http-api-common",
"nym-mixnet-contract-common",
"nym-network-defaults",
"nym-node-metrics",
"nym-node-requests",
@@ -6513,7 +6515,7 @@ dependencies = [
[[package]]
name = "nym-socks5-client"
version = "1.1.54"
version = "1.1.53"
dependencies = [
"bs58",
"clap",
@@ -7117,7 +7119,7 @@ dependencies = [
[[package]]
name = "nymvisor"
version = "0.1.19"
version = "0.1.18"
dependencies = [
"anyhow",
"bytes",
+1 -1
View File
@@ -211,7 +211,7 @@ chacha20 = "0.9.0"
chacha20poly1305 = "0.10.1"
chrono = "0.4.40"
cipher = "0.4.3"
clap = "4.5.36"
clap = "4.5.37"
clap_complete = "4.5"
clap_complete_fig = "4.5"
colored = "2.2"
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-client"
version = "1.1.54"
version = "1.1.53"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
description = "Implementation of the Nym Client"
edition = "2021"
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-socks5-client"
version = "1.1.54"
version = "1.1.53"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
edition = "2021"
@@ -9,13 +9,35 @@ use axum::response::IntoResponse;
use axum_client_ip::InsecureClientIp;
use colored::Colorize;
use std::time::Instant;
use tracing::info;
use tracing::{debug, info};
enum LogLevel {
Debug,
Info,
}
pub async fn log_request_info(
insecure_client_ip: InsecureClientIp,
request: Request,
next: Next,
) -> impl IntoResponse {
log_request(insecure_client_ip, request, next, LogLevel::Info).await
}
pub async fn log_request_debug(
insecure_client_ip: InsecureClientIp,
request: Request,
next: Next,
) -> impl IntoResponse {
log_request(insecure_client_ip, request, next, LogLevel::Debug).await
}
/// Simple logger for requests
pub async fn logger(
async fn log_request(
InsecureClientIp(addr): InsecureClientIp,
request: Request,
next: Next,
level: LogLevel,
) -> impl IntoResponse {
// TODO dz use `OriginalUri` extractor to get full URI even for nested
// routers if routes aren't logged correctly in handlers
@@ -58,7 +80,14 @@ pub async fn logger(
let agent_str = "agent".bold();
info!("[{addr} -> {host}] {method} '{uri}': {print_status} {time_taken} {agent_str}: {agent}");
match level {
LogLevel::Debug => debug!(
"[{addr} -> {host}] {method} '{uri}': {print_status} {time_taken} {agent_str}: {agent}"
),
LogLevel::Info => info!(
"[{addr} -> {host}] {method} '{uri}': {print_status} {time_taken} {agent_str}: {agent}"
),
}
res
}
+1 -1
View File
@@ -34,7 +34,7 @@ const config: DocsThemeConfig = {
bookDescriptions[topLevel] ||
defaultDescription;
const title = config.title + (route === "/" ? "" : " - Nym docs");
const title = (route === "/" ? "Nym docs" : config.title + " - Nym docs");
return (
<>
+1 -1
View File
@@ -4,7 +4,7 @@
[package]
name = "nym-api"
license = "GPL-3.0"
version = "1.1.56"
version = "1.1.55"
authors.workspace = true
edition = "2021"
rust-version.workspace = true
@@ -6,7 +6,9 @@ use crate::epoch_operations::helpers::stake_to_f64;
use crate::EpochAdvancer;
use cosmwasm_std::Decimal;
use nym_mixnet_contract_common::reward_params::{Performance, RewardedSetParams};
use nym_mixnet_contract_common::{EpochState, NodeId, NymNodeDetails, RewardedSet};
use nym_mixnet_contract_common::{
EpochState, NodeId, NymNodeDetails, RewardedSet, RewardingParams,
};
use rand::prelude::SliceRandom;
use rand::rngs::OsRng;
use std::collections::HashSet;
@@ -25,14 +27,14 @@ enum AvailableRole {
}
#[derive(Debug, Clone)]
struct NodeWithStakeAndPerformance {
struct NodeWithSaturationAndPerformance {
node_id: NodeId,
available_roles: Vec<AvailableRole>,
total_stake: Decimal,
saturation: Decimal,
performance: Performance,
}
impl NodeWithStakeAndPerformance {
impl NodeWithSaturationAndPerformance {
fn to_selection_weight(&self) -> f64 {
let scaled_performance = match self.performance.checked_pow(20) {
Ok(perf) => perf,
@@ -42,7 +44,7 @@ impl NodeWithStakeAndPerformance {
}
};
let scaled_stake = self.total_stake * scaled_performance;
let scaled_stake = self.saturation * scaled_performance;
stake_to_f64(scaled_stake)
}
@@ -62,7 +64,7 @@ impl NodeWithStakeAndPerformance {
impl EpochAdvancer {
fn determine_rewarded_set(
&self,
nodes: Vec<NodeWithStakeAndPerformance>,
nodes: Vec<NodeWithSaturationAndPerformance>,
spec: RewardedSetParams,
) -> Result<RewardedSet, RewardingError> {
if nodes.is_empty() {
@@ -204,7 +206,8 @@ impl EpochAdvancer {
async fn attach_performance_to_eligible_nodes(
&self,
nym_nodes: &[NymNodeDetails],
) -> Vec<NodeWithStakeAndPerformance> {
reward_params: &RewardingParams,
) -> Vec<NodeWithSaturationAndPerformance> {
let mut with_performance = Vec::new();
// SAFETY: the cache MUST HAVE been initialised before now
@@ -218,7 +221,7 @@ impl EpochAdvancer {
for nym_node in nym_nodes {
let node_id = nym_node.node_id();
let total_stake = nym_node.total_stake();
let saturation = nym_node.rewarding_details.bond_saturation(reward_params);
let Some(self_described) = described_cache.get_description(&node_id) else {
continue;
@@ -230,7 +233,7 @@ impl EpochAdvancer {
};
let performance = annotation.detailed_performance.to_rewarding_performance();
debug!("nym-node {node_id}: stake: {total_stake}, performance: {performance}");
debug!("nym-node {node_id}: saturation: {saturation}, performance: {performance}");
let mut available_roles = Vec::new();
if self_described.declared_role.mixnode {
@@ -248,10 +251,10 @@ impl EpochAdvancer {
continue;
}
with_performance.push(NodeWithStakeAndPerformance {
with_performance.push(NodeWithSaturationAndPerformance {
node_id: nym_node.node_id(),
available_roles,
total_stake,
saturation,
performance,
})
}
@@ -273,13 +276,8 @@ impl EpochAdvancer {
}
info!("attempting to assign the rewarded set for the upcoming epoch...");
let nodes_with_performance =
self.attach_performance_to_eligible_nodes(nym_nodes).await;
if let Err(err) = self
._update_rewarded_set_and_advance_epoch(nodes_with_performance)
.await
{
if let Err(err) = self._update_rewarded_set_and_advance_epoch(nym_nodes).await {
error!("FAILED to assign the rewarded set... - {err}");
Err(err)
} else {
@@ -300,15 +298,19 @@ impl EpochAdvancer {
async fn _update_rewarded_set_and_advance_epoch(
&self,
all_nodes: Vec<NodeWithStakeAndPerformance>,
nym_nodes: &[NymNodeDetails],
) -> Result<(), RewardingError> {
// we grab rewarding parameters here as they might have gotten updated when performing epoch actions
let rewarding_parameters = self.nyxd_client.get_current_rewarding_parameters().await?;
debug!("Rewarding parameters: {rewarding_parameters:?}");
let nodes_with_performance = self
.attach_performance_to_eligible_nodes(nym_nodes, &rewarding_parameters)
.await;
let new_rewarded_set =
self.determine_rewarded_set(all_nodes, rewarding_parameters.rewarded_set)?;
self.determine_rewarded_set(nodes_with_performance, rewarding_parameters.rewarded_set)?;
debug!("New rewarded set: {:?}", new_rewarded_set);
+2 -2
View File
@@ -17,7 +17,7 @@ use axum::response::Redirect;
use axum::routing::get;
use axum::Router;
use core::net::SocketAddr;
use nym_http_api_common::middleware::logging::logger;
use nym_http_api_common::middleware::logging::log_request_info;
use tokio::net::TcpListener;
use tokio_util::sync::WaitForCancellationFutureOwned;
use tower_http::cors::CorsLayer;
@@ -91,7 +91,7 @@ impl RouterBuilder {
fn finalize_routes(self) -> Router<AppState> {
self.unfinished_router
.layer(setup_cors())
.layer(axum::middleware::from_fn(logger))
.layer(axum::middleware::from_fn(log_request_info))
}
}
@@ -31,7 +31,7 @@ pub fn build_router(state: ApiState, auth_token: String) -> Router {
.nest(routes::API, api::routes(auth_middleware))
// we don't have to be using middleware, but we already had that code
// we might want something like: https://github.com/tokio-rs/axum/blob/main/examples/tracing-aka-logging/src/main.rs#L44 instead
.layer(axum::middleware::from_fn(logging::logger))
.layer(axum::middleware::from_fn(logging::log_request_info))
.with_state(state);
cfg_if::cfg_if! {
@@ -20,7 +20,7 @@ nym-bin-common = { path = "../../common/bin-common", features = ["models"]}
nym-node-status-client = { path = "../nym-node-status-client" }
nym-crypto = { path = "../../common/crypto", features = ["asymmetric", "rand"] }
rand = { workspace = true }
tokio = { workspace = true, features = ["macros", "rt-multi-thread", "process"] }
tokio = { workspace = true, features = ["macros", "rt-multi-thread", "process", "fs"] }
tracing = { workspace = true }
tracing-subscriber = { workspace = true, features = ["env-filter"] }
@@ -3,7 +3,7 @@
[package]
name = "nym-node-status-api"
version = "2.1.0"
version = "2.3.0"
authors.workspace = true
repository.workspace = true
homepage.workspace = true
@@ -25,10 +25,12 @@ futures-util = { workspace = true }
itertools = { workspace = true }
moka = { workspace = true, features = ["future"] }
nym-contracts-common = { path = "../../common/cosmwasm-smart-contracts/contracts-common" }
nym-mixnet-contract-common = { path = "../../common/cosmwasm-smart-contracts/mixnet-contract", features = ["utoipa"] }
nym-bin-common = { path = "../../common/bin-common", features = ["models"] }
nym-node-status-client = { path = "../nym-node-status-client" }
nym-crypto = { path = "../../common/crypto", features = ["asymmetric", "serde"] }
nym-http-api-client = { path = "../../common/http-api-client" }
nym-http-api-common = { path = "../../common/http-api-common" }
nym-network-defaults = { path = "../../common/network-defaults" }
nym-serde-helpers = { path = "../../common/serde-helpers" }
nym-statistics-common = { path = "../../common/statistics" }
@@ -6,6 +6,7 @@ use crate::{
DbPool,
},
http::models::Gateway,
mixnet_scraper::helpers::NodeDescriptionResponse,
};
use futures_util::TryStreamExt;
use sqlx::{pool::PoolConnection, Sqlite};
@@ -131,3 +132,39 @@ pub(crate) async fn get_bonded_gateway_id_keys(pool: &DbPool) -> anyhow::Result<
Ok(items)
}
pub(crate) async fn insert_gateway_description(
conn: &mut PoolConnection<Sqlite>,
identity_key: &str,
description: &NodeDescriptionResponse,
timestamp: i64,
) -> anyhow::Result<()> {
sqlx::query!(
r#"
INSERT INTO gateway_description (
gateway_identity_key,
moniker,
website,
security_contact,
details,
last_updated_utc
) VALUES (?, ?, ?, ?, ?, ?)
ON CONFLICT (gateway_identity_key) DO UPDATE SET
moniker = excluded.moniker,
website = excluded.website,
security_contact = excluded.security_contact,
details = excluded.details,
last_updated_utc = excluded.last_updated_utc
"#,
identity_key,
description.moniker,
description.website,
description.security_contact,
description.details,
timestamp,
)
.execute(conn.as_mut())
.await
.map(drop)
.map_err(From::from)
}
@@ -1,6 +1,7 @@
use std::collections::HashSet;
use futures_util::TryStreamExt;
use sqlx::{pool::PoolConnection, Sqlite};
use tracing::error;
use crate::{
@@ -9,6 +10,7 @@ use crate::{
DbPool,
},
http::models::{DailyStats, Mixnode},
mixnet_scraper::helpers::NodeDescriptionResponse,
};
pub(crate) async fn update_mixnodes(
@@ -157,3 +159,34 @@ pub(crate) async fn get_bonded_mix_ids(pool: &DbPool) -> anyhow::Result<HashSet<
Ok(items)
}
pub(crate) async fn insert_mixnode_description(
conn: &mut PoolConnection<Sqlite>,
mix_id: &i64,
description: &NodeDescriptionResponse,
timestamp: i64,
) -> anyhow::Result<()> {
sqlx::query!(
r#"
INSERT INTO mixnode_description (
mix_id, moniker, website, security_contact, details, last_updated_utc
) VALUES (?, ?, ?, ?, ?, ?)
ON CONFLICT (mix_id) DO UPDATE SET
moniker = excluded.moniker,
website = excluded.website,
security_contact = excluded.security_contact,
details = excluded.details,
last_updated_utc = excluded.last_updated_utc
"#,
mix_id,
description.moniker,
description.website,
description.security_contact,
description.details,
timestamp,
)
.execute(conn.as_mut())
.await
.map(drop)
.map_err(From::from)
}
@@ -4,12 +4,16 @@ use nym_validator_client::{
client::{NodeId, NymNodeDetails},
models::NymNodeDescription,
};
use sqlx::{pool::PoolConnection, Sqlite};
use std::collections::HashMap;
use tracing::instrument;
use crate::db::{
models::{NymNodeDto, NymNodeInsertRecord},
DbPool,
use crate::{
db::{
models::{NymNodeDto, NymNodeInsertRecord},
DbPool,
},
mixnet_scraper::helpers::NodeDescriptionResponse,
};
pub(crate) async fn get_all_nym_nodes(pool: &DbPool) -> anyhow::Result<Vec<NymNodeDto>> {
@@ -194,6 +198,8 @@ pub(crate) async fn get_node_self_description(
nym_nodes
WHERE
self_described IS NOT NULL
ORDER BY
node_id
"#,
)
.fetch_all(&mut *conn)
@@ -256,3 +262,34 @@ pub(crate) async fn get_bonded_node_description(
})
.map_err(From::from)
}
pub(crate) async fn insert_nym_node_description(
conn: &mut PoolConnection<Sqlite>,
node_id: &i64,
description: &NodeDescriptionResponse,
timestamp: i64,
) -> anyhow::Result<()> {
sqlx::query!(
r#"
INSERT INTO nym_node_descriptions (
node_id, moniker, website, security_contact, details, last_updated_utc
) VALUES (?, ?, ?, ?, ?, ?)
ON CONFLICT (node_id) DO UPDATE SET
moniker = excluded.moniker,
website = excluded.website,
security_contact = excluded.security_contact,
details = excluded.details,
last_updated_utc = excluded.last_updated_utc
"#,
node_id,
description.moniker,
description.website,
description.security_contact,
description.details,
timestamp,
)
.execute(conn.as_mut())
.await
.map(drop)
.map_err(From::from)
}
@@ -1,7 +1,11 @@
use crate::{
db::{
models::{ScrapeNodeKind, ScraperNodeInfo},
queries, DbPool,
queries::{
self, gateways::insert_gateway_description, mixnodes::insert_mixnode_description,
nym_nodes::insert_nym_node_description,
},
DbPool,
},
mixnet_scraper::helpers::NodeDescriptionResponse,
};
@@ -125,78 +129,18 @@ pub(crate) async fn insert_scraped_node_description(
match node_kind {
ScrapeNodeKind::LegacyMixnode { mix_id } => {
sqlx::query!(
r#"
INSERT INTO mixnode_description (
mix_id, moniker, website, security_contact, details, last_updated_utc
) VALUES (?, ?, ?, ?, ?, ?)
ON CONFLICT (mix_id) DO UPDATE SET
moniker = excluded.moniker,
website = excluded.website,
security_contact = excluded.security_contact,
details = excluded.details,
last_updated_utc = excluded.last_updated_utc
"#,
mix_id,
description.moniker,
description.website,
description.security_contact,
description.details,
timestamp,
)
.execute(&mut *conn)
.await?;
insert_mixnode_description(&mut conn, mix_id, description, timestamp).await?;
}
ScrapeNodeKind::MixingNymNode { node_id } => {
sqlx::query!(
r#"
INSERT INTO nym_node_descriptions (
node_id, moniker, website, security_contact, details, last_updated_utc
) VALUES (?, ?, ?, ?, ?, ?)
ON CONFLICT (node_id) DO UPDATE SET
moniker = excluded.moniker,
website = excluded.website,
security_contact = excluded.security_contact,
details = excluded.details,
last_updated_utc = excluded.last_updated_utc
"#,
node_id,
description.moniker,
description.website,
description.security_contact,
description.details,
timestamp,
)
.execute(&mut *conn)
.await?;
insert_nym_node_description(&mut conn, node_id, description, timestamp).await?;
}
ScrapeNodeKind::EntryExitNymNode { identity_key, .. } => {
sqlx::query!(
r#"
INSERT INTO gateway_description (
gateway_identity_key,
moniker,
website,
security_contact,
details,
last_updated_utc
) VALUES (?, ?, ?, ?, ?, ?)
ON CONFLICT (gateway_identity_key) DO UPDATE SET
moniker = excluded.moniker,
website = excluded.website,
security_contact = excluded.security_contact,
details = excluded.details,
last_updated_utc = excluded.last_updated_utc
"#,
identity_key,
description.moniker,
description.website,
description.security_contact,
description.details,
timestamp,
)
.execute(&mut *conn)
.await?;
ScrapeNodeKind::EntryExitNymNode {
node_id,
identity_key,
} => {
insert_nym_node_description(&mut conn, node_id, description, timestamp).await?;
// for historic reasons (/gateways API), store this info into gateways table as well
insert_gateway_description(&mut conn, identity_key, description, timestamp).await?;
}
}
@@ -77,7 +77,7 @@ async fn get_all_sessions(
};
Ok(Json(PagedResult::paginate(
Pagination { size, page },
Pagination::new(size, page),
day_and_node_filtered,
)))
}
@@ -1,7 +1,8 @@
use anyhow::anyhow;
use axum::{response::Redirect, Router};
use nym_http_api_common::middleware::logging::log_request_debug;
use tokio::net::ToSocketAddrs;
use tower_http::{cors::CorsLayer, trace::TraceLayer};
use tower_http::cors::CorsLayer;
use utoipa::OpenApi;
use utoipa_swagger_ui::SwaggerUi;
@@ -39,7 +40,10 @@ impl RouterBuilder {
.nest("/summary", summary::routes())
.nest("/metrics", metrics::routes()),
)
.nest("/v3", Router::new().nest("/nym-nodes", nym_nodes::routes()))
.nest(
"/explorer/v3",
Router::new().nest("/nym-nodes", nym_nodes::routes()),
)
.nest(
"/internal",
Router::new().nest("/testruns", testruns::routes()),
@@ -62,7 +66,7 @@ impl RouterBuilder {
// CORS layer needs to wrap other API layers
.layer(setup_cors())
// logger should be outermost layer
.layer(TraceLayer::new_for_http())
.layer(axum::middleware::from_fn(log_request_debug))
}
}
@@ -1,32 +1,41 @@
use axum::{
extract::{Query, State},
extract::{Path, Query, State},
Json, Router,
};
use nym_validator_client::client::NodeId;
use serde::Deserialize;
use tracing::instrument;
use utoipa::IntoParams;
use crate::http::{
error::{HttpError, HttpResult},
models::ExtendedNymNode,
models::{ExtendedNymNode, NodeDelegation},
state::AppState,
PagedResult, Pagination,
};
pub(crate) fn routes() -> Router<AppState> {
Router::new().route("/", axum::routing::get(nym_nodes))
Router::new()
.route("/", axum::routing::get(nym_nodes))
.route(
"/:node_id/delegations",
axum::routing::get(node_delegations),
)
}
#[utoipa::path(
tag = "Nym Nodes",
tag = "Nym Explorer",
get,
params(
Pagination
),
path = "/v3/nym-nodes",
path = "/nym-nodes",
context_path = "/explorer/v3",
responses(
(status = 200, body = PagedResult<ExtendedNymNode>)
)
)]
#[instrument(level = tracing::Level::DEBUG, skip_all, fields(page=pagination.page, size=pagination.size))]
#[instrument(level = tracing::Level::INFO, skip_all, fields(page=pagination.page, size=pagination.size))]
async fn nym_nodes(
Query(pagination): Query<Pagination>,
State(state): State<AppState>,
@@ -45,3 +54,35 @@ async fn nym_nodes(
Ok(Json(PagedResult::paginate(pagination, nodes)))
}
#[allow(dead_code)] // clippy doesn't detect usage in utoipa macros
#[derive(Deserialize, IntoParams)]
#[into_params(parameter_in = Path)]
struct NodeIdParam {
#[param(minimum = 0)]
node_id: NodeId,
}
#[utoipa::path(
tag = "Nym Explorer",
get,
params(
NodeIdParam
),
path = "/{node_id}/delegations",
context_path = "/explorer/v3/nym-nodes",
responses(
(status = 200, body = NodeDelegation)
)
)]
#[instrument(level = tracing::Level::INFO, skip(state))]
async fn node_delegations(
Path(node_id): Path<NodeId>,
State(state): State<AppState>,
) -> HttpResult<Json<Vec<NodeDelegation>>> {
state
.node_delegations(node_id)
.await
.ok_or_else(|| HttpError::no_delegations_for_node(node_id))
.map(Json)
}
@@ -104,7 +104,10 @@ async fn mixnodes(
.map(|(s, _)| s)
.collect();
Ok(Json(PagedResult::paginate(Pagination { size, page }, res)))
Ok(Json(PagedResult::paginate(
Pagination::new(size, page),
res,
)))
}
struct ServiceFilter {
@@ -1,3 +1,4 @@
use nym_mixnet_contract_common::NodeId;
use std::fmt::Display;
pub(crate) type HttpResult<T> = Result<T, HttpError>;
@@ -40,6 +41,14 @@ impl HttpError {
status: axum::http::StatusCode::SERVICE_UNAVAILABLE,
}
}
pub(crate) fn no_delegations_for_node(node_id: NodeId) -> Self {
Self {
message: serde_json::json!({"message": format!("No delegation data for node_id={}", node_id)})
.to_string(),
status: axum::http::StatusCode::NOT_FOUND,
}
}
}
impl axum::response::IntoResponse for HttpError {
@@ -18,7 +18,7 @@ pub struct PagedResult<T: ToSchema> {
impl<T: Clone + ToSchema> PagedResult<T> {
pub fn paginate(pagination: Pagination, res: Vec<T>) -> Self {
let total = res.len();
let (size, mut page) = pagination.intoto_inner_values();
let (size, mut page) = pagination.into_inner_values();
if page * size > total {
page = total / size;
@@ -42,14 +42,25 @@ pub(crate) struct Pagination {
page: Option<usize>,
}
const SIZE_DEFAULT: usize = 10;
const SIZE_MAX: usize = 200;
const PAGE_DEFAULT: usize = 0;
impl Default for Pagination {
fn default() -> Self {
Self {
size: Some(SIZE_DEFAULT),
page: Some(PAGE_DEFAULT),
}
}
}
impl Pagination {
// unwrap stored values or use predefined defaults
pub(crate) fn intoto_inner_values(self) -> (usize, usize) {
const SIZE_DEFAULT: usize = 10;
const SIZE_MAX: usize = 200;
const PAGE_DEFAULT: usize = 0;
pub(crate) fn new(size: Option<usize>, page: Option<usize>) -> Self {
Self { size, page }
}
pub(crate) fn into_inner_values(self) -> (usize, usize) {
(
self.size.unwrap_or(SIZE_DEFAULT).min(SIZE_MAX),
self.page.unwrap_or(PAGE_DEFAULT),
@@ -1,4 +1,5 @@
use cosmwasm_std::Decimal;
use cosmwasm_std::{Addr, Coin, Decimal};
use nym_mixnet_contract_common::CoinSchema;
use nym_node_requests::api::v1::node::models::NodeDescription;
use nym_validator_client::client::NodeId;
use serde::{Deserialize, Serialize};
@@ -55,13 +56,13 @@ pub(crate) struct ExtendedNymNode {
#[schema(value_type = String)]
pub(crate) total_stake: Decimal,
pub(crate) original_pledge: u128,
pub(crate) bonding_address: String,
pub(crate) bonding_address: Option<String>,
pub(crate) bonded: bool,
pub(crate) node_type: String,
pub(crate) node_type: nym_validator_client::models::DescribedNodeType,
pub(crate) ip_address: String,
pub(crate) accepted_tnc: bool,
pub(crate) self_description: serde_json::Value,
pub(crate) rewarding_details: serde_json::Value,
pub(crate) self_description: nym_validator_client::models::NymNodeData,
pub(crate) rewarding_details: Option<nym_mixnet_contract_common::NodeRewarding>,
pub(crate) description: NodeDescription,
pub(crate) geoip: Option<NodeGeoData>,
}
@@ -120,3 +121,27 @@ pub struct SessionStats {
pub mixnet_sessions: Option<serde_json::Value>,
pub unknown_sessions: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Deserialize, Serialize, ToSchema)]
pub struct NodeDelegation {
#[schema(value_type = CoinSchema)]
pub amount: Coin,
pub cumulative_reward_ratio: String,
pub block_height: u64,
#[schema(value_type = String)]
pub owner: Addr,
#[schema(value_type = Option<String>)]
pub proxy: Option<Addr>,
}
impl From<nym_mixnet_contract_common::Delegation> for NodeDelegation {
fn from(value: nym_mixnet_contract_common::Delegation) -> Self {
Self {
amount: value.amount,
cumulative_reward_ratio: value.cumulative_reward_ratio.to_string(),
block_height: value.height,
owner: value.owner,
proxy: value.proxy,
}
}
}
@@ -1,13 +1,14 @@
use axum::Router;
use core::net::SocketAddr;
use nym_crypto::asymmetric::ed25519::PublicKey;
use tokio::{net::TcpListener, task::JoinHandle};
use std::sync::Arc;
use tokio::{net::TcpListener, sync::RwLock, task::JoinHandle};
use tokio_util::sync::{CancellationToken, WaitForCancellationFutureOwned};
use crate::{
db::DbPool,
http::{api::RouterBuilder, state::AppState},
monitor::NodeGeoCache,
monitor::{DelegationsCache, NodeGeoCache},
};
/// Return handles that allow for graceful shutdown of server + awaiting its
@@ -19,6 +20,7 @@ pub(crate) async fn start_http_api(
agent_key_list: Vec<PublicKey>,
agent_max_count: i64,
node_geocache: NodeGeoCache,
node_delegations: Arc<RwLock<DelegationsCache>>,
) -> anyhow::Result<ShutdownHandles> {
let router_builder = RouterBuilder::with_default_routes();
@@ -28,6 +30,7 @@ pub(crate) async fn start_http_api(
agent_key_list,
agent_max_count,
node_geocache,
node_delegations,
)
.await;
let router = router_builder.with_state(state);
@@ -1,17 +1,17 @@
use std::{collections::HashMap, sync::Arc, time::Duration};
use cosmwasm_std::Decimal;
use moka::{future::Cache, Entry};
use nym_contracts_common::NaiveFloat;
use nym_crypto::asymmetric::ed25519::PublicKey;
use nym_validator_client::{models::DescribedNodeType, nym_api::SkimmedNode};
use nym_mixnet_contract_common::NodeId;
use nym_validator_client::nym_api::SkimmedNode;
use std::{collections::HashMap, sync::Arc, time::Duration};
use tokio::sync::RwLock;
use tracing::instrument;
use crate::{
db::{queries, DbPool},
http::models::{DailyStats, ExtendedNymNode, Gateway, Mixnode, NodeGeoData, SummaryHistory},
monitor::NodeGeoCache,
monitor::{DelegationsCache, NodeGeoCache},
};
use super::models::SessionStats;
@@ -23,6 +23,7 @@ pub(crate) struct AppState {
agent_key_list: Vec<PublicKey>,
agent_max_count: i64,
node_geocache: NodeGeoCache,
node_delegations: Arc<RwLock<DelegationsCache>>,
}
impl AppState {
@@ -32,6 +33,7 @@ impl AppState {
agent_key_list: Vec<PublicKey>,
agent_max_count: i64,
node_geocache: NodeGeoCache,
node_delegations: Arc<RwLock<DelegationsCache>>,
) -> Self {
Self {
db_pool,
@@ -39,6 +41,7 @@ impl AppState {
agent_key_list,
agent_max_count,
node_geocache,
node_delegations,
}
}
@@ -61,6 +64,16 @@ impl AppState {
pub(crate) fn node_geocache(&self) -> NodeGeoCache {
self.node_geocache.clone()
}
pub(crate) async fn node_delegations(
&self,
node_id: NodeId,
) -> Option<Vec<super::models::NodeDelegation>> {
self.node_delegations
.read()
.await
.delegations_owned(node_id)
}
}
static GATEWAYS_LIST_KEY: &str = "gateways";
@@ -401,11 +414,7 @@ async fn aggregate_node_info_from_db(
.get(&node_id)
.map(|node| node.performance.naive_to_f64())
.unwrap_or(0.0);
let node_type = match described_node.contract_node_type {
DescribedNodeType::NymNode => "nym_node".to_string(),
DescribedNodeType::LegacyMixnode => "legacy_mixnode".to_string(),
DescribedNodeType::LegacyGateway => "legacy_gateway".to_string(),
};
let node_type = described_node.contract_node_type;
let ip_address = described_node
.description
.host_information
@@ -417,11 +426,10 @@ async fn aggregate_node_info_from_db(
.description
.auxiliary_details
.accepted_operator_terms_and_conditions;
let description = described_node.description;
let self_described = described_node.description;
let bonding_address = bond_details
.map(|details| details.bond_information.owner.to_string())
.unwrap_or_default();
let bonding_address =
bond_details.map(|details| details.bond_information.owner.to_string());
let node_description = node_descriptions.get(&node_id).cloned().unwrap_or_default();
let geoip = {
@@ -449,8 +457,8 @@ async fn aggregate_node_info_from_db(
bonded,
node_type,
accepted_tnc,
self_description: serde_json::to_value(description).unwrap_or_default(),
rewarding_details: serde_json::to_value(rewarding_details).unwrap_or_default(),
self_description: self_described,
rewarding_details: rewarding_details.to_owned(),
description: node_description,
geoip,
});
@@ -1,6 +1,9 @@
use crate::monitor::DelegationsCache;
use clap::Parser;
use nym_crypto::asymmetric::ed25519::PublicKey;
use nym_task::signal::wait_for_signal;
use nym_validator_client::nyxd::NyxdClient;
use std::sync::Arc;
mod cli;
mod db;
@@ -41,19 +44,27 @@ async fn main() -> anyhow::Result<()> {
let geocache = moka::future::Cache::builder()
.time_to_live(args.geodata_ttl)
.build();
let delegations_cache = DelegationsCache::new();
// Start the monitor
let args_clone = args.clone();
let geocache_clone = geocache.clone();
let delegations_cache_clone = Arc::clone(&delegations_cache);
let config = nym_validator_client::nyxd::Config::try_from_nym_network_details(
&nym_network_defaults::NymNetworkDetails::new_from_env(),
)?;
let nyxd_client = NyxdClient::connect(config, args.nyxd_addr.as_str())
.map_err(|err| anyhow::anyhow!("Couldn't connect: {}", err))?;
tokio::spawn(async move {
monitor::spawn_in_background(
db_pool,
args_clone.nym_api_client_timeout,
args_clone.nyxd_addr,
nyxd_client,
args_clone.monitor_refresh_interval,
args_clone.ipinfo_api_token,
geocache_clone,
delegations_cache_clone,
)
.await;
tracing::info!("Started monitor task");
@@ -74,6 +85,7 @@ async fn main() -> anyhow::Result<()> {
agent_key_list.to_owned(),
args.max_agent_count,
geocache,
delegations_cache,
)
.await
.expect("Failed to start server");
@@ -12,25 +12,30 @@ use crate::utils::{decimal_to_i64, LogError, NumericalCheckedCast};
use anyhow::anyhow;
use moka::future::Cache;
use nym_network_defaults::NymNetworkDetails;
use nym_validator_client::client::{NodeId, NymApiClientExt, NymNodeDetails};
use nym_validator_client::models::{
LegacyDescribedMixNode, MixNodeBondAnnotated, NymNodeDescription,
use nym_validator_client::{
client::{NodeId, NymApiClientExt, NymNodeDetails},
models::{LegacyDescribedMixNode, MixNodeBondAnnotated, NymNodeDescription},
};
use nym_validator_client::nym_nodes::{NodeRole, SkimmedNode};
use nym_validator_client::nyxd::contract_traits::PagedMixnetQueryClient;
use nym_validator_client::nyxd::{AccountId, NyxdClient};
use nym_validator_client::NymApiClient;
use reqwest::Url;
use std::collections::{HashMap, HashSet};
use std::str::FromStr;
use tokio::time::Duration;
use nym_validator_client::{
nym_nodes::{NodeRole, SkimmedNode},
nyxd::{contract_traits::PagedMixnetQueryClient, AccountId},
NymApiClient, QueryHttpRpcNyxdClient,
};
use std::{
collections::{HashMap, HashSet},
str::FromStr,
sync::Arc,
};
use tokio::{sync::RwLock, time::Duration};
use tracing::instrument;
pub(crate) use geodata::IpInfoClient;
pub(crate) use node_delegations::DelegationsCache;
mod geodata;
mod node_delegations;
const FAILURE_RETRY_DELAY: Duration = Duration::from_secs(60);
const MONITOR_FAILURE_RETRY_DELAY: Duration = Duration::from_secs(60);
static DELEGATION_PROGRAM_WALLET: &str = "n1rnxpdpx3kldygsklfft0gech7fhfcux4zst5lw";
pub(crate) type NodeGeoCache = Cache<NodeId, Location>;
@@ -38,9 +43,10 @@ struct Monitor {
db_pool: DbPool,
network_details: NymNetworkDetails,
nym_api_client_timeout: Duration,
nyxd_addr: Url,
nyxd_client: QueryHttpRpcNyxdClient,
ipinfo: IpInfoClient,
geocache: NodeGeoCache,
node_delegations: Arc<RwLock<DelegationsCache>>,
}
// TODO dz: query many NYM APIs:
@@ -49,19 +55,22 @@ struct Monitor {
pub(crate) async fn spawn_in_background(
db_pool: DbPool,
nym_api_client_timeout: Duration,
nyxd_addr: Url,
nyxd_client: nym_validator_client::QueryHttpRpcNyxdClient,
refresh_interval: Duration,
ipinfo_api_token: String,
geocache: NodeGeoCache,
node_delegations: Arc<RwLock<DelegationsCache>>,
) {
let ipinfo = IpInfoClient::new(ipinfo_api_token.clone());
let mut monitor = Monitor {
db_pool,
network_details: nym_network_defaults::NymNetworkDetails::new_from_env(),
nym_api_client_timeout,
nyxd_addr,
nyxd_client,
ipinfo,
geocache,
node_delegations,
};
loop {
@@ -70,10 +79,9 @@ pub(crate) async fn spawn_in_background(
if let Err(e) = monitor.run().await {
tracing::error!(
"Monitor run failed: {e}, retrying in {}s...",
FAILURE_RETRY_DELAY.as_secs()
MONITOR_FAILURE_RETRY_DELAY.as_secs()
);
// TODO dz implement some sort of backoff
tokio::time::sleep(FAILURE_RETRY_DELAY).await;
tokio::time::sleep(MONITOR_FAILURE_RETRY_DELAY).await;
} else {
tracing::info!(
"Info successfully collected, sleeping for {}s...",
@@ -151,7 +159,7 @@ impl Monitor {
})?;
// refresh geodata for all nodes
for (_, node_description) in described_nodes.iter() {
for node_description in described_nodes.values() {
self.location_cached(node_description).await;
}
@@ -193,8 +201,7 @@ impl Monitor {
.nodes
.data;
let delegation_program_members =
get_delegation_program_details(&self.network_details, &self.nyxd_addr).await?;
let delegation_program_members = self.get_delegation_program_details().await?;
// keep stats for later
let assigned_entry_count = nym_nodes
@@ -233,7 +240,9 @@ impl Monitor {
tracing::debug!("{} mixnode info written to DB!", mixnodes_count);
})?;
let (all_historical_gateways, all_historical_mixnodes) = calculate_stats(&pool).await?;
self.refresh_node_delegations(&bonded_nym_nodes).await;
let (all_historical_gateways, all_historical_mixnodes) = historical_count(&pool).await?;
//
// write summary keys and values to table
@@ -295,7 +304,7 @@ impl Monitor {
Ok(())
}
#[instrument(level = "debug", skip_all)]
#[instrument(level = "info", skip_all)]
async fn location_cached(&mut self, node: &NymNodeDescription) -> Location {
let node_id = node.node_id;
@@ -457,9 +466,34 @@ impl Monitor {
}
}
}
#[instrument(level = "info", skip_all)]
async fn refresh_node_delegations(&mut self, bonded_nodes: &HashMap<NodeId, NymNodeDetails>) {
let delegations_per_node = node_delegations::refresh(&self.nyxd_client, bonded_nodes).await;
// update after refreshing all to avoid holding write lock for too long
*self.node_delegations.write().await = delegations_per_node;
}
async fn get_delegation_program_details(&self) -> anyhow::Result<Vec<NodeId>> {
let account_id = AccountId::from_str(DELEGATION_PROGRAM_WALLET)
.map_err(|e| anyhow!("Invalid bech32 address: {}", e))?;
let delegations = self
.nyxd_client
.get_all_delegator_delegations(&account_id)
.await?;
let mix_ids: Vec<NodeId> = delegations
.iter()
.map(|delegation| delegation.node_id)
.collect();
Ok(mix_ids)
}
}
async fn calculate_stats(pool: &DbPool) -> anyhow::Result<(usize, usize)> {
async fn historical_count(pool: &DbPool) -> anyhow::Result<(usize, usize)> {
let mut conn = pool.acquire().await?;
let all_historical_gateways = sqlx::query_scalar!(r#"SELECT count(id) FROM gateways"#)
@@ -474,25 +508,3 @@ async fn calculate_stats(pool: &DbPool) -> anyhow::Result<(usize, usize)> {
Ok((all_historical_gateways, all_historical_mixnodes))
}
async fn get_delegation_program_details(
network_details: &NymNetworkDetails,
nyxd_addr: &Url,
) -> anyhow::Result<Vec<u32>> {
let config = nym_validator_client::nyxd::Config::try_from_nym_network_details(network_details)?;
let client = NyxdClient::connect(config, nyxd_addr.as_str())
.map_err(|err| anyhow::anyhow!("Couldn't connect: {}", err))?;
let account_id = AccountId::from_str(DELEGATION_PROGRAM_WALLET)
.map_err(|e| anyhow!("Invalid bech32 address: {}", e))?;
let delegations = client.get_all_delegator_delegations(&account_id).await?;
let mix_ids: Vec<u32> = delegations
.iter()
.map(|delegation| delegation.node_id)
.collect();
Ok(mix_ids)
}
@@ -0,0 +1,53 @@
use nym_mixnet_contract_common::{NodeId, NymNodeDetails};
use nym_validator_client::{nyxd::contract_traits::PagedMixnetQueryClient, QueryHttpRpcNyxdClient};
use std::{collections::HashMap, sync::Arc};
use tokio::{sync::RwLock, time::Instant};
use tracing::{info, warn};
// abstracts away data structure that holds delegations
#[derive(Clone, Debug)]
pub(crate) struct DelegationsCache {
pub inner: HashMap<NodeId, Vec<crate::http::models::NodeDelegation>>,
}
impl DelegationsCache {
pub(crate) fn new() -> Arc<RwLock<Self>> {
let a = Self {
inner: HashMap::new(),
};
Arc::new(RwLock::new(a))
}
pub(crate) fn delegations_owned(
&self,
node_id: NodeId,
) -> Option<Vec<crate::http::models::NodeDelegation>> {
self.inner.get(&node_id).cloned()
}
}
pub(super) async fn refresh(
client: &QueryHttpRpcNyxdClient,
bonded_nodes: &HashMap<NodeId, NymNodeDetails>,
) -> DelegationsCache {
info!("👥 Refreshing {} node delegations...", bonded_nodes.len());
let now = Instant::now();
let mut delegations_per_node = HashMap::new();
for node_id in bonded_nodes.keys() {
if let Ok(delegations) = client
.get_all_single_mixnode_delegations(*node_id)
.await
.inspect_err(|err| warn!("Failed to get delegations for {}: {}", node_id, err))
{
delegations_per_node
.insert(*node_id, delegations.into_iter().map(From::from).collect());
}
}
let time_taken = Instant::now() - now;
info!("👥 Node delegations refreshed in {}s", time_taken.as_secs(),);
DelegationsCache {
inner: delegations_per_node,
}
}
+1 -1
View File
@@ -3,7 +3,7 @@
[package]
name = "nym-node"
version = "1.10.0"
version = "1.9.0"
authors.workspace = true
repository.workspace = true
homepage.workspace = true
+1 -1
View File
@@ -159,7 +159,7 @@ impl NymNodeRouter {
)
.nest(routes::LANDING_PAGE, landing_page::routes(config.landing))
.nest(routes::API, api::routes(config.api))
.layer(axum::middleware::from_fn(logging::logger))
.layer(axum::middleware::from_fn(logging::log_request_info))
.with_state(state),
}
}
+259 -45
View File
@@ -1326,6 +1326,12 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "critical-section"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
[[package]]
name = "crossbeam-channel"
version = "0.5.15"
@@ -2458,6 +2464,19 @@ dependencies = [
"x11",
]
[[package]]
name = "generator"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc6bd114ceda131d3b1d665eba35788690ad37f5916457286b32ab6fd3c438dd"
dependencies = [
"cfg-if",
"libc",
"log",
"rustversion",
"windows 0.58.0",
]
[[package]]
name = "generic-array"
version = "0.14.7"
@@ -2849,57 +2868,59 @@ checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0"
[[package]]
name = "hickory-proto"
version = "0.24.4"
version = "0.25.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92652067c9ce6f66ce53cc38d1169daa36e6e7eb7dd3b63b5103bd9d97117248"
checksum = "6d844af74f7b799e41c78221be863bade11c430d46042c3b49ca8ae0c6d27287"
dependencies = [
"async-recursion",
"async-trait",
"bytes",
"cfg-if",
"critical-section",
"data-encoding",
"enum-as-inner",
"futures-channel",
"futures-io",
"futures-util",
"h2 0.3.26",
"http 0.2.12",
"h2 0.4.8",
"http 1.3.1",
"idna",
"ipnet",
"once_cell",
"rand 0.8.5",
"rustls 0.21.12",
"rustls-pemfile 1.0.4",
"thiserror 1.0.69",
"rand 0.9.0",
"ring",
"rustls 0.23.25",
"thiserror 2.0.12",
"tinyvec",
"tokio",
"tokio-rustls 0.24.1",
"tokio-rustls 0.26.2",
"tracing",
"url",
"webpki-roots 0.25.4",
"webpki-roots",
]
[[package]]
name = "hickory-resolver"
version = "0.24.4"
version = "0.25.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cbb117a1ca520e111743ab2f6688eddee69db4e0ea242545a604dce8a66fd22e"
checksum = "a128410b38d6f931fcc6ca5c107a3b02cabd6c05967841269a4ad65d23c44331"
dependencies = [
"cfg-if",
"futures-util",
"hickory-proto",
"ipconfig",
"lru-cache",
"moka",
"once_cell",
"parking_lot",
"rand 0.8.5",
"rand 0.9.0",
"resolv-conf",
"rustls 0.21.12",
"rustls 0.23.25",
"smallvec",
"thiserror 1.0.69",
"thiserror 2.0.12",
"tokio",
"tokio-rustls 0.24.1",
"tokio-rustls 0.26.2",
"tracing",
"webpki-roots 0.25.4",
"webpki-roots",
]
[[package]]
@@ -3102,7 +3123,7 @@ dependencies = [
"tokio",
"tokio-rustls 0.26.2",
"tower-service",
"webpki-roots 0.26.8",
"webpki-roots",
]
[[package]]
@@ -3641,12 +3662,6 @@ dependencies = [
"redox_syscall",
]
[[package]]
name = "linked-hash-map"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
[[package]]
name = "linux-raw-sys"
version = "0.4.15"
@@ -3685,12 +3700,16 @@ dependencies = [
]
[[package]]
name = "lru-cache"
version = "0.1.2"
name = "loom"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c"
checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca"
dependencies = [
"linked-hash-map",
"cfg-if",
"generator",
"scoped-tls",
"tracing",
"tracing-subscriber",
]
[[package]]
@@ -3713,6 +3732,15 @@ dependencies = [
"tendril",
]
[[package]]
name = "matchers"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
dependencies = [
"regex-automata 0.1.10",
]
[[package]]
name = "matches"
version = "0.1.10"
@@ -3773,6 +3801,25 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "moka"
version = "0.12.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9321642ca94a4282428e6ea4af8cc2ca4eac48ac7a6a4ea8f33f76d0ce70926"
dependencies = [
"crossbeam-channel",
"crossbeam-epoch",
"crossbeam-utils",
"loom",
"parking_lot",
"portable-atomic",
"rustc_version",
"smallvec",
"tagptr",
"thiserror 1.0.69",
"uuid",
]
[[package]]
name = "muda"
version = "0.16.1"
@@ -3876,6 +3923,16 @@ dependencies = [
"minimal-lexical",
]
[[package]]
name = "nu-ansi-term"
version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
dependencies = [
"overload",
"winapi",
]
[[package]]
name = "num-bigint"
version = "0.4.6"
@@ -4647,6 +4704,10 @@ name = "once_cell"
version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
dependencies = [
"critical-section",
"portable-atomic",
]
[[package]]
name = "opaque-debug"
@@ -4750,6 +4811,12 @@ dependencies = [
"thiserror 2.0.12",
]
[[package]]
name = "overload"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
[[package]]
name = "p256"
version = "0.13.2"
@@ -5193,6 +5260,12 @@ dependencies = [
"universal-hash",
]
[[package]]
name = "portable-atomic"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e"
[[package]]
name = "powerfmt"
version = "0.2.0"
@@ -5620,8 +5693,17 @@ checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
"regex-automata 0.4.9",
"regex-syntax 0.8.5",
]
[[package]]
name = "regex-automata"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
dependencies = [
"regex-syntax 0.6.29",
]
[[package]]
@@ -5632,9 +5714,15 @@ checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
"regex-syntax 0.8.5",
]
[[package]]
name = "regex-syntax"
version = "0.6.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
[[package]]
name = "regex-syntax"
version = "0.8.5"
@@ -5730,7 +5818,7 @@ dependencies = [
"wasm-bindgen-futures",
"wasm-streams",
"web-sys",
"webpki-roots 0.26.8",
"webpki-roots",
"windows-registry",
]
@@ -5872,6 +5960,7 @@ version = "0.23.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "822ee9188ac4ec04a2f0531e55d035fb2de73f18b41a63c70c2712503b6fb13c"
dependencies = [
"log",
"once_cell",
"ring",
"rustls-pki-types",
@@ -5997,6 +6086,12 @@ dependencies = [
"syn 2.0.100",
]
[[package]]
name = "scoped-tls"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
[[package]]
name = "scopeguard"
version = "1.2.0"
@@ -6308,6 +6403,15 @@ dependencies = [
"digest 0.10.7",
]
[[package]]
name = "sharded-slab"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
dependencies = [
"lazy_static",
]
[[package]]
name = "shared_child"
version = "1.0.1"
@@ -6666,6 +6770,12 @@ dependencies = [
"version-compare",
]
[[package]]
name = "tagptr"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417"
[[package]]
name = "tao"
version = "0.32.8"
@@ -7235,6 +7345,16 @@ dependencies = [
"syn 2.0.100",
]
[[package]]
name = "thread_local"
version = "1.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
dependencies = [
"cfg-if",
"once_cell",
]
[[package]]
name = "tiff"
version = "0.9.1"
@@ -7497,6 +7617,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c"
dependencies = [
"once_cell",
"valuable",
]
[[package]]
name = "tracing-log"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
dependencies = [
"log",
"once_cell",
"tracing-core",
]
[[package]]
name = "tracing-subscriber"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008"
dependencies = [
"matchers",
"nu-ansi-term",
"once_cell",
"regex",
"sharded-slab",
"smallvec",
"thread_local",
"tracing",
"tracing-core",
"tracing-log",
]
[[package]]
@@ -7757,6 +7907,12 @@ dependencies = [
"serde",
]
[[package]]
name = "valuable"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
[[package]]
name = "vcpkg"
version = "0.2.15"
@@ -8082,12 +8238,6 @@ dependencies = [
"system-deps",
]
[[package]]
name = "webpki-roots"
version = "0.25.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1"
[[package]]
name = "webpki-roots"
version = "0.26.8"
@@ -8108,7 +8258,7 @@ dependencies = [
"windows 0.60.0",
"windows-core 0.60.1",
"windows-implement 0.59.0",
"windows-interface",
"windows-interface 0.59.1",
]
[[package]]
@@ -8201,6 +8351,16 @@ dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "windows"
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6"
dependencies = [
"windows-core 0.58.0",
"windows-targets 0.52.6",
]
[[package]]
name = "windows"
version = "0.60.0"
@@ -8232,6 +8392,19 @@ dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "windows-core"
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99"
dependencies = [
"windows-implement 0.58.0",
"windows-interface 0.58.0",
"windows-result 0.2.0",
"windows-strings 0.1.0",
"windows-targets 0.52.6",
]
[[package]]
name = "windows-core"
version = "0.60.1"
@@ -8239,9 +8412,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca21a92a9cae9bf4ccae5cf8368dce0837100ddf6e6d57936749e85f152f6247"
dependencies = [
"windows-implement 0.59.0",
"windows-interface",
"windows-interface 0.59.1",
"windows-link",
"windows-result",
"windows-result 0.3.2",
"windows-strings 0.3.1",
]
@@ -8252,9 +8425,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980"
dependencies = [
"windows-implement 0.60.0",
"windows-interface",
"windows-interface 0.59.1",
"windows-link",
"windows-result",
"windows-result 0.3.2",
"windows-strings 0.4.0",
]
@@ -8268,6 +8441,17 @@ dependencies = [
"windows-link",
]
[[package]]
name = "windows-implement"
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.100",
]
[[package]]
name = "windows-implement"
version = "0.59.0"
@@ -8290,6 +8474,17 @@ dependencies = [
"syn 2.0.100",
]
[[package]]
name = "windows-interface"
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.100",
]
[[package]]
name = "windows-interface"
version = "0.59.1"
@@ -8323,11 +8518,20 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3"
dependencies = [
"windows-result",
"windows-result 0.3.2",
"windows-strings 0.3.1",
"windows-targets 0.53.0",
]
[[package]]
name = "windows-result"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e"
dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "windows-result"
version = "0.3.2"
@@ -8337,6 +8541,16 @@ dependencies = [
"windows-link",
]
[[package]]
name = "windows-strings"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10"
dependencies = [
"windows-result 0.2.0",
"windows-targets 0.52.6",
]
[[package]]
name = "windows-strings"
version = "0.3.1"
@@ -4,7 +4,7 @@
[package]
name = "nym-network-requester"
license = "GPL-3.0"
version = "1.1.55"
version = "1.1.54"
authors.workspace = true
edition.workspace = true
rust-version = "1.70"
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-cli"
version = "1.1.54"
version = "1.1.53"
authors.workspace = true
edition = "2021"
license.workspace = true
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nymvisor"
version = "0.1.19"
version = "0.1.18"
authors.workspace = true
repository.workspace = true
homepage.workspace = true
+3 -3
View File
@@ -1943,9 +1943,9 @@
}
},
"node_modules/http-proxy-middleware": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz",
"integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==",
"version": "2.0.9",
"resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.9.tgz",
"integrity": "sha512-c1IyJYLYppU574+YI7R4QyX2ystMtVXZwIdzazUIPIJsHuWNd+mho2j+bKoHftndicGj9yh+xjd+l0yj7VeT1Q==",
"dev": true,
"license": "MIT",
"dependencies": {
+3 -3
View File
@@ -1162,9 +1162,9 @@ http-parser-js@>=0.5.1:
integrity sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==
http-proxy-middleware@^2.0.3:
version "2.0.6"
resolved "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz"
integrity sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==
version "2.0.9"
resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-2.0.9.tgz#e9e63d68afaa4eee3d147f39149ab84c0c2815ef"
integrity sha512-c1IyJYLYppU574+YI7R4QyX2ystMtVXZwIdzazUIPIJsHuWNd+mho2j+bKoHftndicGj9yh+xjd+l0yj7VeT1Q==
dependencies:
"@types/http-proxy" "^1.17.8"
http-proxy "^1.18.1"
+2 -2
View File
@@ -4,6 +4,6 @@ go 1.23.0
toolchain go1.23.3
require golang.org/x/net v0.36.0
require golang.org/x/net v0.38.0
require golang.org/x/text v0.22.0 // indirect
require golang.org/x/text v0.23.0 // indirect
+4 -4
View File
@@ -1,4 +1,4 @@
golang.org/x/net v0.36.0 h1:vWF2fRbw4qslQsQzgFqZff+BItCvGFQqKzKIzx1rmoA=
golang.org/x/net v0.36.0/go.mod h1:bFmbeoIPfrw4sMHNhb4J9f6+tPziuGjq7Jk/38fxi1I=
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=