From daafb5cae48161521b614ae5e83c7d8af9832e0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bogdan-=C8=98tefan=20Neac=C5=9Fu?= Date: Tue, 22 Oct 2024 17:46:46 +0300 Subject: [PATCH 01/48] Consume only positive bandwidth (#5013) --- common/wireguard/src/peer_handle.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/common/wireguard/src/peer_handle.rs b/common/wireguard/src/peer_handle.rs index cd91d99b3c..9b737c53d0 100644 --- a/common/wireguard/src/peer_handle.rs +++ b/common/wireguard/src/peer_handle.rs @@ -84,12 +84,13 @@ impl PeerHandle { .ok_or(Error::InconsistentConsumedBytes)? .try_into() .map_err(|_| Error::InconsistentConsumedBytes)?; - if bandwidth_manager - .write() - .await - .try_use_bandwidth(spent_bandwidth) - .await - .is_err() + if spent_bandwidth > 0 + && bandwidth_manager + .write() + .await + .try_use_bandwidth(spent_bandwidth) + .await + .is_err() { let success = self.remove_peer().await?; return Ok(!success); From b9fbe0b8f39150b948860fc12d8c16006201662b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bogdan-=C8=98tefan=20Neac=C5=9Fu?= Date: Tue, 22 Oct 2024 18:33:18 +0300 Subject: [PATCH 02/48] Reapply fixes to new branch (#5014) --- common/wireguard/src/peer_controller.rs | 7 +++++-- service-providers/authenticator/src/mixnet_listener.rs | 4 ++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/common/wireguard/src/peer_controller.rs b/common/wireguard/src/peer_controller.rs index 23dbd3a01c..0c77bcf010 100644 --- a/common/wireguard/src/peer_controller.rs +++ b/common/wireguard/src/peer_controller.rs @@ -158,10 +158,13 @@ impl PeerController { .ok_or(Error::MissingClientBandwidthEntry)? .client_id { - storage.create_bandwidth_entry(client_id).await?; + let bandwidth = storage + .get_available_bandwidth(client_id) + .await? + .ok_or(Error::MissingClientBandwidthEntry)?; Ok(Some(BandwidthStorageManager::new( storage, - ClientBandwidth::new(Default::default()), + ClientBandwidth::new(bandwidth.into()), client_id, BandwidthFlushingBehaviourConfig::default(), true, diff --git a/service-providers/authenticator/src/mixnet_listener.rs b/service-providers/authenticator/src/mixnet_listener.rs index 009dab5427..679d8a1b9e 100644 --- a/service-providers/authenticator/src/mixnet_listener.rs +++ b/service-providers/authenticator/src/mixnet_listener.rs @@ -297,6 +297,10 @@ impl MixnetListener { credential: CredentialSpendingData, client_id: i64, ) -> Result { + ecash_verifier + .storage() + .create_bandwidth_entry(client_id) + .await?; let bandwidth = ecash_verifier .storage() .get_available_bandwidth(client_id) From 38e66f6ddf16164ab319fa7447b19add7e66a3bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Stuczy=C5=84ski?= Date: Wed, 23 Oct 2024 09:48:25 +0100 Subject: [PATCH 03/48] added 'get_all_described_nodes' to NymApiClient and adjusted return type on api itself (#5016) --- .../validator-client/src/client.rs | 25 +++++++++++++++++-- .../validator-client/src/nym_api/mod.rs | 24 ++++++++++++++++-- nym-api/src/nym_nodes/handlers/mod.rs | 19 ++++++-------- 3 files changed, 52 insertions(+), 16 deletions(-) diff --git a/common/client-libs/validator-client/src/client.rs b/common/client-libs/validator-client/src/client.rs index b8375abdbd..6205e55998 100644 --- a/common/client-libs/validator-client/src/client.rs +++ b/common/client-libs/validator-client/src/client.rs @@ -19,7 +19,7 @@ use nym_api_requests::ecash::{ }; use nym_api_requests::models::{ GatewayCoreStatusResponse, MixnodeCoreStatusResponse, MixnodeStatusResponse, - RewardEstimationResponse, StakeSaturationResponse, + NymNodeDescription, RewardEstimationResponse, StakeSaturationResponse, }; use nym_api_requests::models::{LegacyDescribedGateway, MixNodeBondAnnotated}; use nym_api_requests::nym_nodes::SkimmedNode; @@ -320,7 +320,7 @@ impl NymApiClient { loop { let mut res = self .nym_api - .get_all_basic_entry_assigned_nodes( + .get_basic_entry_assigned_nodes( semver_compatibility.clone(), false, Some(page), @@ -397,6 +397,27 @@ impl NymApiClient { Ok(self.nym_api.get_gateways_described().await?) } + pub async fn get_all_described_nodes( + &self, + ) -> Result, 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 descriptions = Vec::new(); + + loop { + let mut res = self.nym_api.get_nodes_described(Some(page), None).await?; + + descriptions.append(&mut res.data); + if descriptions.len() < res.pagination.total { + page += 1 + } else { + break; + } + } + + Ok(descriptions) + } + pub async fn get_gateway_core_status_count( &self, identity: IdentityKeyRef<'_>, diff --git a/common/client-libs/validator-client/src/nym_api/mod.rs b/common/client-libs/validator-client/src/nym_api/mod.rs index 9660e470c7..8e13c84580 100644 --- a/common/client-libs/validator-client/src/nym_api/mod.rs +++ b/common/client-libs/validator-client/src/nym_api/mod.rs @@ -11,9 +11,10 @@ use nym_api_requests::ecash::models::{ }; use nym_api_requests::ecash::VerificationKeyResponse; use nym_api_requests::models::{ - AnnotationResponse, LegacyDescribedMixNode, NodePerformanceResponse, + AnnotationResponse, LegacyDescribedMixNode, NodePerformanceResponse, NymNodeDescription, }; use nym_api_requests::nym_nodes::PaginatedCachedNodesResponse; +use nym_api_requests::pagination::PaginatedResponse; pub use nym_api_requests::{ ecash::{ models::{ @@ -119,6 +120,25 @@ pub trait NymApiClientExt: ApiClient { .await } + async fn get_nodes_described( + &self, + page: Option, + per_page: Option, + ) -> Result, NymAPIError> { + let mut params = Vec::new(); + + if let Some(page) = page { + params.push(("page", page.to_string())) + } + + if let Some(per_page) = per_page { + params.push(("per_page", per_page.to_string())) + } + + self.get_json(&[routes::API_VERSION, "nym-nodes", "described"], ¶ms) + .await + } + async fn get_basic_mixnodes( &self, semver_compatibility: Option, @@ -167,7 +187,7 @@ pub trait NymApiClientExt: ApiClient { /// retrieve basic information for nodes are capable of operating as an entry gateway /// this includes legacy gateways and nym-nodes - async fn get_all_basic_entry_assigned_nodes( + async fn get_basic_entry_assigned_nodes( &self, semver_compatibility: Option, no_legacy: bool, diff --git a/nym-api/src/nym_nodes/handlers/mod.rs b/nym-api/src/nym_nodes/handlers/mod.rs index ad92a689c0..4ccb3c02de 100644 --- a/nym-api/src/nym_nodes/handlers/mod.rs +++ b/nym-api/src/nym_nodes/handlers/mod.rs @@ -9,7 +9,7 @@ use axum::routing::get; use axum::{Json, Router}; use nym_api_requests::models::{ AnnotationResponse, NodeDatePerformanceResponse, NodePerformanceResponse, NoiseDetails, - NymNodeData, PerformanceHistoryResponse, UptimeHistoryResponse, + NymNodeDescription, PerformanceHistoryResponse, UptimeHistoryResponse, }; use nym_api_requests::pagination::{PaginatedResponse, Pagination}; use nym_contracts_common::NaiveFloat; @@ -125,32 +125,27 @@ async fn get_bonded_nodes( path = "/described", context_path = "/v1/nym-nodes", responses( - (status = 200, body = PaginatedResponse) + (status = 200, body = PaginatedResponse) ), params(PaginationRequest) )] async fn get_described_nodes( State(state): State, Query(pagination): Query, -) -> AxumResult>> { +) -> AxumResult>> { // TODO: implement it let _ = pagination; let cache = state.described_nodes_cache.get().await?; - let descriptions = cache.all_nodes(); - - let data = descriptions - .map(|n| &n.description) - .cloned() - .collect::>(); + let descriptions = cache.all_nodes().cloned().collect::>(); Ok(Json(PaginatedResponse { pagination: Pagination { - total: data.len(), + total: descriptions.len(), page: 0, - size: data.len(), + size: descriptions.len(), }, - data, + data: descriptions, })) } From 6fafd8c03a88060d4a6ff42e6ff5813cd60e981a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Stuczy=C5=84ski?= Date: Wed, 23 Oct 2024 16:36:21 +0100 Subject: [PATCH 04/48] bugfix: directory v2.1 `get_all_avg_gateway_reliability_in_interval` query (#5023) * log full storage errors on failures * use query_as! macro --- nym-api/src/node_status_api/models.rs | 11 ++++++++++- nym-api/src/support/storage/manager.rs | 9 ++++----- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/nym-api/src/node_status_api/models.rs b/nym-api/src/node_status_api/models.rs index 92e788047e..bce5d6eea6 100644 --- a/nym-api/src/node_status_api/models.rs +++ b/nym-api/src/node_status_api/models.rs @@ -16,6 +16,7 @@ use nym_serde_helpers::date::DATE_FORMAT; use reqwest::StatusCode; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; +use sqlx::Error; use std::fmt::Display; use thiserror::Error; use time::{Date, OffsetDateTime}; @@ -438,7 +439,7 @@ pub enum NymApiStorageError { // I don't think we want to expose errors to the user about what really happened #[error("experienced internal database error")] - InternalDatabaseError(#[from] sqlx::Error), + InternalDatabaseError(sqlx::Error), // the same is true here (also note that the message is subtly different so we would be able to distinguish them) #[error("experienced internal storage error")] @@ -449,6 +450,14 @@ pub enum NymApiStorageError { StartupMigrationFailure(#[from] sqlx::migrate::MigrateError), } +impl From for NymApiStorageError { + fn from(err: Error) -> Self { + // those should realistically never be happening so an `error!` is warranted + error!("storage failure: {err}"); + NymApiStorageError::InternalDatabaseError(err) + } +} + impl NymApiStorageError { pub fn database_inconsistency>(reason: S) -> NymApiStorageError { NymApiStorageError::DatabaseInconsistency { diff --git a/nym-api/src/support/storage/manager.rs b/nym-api/src/support/storage/manager.rs index 5b4093ecbe..f41eabf508 100644 --- a/nym-api/src/support/storage/manager.rs +++ b/nym-api/src/support/storage/manager.rs @@ -119,9 +119,8 @@ impl StorageManager { start_ts_secs: i64, end_ts_secs: i64, ) -> Result, sqlx::Error> { - // we can't use `query_as!` macro because we don't apply all required table changes during sqlx migrations. - // some (like v3 directory) happens at runtime - let result = sqlx::query_as( + let result = sqlx::query_as!( + AvgGatewayReliability, r#" SELECT d.node_id as "node_id: NodeId", @@ -135,9 +134,9 @@ impl StorageManager { timestamp <= ? GROUP BY 1 "#, + start_ts_secs, + end_ts_secs ) - .bind(start_ts_secs) - .bind(end_ts_secs) .fetch_all(&self.connection_pool) .await?; Ok(result) From d2df5422802f2306bf0580d2b5993919f65756dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Stuczy=C5=84ski?= Date: Wed, 23 Oct 2024 16:52:17 +0100 Subject: [PATCH 05/48] bugfix: missing #[serde(default)] for announce port (#5024) --- nym-node/src/config/mixnode.rs | 1 + nym-node/src/config/mod.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/nym-node/src/config/mixnode.rs b/nym-node/src/config/mixnode.rs index f5a56e128a..c938610255 100644 --- a/nym-node/src/config/mixnode.rs +++ b/nym-node/src/config/mixnode.rs @@ -46,6 +46,7 @@ pub struct Verloc { /// will use. /// Useful when the node is behind a proxy. #[serde(deserialize_with = "de_maybe_port")] + #[serde(default)] pub announce_port: Option, #[serde(default)] diff --git a/nym-node/src/config/mod.rs b/nym-node/src/config/mod.rs index 60ed79ab20..65ca8983d7 100644 --- a/nym-node/src/config/mod.rs +++ b/nym-node/src/config/mod.rs @@ -421,6 +421,7 @@ pub struct Mixnet { /// will use. /// Useful when the node is behind a proxy. #[serde(deserialize_with = "de_maybe_port")] + #[serde(default)] pub announce_port: Option, /// Addresses to nym APIs from which the node gets the view of the network. From d18ddcdc114e73a6f80b1d580f7b658d0fbb2ec9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Stuczy=C5=84ski?= Date: Thu, 24 Oct 2024 10:54:00 +0100 Subject: [PATCH 06/48] bugfix: introduce 'LegacyPendingMixNodeChanges' that does not contain 'cost_params_change' (#5028) * bugfix: introduce 'LegacyPendingMixNodeChanges' that does not contain 'cost_params_change' * updated schema files due to removal of '#[serde(deny_unknown_fields)]' --- .../mixnet-contract/src/mixnode.rs | 30 +++++++++++++++++-- .../mixnet/schema/nym-mixnet-contract.json | 12 +++----- ...et_bonded_mixnode_details_by_identity.json | 3 +- .../response_to_get_mix_nodes_detailed.json | 3 +- .../raw/response_to_get_mixnode_details.json | 3 +- .../raw/response_to_get_owned_mixnode.json | 3 +- nym-api/nym-api-requests/src/legacy.rs | 18 ++--------- .../src/nym_contract_cache/cache/refresher.rs | 2 +- 8 files changed, 40 insertions(+), 34 deletions(-) diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/mixnode.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/mixnode.rs index 43a33c5d3e..fb03a1034b 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/mixnode.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/mixnode.rs @@ -17,6 +17,7 @@ use crate::{ use cosmwasm_schema::cw_serde; use cosmwasm_std::{Addr, Coin, Decimal, StdResult, Uint128}; use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; use serde_repr::{Deserialize_repr, Serialize_repr}; /// Full details associated with given mixnode. @@ -647,14 +648,39 @@ impl From for u8 { export_to = "ts-packages/types/src/types/rust/PendingMixnodeChanges.ts" ) )] -#[cw_serde] -#[derive(Default, Copy)] +// note: we had to remove `#[cw_serde]` as it enforces `#[serde(deny_unknown_fields)]` which we do not want +// with the addition of .cost_params_change field +#[derive( + ::cosmwasm_schema::serde::Serialize, + ::cosmwasm_schema::serde::Deserialize, + ::std::clone::Clone, + ::std::fmt::Debug, + ::std::cmp::PartialEq, + ::cosmwasm_schema::schemars::JsonSchema, + Default, + Copy, +)] +#[schemars(crate = "::cosmwasm_schema::schemars")] pub struct PendingMixNodeChanges { pub pledge_change: Option, + #[serde(default)] pub cost_params_change: Option, } +#[derive(Default, Copy, Clone, Debug, Serialize, Deserialize, JsonSchema)] +pub struct LegacyPendingMixNodeChanges { + pub pledge_change: Option, +} + +impl From for LegacyPendingMixNodeChanges { + fn from(value: PendingMixNodeChanges) -> Self { + LegacyPendingMixNodeChanges { + pledge_change: value.pledge_change, + } + } +} + impl PendingMixNodeChanges { pub fn new_empty() -> PendingMixNodeChanges { PendingMixNodeChanges { diff --git a/contracts/mixnet/schema/nym-mixnet-contract.json b/contracts/mixnet/schema/nym-mixnet-contract.json index 7d43c71228..4fa3465b4b 100644 --- a/contracts/mixnet/schema/nym-mixnet-contract.json +++ b/contracts/mixnet/schema/nym-mixnet-contract.json @@ -3689,8 +3689,7 @@ "format": "uint32", "minimum": 0.0 } - }, - "additionalProperties": false + } }, "Percent": { "description": "Percent represents a value between 0 and 100% (i.e. between 0.0 and 1.0)", @@ -5239,8 +5238,7 @@ "format": "uint32", "minimum": 0.0 } - }, - "additionalProperties": false + } }, "Percent": { "description": "Percent represents a value between 0 and 100% (i.e. between 0.0 and 1.0)", @@ -5575,8 +5573,7 @@ "format": "uint32", "minimum": 0.0 } - }, - "additionalProperties": false + } }, "Percent": { "description": "Percent represents a value between 0 and 100% (i.e. between 0.0 and 1.0)", @@ -7595,8 +7592,7 @@ "format": "uint32", "minimum": 0.0 } - }, - "additionalProperties": false + } }, "Percent": { "description": "Percent represents a value between 0 and 100% (i.e. between 0.0 and 1.0)", diff --git a/contracts/mixnet/schema/raw/response_to_get_bonded_mixnode_details_by_identity.json b/contracts/mixnet/schema/raw/response_to_get_bonded_mixnode_details_by_identity.json index 8bde1a5732..239f0fc353 100644 --- a/contracts/mixnet/schema/raw/response_to_get_bonded_mixnode_details_by_identity.json +++ b/contracts/mixnet/schema/raw/response_to_get_bonded_mixnode_details_by_identity.json @@ -315,8 +315,7 @@ "format": "uint32", "minimum": 0.0 } - }, - "additionalProperties": false + } }, "Percent": { "description": "Percent represents a value between 0 and 100% (i.e. between 0.0 and 1.0)", diff --git a/contracts/mixnet/schema/raw/response_to_get_mix_nodes_detailed.json b/contracts/mixnet/schema/raw/response_to_get_mix_nodes_detailed.json index 4bcb8772ac..8db9d629b6 100644 --- a/contracts/mixnet/schema/raw/response_to_get_mix_nodes_detailed.json +++ b/contracts/mixnet/schema/raw/response_to_get_mix_nodes_detailed.json @@ -323,8 +323,7 @@ "format": "uint32", "minimum": 0.0 } - }, - "additionalProperties": false + } }, "Percent": { "description": "Percent represents a value between 0 and 100% (i.e. between 0.0 and 1.0)", diff --git a/contracts/mixnet/schema/raw/response_to_get_mixnode_details.json b/contracts/mixnet/schema/raw/response_to_get_mixnode_details.json index 68d3856e11..bee2b30522 100644 --- a/contracts/mixnet/schema/raw/response_to_get_mixnode_details.json +++ b/contracts/mixnet/schema/raw/response_to_get_mixnode_details.json @@ -317,8 +317,7 @@ "format": "uint32", "minimum": 0.0 } - }, - "additionalProperties": false + } }, "Percent": { "description": "Percent represents a value between 0 and 100% (i.e. between 0.0 and 1.0)", diff --git a/contracts/mixnet/schema/raw/response_to_get_owned_mixnode.json b/contracts/mixnet/schema/raw/response_to_get_owned_mixnode.json index 94867ce877..94499e2fd0 100644 --- a/contracts/mixnet/schema/raw/response_to_get_owned_mixnode.json +++ b/contracts/mixnet/schema/raw/response_to_get_owned_mixnode.json @@ -319,8 +319,7 @@ "format": "uint32", "minimum": 0.0 } - }, - "additionalProperties": false + } }, "Percent": { "description": "Percent represents a value between 0 and 100% (i.e. between 0.0 and 1.0)", diff --git a/nym-api/nym-api-requests/src/legacy.rs b/nym-api/nym-api-requests/src/legacy.rs index f7af56252c..f0810ffa8e 100644 --- a/nym-api/nym-api-requests/src/legacy.rs +++ b/nym-api/nym-api-requests/src/legacy.rs @@ -2,10 +2,8 @@ // SPDX-License-Identifier: GPL-3.0-only use cosmwasm_std::Decimal; -use nym_mixnet_contract_common::mixnode::PendingMixNodeChanges; -use nym_mixnet_contract_common::{ - GatewayBond, LegacyMixLayer, MixNodeBond, MixNodeDetails, NodeId, NodeRewarding, -}; +use nym_mixnet_contract_common::mixnode::LegacyPendingMixNodeChanges; +use nym_mixnet_contract_common::{GatewayBond, LegacyMixLayer, MixNodeBond, NodeId, NodeRewarding}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use std::ops::Deref; @@ -64,7 +62,7 @@ pub struct LegacyMixNodeDetailsWithLayer { /// Adjustments to the mixnode that are ought to happen during future epoch transitions. #[serde(default)] - pub pending_changes: PendingMixNodeChanges, + pub pending_changes: LegacyPendingMixNodeChanges, } impl LegacyMixNodeDetailsWithLayer { @@ -80,13 +78,3 @@ impl LegacyMixNodeDetailsWithLayer { self.bond_information.is_unbonding } } - -impl From for MixNodeDetails { - fn from(value: LegacyMixNodeDetailsWithLayer) -> Self { - MixNodeDetails { - bond_information: value.bond_information.into(), - rewarding_details: value.rewarding_details, - pending_changes: value.pending_changes, - } - } -} diff --git a/nym-api/src/nym_contract_cache/cache/refresher.rs b/nym-api/src/nym_contract_cache/cache/refresher.rs index 0e9e6f45f9..badf0e2344 100644 --- a/nym-api/src/nym_contract_cache/cache/refresher.rs +++ b/nym-api/src/nym_contract_cache/cache/refresher.rs @@ -173,7 +173,7 @@ impl NymContractCacheRefresher { layer, }, rewarding_details: detail.rewarding_details, - pending_changes: detail.pending_changes, + pending_changes: detail.pending_changes.into(), }) } From c09a17b66d94e6687afd7c8ef0ac5842a4d2e8fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Stuczy=C5=84ski?= Date: Thu, 24 Oct 2024 15:00:34 +0100 Subject: [PATCH 07/48] bugfix: verifying signed information of legacy nodes (#5029) * Added new legacy variant of HostInformation * fixed 'option_bs58_x25519_pubkey' for empty string * 'Debug' impl for x25519 and ed25519 to use human-readable representation * HttpClient to use explicit 'serde_json' conversion for better errors * additional 'Debug' derives --- .../crypto/src/asymmetric/encryption/mod.rs | 12 ++- .../asymmetric/encryption/serde_helpers.rs | 14 ++- common/crypto/src/asymmetric/identity/mod.rs | 12 ++- common/http-api-client/src/lib.rs | 9 +- nym-api/src/node_describe_cache/mod.rs | 2 + .../src/node_describe_cache/query_helpers.rs | 1 + nym-node/nym-node-requests/src/api/mod.rs | 92 ++++++++++++++++++- .../src/api/v1/node/models.rs | 53 +++++++++-- 8 files changed, 174 insertions(+), 21 deletions(-) diff --git a/common/crypto/src/asymmetric/encryption/mod.rs b/common/crypto/src/asymmetric/encryption/mod.rs index fb841541fc..7d7b988fc8 100644 --- a/common/crypto/src/asymmetric/encryption/mod.rs +++ b/common/crypto/src/asymmetric/encryption/mod.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use nym_pemstore::traits::{PemStorableKey, PemStorableKeyPair}; -use std::fmt::{self, Display, Formatter}; +use std::fmt::{self, Debug, Display, Formatter}; use std::str::FromStr; use thiserror::Error; use zeroize::{Zeroize, ZeroizeOnDrop}; @@ -112,12 +112,18 @@ impl PemStorableKeyPair for KeyPair { } } -#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)] +#[derive(PartialEq, Eq, Hash, Copy, Clone)] pub struct PublicKey(x25519_dalek::PublicKey); impl Display for PublicKey { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.to_base58_string()) + Display::fmt(&self.to_base58_string(), f) + } +} + +impl Debug for PublicKey { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + Debug::fmt(&self.to_base58_string(), f) } } diff --git a/common/crypto/src/asymmetric/encryption/serde_helpers.rs b/common/crypto/src/asymmetric/encryption/serde_helpers.rs index 374afbd40d..02a5282cdd 100644 --- a/common/crypto/src/asymmetric/encryption/serde_helpers.rs +++ b/common/crypto/src/asymmetric/encryption/serde_helpers.rs @@ -31,8 +31,16 @@ pub mod option_bs58_x25519_pubkey { pub fn deserialize<'de, D: Deserializer<'de>>( deserializer: D, ) -> Result, D::Error> { - let s = Option::::deserialize(deserializer)?; - s.map(|s| PublicKey::from_base58_string(&s).map_err(serde::de::Error::custom)) - .transpose() + match Option::::deserialize(deserializer)? { + None => Ok(None), + Some(s) => { + if s.is_empty() { + Ok(None) + } else { + Some(PublicKey::from_base58_string(&s).map_err(serde::de::Error::custom)) + .transpose() + } + } + } } } diff --git a/common/crypto/src/asymmetric/identity/mod.rs b/common/crypto/src/asymmetric/identity/mod.rs index e2d3df624d..4b51aa2f64 100644 --- a/common/crypto/src/asymmetric/identity/mod.rs +++ b/common/crypto/src/asymmetric/identity/mod.rs @@ -5,7 +5,7 @@ pub use ed25519_dalek::SignatureError; use ed25519_dalek::{Signer, SigningKey}; pub use ed25519_dalek::{Verifier, PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH, SIGNATURE_LENGTH}; use nym_pemstore::traits::{PemStorableKey, PemStorableKeyPair}; -use std::fmt::{self, Display, Formatter}; +use std::fmt::{self, Debug, Display, Formatter}; use std::str::FromStr; use thiserror::Error; use zeroize::{Zeroize, ZeroizeOnDrop}; @@ -119,12 +119,18 @@ impl PemStorableKeyPair for KeyPair { } /// ed25519 EdDSA Public Key -#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[derive(Copy, Clone, Eq, PartialEq)] pub struct PublicKey(ed25519_dalek::VerifyingKey); impl Display for PublicKey { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.to_base58_string()) + Display::fmt(&self.to_base58_string(), f) + } +} + +impl Debug for PublicKey { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + Debug::fmt(&self.to_base58_string(), f) } } diff --git a/common/http-api-client/src/lib.rs b/common/http-api-client/src/lib.rs index a8ad8e64d1..84ab2d2cb9 100644 --- a/common/http-api-client/src/lib.rs +++ b/common/http-api-client/src/lib.rs @@ -35,6 +35,9 @@ pub enum HttpClientError { source: reqwest::Error, }, + #[error("failed to deserialise received response: {source}")] + ResponseDeserialisationFailure { source: serde_json::Error }, + #[error("provided url is malformed: {source}")] MalformedUrl { #[from] @@ -526,7 +529,11 @@ where } if res.status().is_success() { - Ok(res.json().await?) + let text = res.text().await?; + match serde_json::from_str(&text) { + Ok(res) => Ok(res), + Err(source) => Err(HttpClientError::ResponseDeserialisationFailure { source }), + } } else if res.status() == StatusCode::NOT_FOUND { Err(HttpClientError::NotFound) } else { diff --git a/nym-api/src/node_describe_cache/mod.rs b/nym-api/src/node_describe_cache/mod.rs index d8e0b3c32b..8368e58a97 100644 --- a/nym-api/src/node_describe_cache/mod.rs +++ b/nym-api/src/node_describe_cache/mod.rs @@ -145,6 +145,7 @@ impl NodeDescriptionTopologyExt for NymNodeDescription { } } +#[derive(Debug, Clone)] pub struct DescribedNodes { nodes: HashMap, } @@ -290,6 +291,7 @@ async fn try_get_description( }) } +#[derive(Debug)] struct RefreshData { host: String, node_id: NodeId, diff --git a/nym-api/src/node_describe_cache/query_helpers.rs b/nym-api/src/node_describe_cache/query_helpers.rs index 1752345f2c..78611ca624 100644 --- a/nym-api/src/node_describe_cache/query_helpers.rs +++ b/nym-api/src/node_describe_cache/query_helpers.rs @@ -210,6 +210,7 @@ impl ResolvedNodeDescribedInfo { } } +#[derive(Debug)] pub(crate) struct UnwrappedResolvedNodeDescribedInfo { pub(crate) build_info: BinaryBuildInformationOwned, pub(crate) roles: DeclaredRoles, diff --git a/nym-node/nym-node-requests/src/api/mod.rs b/nym-node/nym-node-requests/src/api/mod.rs index 4db9d49297..6bf586f71b 100644 --- a/nym-node/nym-node-requests/src/api/mod.rs +++ b/nym-node/nym-node-requests/src/api/mod.rs @@ -1,7 +1,9 @@ // Copyright 2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::api::v1::node::models::{HostInformation, LegacyHostInformation}; +use crate::api::v1::node::models::{ + HostInformation, LegacyHostInformation, LegacyHostInformationV2, +}; use crate::error::Error; use nym_crypto::asymmetric::identity; use schemars::JsonSchema; @@ -61,9 +63,18 @@ impl SignedHostInformation { return true; } - // attempt to verify legacy signature + // attempt to verify legacy signatures + let legacy_v2 = SignedData { + data: LegacyHostInformationV2::from(self.data.clone()), + signature: self.signature.clone(), + }; + + if legacy_v2.verify(&self.keys.ed25519_identity) { + return true; + } + SignedData { - data: LegacyHostInformation::from(self.data.clone()), + data: LegacyHostInformation::from(legacy_v2.data), signature: self.signature.clone(), } .verify(&self.keys.ed25519_identity) @@ -133,6 +144,81 @@ mod tests { assert!(signed_info.verify_host_information()); } + #[test] + fn dummy_legacy_v2_signed_host_verification() { + let mut rng = rand_chacha::ChaCha20Rng::from_seed([0u8; 32]); + let ed22519 = ed25519::KeyPair::new(&mut rng); + let x25519_sphinx = x25519::KeyPair::new(&mut rng); + let x25519_noise = x25519::KeyPair::new(&mut rng); + + let legacy_info_no_noise = crate::api::v1::node::models::LegacyHostInformationV2 { + ip_address: vec!["1.1.1.1".parse().unwrap()], + hostname: Some("foomp.com".to_string()), + keys: crate::api::v1::node::models::LegacyHostKeysV2 { + ed25519_identity: ed22519.public_key().to_base58_string(), + x25519_sphinx: x25519_sphinx.public_key().to_base58_string(), + x25519_noise: "".to_string(), + }, + }; + + let legacy_info_noise = crate::api::v1::node::models::LegacyHostInformationV2 { + ip_address: vec!["1.1.1.1".parse().unwrap()], + hostname: Some("foomp.com".to_string()), + keys: crate::api::v1::node::models::LegacyHostKeysV2 { + ed25519_identity: ed22519.public_key().to_base58_string(), + x25519_sphinx: x25519_sphinx.public_key().to_base58_string(), + x25519_noise: x25519_noise.public_key().to_base58_string(), + }, + }; + + let host_info_no_noise = crate::api::v1::node::models::HostInformation { + ip_address: legacy_info_no_noise.ip_address.clone(), + hostname: legacy_info_no_noise.hostname.clone(), + keys: crate::api::v1::node::models::HostKeys { + ed25519_identity: legacy_info_no_noise.keys.ed25519_identity.parse().unwrap(), + x25519_sphinx: legacy_info_no_noise.keys.x25519_sphinx.parse().unwrap(), + x25519_noise: None, + }, + }; + + let host_info_noise = crate::api::v1::node::models::HostInformation { + ip_address: legacy_info_noise.ip_address.clone(), + hostname: legacy_info_noise.hostname.clone(), + keys: crate::api::v1::node::models::HostKeys { + ed25519_identity: legacy_info_noise.keys.ed25519_identity.parse().unwrap(), + x25519_sphinx: legacy_info_noise.keys.x25519_sphinx.parse().unwrap(), + x25519_noise: Some(legacy_info_noise.keys.x25519_noise.parse().unwrap()), + }, + }; + + // signature on legacy data + let signature_no_noise = SignedData::new(legacy_info_no_noise, ed22519.private_key()) + .unwrap() + .signature; + + let signature_noise = SignedData::new(legacy_info_noise, ed22519.private_key()) + .unwrap() + .signature; + + // signed blob with the 'current' structure + let current_struct_no_noise = SignedData { + data: host_info_no_noise, + signature: signature_no_noise, + }; + + let current_struct_noise = SignedData { + data: host_info_noise, + signature: signature_noise, + }; + + assert!(!current_struct_no_noise.verify(ed22519.public_key())); + assert!(current_struct_no_noise.verify_host_information()); + + // if noise key is present, the signature is actually valid + assert!(current_struct_noise.verify(ed22519.public_key())); + assert!(current_struct_noise.verify_host_information()) + } + #[test] fn dummy_legacy_signed_host_verification() { let mut rng = rand_chacha::ChaCha20Rng::from_seed([0u8; 32]); diff --git a/nym-node/nym-node-requests/src/api/v1/node/models.rs b/nym-node/nym-node-requests/src/api/v1/node/models.rs index dcaa12435f..6979beb949 100644 --- a/nym-node/nym-node-requests/src/api/v1/node/models.rs +++ b/nym-node/nym-node-requests/src/api/v1/node/models.rs @@ -59,6 +59,13 @@ pub struct HostInformation { pub keys: HostKeys, } +#[derive(Serialize)] +pub struct LegacyHostInformationV2 { + pub ip_address: Vec, + pub hostname: Option, + pub keys: LegacyHostKeysV2, +} + #[derive(Serialize)] pub struct LegacyHostInformation { pub ip_address: Vec, @@ -66,8 +73,18 @@ pub struct LegacyHostInformation { pub keys: LegacyHostKeys, } -impl From for LegacyHostInformation { +impl From for LegacyHostInformationV2 { fn from(value: HostInformation) -> Self { + LegacyHostInformationV2 { + ip_address: value.ip_address, + hostname: value.hostname, + keys: value.keys.into(), + } + } +} + +impl From for LegacyHostInformation { + fn from(value: LegacyHostInformationV2) -> Self { LegacyHostInformation { ip_address: value.ip_address, hostname: value.hostname, @@ -99,13 +116,11 @@ pub struct HostKeys { pub x25519_noise: Option, } -impl From for LegacyHostKeys { - fn from(value: HostKeys) -> Self { - LegacyHostKeys { - ed25519: value.ed25519_identity.to_base58_string(), - x25519: value.x25519_sphinx.to_base58_string(), - } - } +#[derive(Serialize)] +pub struct LegacyHostKeysV2 { + pub ed25519_identity: String, + pub x25519_sphinx: String, + pub x25519_noise: String, } #[derive(Serialize)] @@ -114,6 +129,28 @@ pub struct LegacyHostKeys { pub x25519: String, } +impl From for LegacyHostKeysV2 { + fn from(value: HostKeys) -> Self { + LegacyHostKeysV2 { + ed25519_identity: value.ed25519_identity.to_base58_string(), + x25519_sphinx: value.x25519_sphinx.to_base58_string(), + x25519_noise: value + .x25519_noise + .map(|k| k.to_base58_string()) + .unwrap_or_default(), + } + } +} + +impl From for LegacyHostKeys { + fn from(value: LegacyHostKeysV2) -> Self { + LegacyHostKeys { + ed25519: value.ed25519_identity, + x25519: value.x25519_sphinx, + } + } +} + #[derive(Clone, Default, Debug, Serialize, Deserialize, JsonSchema)] #[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] pub struct HostSystem { From 4b0153f5f27fb4f6ddf8cb4c71821e1717be00f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Stuczy=C5=84ski?= Date: Thu, 24 Oct 2024 15:37:41 +0100 Subject: [PATCH 08/48] bugfix: fixed backwards incompatibility for /gateways/described endpoint (#5030) --- nym-api/nym-api-requests/src/models.rs | 6 +++--- nym-api/src/nym_nodes/handlers/legacy.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/nym-api/nym-api-requests/src/models.rs b/nym-api/nym-api-requests/src/models.rs index ce84b7a53e..b1da3b672d 100644 --- a/nym-api/nym-api-requests/src/models.rs +++ b/nym-api/nym-api-requests/src/models.rs @@ -16,7 +16,7 @@ use nym_crypto::asymmetric::x25519::{ use nym_mixnet_contract_common::nym_node::Role; use nym_mixnet_contract_common::reward_params::{Performance, RewardingParams}; use nym_mixnet_contract_common::rewarding::RewardEstimate; -use nym_mixnet_contract_common::{IdentityKey, Interval, MixNode, NodeId, Percent}; +use nym_mixnet_contract_common::{GatewayBond, IdentityKey, Interval, MixNode, NodeId, Percent}; use nym_network_defaults::{DEFAULT_MIX_LISTENING_PORT, DEFAULT_VERLOC_LISTENING_PORT}; use nym_node_requests::api::v1::authenticator::models::Authenticator; use nym_node_requests::api::v1::gateway::models::Wireguard; @@ -935,14 +935,14 @@ impl NymNodeData { #[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema, ToSchema)] pub struct LegacyDescribedGateway { - pub bond: LegacyGatewayBondWithId, + pub bond: GatewayBond, pub self_described: Option, } impl From for LegacyDescribedGateway { fn from(bond: LegacyGatewayBondWithId) -> Self { LegacyDescribedGateway { - bond, + bond: bond.bond, self_described: None, } } diff --git a/nym-api/src/nym_nodes/handlers/legacy.rs b/nym-api/src/nym_nodes/handlers/legacy.rs index 1ebd9260c7..cae94d5254 100644 --- a/nym-api/src/nym_nodes/handlers/legacy.rs +++ b/nym-api/src/nym_nodes/handlers/legacy.rs @@ -49,7 +49,7 @@ async fn get_gateways_described( .into_iter() .map(|bond| LegacyDescribedGateway { self_described: self_descriptions.get_description(&bond.node_id).cloned(), - bond, + bond: bond.bond, }) .collect(), ) From e65bfaeb31302374e31ee6dbd30eed5b8ee992ce Mon Sep 17 00:00:00 2001 From: Fran Arbanas Date: Thu, 24 Oct 2024 18:10:34 +0200 Subject: [PATCH 09/48] Fix/nym data observatory dockerfile (#5021) * fix: added needed env vars to dockerfile, updated db env for a bit * feat: add github workflow for pushing data observatory * feat: split the postgresql connection string into multiple variables * fix docker compose * fix workflow * fix: short in clap --- .github/workflows/push-data-observatory.yaml | 55 ++++++++++++++++++++ nym-data-observatory/Dockerfile | 12 +++++ nym-data-observatory/docker-compose.yml | 8 ++- nym-data-observatory/src/db/mod.rs | 4 +- nym-data-observatory/src/main.rs | 33 ++++++++++-- 5 files changed, 104 insertions(+), 8 deletions(-) create mode 100644 .github/workflows/push-data-observatory.yaml diff --git a/.github/workflows/push-data-observatory.yaml b/.github/workflows/push-data-observatory.yaml new file mode 100644 index 0000000000..415d90bd7c --- /dev/null +++ b/.github/workflows/push-data-observatory.yaml @@ -0,0 +1,55 @@ +name: Build and upload Data observatory container to harbor.nymte.ch +on: + workflow_dispatch: + +env: + WORKING_DIRECTORY: "nym-data-observatory" + CONTAINER_NAME: "data-observatory" + +jobs: + build-container: + runs-on: arc-ubuntu-22.04-dind + steps: + - name: Login to Harbor + uses: docker/login-action@v3 + with: + registry: harbor.nymte.ch + username: ${{ secrets.HARBOR_ROBOT_USERNAME }} + password: ${{ secrets.HARBOR_ROBOT_SECRET }} + + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Configure git identity + run: | + git config --global user.email "lawrence@nymtech.net" + git config --global user.name "Lawrence Stalder" + + - name: Get version from cargo.toml + uses: mikefarah/yq@v4.44.3 + id: get_version + with: + cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml + + - name: Check if tag exists + run: | + if git rev-parse ${{ steps.get_version.outputs.value }} >/dev/null 2>&1; then + echo "Tag ${{ steps.get_version.outputs.value }} already exists" + fi + + - name: Remove existing tag if exists + run: | + if git rev-parse ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }} >/dev/null 2>&1; then + git push --delete origin ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }} + git tag -d ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }} + fi + + - name: Create tag + run: | + git tag -a ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }} -m "Version ${{ steps.get_version.outputs.result }}" + git push origin ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }} + + - name: BuildAndPushImageOnHarbor + run: | + docker build -f ${{ env.WORKING_DIRECTORY }}/Dockerfile . -t harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }}:${{ steps.get_version.outputs.result }} -t harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }}:latest + docker push harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }} --all-tags diff --git a/nym-data-observatory/Dockerfile b/nym-data-observatory/Dockerfile index bc756ebaa5..d36a910faf 100644 --- a/nym-data-observatory/Dockerfile +++ b/nym-data-observatory/Dockerfile @@ -5,6 +5,18 @@ WORKDIR /usr/src/nym/nym-data-observatory RUN cargo build --release +#------------------------------------------------------------------- +# The following environment variables are required at runtime: +# +# NYM_DATA_OBSERVATORY_CONNECTION_URL +# +# And optionally: +# +# NYM_DATA_OBSERVATORY_HTTP_PORT +# +# see https://github.com/nymtech/nym/blob/develop/nym-data-observatory/src/main.rs for details +#------------------------------------------------------------------- + FROM ubuntu:24.04 RUN apt update && apt install -yy curl ca-certificates diff --git a/nym-data-observatory/docker-compose.yml b/nym-data-observatory/docker-compose.yml index 15da0609b7..b06a988e97 100644 --- a/nym-data-observatory/docker-compose.yml +++ b/nym-data-observatory/docker-compose.yml @@ -18,8 +18,14 @@ services: dockerfile: nym-data-observatory/Dockerfile container_name: nym-data-observatory environment: - NYM_DATA_OBSERVATORY_CONNECTION_URL: "postgres://postgres:password@postgres:5432" + NYM_DATA_OBSERVATORY_CONNECTION_USERNAME: "postgres" + NYM_DATA_OBSERVATORY_CONNECTION_PASSWORD: "password" + NYM_DATA_OBSERVATORY_CONNECTION_HOST: "postgres" + NYM_DATA_OBSERVATORY_CONNECTION_PORT: "5432" + NYM_DATA_OBSERVATORY_CONNECTION_DB: "" NYM_DATA_OBSERVATORY_HTTP_PORT: 8000 + env_file: + - ../envs/qa.env volumes: pgdata: diff --git a/nym-data-observatory/src/db/mod.rs b/nym-data-observatory/src/db/mod.rs index 05c71a1728..6e8938a578 100644 --- a/nym-data-observatory/src/db/mod.rs +++ b/nym-data-observatory/src/db/mod.rs @@ -14,9 +14,7 @@ pub(crate) struct Storage { } impl Storage { - pub async fn init(connection_url: Option) -> Result { - let connection_url = - connection_url.ok_or_else(|| anyhow!("Missing the connection url for database!"))?; + pub async fn init(connection_url: String) -> Result { let connect_options = PgConnectOptions::from_str(&connection_url)?.disable_statement_logging(); diff --git a/nym-data-observatory/src/main.rs b/nym-data-observatory/src/main.rs index 4f902f4b46..da8f53d4f9 100644 --- a/nym-data-observatory/src/main.rs +++ b/nym-data-observatory/src/main.rs @@ -18,9 +18,25 @@ struct Args { #[arg(short, long, default_value = None, env = "NYM_DATA_OBSERVATORY_ENV_FILE")] env_file: Option, - /// DB connection url - #[arg(short, long, default_value = None, env = "NYM_DATA_OBSERVATORY_CONNECTION_URL")] - connection_url: Option, + /// DB connection username + #[arg(long, default_value = None, env = "NYM_DATA_OBSERVATORY_CONNECTION_USERNAME")] + connection_username: String, + + /// DB connection password + #[arg(long, default_value = None, env = "NYM_DATA_OBSERVATORY_CONNECTION_PASSWORD")] + connection_password: String, + + /// DB connection host + #[arg(long, default_value = None, env = "NYM_DATA_OBSERVATORY_CONNECTION_HOST")] + connection_host: String, + + /// DB connection port + #[arg(long, default_value = None, env = "NYM_DATA_OBSERVATORY_CONNECTION_PORT")] + connection_port: String, + + /// DB connection database name + #[arg(long, default_value = None, env = "NYM_DATA_OBSERVATORY_CONNECTION_DB")] + connection_db: String, } #[tokio::main] @@ -31,7 +47,16 @@ async fn main() -> anyhow::Result<()> { setup_env(args.env_file); // Defaults to mainnet if empty - let storage = db::Storage::init(args.connection_url).await?; + let connection_url = format!( + "postgres://{}:{}@{}:{}/{}", + args.connection_username, + args.connection_password, + args.connection_host, + args.connection_port, + args.connection_db + ); + + let storage = db::Storage::init(connection_url).await?; let db_pool = storage.pool_owned().await; tokio::spawn(async move { background_task::spawn_in_background(db_pool).await; From 0edb9631a681473cdc2b78b81026671f33eb585f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Stuczy=C5=84ski?= Date: Thu, 24 Oct 2024 17:38:32 +0100 Subject: [PATCH 10/48] feature: use axum_client_ip for attempting to extract source ip (#5031) --- Cargo.lock | 28 +++++++++++++++++++++++++++ Cargo.toml | 1 + common/http-api-common/Cargo.toml | 1 + common/http-api-common/src/logging.rs | 6 +++--- 4 files changed, 33 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index eb95cb7932..f96d43110d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -481,6 +481,17 @@ dependencies = [ "tracing", ] +[[package]] +name = "axum-client-ip" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eefda7e2b27e1bda4d6fa8a06b50803b8793769045918bc37ad062d48a6efac" +dependencies = [ + "axum 0.7.7", + "forwarded-header-value", + "serde", +] + [[package]] name = "axum-core" version = "0.3.4" @@ -2585,6 +2596,16 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8cbd1169bd7b4a0a20d92b9af7a7e0422888bd38a6f5ec29c1fd8c1558a272e" +[[package]] +name = "forwarded-header-value" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8835f84f38484cc86f110a805655697908257fb9a7af005234060891557198e9" +dependencies = [ + "nonempty", + "thiserror", +] + [[package]] name = "fs-err" version = "2.11.0" @@ -4198,6 +4219,12 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nonempty" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9e591e719385e6ebaeb5ce5d3887f7d5676fceca6411d1925ccc95745f3d6f7" + [[package]] name = "notify" version = "5.2.0" @@ -5419,6 +5446,7 @@ name = "nym-http-api-common" version = "0.1.0" dependencies = [ "axum 0.7.7", + "axum-client-ip", "bytes", "colored", "mime", diff --git a/Cargo.toml b/Cargo.toml index f64f8ddbb3..fd480fa59e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -186,6 +186,7 @@ aead = "0.5.2" anyhow = "1.0.89" argon2 = "0.5.0" async-trait = "0.1.83" +axum-client-ip = "0.6.1" axum = "0.7.5" axum-extra = "0.9.4" base64 = "0.22.1" diff --git a/common/http-api-common/Cargo.toml b/common/http-api-common/Cargo.toml index b9252c2b05..c7b6db2ff6 100644 --- a/common/http-api-common/Cargo.toml +++ b/common/http-api-common/Cargo.toml @@ -11,6 +11,7 @@ license.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +axum-client-ip.workspace = true axum.workspace = true bytes = { workspace = true } colored.workspace = true diff --git a/common/http-api-common/src/logging.rs b/common/http-api-common/src/logging.rs index ca2f5c63ce..8de60b338f 100644 --- a/common/http-api-common/src/logging.rs +++ b/common/http-api-common/src/logging.rs @@ -1,18 +1,18 @@ // Copyright 2024 - Nym Technologies SA // SPDX-License-Identifier: GPL-3.0-only -use axum::extract::{ConnectInfo, Request}; +use axum::extract::Request; use axum::http::header::{HOST, USER_AGENT}; use axum::http::HeaderValue; use axum::middleware::Next; use axum::response::IntoResponse; +use axum_client_ip::InsecureClientIp; use colored::Colorize; -use std::net::SocketAddr; use std::time::Instant; use tracing::info; pub async fn logger( - ConnectInfo(addr): ConnectInfo, + InsecureClientIp(addr): InsecureClientIp, request: Request, next: Next, ) -> impl IntoResponse { From 29f8386b50151ba38baa134026fa930120b8e10d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Stuczy=C5=84ski?= Date: Thu, 24 Oct 2024 17:47:57 +0100 Subject: [PATCH 11/48] bugfix: make sure to use correct highest node id when assigning role (#5032) * bugfix: make sure to use correct highest node id when assigning role * make sure nym-api provides sorted values for older contracts --- contracts/mixnet/src/nodes/storage/helpers.rs | 34 +++++++++++++++++-- .../rewarded_set_assignment.rs | 14 ++++++-- 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/contracts/mixnet/src/nodes/storage/helpers.rs b/contracts/mixnet/src/nodes/storage/helpers.rs index ab59cd2617..1d15c6b03a 100644 --- a/contracts/mixnet/src/nodes/storage/helpers.rs +++ b/contracts/mixnet/src/nodes/storage/helpers.rs @@ -52,8 +52,8 @@ pub(crate) fn save_assignment( // update metadata let mut metadata = ROLES_METADATA.load(storage, inactive)?; - let last = assignment.nodes.last().copied().unwrap_or_default(); - metadata.set_highest_id(last, assignment.role); + let highest_id = assignment.nodes.iter().max().copied().unwrap_or_default(); + metadata.set_highest_id(highest_id, assignment.role); metadata.set_role_count(assignment.role, assignment.nodes.len() as u32); if assignment.is_final_assignment() { metadata.fully_assigned = true @@ -140,6 +140,7 @@ pub(crate) fn initialise_storage(storage: &mut dyn Storage) -> Result<(), Mixnet mod tests { use super::*; use crate::support::tests::test_helpers; + use crate::support::tests::test_helpers::TestSetup; #[test] fn next_id() { @@ -149,4 +150,33 @@ mod tests { assert_eq!(i, next_nymnode_id_counter(deps.as_mut().storage).unwrap()); } } + + #[test] + fn assigning_role_uses_highest_id_even_if_not_sorted() { + let mut test = TestSetup::new(); + let deps = test.deps_mut(); + + let sorted = RoleAssignment { + role: Role::EntryGateway, + nodes: vec![1, 2, 3], + }; + + let unsorted = RoleAssignment { + role: Role::Layer1, + nodes: vec![8, 5, 4], + }; + + save_assignment(deps.storage, sorted).unwrap(); + save_assignment(deps.storage, unsorted).unwrap(); + + let storage = deps.as_ref().storage; + + let active_bucket = ACTIVE_ROLES_BUCKET.load(storage).unwrap(); + let inactive = active_bucket.other() as u8; + let metadata = ROLES_METADATA.load(storage, inactive).unwrap(); + + assert_eq!(metadata.entry_gateway_metadata.highest_id, 3); + assert_eq!(metadata.layer1_metadata.highest_id, 8); + assert_eq!(metadata.highest_rewarded_id(), 8) + } } diff --git a/nym-api/src/epoch_operations/rewarded_set_assignment.rs b/nym-api/src/epoch_operations/rewarded_set_assignment.rs index 789053d0d1..3e401b32c5 100644 --- a/nym-api/src/epoch_operations/rewarded_set_assignment.rs +++ b/nym-api/src/epoch_operations/rewarded_set_assignment.rs @@ -228,14 +228,24 @@ impl EpochAdvancer { ) } - Ok(RewardedSet { + let mut rewarded_set = RewardedSet { entry_gateways: entry_gateways.into_iter().collect(), exit_gateways: exit_gateways.into_iter().collect(), layer1, layer2, layer3, standby, - }) + }; + + // make sure to sort the rewarded set values + rewarded_set.entry_gateways.sort(); + rewarded_set.exit_gateways.sort(); + rewarded_set.layer1.sort(); + rewarded_set.layer2.sort(); + rewarded_set.layer3.sort(); + rewarded_set.standby.sort(); + + Ok(rewarded_set) } async fn attach_performance_to_eligible_nodes( From 92344745656081f10095611a2511a260a7f78f74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Stuczy=C5=84ski?= Date: Fri, 25 Oct 2024 09:29:37 +0100 Subject: [PATCH 12/48] bugfix: use old name for 'epoch_role' in SkimmedNode (#5034) * bugfix: use old name for 'epoch_role' in SkimmedNode * clippy --- common/topology/src/mix.rs | 2 +- nym-api/nym-api-requests/src/models.rs | 6 +++--- nym-api/nym-api-requests/src/nym_nodes.rs | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/common/topology/src/mix.rs b/common/topology/src/mix.rs index edfbe8740f..170f1000a5 100644 --- a/common/topology/src/mix.rs +++ b/common/topology/src/mix.rs @@ -116,7 +116,7 @@ impl<'a> TryFrom<&'a SkimmedNode> for LegacyNode { }); } - let layer = match value.epoch_role { + let layer = match value.role { NodeRole::Mixnode { layer } => layer .try_into() .map_err(|_| MixnodeConversionError::InvalidLayer)?, diff --git a/nym-api/nym-api-requests/src/models.rs b/nym-api/nym-api-requests/src/models.rs index b1da3b672d..b16c22f467 100644 --- a/nym-api/nym-api-requests/src/models.rs +++ b/nym-api/nym-api-requests/src/models.rs @@ -286,7 +286,7 @@ impl MixNodeBondAnnotated { .sphinx_key .parse() .map_err(|_| MalformedNodeBond::InvalidX25519Key)?, - epoch_role: role, + role, supported_roles: DeclaredRoles { mixnode: true, entry: false, @@ -345,7 +345,7 @@ impl GatewayBondAnnotated { .sphinx_key .parse() .map_err(|_| MalformedNodeBond::InvalidX25519Key)?, - epoch_role: role, + role, supported_roles: DeclaredRoles { mixnode: false, entry: true, @@ -827,7 +827,7 @@ impl NymNodeDescription { // we can't use the declared roles, we have to take whatever was provided in the contract. // why? say this node COULD operate as an exit, but it might be the case the contract decided // to assign it an ENTRY role only. we have to use that one instead. - epoch_role: role, + role, supported_roles: self.description.declared_role, entry, performance, diff --git a/nym-api/nym-api-requests/src/nym_nodes.rs b/nym-api/nym-api-requests/src/nym_nodes.rs index 27d3e6ba3d..fcfd09c2be 100644 --- a/nym-api/nym-api-requests/src/nym_nodes.rs +++ b/nym-api/nym-api-requests/src/nym_nodes.rs @@ -141,8 +141,8 @@ pub struct SkimmedNode { #[schemars(with = "String")] pub x25519_sphinx_pubkey: x25519::PublicKey, - #[serde(alias = "role")] - pub epoch_role: NodeRole, + #[serde(alias = "epoch_role")] + pub role: NodeRole, // needed for the purposes of sending appropriate test packets #[serde(default)] @@ -157,7 +157,7 @@ pub struct SkimmedNode { impl SkimmedNode { pub fn get_mix_layer(&self) -> Option { - match self.epoch_role { + match self.role { NodeRole::Mixnode { layer } => Some(layer), _ => None, } From d626e7689fc176d407379b30cec1accce53bfb55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Stuczy=C5=84ski?= Date: Fri, 25 Oct 2024 12:06:16 +0100 Subject: [PATCH 13/48] bugfix: make gateways insert themselves into [local] topology (#5038) * added explicit SP suffix to started tasks * added 'GatewayTopologyProvider' that always injects itself into the network * use the new topology provider to bypass described bootstrapping problem --- Cargo.lock | 2 + .../src/client/topology_control/mod.rs | 7 ++- .../topology_control/nym_api_provider.rs | 11 ++-- common/topology/src/lib.rs | 4 ++ gateway/Cargo.toml | 2 + gateway/src/node/helpers.rs | 62 ++++++++++++++++++- gateway/src/node/mod.rs | 54 +++++++++++++++- sdk/rust/nym-sdk/src/lib.rs | 8 ++- .../authenticator/src/authenticator.rs | 4 +- .../ip-packet-router/src/ip_packet_router.rs | 2 +- .../network-requester/src/core.rs | 2 +- 11 files changed, 143 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f96d43110d..aaffaa5570 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5293,9 +5293,11 @@ dependencies = [ "nym-network-requester", "nym-node-http-api", "nym-pemstore", + "nym-sdk", "nym-sphinx", "nym-statistics-common", "nym-task", + "nym-topology", "nym-types", "nym-validator-client", "nym-wireguard", diff --git a/common/client-core/src/client/topology_control/mod.rs b/common/client-core/src/client/topology_control/mod.rs index bbf32f377c..4e60278a22 100644 --- a/common/client-core/src/client/topology_control/mod.rs +++ b/common/client-core/src/client/topology_control/mod.rs @@ -6,7 +6,6 @@ pub(crate) use accessor::{TopologyAccessor, TopologyReadPermit}; use futures::StreamExt; use log::*; use nym_sphinx::addressing::nodes::NodeIdentity; -use nym_topology::provider_trait::TopologyProvider; use nym_topology::NymTopologyError; use std::time::Duration; @@ -18,7 +17,11 @@ use wasmtimer::tokio::sleep; mod accessor; pub mod geo_aware_provider; -pub(crate) mod nym_api_provider; +pub mod nym_api_provider; + +pub use geo_aware_provider::GeoAwareTopologyProvider; +pub use nym_api_provider::{Config as NymApiTopologyProviderConfig, NymApiTopologyProvider}; +pub use nym_topology::provider_trait::TopologyProvider; // TODO: move it to config later const MAX_FAILURE_COUNT: usize = 10; diff --git a/common/client-core/src/client/topology_control/nym_api_provider.rs b/common/client-core/src/client/topology_control/nym_api_provider.rs index c2b2dd9003..d3b4713c44 100644 --- a/common/client-core/src/client/topology_control/nym_api_provider.rs +++ b/common/client-core/src/client/topology_control/nym_api_provider.rs @@ -14,9 +14,10 @@ use url::Url; pub const DEFAULT_MIN_MIXNODE_PERFORMANCE: u8 = 50; pub const DEFAULT_MIN_GATEWAY_PERFORMANCE: u8 = 50; -pub(crate) struct Config { - pub(crate) min_mixnode_performance: u8, - pub(crate) min_gateway_performance: u8, +#[derive(Debug)] +pub struct Config { + pub min_mixnode_performance: u8, + pub min_gateway_performance: u8, } impl Default for Config { @@ -29,7 +30,7 @@ impl Default for Config { } } -pub(crate) struct NymApiTopologyProvider { +pub struct NymApiTopologyProvider { config: Config, validator_client: nym_validator_client::client::NymApiClient, @@ -40,7 +41,7 @@ pub(crate) struct NymApiTopologyProvider { } impl NymApiTopologyProvider { - pub(crate) fn new( + pub fn new( config: Config, mut nym_api_urls: Vec, client_version: String, diff --git a/common/topology/src/lib.rs b/common/topology/src/lib.rs index 62c0c378bc..6e89cf2753 100644 --- a/common/topology/src/lib.rs +++ b/common/topology/src/lib.rs @@ -286,6 +286,10 @@ impl NymTopology { self.get_gateway(gateway_identity).is_some() } + pub fn insert_gateway(&mut self, gateway: gateway::LegacyNode) { + self.gateways.push(gateway) + } + pub fn set_gateways(&mut self, gateways: Vec) { self.gateways = gateways } diff --git a/gateway/Cargo.toml b/gateway/Cargo.toml index f910f13b6e..8c6014aef6 100644 --- a/gateway/Cargo.toml +++ b/gateway/Cargo.toml @@ -76,9 +76,11 @@ nym-network-defaults = { path = "../common/network-defaults" } nym-network-requester = { path = "../service-providers/network-requester" } nym-node-http-api = { path = "../nym-node/nym-node-http-api" } nym-pemstore = { path = "../common/pemstore" } +nym-sdk = { path = "../sdk/rust/nym-sdk" } nym-sphinx = { path = "../common/nymsphinx" } nym-statistics-common = { path = "../common/statistics" } nym-task = { path = "../common/task" } +nym-topology = { path = "../common/topology" } nym-types = { path = "../common/types" } nym-validator-client = { path = "../common/client-libs/validator-client" } nym-ip-packet-router = { path = "../service-providers/ip-packet-router" } diff --git a/gateway/src/node/helpers.rs b/gateway/src/node/helpers.rs index 854254e215..8823a8e931 100644 --- a/gateway/src/node/helpers.rs +++ b/gateway/src/node/helpers.rs @@ -3,13 +3,18 @@ use crate::config::Config; use crate::error::GatewayError; - +use async_trait::async_trait; use nym_crypto::asymmetric::encryption; use nym_gateway_storage::PersistentStorage; use nym_pemstore::traits::PemStorableKeyPair; use nym_pemstore::KeyPairPath; - +use nym_sdk::{NymApiTopologyProvider, NymApiTopologyProviderConfig, UserAgent}; +use nym_topology::{gateway, NymTopology, TopologyProvider}; use std::path::Path; +use std::sync::Arc; +use tokio::sync::Mutex; +use tracing::debug; +use url::Url; pub async fn load_network_requester_config>( id: &str, @@ -93,3 +98,56 @@ pub(crate) fn load_sphinx_keys(config: &Config) -> Result>, +} + +impl GatewayTopologyProvider { + pub fn new( + gateway_node: gateway::LegacyNode, + user_agent: UserAgent, + nym_api_url: Vec, + ) -> GatewayTopologyProvider { + GatewayTopologyProvider { + inner: Arc::new(Mutex::new(GatewayTopologyProviderInner { + inner: NymApiTopologyProvider::new( + NymApiTopologyProviderConfig { + min_mixnode_performance: 50, + min_gateway_performance: 0, + }, + nym_api_url, + env!("CARGO_PKG_VERSION").to_string(), + Some(user_agent), + ), + gateway_node, + })), + } + } +} + +struct GatewayTopologyProviderInner { + inner: NymApiTopologyProvider, + gateway_node: gateway::LegacyNode, +} + +#[async_trait] +impl TopologyProvider for GatewayTopologyProvider { + async fn get_new_topology(&mut self) -> Option { + let mut guard = self.inner.lock().await; + match guard.inner.get_new_topology().await { + None => None, + Some(mut base) => { + if !base.gateway_exists(&guard.gateway_node.identity_key) { + debug!( + "{} didn't exist in topology. inserting it.", + guard.gateway_node.identity_key + ); + base.insert_gateway(guard.gateway_node.clone()); + } + Some(base) + } + } + } +} diff --git a/gateway/src/node/mod.rs b/gateway/src/node/mod.rs index c095f1fa86..e899919d95 100644 --- a/gateway/src/node/mod.rs +++ b/gateway/src/node/mod.rs @@ -12,9 +12,12 @@ use crate::http::HttpApiBuilder; use crate::node::client_handling::active_clients::ActiveClientsStore; use crate::node::client_handling::embedded_clients::{LocalEmbeddedClientHandle, MessageRouter}; use crate::node::client_handling::websocket; -use crate::node::helpers::{initialise_main_storage, load_network_requester_config}; +use crate::node::helpers::{ + initialise_main_storage, load_network_requester_config, GatewayTopologyProvider, +}; use crate::node::mixnet_handling::receiver::connection_handler::ConnectionHandler; use futures::channel::{mpsc, oneshot}; +use nym_bin_common::bin_info; use nym_credential_verification::ecash::{ credential_sender::CredentialHandlerConfig, EcashManager, }; @@ -25,13 +28,15 @@ use nym_network_requester::{LocalGateway, NRServiceProviderBuilder, RequestFilte use nym_node_http_api::state::metrics::SharedSessionStats; use nym_statistics_common::events::{self, StatsEventSender}; use nym_task::{TaskClient, TaskHandle, TaskManager}; +use nym_topology::NetworkAddress; use nym_types::gateway::GatewayNodeDetailsResponse; +use nym_validator_client::client::NodeId; use nym_validator_client::nyxd::{Coin, CosmWasmClient}; use nym_validator_client::{nyxd, DirectSigningHttpRpcNyxdClient}; use rand::seq::SliceRandom; use rand::thread_rng; use statistics::GatewayStatisticsCollector; -use std::net::SocketAddr; +use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use std::path::PathBuf; use std::sync::Arc; use tracing::*; @@ -225,6 +230,39 @@ impl Gateway { crate::helpers::node_details(&self.config).await } + fn gateway_topology_provider(&self) -> GatewayTopologyProvider { + GatewayTopologyProvider::new( + self.as_topology_node(), + bin_info!().into(), + self.config.gateway.nym_api_urls.clone(), + ) + } + + fn as_topology_node(&self) -> nym_topology::gateway::LegacyNode { + let ip = self + .config + .host + .public_ips + .first() + .copied() + .unwrap_or(IpAddr::V4(Ipv4Addr::LOCALHOST)); + let mix_host = SocketAddr::new(ip, self.config.gateway.mix_port); + + nym_topology::gateway::LegacyNode { + // those fields are irrelevant for the purposes of routing so it's fine if they're inaccurate. + // the only thing that matters is the identity key (and maybe version) + node_id: NodeId::MAX, + mix_host, + host: NetworkAddress::IpAddr(ip), + clients_ws_port: self.config.gateway.clients_port, + clients_wss_port: self.config.gateway.clients_wss_port, + sphinx_key: *self.sphinx_keypair.public_key(), + + identity_key: *self.identity_keypair.public_key(), + version: env!("CARGO_PKG_VERSION").into(), + } + } + fn start_mix_socket_listener( &self, ack_sender: MixForwardingSender, @@ -257,6 +295,7 @@ impl Gateway { async fn start_authenticator( &mut self, forwarding_channel: MixForwardingSender, + topology_provider: GatewayTopologyProvider, shutdown: TaskClient, ecash_verifier: Arc>, ) -> Result> @@ -304,6 +343,7 @@ impl Gateway { .with_shutdown(shutdown.fork("authenticator")) .with_wait_for_gateway(true) .with_minimum_gateway_performance(0) + .with_custom_topology_provider(Box::new(topology_provider)) .with_on_start(on_start_tx); if let Some(custom_mixnet) = &opts.custom_mixnet_path { @@ -352,6 +392,7 @@ impl Gateway { async fn start_authenticator( &self, _forwarding_channel: MixForwardingSender, + _topology_provider: GatewayTopologyProvider, _shutdown: TaskClient, _ecash_verifier: Arc>, ) -> Result> { @@ -424,6 +465,7 @@ impl Gateway { async fn start_network_requester( &self, forwarding_channel: MixForwardingSender, + topology_provider: GatewayTopologyProvider, shutdown: TaskClient, ) -> Result { info!("Starting network requester..."); @@ -451,6 +493,7 @@ impl Gateway { .with_custom_gateway_transceiver(Box::new(transceiver)) .with_wait_for_gateway(true) .with_minimum_gateway_performance(0) + .with_custom_topology_provider(Box::new(topology_provider)) .with_on_start(on_start_tx); if let Some(custom_mixnet) = &nr_opts.custom_mixnet_path { @@ -488,6 +531,7 @@ impl Gateway { async fn start_ip_packet_router( &self, forwarding_channel: MixForwardingSender, + topology_provider: GatewayTopologyProvider, shutdown: TaskClient, ) -> Result { info!("Starting IP packet provider..."); @@ -516,6 +560,7 @@ impl Gateway { .with_custom_gateway_transceiver(Box::new(transceiver)) .with_wait_for_gateway(true) .with_minimum_gateway_performance(0) + .with_custom_topology_provider(Box::new(topology_provider)) .with_on_start(on_start_tx); if let Some(custom_mixnet) = &ip_opts.custom_mixnet_path { @@ -632,6 +677,8 @@ impl Gateway { shutdown.fork("statistics::GatewayStatisticsCollector"), ); + let topology_provider = self.gateway_topology_provider(); + let handler_config = CredentialHandlerConfig { revocation_bandwidth_penalty: self .config @@ -680,6 +727,7 @@ impl Gateway { let embedded_nr = self .start_network_requester( mix_forwarding_channel.clone(), + topology_provider.clone(), shutdown.fork("NetworkRequester"), ) .await?; @@ -695,6 +743,7 @@ impl Gateway { let embedded_ip_sp = self .start_ip_packet_router( mix_forwarding_channel.clone(), + topology_provider.clone(), shutdown.fork("ip_service_provider"), ) .await?; @@ -707,6 +756,7 @@ impl Gateway { let embedded_auth = self .start_authenticator( mix_forwarding_channel, + topology_provider, shutdown.fork("authenticator"), ecash_verifier, ) diff --git a/sdk/rust/nym-sdk/src/lib.rs b/sdk/rust/nym-sdk/src/lib.rs index 0707730ec6..545090e3ae 100644 --- a/sdk/rust/nym-sdk/src/lib.rs +++ b/sdk/rust/nym-sdk/src/lib.rs @@ -10,7 +10,13 @@ pub mod mixnet; pub mod tcp_proxy; pub use error::{Error, Result}; -pub use nym_client_core::client::mix_traffic::transceiver::*; +pub use nym_client_core::client::{ + mix_traffic::transceiver::*, + topology_control::{ + GeoAwareTopologyProvider, NymApiTopologyProvider, NymApiTopologyProviderConfig, + TopologyProvider, + }, +}; pub use nym_network_defaults::{ ChainDetails, DenomDetails, DenomDetailsOwned, NymContracts, NymNetworkDetails, ValidatorDetails, diff --git a/service-providers/authenticator/src/authenticator.rs b/service-providers/authenticator/src/authenticator.rs index d5a69e7d44..f36ed25267 100644 --- a/service-providers/authenticator/src/authenticator.rs +++ b/service-providers/authenticator/src/authenticator.rs @@ -128,7 +128,9 @@ impl Authenticator { // Connect to the mixnet let mixnet_client = crate::mixnet_client::create_mixnet_client( &self.config.base, - task_handle.get_handle().named("nym_sdk::MixnetClient"), + task_handle + .get_handle() + .named("nym_sdk::MixnetClient[AUTH]"), self.custom_gateway_transceiver, self.custom_topology_provider, self.wait_for_gateway, diff --git a/service-providers/ip-packet-router/src/ip_packet_router.rs b/service-providers/ip-packet-router/src/ip_packet_router.rs index 780a550f1a..e002559ca1 100644 --- a/service-providers/ip-packet-router/src/ip_packet_router.rs +++ b/service-providers/ip-packet-router/src/ip_packet_router.rs @@ -133,7 +133,7 @@ impl IpPacketRouter { // Connect to the mixnet let mixnet_client = crate::mixnet_client::create_mixnet_client( &self.config.base, - task_handle.get_handle().named("nym_sdk::MixnetClient"), + task_handle.get_handle().named("nym_sdk::MixnetClient[IPR]"), self.custom_gateway_transceiver, self.custom_topology_provider, self.wait_for_gateway, diff --git a/service-providers/network-requester/src/core.rs b/service-providers/network-requester/src/core.rs index f9a6b3e5aa..6ba73731c4 100644 --- a/service-providers/network-requester/src/core.rs +++ b/service-providers/network-requester/src/core.rs @@ -239,7 +239,7 @@ impl NRServiceProviderBuilder { // Connect to the mixnet let mixnet_client = create_mixnet_client( &self.config.base, - shutdown.get_handle().named("nym_sdk::MixnetClient"), + shutdown.get_handle().named("nym_sdk::MixnetClient[NR]"), self.custom_gateway_transceiver, self.custom_topology_provider, self.wait_for_gateway, From bfa3825d7053eb24c7616b5e2b0073cd352b6e53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bogdan-=C8=98tefan=20Neac=C5=9Fu?= Date: Fri, 25 Oct 2024 14:08:52 +0300 Subject: [PATCH 14/48] Pass the Poisson flag on authenticator config (#5037) --- nym-node/src/config/entry_gateway.rs | 6 +++++- nym-node/src/config/exit_gateway.rs | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/nym-node/src/config/entry_gateway.rs b/nym-node/src/config/entry_gateway.rs index 6807e55d58..f7c0a51c06 100644 --- a/nym-node/src/config/entry_gateway.rs +++ b/nym-node/src/config/entry_gateway.rs @@ -154,7 +154,7 @@ pub fn ephemeral_entry_gateway_config( config: Config, mnemonic: &bip39::Mnemonic, ) -> Result { - let auth_opts = LocalAuthenticatorOpts { + let mut auth_opts = LocalAuthenticatorOpts { config: nym_authenticator::Config { base: nym_client_core_config_types::Config { client: base_client_config(&config), @@ -173,6 +173,10 @@ pub fn ephemeral_entry_gateway_config( custom_mixnet_path: None, }; + if config.authenticator.debug.disable_poisson_rate { + auth_opts.config.base.set_no_poisson_process(); + } + let wg_opts = LocalWireguardOpts { config: super::Wireguard { enabled: config.wireguard.enabled, diff --git a/nym-node/src/config/exit_gateway.rs b/nym-node/src/config/exit_gateway.rs index 4c3c55fad2..0fc6b9e8ee 100644 --- a/nym-node/src/config/exit_gateway.rs +++ b/nym-node/src/config/exit_gateway.rs @@ -243,7 +243,7 @@ pub fn ephemeral_exit_gateway_config( ipr_opts.config.base.set_no_poisson_process() } - let auth_opts = LocalAuthenticatorOpts { + let mut auth_opts = LocalAuthenticatorOpts { config: nym_authenticator::Config { base: nym_client_core_config_types::Config { client: base_client_config(&config), @@ -262,6 +262,10 @@ pub fn ephemeral_exit_gateway_config( custom_mixnet_path: None, }; + if config.authenticator.debug.disable_poisson_rate { + auth_opts.config.base.set_no_poisson_process(); + } + let pub_id_path = config .storage_paths .keys From e16a73338e6defe1e6c3e27654dd5dddd726f527 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Stuczy=C5=84ski?= Date: Fri, 25 Oct 2024 12:34:25 +0100 Subject: [PATCH 15/48] bugfix: use bonded nym-nodes for determining initial network monitor nodes (#5039) --- .../src/network_monitor/monitor/preparer.rs | 40 ++++++++++--------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/nym-api/src/network_monitor/monitor/preparer.rs b/nym-api/src/network_monitor/monitor/preparer.rs index 69a0ae3e4d..63462b559a 100644 --- a/nym-api/src/network_monitor/monitor/preparer.rs +++ b/nym-api/src/network_monitor/monitor/preparer.rs @@ -25,7 +25,7 @@ use std::collections::{HashMap, HashSet}; use std::fmt::{self, Display, Formatter}; use std::sync::Arc; use std::time::Duration; -use tracing::{error, info, trace}; +use tracing::{debug, error, info, trace}; const DEFAULT_AVERAGE_PACKET_DELAY: Duration = Duration::from_millis(200); const DEFAULT_AVERAGE_ACK_DELAY: Duration = Duration::from_millis(200); @@ -151,34 +151,38 @@ impl PacketPreparer { self.contract_cache.wait_for_initial_values().await; self.described_cache.naive_wait_for_initial_values().await; + let described_nodes = self + .described_cache + .get() + .await + .expect("the self-describe cache should have been initialised!"); + // now wait for at least `minimum_full_routes` mixnodes per layer and `minimum_full_routes` gateway to be online info!("Waiting for minimal topology to be online"); let initialisation_backoff = Duration::from_secs(30); loop { let gateways = self.contract_cache.legacy_gateways_all().await; let mixnodes = self.contract_cache.legacy_mixnodes_all_basic().await; + let nym_nodes = self.contract_cache.nym_nodes().await; - if gateways.len() < minimum_full_routes { - self.topology_wait_backoff(initialisation_backoff).await; - continue; - } + let mut gateways_count = gateways.len(); + let mut mixnodes_count = mixnodes.len(); - let mut layer1_count = 0; - let mut layer2_count = 0; - let mut layer3_count = 0; - - for mix in mixnodes { - match mix.layer { - LegacyMixLayer::One => layer1_count += 1, - LegacyMixLayer::Two => layer2_count += 1, - LegacyMixLayer::Three => layer3_count += 1, + for nym_node in nym_nodes { + if let Some(described) = described_nodes.get_description(&nym_node.node_id()) { + if described.declared_role.mixnode { + mixnodes_count += 1; + } else if described.declared_role.entry { + gateways_count += 1; + } } } - if layer1_count >= minimum_full_routes - && layer2_count >= minimum_full_routes - && layer3_count >= minimum_full_routes - { + debug!( + "we have {mixnodes_count} possible mixnodes and {gateways_count} possible gateways" + ); + + if gateways_count >= minimum_full_routes && mixnodes_count * 3 >= minimum_full_routes { break; } From 9ca6301e1c04d1165021d32377cbeb7a1356b4af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Stuczy=C5=84ski?= Date: Fri, 25 Oct 2024 15:20:39 +0100 Subject: [PATCH 16/48] bugfix: make sure nym-nodes are also tested by network monitor (#5040) --- nym-api/src/network_monitor/monitor/mod.rs | 4 +- .../src/network_monitor/monitor/preparer.rs | 37 ++++++++++++------- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/nym-api/src/network_monitor/monitor/mod.rs b/nym-api/src/network_monitor/monitor/mod.rs index 1fdb0ad601..0f04d971c6 100644 --- a/nym-api/src/network_monitor/monitor/mod.rs +++ b/nym-api/src/network_monitor/monitor/mod.rs @@ -274,8 +274,8 @@ impl Monitor { info!("Received {}/{} packets", total_received, total_sent); let summary = self.summary_producer.produce_summary( - prepared_packets.tested_mixnodes, - prepared_packets.tested_gateways, + prepared_packets.mixnodes_under_test, + prepared_packets.gateways_under_test, received, prepared_packets.invalid_mixnodes, prepared_packets.invalid_gateways, diff --git a/nym-api/src/network_monitor/monitor/preparer.rs b/nym-api/src/network_monitor/monitor/preparer.rs index 63462b559a..8650cf257f 100644 --- a/nym-api/src/network_monitor/monitor/preparer.rs +++ b/nym-api/src/network_monitor/monitor/preparer.rs @@ -59,10 +59,10 @@ pub(crate) struct PreparedPackets { pub(super) packets: Vec, /// Vector containing list of public keys and owners of all nodes mixnodes being tested. - pub(super) tested_mixnodes: Vec, + pub(super) mixnodes_under_test: Vec, /// Vector containing list of public keys and owners of all gateways being tested. - pub(super) tested_gateways: Vec, + pub(super) gateways_under_test: Vec, /// All mixnodes that failed to get parsed correctly or were not version compatible. /// They will be marked to the validator as being down for the test. @@ -533,30 +533,41 @@ impl PacketPreparer { let mixing_nym_nodes = descriptions.mixing_nym_nodes(); let gateway_capable_nym_nodes = descriptions.entry_capable_nym_nodes(); - let (mixnodes, invalid_mixnodes) = self.filter_outdated_and_malformed_mixnodes(mixnodes); - let (gateways, invalid_gateways) = self.filter_outdated_and_malformed_gateways(gateways); + let (mut mixnodes_to_test_details, invalid_mixnodes) = + self.filter_outdated_and_malformed_mixnodes(mixnodes); + let (mut gateways_to_test_details, invalid_gateways) = + self.filter_outdated_and_malformed_gateways(gateways); - let mut tested_mixnodes = mixnodes.iter().map(|node| node.into()).collect::>(); - let mut tested_gateways = gateways.iter().map(|node| node.into()).collect::>(); + // summary of nodes that got tested + let mut mixnodes_under_test = mixnodes_to_test_details + .iter() + .map(|node| node.into()) + .collect::>(); + let mut gateways_under_test = gateways_to_test_details + .iter() + .map(|node| node.into()) + .collect::>(); // try to add nym-nodes into the fold if let Some(rewarded_set) = rewarded_set { let mut rng = thread_rng(); for mix in mixing_nym_nodes { if let Some(parsed) = self.nym_node_to_legacy_mix(&mut rng, &rewarded_set, mix) { - tested_mixnodes.push(TestableNode::from(&parsed)); + mixnodes_under_test.push(TestableNode::from(&parsed)); + mixnodes_to_test_details.push(parsed); } } } for gateway in gateway_capable_nym_nodes { if let Some(parsed) = self.nym_node_to_legacy_gateway(gateway) { - tested_gateways.push((&parsed, gateway.node_id).into()) + gateways_under_test.push((&parsed, gateway.node_id).into()); + gateways_to_test_details.push((parsed, gateway.node_id)); } } let packets_to_create = (test_routes.len() * self.per_node_test_packets) - * (tested_mixnodes.len() + tested_gateways.len()); + * (mixnodes_under_test.len() + gateways_under_test.len()); info!("Need to create {} mix packets", packets_to_create); let mut all_gateway_packets = HashMap::new(); @@ -578,7 +589,7 @@ impl PacketPreparer { #[allow(clippy::unwrap_used)] let mixnode_test_packets = mix_tester .mixnodes_test_packets( - &mixnodes, + &mixnodes_to_test_details, route_ext, self.per_node_test_packets as u32, None, @@ -592,7 +603,7 @@ impl PacketPreparer { gateway_packets.push_packets(mix_packets); // and generate test packets for gateways (note the variable recipient) - for (gateway, node_id) in &gateways { + for (gateway, node_id) in &gateways_to_test_details { let recipient = self.create_packet_sender(gateway); let gateway_identity = gateway.identity_key; let gateway_address = gateway.clients_address(); @@ -628,8 +639,8 @@ impl PacketPreparer { PreparedPackets { packets, - tested_mixnodes, - tested_gateways, + mixnodes_under_test, + gateways_under_test, invalid_mixnodes, invalid_gateways, } From 3167fb34e68f899a7c5060d575b3d03e8fe5f404 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Stuczy=C5=84ski?= Date: Fri, 25 Oct 2024 16:53:51 +0100 Subject: [PATCH 17/48] bugfix: don't assign exit gateways to standby set (#5041) --- nym-api/src/epoch_operations/rewarded_set_assignment.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nym-api/src/epoch_operations/rewarded_set_assignment.rs b/nym-api/src/epoch_operations/rewarded_set_assignment.rs index 3e401b32c5..0d5c97eacd 100644 --- a/nym-api/src/epoch_operations/rewarded_set_assignment.rs +++ b/nym-api/src/epoch_operations/rewarded_set_assignment.rs @@ -169,7 +169,7 @@ impl EpochAdvancer { let standby_eligible = all_choices .iter() .filter(|node| { - exit_gateways.contains(&node.0.node_id) + !exit_gateways.contains(&node.0.node_id) && !entry_gateways.contains(&node.0.node_id) && !mixnodes.contains(&node.0.node_id) }) From ab115082351f47b043cf3f5b7daf7abbd68573fa Mon Sep 17 00:00:00 2001 From: Simon Wicky Date: Mon, 28 Oct 2024 09:25:37 +0100 Subject: [PATCH 18/48] [Product Data] Introduce data persistence on gateways (#5022) * add stats storage to gateways * config fix * add stats storage model and logic * adapt stats collection to new storage * stats cleanup on start * change to linux only code * tweaks * modified stats cleanup + change session started * change wrong table name * store crashed session as 0 duration * adapt for sqlx 0.7 * remove unused dependencies * revert changes from gateway config, as it is broken anyway * copyright and misc stuff --------- Co-authored-by: Simon Wicky --- Cargo.lock | 14 + Cargo.toml | 1 + common/gateway-stats-storage/Cargo.toml | 34 + common/gateway-stats-storage/build.rs | 28 + .../20241017120000_create_initial_tables.sql | 26 + common/gateway-stats-storage/src/error.rs | 13 + common/gateway-stats-storage/src/lib.rs | 195 +++ common/gateway-stats-storage/src/models.rs | 109 ++ common/gateway-stats-storage/src/sessions.rs | 177 ++ gateway/Cargo.toml | 1 + gateway/src/config/persistence/paths.rs | 9 +- gateway/src/error.rs | 7 + gateway/src/node/helpers.rs | 9 + gateway/src/node/mod.rs | 39 +- gateway/src/node/statistics/mod.rs | 29 +- gateway/src/node/statistics/sessions.rs | 244 ++- nym-node/src/config/old_configs/mod.rs | 2 + .../src/config/old_configs/old_config_v3.rs | 92 +- .../src/config/old_configs/old_config_v4.rs | 1422 +++++++++++++++++ nym-node/src/config/persistence.rs | 9 + nym-node/src/config/template.rs | 7 + nym-node/src/config/upgrade_helpers.rs | 3 +- nym-node/src/node/mod.rs | 15 + 23 files changed, 2277 insertions(+), 208 deletions(-) create mode 100644 common/gateway-stats-storage/Cargo.toml create mode 100644 common/gateway-stats-storage/build.rs create mode 100644 common/gateway-stats-storage/migrations/20241017120000_create_initial_tables.sql create mode 100644 common/gateway-stats-storage/src/error.rs create mode 100644 common/gateway-stats-storage/src/lib.rs create mode 100644 common/gateway-stats-storage/src/models.rs create mode 100644 common/gateway-stats-storage/src/sessions.rs create mode 100644 nym-node/src/config/old_configs/old_config_v4.rs diff --git a/Cargo.lock b/Cargo.lock index ca24362826..0a402bea90 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5258,6 +5258,7 @@ dependencies = [ "nym-credentials-interface", "nym-crypto", "nym-gateway-requests", + "nym-gateway-stats-storage", "nym-gateway-storage", "nym-ip-packet-router", "nym-mixnet-client", @@ -5353,6 +5354,19 @@ dependencies = [ "zeroize", ] +[[package]] +name = "nym-gateway-stats-storage" +version = "0.1.0" +dependencies = [ + "nym-credentials-interface", + "nym-sphinx", + "sqlx", + "thiserror", + "time", + "tokio", + "tracing", +] + [[package]] name = "nym-gateway-storage" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index ee9b482356..6303eccca0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -54,6 +54,7 @@ members = [ "common/exit-policy", "common/gateway-requests", "common/gateway-storage", + "common/gateway-stats-storage", "common/http-api-client", "common/http-api-common", "common/inclusion-probability", diff --git a/common/gateway-stats-storage/Cargo.toml b/common/gateway-stats-storage/Cargo.toml new file mode 100644 index 0000000000..d439b34a16 --- /dev/null +++ b/common/gateway-stats-storage/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "nym-gateway-stats-storage" +version = "0.1.0" +authors.workspace = true +repository.workspace = true +homepage.workspace = true +documentation.workspace = true +edition.workspace = true +license.workspace = true + +[dependencies] +sqlx = { workspace = true, features = [ + "runtime-tokio-rustls", + "sqlite", + "macros", + "migrate", + "time", +] } +time = { workspace = true } +thiserror = { workspace = true } +tracing = { workspace = true } + +nym-sphinx = { path = "../nymsphinx" } +nym-credentials-interface = { path = "../credentials-interface" } + + +[build-dependencies] +tokio = { workspace = true, features = ["rt-multi-thread", "macros"] } +sqlx = { workspace = true, features = [ + "runtime-tokio-rustls", + "sqlite", + "macros", + "migrate", +] } diff --git a/common/gateway-stats-storage/build.rs b/common/gateway-stats-storage/build.rs new file mode 100644 index 0000000000..166349c67a --- /dev/null +++ b/common/gateway-stats-storage/build.rs @@ -0,0 +1,28 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: GPL-3.0-only + +use sqlx::{Connection, SqliteConnection}; +use std::env; + +#[tokio::main] +async fn main() { + let out_dir = env::var("OUT_DIR").unwrap(); + let database_path = format!("{}/gateway-stats-example.sqlite", out_dir); + + let mut conn = SqliteConnection::connect(&format!("sqlite://{}?mode=rwc", database_path)) + .await + .expect("Failed to create SQLx database connection"); + + sqlx::migrate!("./migrations") + .run(&mut conn) + .await + .expect("Failed to perform SQLx migrations"); + + #[cfg(target_family = "unix")] + println!("cargo:rustc-env=DATABASE_URL=sqlite://{}", &database_path); + + #[cfg(target_family = "windows")] + // for some strange reason we need to add a leading `/` to the windows path even though it's + // not a valid windows path... but hey, it works... + println!("cargo:rustc-env=DATABASE_URL=sqlite:///{}", &database_path); +} diff --git a/common/gateway-stats-storage/migrations/20241017120000_create_initial_tables.sql b/common/gateway-stats-storage/migrations/20241017120000_create_initial_tables.sql new file mode 100644 index 0000000000..aff233dff9 --- /dev/null +++ b/common/gateway-stats-storage/migrations/20241017120000_create_initial_tables.sql @@ -0,0 +1,26 @@ +/* + * Copyright 2024 - Nym Technologies SA + * SPDX-License-Identifier: GPL-3.0-only + */ + +CREATE TABLE sessions_active +( + client_address TEXT NOT NULL PRIMARY KEY UNIQUE, + start_time TIMESTAMP WITHOUT TIME ZONE NOT NULL, + typ TEXT NOT NULL +); + +CREATE TABLE sessions_finished +( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + day DATE NOT NULL, + duration_ms INTEGER NOT NULL, + typ TEXT NOT NULL +); + +CREATE TABLE sessions_unique_users +( + day DATE NOT NULL, + client_address TEXT NOT NULL, + PRIMARY KEY (day, client_address) +); \ No newline at end of file diff --git a/common/gateway-stats-storage/src/error.rs b/common/gateway-stats-storage/src/error.rs new file mode 100644 index 0000000000..b51ae6defe --- /dev/null +++ b/common/gateway-stats-storage/src/error.rs @@ -0,0 +1,13 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: GPL-3.0-only + +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum StatsStorageError { + #[error("Database experienced an internal error: {0}")] + InternalDatabaseError(#[from] sqlx::Error), + + #[error("Failed to perform database migration: {0}")] + MigrationError(#[from] sqlx::migrate::MigrateError), +} diff --git a/common/gateway-stats-storage/src/lib.rs b/common/gateway-stats-storage/src/lib.rs new file mode 100644 index 0000000000..51fd84f2d9 --- /dev/null +++ b/common/gateway-stats-storage/src/lib.rs @@ -0,0 +1,195 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: GPL-3.0-only + +use error::StatsStorageError; +use models::{ActiveSession, FinishedSession, SessionType, StoredFinishedSession}; +use nym_sphinx::DestinationAddressBytes; +use sessions::SessionManager; +use sqlx::ConnectOptions; +use std::path::Path; +use time::Date; +use tracing::{debug, error}; + +pub mod error; +pub mod models; +mod sessions; + +// note that clone here is fine as upon cloning the same underlying pool will be used +#[derive(Clone)] +pub struct PersistentStatsStorage { + session_manager: SessionManager, +} + +impl PersistentStatsStorage { + /// Initialises `PersistentStatsStorage` using the provided path. + /// + /// # Arguments + /// + /// * `database_path`: path to the database. + pub async fn init + Send>(database_path: P) -> Result { + debug!( + "Attempting to connect to database {:?}", + database_path.as_ref().as_os_str() + ); + + // TODO: we can inject here more stuff based on our gateway global config + // struct. Maybe different pool size or timeout intervals? + let opts = sqlx::sqlite::SqliteConnectOptions::new() + .filename(database_path) + .create_if_missing(true) + .disable_statement_logging(); + + // TODO: do we want auto_vacuum ? + + let connection_pool = match sqlx::SqlitePool::connect_with(opts).await { + Ok(db) => db, + Err(err) => { + error!("Failed to connect to SQLx database: {err}"); + return Err(err.into()); + } + }; + + if let Err(err) = sqlx::migrate!("./migrations").run(&connection_pool).await { + error!("Failed to perform migration on the SQLx database: {err}"); + return Err(err.into()); + } + + // the cloning here are cheap as connection pool is stored behind an Arc + Ok(PersistentStatsStorage { + session_manager: sessions::SessionManager::new(connection_pool), + }) + } + + //Sessions fn + pub async fn insert_finished_session( + &self, + date: Date, + session: FinishedSession, + ) -> Result<(), StatsStorageError> { + Ok(self + .session_manager + .insert_finished_session( + date, + session.duration.whole_milliseconds() as i64, + session.typ.to_string().into(), + ) + .await?) + } + + pub async fn get_finished_sessions( + &self, + date: Date, + ) -> Result, StatsStorageError> { + Ok(self.session_manager.get_finished_sessions(date).await?) + } + + pub async fn delete_finished_sessions( + &self, + before_date: Date, + ) -> Result<(), StatsStorageError> { + Ok(self + .session_manager + .delete_finished_sessions(before_date) + .await?) + } + + pub async fn insert_unique_user( + &self, + date: Date, + client_address_bs58: String, + ) -> Result<(), StatsStorageError> { + Ok(self + .session_manager + .insert_unique_user(date, client_address_bs58) + .await?) + } + + pub async fn get_unique_users_count(&self, date: Date) -> Result { + Ok(self.session_manager.get_unique_users_count(date).await?) + } + + pub async fn delete_unique_users(&self, before_date: Date) -> Result<(), StatsStorageError> { + Ok(self + .session_manager + .delete_unique_users(before_date) + .await?) + } + + pub async fn insert_active_session( + &self, + client_address: DestinationAddressBytes, + session: ActiveSession, + ) -> Result<(), StatsStorageError> { + Ok(self + .session_manager + .insert_active_session( + client_address.as_base58_string(), + session.start, + session.typ.to_string().into(), + ) + .await?) + } + + pub async fn update_active_session_type( + &self, + client_address: DestinationAddressBytes, + session_type: SessionType, + ) -> Result<(), StatsStorageError> { + Ok(self + .session_manager + .update_active_session_type( + client_address.as_base58_string(), + session_type.to_string().into(), + ) + .await?) + } + + pub async fn get_active_session( + &self, + client_address: DestinationAddressBytes, + ) -> Result, StatsStorageError> { + Ok(self + .session_manager + .get_active_session(client_address.as_base58_string()) + .await? + .map(Into::into)) + } + + pub async fn get_all_active_sessions(&self) -> Result, StatsStorageError> { + Ok(self + .session_manager + .get_all_active_sessions() + .await? + .into_iter() + .map(Into::into) + .collect()) + } + + pub async fn get_started_sessions_count( + &self, + start_date: Date, + ) -> Result { + Ok(self + .session_manager + .get_started_sessions_count(start_date) + .await?) + } + + pub async fn get_active_users(&self) -> Result, StatsStorageError> { + Ok(self.session_manager.get_active_users().await?) + } + + pub async fn delete_active_session( + &self, + client_address: DestinationAddressBytes, + ) -> Result<(), StatsStorageError> { + Ok(self + .session_manager + .delete_active_session(client_address.as_base58_string()) + .await?) + } + + pub async fn cleanup_active_sessions(&self) -> Result<(), StatsStorageError> { + Ok(self.session_manager.cleanup_active_sessions().await?) + } +} diff --git a/common/gateway-stats-storage/src/models.rs b/common/gateway-stats-storage/src/models.rs new file mode 100644 index 0000000000..6f53cf429a --- /dev/null +++ b/common/gateway-stats-storage/src/models.rs @@ -0,0 +1,109 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: GPL-3.0-only + +use nym_credentials_interface::TicketType; +use sqlx::prelude::FromRow; +use time::{Duration, OffsetDateTime}; + +#[derive(FromRow)] +pub struct StoredFinishedSession { + duration_ms: i64, + typ: String, +} + +impl StoredFinishedSession { + pub fn serialize(&self) -> (u64, String) { + ( + self.duration_ms as u64, //we are sure that it fits in a u64, see `fn end_at` + self.typ.clone(), + ) + } +} + +pub struct FinishedSession { + pub duration: Duration, + pub typ: SessionType, +} + +#[derive(PartialEq)] +pub enum SessionType { + Vpn, + Mixnet, + Unknown, +} + +impl SessionType { + pub fn to_string(&self) -> &str { + match self { + Self::Vpn => "vpn", + Self::Mixnet => "mixnet", + Self::Unknown => "unknown", + } + } + + pub fn from_string(s: &str) -> Self { + match s { + "vpn" => Self::Vpn, + "mixnet" => Self::Mixnet, + _ => Self::Unknown, + } + } +} + +impl From for SessionType { + fn from(value: TicketType) -> Self { + match value { + TicketType::V1MixnetEntry => Self::Mixnet, + TicketType::V1MixnetExit => Self::Mixnet, + TicketType::V1WireguardEntry => Self::Vpn, + TicketType::V1WireguardExit => Self::Vpn, + } + } +} + +#[derive(FromRow)] +pub(crate) struct StoredActiveSession { + start_time: OffsetDateTime, + typ: String, +} + +pub struct ActiveSession { + pub start: OffsetDateTime, + pub typ: SessionType, +} + +impl ActiveSession { + pub fn new(start_time: OffsetDateTime) -> Self { + ActiveSession { + start: start_time, + typ: SessionType::Unknown, + } + } + + pub fn set_type(&mut self, ticket_type: TicketType) { + self.typ = ticket_type.into(); + } + + pub fn end_at(self, stop_time: OffsetDateTime) -> Option { + let session_duration = stop_time - self.start; + //ensure duration is positive to fit in a u64 + //u64::max milliseconds is 500k millenia so no overflow issue + if session_duration > Duration::ZERO { + Some(FinishedSession { + duration: session_duration, + typ: self.typ, + }) + } else { + None + } + } +} + +impl From for ActiveSession { + fn from(value: StoredActiveSession) -> Self { + ActiveSession { + start: value.start_time, + typ: SessionType::from_string(&value.typ), + } + } +} diff --git a/common/gateway-stats-storage/src/sessions.rs b/common/gateway-stats-storage/src/sessions.rs new file mode 100644 index 0000000000..660bc684e3 --- /dev/null +++ b/common/gateway-stats-storage/src/sessions.rs @@ -0,0 +1,177 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: GPL-3.0-only + +use time::{Date, OffsetDateTime}; + +use crate::models::{StoredActiveSession, StoredFinishedSession}; + +pub(crate) type Result = std::result::Result; + +#[derive(Clone)] +pub(crate) struct SessionManager { + connection_pool: sqlx::SqlitePool, +} + +impl SessionManager { + /// Creates new instance of the `SessionsManager` with the provided sqlite connection pool. + /// + /// # Arguments + /// + /// * `connection_pool`: database connection pool to use. + pub(crate) fn new(connection_pool: sqlx::SqlitePool) -> Self { + SessionManager { connection_pool } + } + + pub(crate) async fn insert_finished_session( + &self, + date: Date, + duration_ms: i64, + typ: String, + ) -> Result<()> { + sqlx::query!( + "INSERT INTO sessions_finished (day, duration_ms, typ) VALUES (?, ?, ?)", + date, + duration_ms, + typ + ) + .execute(&self.connection_pool) + .await?; + Ok(()) + } + + pub(crate) async fn get_finished_sessions( + &self, + date: Date, + ) -> Result> { + sqlx::query_as("SELECT duration_ms, typ FROM sessions_finished WHERE day = ?") + .bind(date) + .fetch_all(&self.connection_pool) + .await + } + + pub(crate) async fn delete_finished_sessions(&self, before_date: Date) -> Result<()> { + sqlx::query!("DELETE FROM sessions_finished WHERE day <= ? ", before_date) + .execute(&self.connection_pool) + .await?; + Ok(()) + } + + pub(crate) async fn insert_unique_user( + &self, + date: Date, + client_address_b58: String, + ) -> Result<()> { + sqlx::query!( + "INSERT OR IGNORE INTO sessions_unique_users (day, client_address) VALUES (?, ?)", + date, + client_address_b58, + ) + .execute(&self.connection_pool) + .await?; + Ok(()) + } + + pub(crate) async fn get_unique_users_count(&self, date: Date) -> Result { + Ok(sqlx::query!( + "SELECT COUNT(*) as count FROM sessions_unique_users WHERE day = ?", + date + ) + .fetch_one(&self.connection_pool) + .await? + .count) + } + + pub(crate) async fn delete_unique_users(&self, before_date: Date) -> Result<()> { + sqlx::query!( + "DELETE FROM sessions_unique_users WHERE day <= ? ", + before_date + ) + .execute(&self.connection_pool) + .await?; + Ok(()) + } + + pub(crate) async fn insert_active_session( + &self, + client_address_b58: String, + start_time: OffsetDateTime, + typ: String, + ) -> Result<()> { + sqlx::query!( + "INSERT INTO sessions_active (client_address, start_time, typ) VALUES (?, ?, ?)", + client_address_b58, + start_time, + typ + ) + .execute(&self.connection_pool) + .await?; + Ok(()) + } + + pub(crate) async fn update_active_session_type( + &self, + client_address_b58: String, + typ: String, + ) -> Result<()> { + sqlx::query!( + "UPDATE sessions_active SET typ = ? WHERE client_address = ?", + typ, + client_address_b58, + ) + .execute(&self.connection_pool) + .await?; + Ok(()) + } + + pub(crate) async fn get_active_session( + &self, + client_address_b58: String, + ) -> Result> { + sqlx::query_as("SELECT start_time, typ FROM sessions_active WHERE client_address = ?") + .bind(client_address_b58) + .fetch_optional(&self.connection_pool) + .await + } + + pub(crate) async fn get_all_active_sessions(&self) -> Result> { + sqlx::query_as("SELECT start_time, typ FROM sessions_active") + .fetch_all(&self.connection_pool) + .await + } + + pub(crate) async fn get_started_sessions_count(&self, start_date: Date) -> Result { + Ok(sqlx::query!( + "SELECT COUNT(*) as count FROM sessions_active WHERE date(start_time) = ?", + start_date + ) + .fetch_one(&self.connection_pool) + .await? + .count) + } + + pub(crate) async fn get_active_users(&self) -> Result> { + Ok(sqlx::query!("SELECT client_address from sessions_active") + .fetch_all(&self.connection_pool) + .await? + .into_iter() + .map(|record| record.client_address) + .collect()) + } + + pub(crate) async fn delete_active_session(&self, client_address_b58: String) -> Result<()> { + sqlx::query!( + "DELETE FROM sessions_active WHERE client_address = ?", + client_address_b58 + ) + .execute(&self.connection_pool) + .await?; + Ok(()) + } + + pub(crate) async fn cleanup_active_sessions(&self) -> Result<()> { + sqlx::query!("DELETE FROM sessions_active") + .execute(&self.connection_pool) + .await?; + Ok(()) + } +} diff --git a/gateway/Cargo.toml b/gateway/Cargo.toml index f910f13b6e..ccfabc8c96 100644 --- a/gateway/Cargo.toml +++ b/gateway/Cargo.toml @@ -69,6 +69,7 @@ nym-credentials-interface = { path = "../common/credentials-interface" } nym-credential-verification = { path = "../common/credential-verification" } nym-crypto = { path = "../common/crypto" } nym-gateway-storage = { path = "../common/gateway-storage" } +nym-gateway-stats-storage = { path = "../common/gateway-stats-storage" } nym-gateway-requests = { path = "../common/gateway-requests" } nym-mixnet-client = { path = "../common/client-libs/mixnet-client" } nym-mixnode-common = { path = "../common/mixnode-common" } diff --git a/gateway/src/config/persistence/paths.rs b/gateway/src/config/persistence/paths.rs index c416bce4b2..4062d41bd1 100644 --- a/gateway/src/config/persistence/paths.rs +++ b/gateway/src/config/persistence/paths.rs @@ -12,6 +12,7 @@ pub const DEFAULT_PRIVATE_SPHINX_KEY_FILENAME: &str = "private_sphinx.pem"; pub const DEFAULT_PUBLIC_SPHINX_KEY_FILENAME: &str = "public_sphinx.pem"; pub const DEFAULT_CLIENTS_STORAGE_FILENAME: &str = "db.sqlite"; +pub const DEFAULT_STATS_STORAGE_FILENAME: &str = "stats.sqlite"; pub const DEFAULT_NETWORK_REQUESTER_CONFIG_FILENAME: &str = "network_requester_config.toml"; pub const DEFAULT_NETWORK_REQUESTER_DATA_DIR: &str = "network-requester-data"; @@ -39,6 +40,9 @@ pub struct GatewayPaths { #[serde(alias = "persistent_storage")] pub clients_storage: PathBuf, + /// Path to sqlite database containing all persistent stats data. + pub stats_storage: PathBuf, + /// Path to the configuration of the embedded network requester. #[serde(deserialize_with = "de_maybe_stringified")] pub network_requester_config: Option, @@ -54,7 +58,9 @@ impl GatewayPaths { pub fn new_default>(id: P) -> Self { GatewayPaths { keys: KeysPaths::new_default(id.as_ref()), - clients_storage: default_data_directory(id).join(DEFAULT_CLIENTS_STORAGE_FILENAME), + clients_storage: default_data_directory(id.as_ref()) + .join(DEFAULT_CLIENTS_STORAGE_FILENAME), + stats_storage: default_data_directory(id).join(DEFAULT_STATS_STORAGE_FILENAME), // node_description: default_config_filepath(id).join(DEFAULT_DESCRIPTION_FILENAME), network_requester_config: None, ip_packet_router_config: None, @@ -70,6 +76,7 @@ impl GatewayPaths { public_sphinx_key_file: Default::default(), }, clients_storage: Default::default(), + stats_storage: Default::default(), network_requester_config: None, ip_packet_router_config: None, } diff --git a/gateway/src/error.rs b/gateway/src/error.rs index dc37c4c710..ca2c9fd7bd 100644 --- a/gateway/src/error.rs +++ b/gateway/src/error.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-3.0-only use nym_authenticator::error::AuthenticatorError; +use nym_gateway_stats_storage::error::StatsStorageError; use nym_gateway_storage::error::StorageError; use nym_ip_packet_router::error::IpPacketRouterError; use nym_network_requester::error::{ClientCoreError, NetworkRequesterError}; @@ -115,6 +116,12 @@ pub enum GatewayError { source: StorageError, }, + #[error("stats storage failure: {source}")] + StatsStorageError { + #[from] + source: StatsStorageError, + }, + #[error("Path to network requester configuration file hasn't been specified. Perhaps try to run `setup-network-requester`?")] UnspecifiedNetworkRequesterConfig, diff --git a/gateway/src/node/helpers.rs b/gateway/src/node/helpers.rs index 854254e215..80f504dae1 100644 --- a/gateway/src/node/helpers.rs +++ b/gateway/src/node/helpers.rs @@ -5,6 +5,7 @@ use crate::config::Config; use crate::error::GatewayError; use nym_crypto::asymmetric::encryption; +use nym_gateway_stats_storage::PersistentStatsStorage; use nym_gateway_storage::PersistentStorage; use nym_pemstore::traits::PemStorableKeyPair; use nym_pemstore::KeyPairPath; @@ -74,6 +75,14 @@ pub(crate) async fn initialise_main_storage( Ok(PersistentStorage::init(path, retrieval_limit).await?) } +pub(crate) async fn initialise_stats_storage( + config: &Config, +) -> Result { + let path = &config.storage_paths.stats_storage; + + Ok(PersistentStatsStorage::init(path).await?) +} + pub fn load_keypair( paths: KeyPairPath, name: impl Into, diff --git a/gateway/src/node/mod.rs b/gateway/src/node/mod.rs index c095f1fa86..738d6f3083 100644 --- a/gateway/src/node/mod.rs +++ b/gateway/src/node/mod.rs @@ -12,7 +12,9 @@ use crate::http::HttpApiBuilder; use crate::node::client_handling::active_clients::ActiveClientsStore; use crate::node::client_handling::embedded_clients::{LocalEmbeddedClientHandle, MessageRouter}; use crate::node::client_handling::websocket; -use crate::node::helpers::{initialise_main_storage, load_network_requester_config}; +use crate::node::helpers::{ + initialise_main_storage, initialise_stats_storage, load_network_requester_config, +}; use crate::node::mixnet_handling::receiver::connection_handler::ConnectionHandler; use futures::channel::{mpsc, oneshot}; use nym_credential_verification::ecash::{ @@ -41,6 +43,7 @@ pub(crate) mod helpers; pub(crate) mod mixnet_handling; pub(crate) mod statistics; +pub use nym_gateway_stats_storage::PersistentStatsStorage; pub use nym_gateway_storage::{PersistentStorage, Storage}; // TODO: should this struct live here? @@ -96,6 +99,8 @@ pub async fn create_gateway( let storage = initialise_main_storage(&config).await?; + let stats_storage = initialise_stats_storage(&config).await?; + let nr_opts = network_requester_config.map(|config| LocalNetworkRequesterOpts { config: config.clone(), custom_mixnet_path: custom_mixnet.clone(), @@ -106,7 +111,7 @@ pub async fn create_gateway( custom_mixnet_path: custom_mixnet.clone(), }); - Gateway::new(config, nr_opts, ip_opts, storage) + Gateway::new(config, nr_opts, ip_opts, storage, stats_storage) } #[derive(Debug, Clone)] @@ -147,7 +152,9 @@ pub struct Gateway { /// x25519 keypair used for Diffie-Hellman. Currently only used for sphinx key derivation. sphinx_keypair: Arc, - storage: St, + client_storage: St, + + stats_storage: PersistentStatsStorage, wireguard_data: Option, @@ -163,10 +170,12 @@ impl Gateway { config: Config, network_requester_opts: Option, ip_packet_router_opts: Option, - storage: St, + client_storage: St, + stats_storage: PersistentStatsStorage, ) -> Result { Ok(Gateway { - storage, + client_storage, + stats_storage, identity_keypair: Arc::new(load_identity_keys(&config)?), sphinx_keypair: Arc::new(helpers::load_sphinx_keys(&config)?), config, @@ -179,7 +188,7 @@ impl Gateway { task_client: None, }) } - + #[allow(clippy::too_many_arguments)] pub fn new_loaded( config: Config, network_requester_opts: Option, @@ -187,7 +196,8 @@ impl Gateway { authenticator_opts: Option, identity_keypair: Arc, sphinx_keypair: Arc, - storage: St, + client_storage: St, + stats_storage: PersistentStatsStorage, ) -> Self { Gateway { config, @@ -196,7 +206,8 @@ impl Gateway { authenticator_opts, identity_keypair, sphinx_keypair, - storage, + client_storage, + stats_storage, wireguard_data: None, session_stats: None, run_http_server: true, @@ -240,7 +251,7 @@ impl Gateway { let connection_handler = ConnectionHandler::new( packet_processor, - self.storage.clone(), + self.client_storage.clone(), ack_sender, active_clients_store, ); @@ -275,7 +286,7 @@ impl Gateway { forwarding_channel, router_tx, ); - let all_peers = self.storage.get_all_wireguard_peers().await?; + let all_peers = self.client_storage.get_all_wireguard_peers().await?; let used_private_network_ips = all_peers .iter() .cloned() @@ -330,7 +341,7 @@ impl Gateway { .start_with_shutdown(router_shutdown); let wg_api = nym_wireguard::start_wireguard( - self.storage.clone(), + self.client_storage.clone(), all_peers, shutdown, wireguard_data, @@ -377,7 +388,7 @@ impl Gateway { let shared_state = websocket::CommonHandlerState { ecash_verifier, - storage: self.storage.clone(), + storage: self.client_storage.clone(), local_identity: Arc::clone(&self.identity_keypair), only_coconut_credentials: self.config.gateway.only_coconut_credentials, bandwidth_cfg: (&self.config).into(), @@ -415,7 +426,7 @@ impl Gateway { info!("Starting gateway stats collector..."); let (mut stats_collector, stats_event_sender) = - GatewayStatisticsCollector::new(shared_session_stats); + GatewayStatisticsCollector::new(shared_session_stats, self.stats_storage.clone()); tokio::spawn(async move { stats_collector.run(shutdown).await }); stats_event_sender } @@ -654,7 +665,7 @@ impl Gateway { nyxd_client, self.identity_keypair.public_key().to_bytes(), shutdown.fork("EcashVerifier"), - self.storage.clone(), + self.client_storage.clone(), ) .await?, ); diff --git a/gateway/src/node/statistics/mod.rs b/gateway/src/node/statistics/mod.rs index 53ea277db5..6cc943cba7 100644 --- a/gateway/src/node/statistics/mod.rs +++ b/gateway/src/node/statistics/mod.rs @@ -2,13 +2,14 @@ // SPDX-License-Identifier: GPL-3.0-only use futures::{channel::mpsc, StreamExt}; +use nym_gateway_stats_storage::PersistentStatsStorage; use nym_node_http_api::state::metrics::SharedSessionStats; use nym_statistics_common::events::{StatsEvent, StatsEventReceiver, StatsEventSender}; use nym_task::TaskClient; use sessions::SessionStatsHandler; use std::time::Duration; use time::OffsetDateTime; -use tracing::trace; +use tracing::{error, trace, warn}; pub mod sessions; @@ -23,21 +24,38 @@ pub(crate) struct GatewayStatisticsCollector { impl GatewayStatisticsCollector { pub fn new( shared_session_stats: SharedSessionStats, + stats_storage: PersistentStatsStorage, ) -> (GatewayStatisticsCollector, StatsEventSender) { let (stats_event_tx, stats_event_rx) = mpsc::unbounded(); + + let session_stats = SessionStatsHandler::new(shared_session_stats, stats_storage); let collector = GatewayStatisticsCollector { stats_event_rx, - session_stats: SessionStatsHandler::new(shared_session_stats), + session_stats, }; (collector, stats_event_tx) } async fn update_shared_state(&mut self, update_time: OffsetDateTime) { - self.session_stats.update_shared_state(update_time).await; + if let Err(e) = self + .session_stats + .maybe_update_shared_state(update_time) + .await + { + error!("Failed to update session stats - {e}"); + } //here goes additionnal stats handler update } + async fn on_start(&mut self) { + if let Err(e) = self.session_stats.on_start().await { + error!("Failed to cleanup session stats handler - {e}"); + } + //here goes additionnal stats handler start cleanup + } + pub async fn run(&mut self, mut shutdown: TaskClient) { + self.on_start().await; let mut update_interval = tokio::time::interval(STATISTICS_UPDATE_TIMER_INTERVAL); while !shutdown.is_shutdown() { tokio::select! { @@ -53,7 +71,10 @@ impl GatewayStatisticsCollector { Some(stat_event) = self.stats_event_rx.next() => { //dispatching event to proper handler match stat_event { - StatsEvent::SessionStatsEvent(event) => self.session_stats.handle_event(event), + StatsEvent::SessionStatsEvent(event) => { + if let Err(e) = self.session_stats.handle_event(event).await{ + warn!("Session event handling error - {e}"); + }}, } }, diff --git a/gateway/src/node/statistics/sessions.rs b/gateway/src/node/statistics/sessions.rs index 2853f75ad5..202047e7fc 100644 --- a/gateway/src/node/statistics/sessions.rs +++ b/gateway/src/node/statistics/sessions.rs @@ -2,176 +2,158 @@ // SPDX-License-Identifier: GPL-3.0-only use nym_credentials_interface::TicketType; +use nym_gateway_stats_storage::models::FinishedSession; +use nym_gateway_stats_storage::PersistentStatsStorage; +use nym_gateway_stats_storage::{error::StatsStorageError, models::ActiveSession}; use nym_node_http_api::state::metrics::SharedSessionStats; use nym_sphinx::DestinationAddressBytes; -use std::collections::{HashMap, HashSet}; use time::{Date, Duration, OffsetDateTime}; use nym_statistics_common::events::SessionEvent; -const FINISHED_SESSIONS_CAP: usize = 1_000_000; //to be on the safe side of memory blowups until persistent storage - -#[derive(PartialEq)] -enum SessionType { - Vpn, - Mixnet, - Unknown, -} - -impl SessionType { - fn to_string(&self) -> &str { - match self { - Self::Vpn => "vpn", - Self::Mixnet => "mixnet", - Self::Unknown => "unknown", - } - } -} - -impl From for SessionType { - fn from(value: TicketType) -> Self { - match value { - TicketType::V1MixnetEntry => Self::Mixnet, - TicketType::V1MixnetExit => Self::Mixnet, - TicketType::V1WireguardEntry => Self::Vpn, - TicketType::V1WireguardExit => Self::Vpn, - } - } -} - -struct FinishedSession { - duration: Duration, - typ: SessionType, -} - -impl FinishedSession { - fn serialize(&self) -> (u64, String) { - ( - self.duration.whole_milliseconds() as u64, //we are sure that it fits in a u64, see `fn end_at` - self.typ.to_string().into(), - ) - } -} - -struct ActiveSession { - start: OffsetDateTime, - typ: SessionType, -} - -impl ActiveSession { - fn new(start_time: OffsetDateTime) -> Self { - ActiveSession { - start: start_time, - typ: SessionType::Unknown, - } - } - - fn set_type(&mut self, ticket_type: TicketType) { - self.typ = ticket_type.into(); - } - - fn end_at(self, stop_time: OffsetDateTime) -> Option { - let session_duration = stop_time - self.start; - //ensure duration is positive to fit in a u64 - //u64::max milliseconds is 500k millenia so no overflow issue - if session_duration > Duration::ZERO { - Some(FinishedSession { - duration: session_duration, - typ: self.typ, - }) - } else { - None - } - } -} - pub(crate) struct SessionStatsHandler { - last_update_day: Date, + storage: PersistentStatsStorage, + current_day: Date, shared_session_stats: SharedSessionStats, - active_sessions: HashMap, - unique_users: HashSet, - sessions_started: u32, - finished_sessions: Vec, } impl SessionStatsHandler { - pub fn new(shared_session_stats: SharedSessionStats) -> Self { + pub fn new(shared_session_stats: SharedSessionStats, storage: PersistentStatsStorage) -> Self { SessionStatsHandler { - last_update_day: OffsetDateTime::now_utc().date(), + storage, + current_day: OffsetDateTime::now_utc().date(), shared_session_stats, - active_sessions: Default::default(), - unique_users: Default::default(), - sessions_started: 0, - finished_sessions: Default::default(), } } - pub(crate) fn handle_event(&mut self, event: SessionEvent) { + pub(crate) async fn handle_event( + &mut self, + event: SessionEvent, + ) -> Result<(), StatsStorageError> { match event { SessionEvent::SessionStart { start_time, client } => { - self.handle_session_start(start_time, client); + self.handle_session_start(start_time, client).await } + SessionEvent::SessionStop { stop_time, client } => { - self.handle_session_stop(stop_time, client); + self.handle_session_stop(stop_time, client).await } + SessionEvent::EcashTicket { ticket_type, client, - } => self.handle_ecash_ticket(ticket_type, client), + } => self.handle_ecash_ticket(ticket_type, client).await, } } - fn handle_session_start( + async fn handle_session_start( &mut self, start_time: OffsetDateTime, client: DestinationAddressBytes, - ) { - self.sessions_started += 1; - self.unique_users.insert(client); - self.active_sessions - .insert(client, ActiveSession::new(start_time)); - } - fn handle_session_stop(&mut self, stop_time: OffsetDateTime, client: DestinationAddressBytes) { - if let Some(session) = self.active_sessions.remove(&client) { - if let Some(finished_session) = session.end_at(stop_time) { - if self.finished_sessions.len() < FINISHED_SESSIONS_CAP { - self.finished_sessions.push(finished_session); - } - } - } + ) -> Result<(), StatsStorageError> { + self.storage + .insert_unique_user(self.current_day, client.as_base58_string()) + .await?; + self.storage + .insert_active_session(client, ActiveSession::new(start_time)) + .await?; + Ok(()) } - fn handle_ecash_ticket(&mut self, ticket_type: TicketType, client: DestinationAddressBytes) { - if let Some(active_session) = self.active_sessions.get_mut(&client) { - if active_session.typ == SessionType::Unknown { - active_session.set_type(ticket_type); + async fn handle_session_stop( + &mut self, + stop_time: OffsetDateTime, + client: DestinationAddressBytes, + ) -> Result<(), StatsStorageError> { + if let Some(session) = self.storage.get_active_session(client).await? { + if let Some(finished_session) = session.end_at(stop_time) { + self.storage + .insert_finished_session(self.current_day, finished_session) + .await?; + self.storage.delete_active_session(client).await?; } } + Ok(()) + } + + async fn handle_ecash_ticket( + &mut self, + ticket_type: TicketType, + client: DestinationAddressBytes, + ) -> Result<(), StatsStorageError> { + self.storage + .update_active_session_type(client, ticket_type.into()) + .await?; + Ok(()) + } + + pub(crate) async fn on_start(&mut self) -> Result<(), StatsStorageError> { + let yesterday = OffsetDateTime::now_utc().date() - Duration::DAY; + //publish yesterday's data if any + self.publish_stats(yesterday).await?; + //store "active" sessions as duration 0 + for active_session in self.storage.get_all_active_sessions().await? { + self.storage + .insert_finished_session( + self.current_day, + FinishedSession { + duration: Duration::ZERO, + typ: active_session.typ, + }, + ) + .await? + } + //cleanup active sessions + self.storage.cleanup_active_sessions().await?; + + //delete old entries + self.delete_old_stats(yesterday - Duration::DAY).await?; + Ok(()) } //update shared state once a day has passed, with data from the previous day - pub(crate) async fn update_shared_state(&mut self, update_time: OffsetDateTime) { - let update_date = update_time.date(); - if update_date != self.last_update_day { - { - let mut shared_state = self.shared_session_stats.write().await; - shared_state.update_time = self.last_update_day; - shared_state.unique_active_users = self.unique_users.len() as u32; - shared_state.session_started = self.sessions_started; - shared_state.sessions = self - .finished_sessions - .iter() - .map(|s| s.serialize()) - .collect(); - } - self.reset_stats(update_date); + async fn publish_stats(&mut self, stats_date: Date) -> Result<(), StatsStorageError> { + let finished_sessions = self.storage.get_finished_sessions(stats_date).await?; + let user_count = self.storage.get_unique_users_count(stats_date).await?; + let session_started = self.storage.get_started_sessions_count(stats_date).await? as u32; + { + let mut shared_state = self.shared_session_stats.write().await; + shared_state.update_time = stats_date; + shared_state.unique_active_users = user_count as u32; + shared_state.session_started = session_started; + shared_state.sessions = finished_sessions.iter().map(|s| s.serialize()).collect(); } + + Ok(()) + } + pub(crate) async fn maybe_update_shared_state( + &mut self, + update_time: OffsetDateTime, + ) -> Result<(), StatsStorageError> { + let update_date = update_time.date(); + if update_date != self.current_day { + self.publish_stats(self.current_day).await?; + self.delete_old_stats(self.current_day - Duration::DAY) + .await?; + self.reset_stats(update_date).await?; + self.current_day = update_date; + } + Ok(()) } - fn reset_stats(&mut self, reset_day: Date) { - self.last_update_day = reset_day; - self.unique_users = self.active_sessions.keys().copied().collect(); - self.finished_sessions = Default::default(); - self.sessions_started = 0; + async fn reset_stats(&mut self, reset_day: Date) -> Result<(), StatsStorageError> { + //active users reset + let new_active_users = self.storage.get_active_users().await?; + for user in new_active_users { + self.storage.insert_unique_user(reset_day, user).await?; + } + + Ok(()) + } + + async fn delete_old_stats(&mut self, delete_before: Date) -> Result<(), StatsStorageError> { + self.storage.delete_finished_sessions(delete_before).await?; + self.storage.delete_unique_users(delete_before).await?; + Ok(()) } } diff --git a/nym-node/src/config/old_configs/mod.rs b/nym-node/src/config/old_configs/mod.rs index 295cf46ed3..3b1f589858 100644 --- a/nym-node/src/config/old_configs/mod.rs +++ b/nym-node/src/config/old_configs/mod.rs @@ -4,7 +4,9 @@ mod old_config_v1; mod old_config_v2; mod old_config_v3; +mod old_config_v4; pub use old_config_v1::try_upgrade_config_v1; pub use old_config_v2::try_upgrade_config_v2; pub use old_config_v3::try_upgrade_config_v3; +pub use old_config_v4::try_upgrade_config_v4; diff --git a/nym-node/src/config/old_configs/old_config_v3.rs b/nym-node/src/config/old_configs/old_config_v3.rs index f13cfdf4a0..01ce8a9e49 100644 --- a/nym-node/src/config/old_configs/old_config_v3.rs +++ b/nym-node/src/config/old_configs/old_config_v3.rs @@ -4,13 +4,7 @@ #![allow(dead_code)] use crate::{config::*, error::KeyIOFailure}; -use entry_gateway::Debug as EntryGatewayConfigDebug; -use exit_gateway::{IpPacketRouter, IpPacketRouterDebug, NetworkRequester, NetworkRequesterDebug}; -use mixnode::{Verloc, VerlocDebug}; -use nym_client_core_config_types::{ - disk_persistence::{ClientKeysPaths, CommonClientPaths}, - DebugConfig as ClientDebugConfig, -}; +use nym_client_core_config_types::DebugConfig as ClientDebugConfig; use nym_config::serde_helpers::de_maybe_port; use nym_crypto::asymmetric::{ed25519, x25519}; use nym_network_requester::{ @@ -19,6 +13,7 @@ use nym_network_requester::{ }; use nym_pemstore::{store_key, store_keypair}; use nym_sphinx_acknowledgements::AckKey; +use old_configs::old_config_v4::*; use persistence::*; use rand::rngs::OsRng; use serde::{Deserialize, Serialize}; @@ -90,12 +85,12 @@ pub enum NodeModeV3 { ExitGateway, } -impl From for NodeMode { +impl From for NodeModeV4 { fn from(config: NodeModeV3) -> Self { match config { - NodeModeV3::Mixnode => NodeMode::Mixnode, - NodeModeV3::EntryGateway => NodeMode::EntryGateway, - NodeModeV3::ExitGateway => NodeMode::ExitGateway, + NodeModeV3::Mixnode => NodeModeV4::Mixnode, + NodeModeV3::EntryGateway => NodeModeV4::EntryGateway, + NodeModeV3::ExitGateway => NodeModeV4::ExitGateway, } } } @@ -601,23 +596,6 @@ impl AuthenticatorPathsV3 { } } - pub fn to_common_client_paths(&self) -> CommonClientPaths { - CommonClientPaths { - keys: ClientKeysPaths { - private_identity_key_file: self.private_ed25519_identity_key_file.clone(), - public_identity_key_file: self.public_ed25519_identity_key_file.clone(), - private_encryption_key_file: self.private_x25519_diffie_hellman_key_file.clone(), - public_encryption_key_file: self.public_x25519_diffie_hellman_key_file.clone(), - ack_key_file: self.ack_key_file.clone(), - }, - gateway_registrations: self.gateway_registrations.clone(), - - // not needed for embedded providers - credentials_database: Default::default(), - reply_surb_database: self.reply_surb_database.clone(), - } - } - pub fn ed25519_identity_storage_paths(&self) -> nym_pemstore::KeyPairPath { nym_pemstore::KeyPairPath::new( &self.private_ed25519_identity_key_file, @@ -963,7 +941,7 @@ pub async fn initialise( pub async fn try_upgrade_config_v3>( path: P, prev_config: Option, -) -> Result { +) -> Result { tracing::debug!("Updating from 1.1.4"); let old_cfg = if let Some(prev_config) = prev_config { prev_config @@ -981,21 +959,21 @@ pub async fn try_upgrade_config_v3>( .ok_or(NymNodeError::DataDirDerivationFailure)?, ); - let cfg = Config { + let cfg = ConfigV4 { save_path: old_cfg.save_path, id: old_cfg.id, mode: old_cfg.mode.into(), - host: Host { + host: HostV4 { public_ips: old_cfg.host.public_ips, hostname: old_cfg.host.hostname, location: old_cfg.host.location, }, - mixnet: Mixnet { + mixnet: MixnetV4 { bind_address: old_cfg.mixnet.bind_address, announce_port: None, nym_api_urls: old_cfg.mixnet.nym_api_urls, nyxd_urls: old_cfg.mixnet.nyxd_urls, - debug: MixnetDebug { + debug: MixnetDebugV4 { packet_forwarding_initial_backoff: old_cfg .mixnet .debug @@ -1009,8 +987,8 @@ pub async fn try_upgrade_config_v3>( unsafe_disable_noise: old_cfg.mixnet.debug.unsafe_disable_noise, }, }, - storage_paths: NymNodePaths { - keys: KeysPaths { + storage_paths: NymNodePathsV4 { + keys: KeysPathsV4 { private_ed25519_identity_key_file: old_cfg .storage_paths .keys @@ -1038,7 +1016,7 @@ pub async fn try_upgrade_config_v3>( }, description: old_cfg.storage_paths.description, }, - http: Http { + http: HttpV4 { bind_address: old_cfg.http.bind_address, landing_page_assets_path: old_cfg.http.landing_page_assets_path, access_token: old_cfg.http.access_token, @@ -1046,13 +1024,13 @@ pub async fn try_upgrade_config_v3>( expose_system_hardware: old_cfg.http.expose_system_hardware, expose_crypto_hardware: old_cfg.http.expose_crypto_hardware, }, - wireguard: Wireguard { + wireguard: WireguardV4 { enabled: old_cfg.wireguard.enabled, bind_address: old_cfg.wireguard.bind_address, private_ip: old_cfg.wireguard.private_ip, announced_port: old_cfg.wireguard.announced_port, private_network_prefix: old_cfg.wireguard.private_network_prefix, - storage_paths: WireguardPaths { + storage_paths: WireguardPathsV4 { private_diffie_hellman_key_file: old_cfg .wireguard .storage_paths @@ -1063,12 +1041,12 @@ pub async fn try_upgrade_config_v3>( .public_diffie_hellman_key_file, }, }, - mixnode: MixnodeConfig { - storage_paths: MixnodePaths {}, - verloc: Verloc { + mixnode: MixnodeConfigV4 { + storage_paths: MixnodePathsV4 {}, + verloc: VerlocV4 { bind_address: old_cfg.mixnode.verloc.bind_address, announce_port: None, - debug: VerlocDebug { + debug: VerlocDebugV4 { packets_per_node: old_cfg.mixnode.verloc.debug.packets_per_node, connection_timeout: old_cfg.mixnode.verloc.debug.connection_timeout, packet_timeout: old_cfg.mixnode.verloc.debug.packet_timeout, @@ -1078,16 +1056,16 @@ pub async fn try_upgrade_config_v3>( retry_timeout: old_cfg.mixnode.verloc.debug.retry_timeout, }, }, - debug: mixnode::Debug { + debug: DebugV4 { node_stats_logging_delay: old_cfg.mixnode.debug.node_stats_logging_delay, node_stats_updating_delay: old_cfg.mixnode.debug.node_stats_updating_delay, }, }, - entry_gateway: EntryGatewayConfig { - storage_paths: EntryGatewayPaths { + entry_gateway: EntryGatewayConfigV4 { + storage_paths: EntryGatewayPathsV4 { clients_storage: old_cfg.entry_gateway.storage_paths.clients_storage, cosmos_mnemonic: old_cfg.entry_gateway.storage_paths.cosmos_mnemonic, - authenticator: AuthenticatorPaths { + authenticator: AuthenticatorPathsV4 { private_ed25519_identity_key_file: old_cfg .entry_gateway .storage_paths @@ -1129,16 +1107,16 @@ pub async fn try_upgrade_config_v3>( bind_address: old_cfg.entry_gateway.bind_address, announce_ws_port: old_cfg.entry_gateway.announce_ws_port, announce_wss_port: old_cfg.entry_gateway.announce_wss_port, - debug: EntryGatewayConfigDebug { + debug: EntryGatewayConfigDebugV4 { message_retrieval_limit: old_cfg.entry_gateway.debug.message_retrieval_limit, // \/ ADDED zk_nym_tickets: Default::default(), }, }, - exit_gateway: ExitGatewayConfig { - storage_paths: ExitGatewayPaths { + exit_gateway: ExitGatewayConfigV4 { + storage_paths: ExitGatewayPathsV4 { clients_storage: exit_gateway_paths.clients_storage, - network_requester: NetworkRequesterPaths { + network_requester: NetworkRequesterPathsV4 { private_ed25519_identity_key_file: old_cfg .exit_gateway .storage_paths @@ -1175,7 +1153,7 @@ pub async fn try_upgrade_config_v3>( .network_requester .gateway_registrations, }, - ip_packet_router: IpPacketRouterPaths { + ip_packet_router: IpPacketRouterPathsV4 { private_ed25519_identity_key_file: old_cfg .exit_gateway .storage_paths @@ -1212,7 +1190,7 @@ pub async fn try_upgrade_config_v3>( .ip_packet_router .gateway_registrations, }, - authenticator: AuthenticatorPaths { + authenticator: AuthenticatorPathsV4 { private_ed25519_identity_key_file: old_cfg .exit_gateway .storage_paths @@ -1252,8 +1230,8 @@ pub async fn try_upgrade_config_v3>( }, open_proxy: old_cfg.exit_gateway.open_proxy, upstream_exit_policy_url: old_cfg.exit_gateway.upstream_exit_policy_url, - network_requester: NetworkRequester { - debug: NetworkRequesterDebug { + network_requester: NetworkRequesterV4 { + debug: NetworkRequesterDebugV4 { enabled: old_cfg.exit_gateway.network_requester.debug.enabled, disable_poisson_rate: old_cfg .exit_gateway @@ -1263,8 +1241,8 @@ pub async fn try_upgrade_config_v3>( client_debug: old_cfg.exit_gateway.network_requester.debug.client_debug, }, }, - ip_packet_router: IpPacketRouter { - debug: IpPacketRouterDebug { + ip_packet_router: IpPacketRouterV4 { + debug: IpPacketRouterDebugV4 { enabled: old_cfg.exit_gateway.ip_packet_router.debug.enabled, disable_poisson_rate: old_cfg .exit_gateway @@ -1277,7 +1255,7 @@ pub async fn try_upgrade_config_v3>( debug: Default::default(), }, authenticator: Default::default(), - logging: LoggingSettings {}, + logging: LoggingSettingsV4 {}, }; Ok(cfg) diff --git a/nym-node/src/config/old_configs/old_config_v4.rs b/nym-node/src/config/old_configs/old_config_v4.rs new file mode 100644 index 0000000000..95fd2714ec --- /dev/null +++ b/nym-node/src/config/old_configs/old_config_v4.rs @@ -0,0 +1,1422 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: GPL-3.0-only + +#![allow(dead_code)] + +use crate::{config::*, error::KeyIOFailure}; +use entry_gateway::{Debug as EntryGatewayConfigDebug, ZkNymTicketHandlerDebug}; +use exit_gateway::{ + Debug as ExitGatewayConfigDebug, IpPacketRouter, IpPacketRouterDebug, NetworkRequester, + NetworkRequesterDebug, +}; +use mixnode::{Verloc, VerlocDebug}; +use nym_client_core_config_types::{ + disk_persistence::{ClientKeysPaths, CommonClientPaths}, + DebugConfig as ClientDebugConfig, +}; +use nym_config::{defaults::TICKETBOOK_VALIDITY_DAYS, serde_helpers::de_maybe_port}; +use nym_crypto::asymmetric::{ed25519, x25519}; +use nym_network_requester::{ + set_active_gateway, setup_fs_gateways_storage, store_gateway_details, CustomGatewayDetails, + GatewayDetails, +}; +use nym_pemstore::{store_key, store_keypair}; +use nym_sphinx_acknowledgements::AckKey; +use persistence::*; +use rand::rngs::OsRng; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)] +#[serde(deny_unknown_fields)] +pub struct WireguardPathsV4 { + pub private_diffie_hellman_key_file: PathBuf, + pub public_diffie_hellman_key_file: PathBuf, +} + +impl WireguardPathsV4 { + pub fn new>(data_dir: P) -> Self { + let data_dir = data_dir.as_ref(); + WireguardPathsV4 { + private_diffie_hellman_key_file: data_dir + .join(persistence::DEFAULT_X25519_WG_DH_KEY_FILENAME), + public_diffie_hellman_key_file: data_dir + .join(persistence::DEFAULT_X25519_WG_PUBLIC_DH_KEY_FILENAME), + } + } + + pub fn x25519_wireguard_storage_paths(&self) -> nym_pemstore::KeyPairPath { + nym_pemstore::KeyPairPath::new( + &self.private_diffie_hellman_key_file, + &self.public_diffie_hellman_key_file, + ) + } +} + +#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)] +#[serde(deny_unknown_fields)] +pub struct WireguardV4 { + /// Specifies whether the wireguard service is enabled on this node. + pub enabled: bool, + + /// Socket address this node will use for binding its wireguard interface. + /// default: `0.0.0.0:51822` + pub bind_address: SocketAddr, + + /// Ip address of the private wireguard network. + /// default: `10.1.0.0` + pub private_ip: IpAddr, + + /// 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_port: u16, + + /// The prefix denoting the maximum number of the clients that can be connected via Wireguard. + /// The maximum value for IPv4 is 32 and for IPv6 is 128 + pub private_network_prefix: u8, + + /// Paths for wireguard keys, client registries, etc. + pub storage_paths: WireguardPathsV4, +} + +// a temporary solution until all "types" are run at the same time +#[derive(Debug, Default, Serialize, Deserialize, ValueEnum, Clone, Copy)] +#[serde(rename_all = "snake_case")] +pub enum NodeModeV4 { + #[default] + #[clap(alias = "mix")] + Mixnode, + + #[clap(alias = "entry", alias = "gateway")] + EntryGateway, + + #[clap(alias = "exit")] + ExitGateway, +} + +impl From for NodeMode { + fn from(config: NodeModeV4) -> Self { + match config { + NodeModeV4::Mixnode => NodeMode::Mixnode, + NodeModeV4::EntryGateway => NodeMode::EntryGateway, + NodeModeV4::ExitGateway => NodeMode::ExitGateway, + } + } +} + +// TODO: this is very much a WIP. we need proper ssl certificate support here +#[derive(Debug, Clone, Default, Deserialize, PartialEq, Serialize)] +#[serde(default)] +#[serde(deny_unknown_fields)] +pub struct HostV4 { + /// Ip address(es) of this host, such as 1.1.1.1 that external clients will use for connections. + /// If no values are provided, when this node gets included in the network, + /// its ip addresses will be populated by whatever value is resolved by associated nym-api. + pub public_ips: Vec, + + /// Optional hostname of this node, for example nymtech.net. + // TODO: this is temporary. to be replaced by pulling the data directly from the certs. + #[serde(deserialize_with = "de_maybe_stringified")] + pub hostname: Option, + + /// Optional ISO 3166 alpha-2 two-letter country code of the node's **physical** location + #[serde(deserialize_with = "de_maybe_stringified")] + pub location: Option, +} + +#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)] +#[serde(default)] +#[serde(deny_unknown_fields)] +pub struct MixnetDebugV4 { + /// Initial value of an exponential backoff to reconnect to dropped TCP connection when + /// forwarding sphinx packets. + #[serde(with = "humantime_serde")] + pub packet_forwarding_initial_backoff: Duration, + + /// Maximum value of an exponential backoff to reconnect to dropped TCP connection when + /// forwarding sphinx packets. + #[serde(with = "humantime_serde")] + pub packet_forwarding_maximum_backoff: Duration, + + /// Timeout for establishing initial connection when trying to forward a sphinx packet. + #[serde(with = "humantime_serde")] + pub initial_connection_timeout: Duration, + + /// Maximum number of packets that can be stored waiting to get sent to a particular connection. + pub maximum_connection_buffer_size: usize, + + /// Specifies whether this node should **NOT** use noise protocol in the connections (currently not implemented) + pub unsafe_disable_noise: bool, +} + +impl MixnetDebugV4 { + const DEFAULT_PACKET_FORWARDING_INITIAL_BACKOFF: Duration = Duration::from_millis(10_000); + const DEFAULT_PACKET_FORWARDING_MAXIMUM_BACKOFF: Duration = Duration::from_millis(300_000); + const DEFAULT_INITIAL_CONNECTION_TIMEOUT: Duration = Duration::from_millis(1_500); + const DEFAULT_MAXIMUM_CONNECTION_BUFFER_SIZE: usize = 2000; +} + +impl Default for MixnetDebugV4 { + fn default() -> Self { + MixnetDebugV4 { + packet_forwarding_initial_backoff: Self::DEFAULT_PACKET_FORWARDING_INITIAL_BACKOFF, + packet_forwarding_maximum_backoff: Self::DEFAULT_PACKET_FORWARDING_MAXIMUM_BACKOFF, + initial_connection_timeout: Self::DEFAULT_INITIAL_CONNECTION_TIMEOUT, + maximum_connection_buffer_size: Self::DEFAULT_MAXIMUM_CONNECTION_BUFFER_SIZE, + // to be changed by @SW once the implementation is there + unsafe_disable_noise: true, + } + } +} + +impl Default for MixnetV4 { + fn default() -> Self { + // SAFETY: + // our hardcoded values should always be valid + #[allow(clippy::expect_used)] + // is if there's anything set in the environment, otherwise fallback to mainnet + let nym_api_urls = if let Ok(env_value) = env::var(var_names::NYM_API) { + parse_urls(&env_value) + } else { + vec![mainnet::NYM_API.parse().expect("Invalid default API URL")] + }; + + #[allow(clippy::expect_used)] + let nyxd_urls = if let Ok(env_value) = env::var(var_names::NYXD) { + parse_urls(&env_value) + } else { + vec![mainnet::NYXD_URL.parse().expect("Invalid default nyxd URL")] + }; + + MixnetV4 { + bind_address: SocketAddr::new(inaddr_any(), DEFAULT_MIXNET_PORT), + announce_port: None, + nym_api_urls, + nyxd_urls, + debug: Default::default(), + } + } +} + +#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)] +#[serde(default)] +#[serde(deny_unknown_fields)] +pub struct MixnetV4 { + /// Address this node will bind to for listening for mixnet packets + /// default: `0.0.0.0:1789` + pub bind_address: SocketAddr, + + /// If applicable, custom port announced in the self-described API that other clients and nodes + /// will use. + /// Useful when the node is behind a proxy. + #[serde(deserialize_with = "de_maybe_port")] + pub announce_port: Option, + + /// Addresses to nym APIs from which the node gets the view of the network. + pub nym_api_urls: Vec, + + /// Addresses to nyxd which the node uses to interact with the nyx chain. + pub nyxd_urls: Vec, + + #[serde(default)] + pub debug: MixnetDebugV4, +} + +#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)] +#[serde(deny_unknown_fields)] +pub struct KeysPathsV4 { + /// Path to file containing ed25519 identity private key. + pub private_ed25519_identity_key_file: PathBuf, + + /// Path to file containing ed25519 identity public key. + pub public_ed25519_identity_key_file: PathBuf, + + /// Path to file containing x25519 sphinx private key. + pub private_x25519_sphinx_key_file: PathBuf, + + /// Path to file containing x25519 sphinx public key. + pub public_x25519_sphinx_key_file: PathBuf, + + /// Path to file containing x25519 noise private key. + pub private_x25519_noise_key_file: PathBuf, + + /// Path to file containing x25519 noise public key. + pub public_x25519_noise_key_file: PathBuf, +} + +impl KeysPathsV4 { + pub fn new>(data_dir: P) -> Self { + let data_dir = data_dir.as_ref(); + + KeysPathsV4 { + private_ed25519_identity_key_file: data_dir + .join(DEFAULT_ED25519_PRIVATE_IDENTITY_KEY_FILENAME), + public_ed25519_identity_key_file: data_dir + .join(DEFAULT_ED25519_PUBLIC_IDENTITY_KEY_FILENAME), + private_x25519_sphinx_key_file: data_dir + .join(DEFAULT_X25519_PRIVATE_SPHINX_KEY_FILENAME), + public_x25519_sphinx_key_file: data_dir.join(DEFAULT_X25519_PUBLIC_SPHINX_KEY_FILENAME), + private_x25519_noise_key_file: data_dir.join(DEFAULT_X25519_PRIVATE_NOISE_KEY_FILENAME), + public_x25519_noise_key_file: data_dir.join(DEFAULT_X25519_PUBLIC_NOISE_KEY_FILENAME), + } + } + + pub fn ed25519_identity_storage_paths(&self) -> nym_pemstore::KeyPairPath { + nym_pemstore::KeyPairPath::new( + &self.private_ed25519_identity_key_file, + &self.public_ed25519_identity_key_file, + ) + } + + pub fn x25519_sphinx_storage_paths(&self) -> nym_pemstore::KeyPairPath { + nym_pemstore::KeyPairPath::new( + &self.private_x25519_sphinx_key_file, + &self.public_x25519_sphinx_key_file, + ) + } + + pub fn x25519_noise_storage_paths(&self) -> nym_pemstore::KeyPairPath { + nym_pemstore::KeyPairPath::new( + &self.private_x25519_noise_key_file, + &self.public_x25519_noise_key_file, + ) + } +} + +#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)] +#[serde(deny_unknown_fields)] +pub struct NymNodePathsV4 { + pub keys: KeysPathsV4, + + /// Path to a file containing basic node description: human-readable name, website, details, etc. + pub description: PathBuf, +} + +#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)] +#[serde(default)] +#[serde(deny_unknown_fields)] +pub struct HttpV4 { + /// Socket address this node will use for binding its http API. + /// default: `0.0.0.0:8080` + pub bind_address: SocketAddr, + + /// Path to assets directory of custom landing page of this node. + #[serde(deserialize_with = "de_maybe_stringified")] + pub landing_page_assets_path: Option, + + /// An optional bearer token for accessing certain http endpoints. + /// Currently only used for obtaining mixnode's stats. + #[serde(default)] + pub access_token: Option, + + /// Specify whether basic system information should be exposed. + /// default: true + pub expose_system_info: bool, + + /// Specify whether basic system hardware information should be exposed. + /// This option is superseded by `expose_system_info` + /// default: true + pub expose_system_hardware: bool, + + /// Specify whether detailed system crypto hardware information should be exposed. + /// This option is superseded by `expose_system_hardware` + /// default: true + pub expose_crypto_hardware: bool, +} + +impl Default for HttpV4 { + fn default() -> Self { + HttpV4 { + bind_address: SocketAddr::new(inaddr_any(), DEFAULT_HTTP_PORT), + landing_page_assets_path: None, + access_token: None, + expose_system_info: true, + expose_system_hardware: true, + expose_crypto_hardware: true, + } + } +} + +#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)] +#[serde(deny_unknown_fields)] +pub struct MixnodePathsV4 {} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct DebugV4 { + /// Delay between each subsequent node statistics being logged to the console + #[serde(with = "humantime_serde")] + pub node_stats_logging_delay: Duration, + + /// Delay between each subsequent node statistics being updated + #[serde(with = "humantime_serde")] + pub node_stats_updating_delay: Duration, +} + +#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)] +#[serde(deny_unknown_fields)] +pub struct VerlocDebugV4 { + /// Specifies number of echo packets sent to each node during a measurement run. + pub packets_per_node: usize, + + /// Specifies maximum amount of time to wait for the connection to get established. + #[serde(with = "humantime_serde")] + pub connection_timeout: Duration, + + /// Specifies maximum amount of time to wait for the reply packet to arrive before abandoning the test. + #[serde(with = "humantime_serde")] + pub packet_timeout: Duration, + + /// Specifies delay between subsequent test packets being sent (after receiving a reply). + #[serde(with = "humantime_serde")] + pub delay_between_packets: Duration, + + /// Specifies number of nodes being tested at once. + pub tested_nodes_batch_size: usize, + + /// Specifies delay between subsequent test runs. + #[serde(with = "humantime_serde")] + pub testing_interval: Duration, + + /// Specifies delay between attempting to run the measurement again if the previous run failed + /// due to being unable to get the list of nodes. + #[serde(with = "humantime_serde")] + pub retry_timeout: Duration, +} + +#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)] +#[serde(deny_unknown_fields)] +pub struct VerlocV4 { + /// Socket address this node will use for binding its verloc API. + /// default: `0.0.0.0:1790` + pub bind_address: SocketAddr, + + #[serde(deserialize_with = "de_maybe_port")] + pub announce_port: Option, + + #[serde(default)] + pub debug: VerlocDebugV4, +} + +impl VerlocDebugV4 { + const DEFAULT_PACKETS_PER_NODE: usize = 100; + const DEFAULT_CONNECTION_TIMEOUT: Duration = Duration::from_millis(5000); + const DEFAULT_PACKET_TIMEOUT: Duration = Duration::from_millis(1500); + const DEFAULT_DELAY_BETWEEN_PACKETS: Duration = Duration::from_millis(50); + const DEFAULT_BATCH_SIZE: usize = 50; + const DEFAULT_TESTING_INTERVAL: Duration = Duration::from_secs(60 * 60 * 12); + const DEFAULT_RETRY_TIMEOUT: Duration = Duration::from_secs(60 * 30); +} + +impl Default for VerlocDebugV4 { + fn default() -> Self { + VerlocDebugV4 { + packets_per_node: Self::DEFAULT_PACKETS_PER_NODE, + connection_timeout: Self::DEFAULT_CONNECTION_TIMEOUT, + packet_timeout: Self::DEFAULT_PACKET_TIMEOUT, + delay_between_packets: Self::DEFAULT_DELAY_BETWEEN_PACKETS, + tested_nodes_batch_size: Self::DEFAULT_BATCH_SIZE, + testing_interval: Self::DEFAULT_TESTING_INTERVAL, + retry_timeout: Self::DEFAULT_RETRY_TIMEOUT, + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct MixnodeConfigV4 { + pub storage_paths: MixnodePathsV4, + + pub verloc: VerlocV4, + + #[serde(default)] + pub debug: DebugV4, +} + +impl DebugV4 { + const DEFAULT_NODE_STATS_LOGGING_DELAY: Duration = Duration::from_millis(60_000); + const DEFAULT_NODE_STATS_UPDATING_DELAY: Duration = Duration::from_millis(30_000); +} + +impl Default for DebugV4 { + fn default() -> Self { + DebugV4 { + node_stats_logging_delay: Self::DEFAULT_NODE_STATS_LOGGING_DELAY, + node_stats_updating_delay: Self::DEFAULT_NODE_STATS_UPDATING_DELAY, + } + } +} + +#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)] +#[serde(deny_unknown_fields)] +pub struct EntryGatewayPathsV4 { + /// Path to sqlite database containing all persistent data: messages for offline clients, + /// derived shared keys and available client bandwidths. + pub clients_storage: PathBuf, + + /// Path to file containing cosmos account mnemonic used for zk-nym redemption. + pub cosmos_mnemonic: PathBuf, + + pub authenticator: AuthenticatorPathsV4, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(default)] +pub struct ZkNymTicketHandlerDebugV4 { + /// Specifies the multiplier for revoking a malformed/double-spent ticket + /// (if it has to go all the way to the nym-api for verification) + /// e.g. if one ticket grants 100Mb and `revocation_bandwidth_penalty` is set to 1.5, + /// the client will lose 150Mb + pub revocation_bandwidth_penalty: f32, + + /// Specifies the interval for attempting to resolve any failed, pending operations, + /// such as ticket verification or redemption. + #[serde(with = "humantime_serde")] + pub pending_poller: Duration, + + pub minimum_api_quorum: f32, + + /// Specifies the minimum number of tickets this gateway will attempt to redeem. + pub minimum_redemption_tickets: usize, + + /// Specifies the maximum time between two subsequent tickets redemptions. + /// That's required as nym-apis will purge all ticket information for tickets older than maximum validity. + #[serde(with = "humantime_serde")] + pub maximum_time_between_redemption: Duration, +} + +impl ZkNymTicketHandlerDebugV4 { + pub const DEFAULT_REVOCATION_BANDWIDTH_PENALTY: f32 = 10.0; + pub const DEFAULT_PENDING_POLLER: Duration = Duration::from_secs(300); + pub const DEFAULT_MINIMUM_API_QUORUM: f32 = 0.8; + pub const DEFAULT_MINIMUM_REDEMPTION_TICKETS: usize = 100; + + // use min(4/5 of max validity, validity - 1), but making sure it's no greater than 1 day + // ASSUMPTION: our validity period is AT LEAST 2 days + // + // this could have been a constant, but it's more readable as a function + pub const fn default_maximum_time_between_redemption() -> Duration { + let desired_secs = TICKETBOOK_VALIDITY_DAYS * (86400 * 4) / 5; + let desired_secs_alt = (TICKETBOOK_VALIDITY_DAYS - 1) * 86400; + + // can't use `min` in const context + let target_secs = if desired_secs < desired_secs_alt { + desired_secs + } else { + desired_secs_alt + }; + + assert!( + target_secs > 86400, + "the maximum time between redemption can't be lower than 1 day!" + ); + Duration::from_secs(target_secs as u64) + } +} + +impl Default for ZkNymTicketHandlerDebugV4 { + fn default() -> Self { + ZkNymTicketHandlerDebugV4 { + revocation_bandwidth_penalty: Self::DEFAULT_REVOCATION_BANDWIDTH_PENALTY, + pending_poller: Self::DEFAULT_PENDING_POLLER, + minimum_api_quorum: Self::DEFAULT_MINIMUM_API_QUORUM, + minimum_redemption_tickets: Self::DEFAULT_MINIMUM_REDEMPTION_TICKETS, + maximum_time_between_redemption: Self::default_maximum_time_between_redemption(), + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct EntryGatewayConfigDebugV4 { + /// Number of messages from offline client that can be pulled at once (i.e. with a single SQL query) from the storage. + pub message_retrieval_limit: i64, + pub zk_nym_tickets: ZkNymTicketHandlerDebugV4, +} + +impl EntryGatewayConfigDebugV4 { + const DEFAULT_MESSAGE_RETRIEVAL_LIMIT: i64 = 100; +} + +impl Default for EntryGatewayConfigDebugV4 { + fn default() -> Self { + EntryGatewayConfigDebugV4 { + message_retrieval_limit: Self::DEFAULT_MESSAGE_RETRIEVAL_LIMIT, + zk_nym_tickets: Default::default(), + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct EntryGatewayConfigV4 { + pub storage_paths: EntryGatewayPathsV4, + + /// Indicates whether this gateway is accepting only coconut credentials for accessing the mixnet + /// or if it also accepts non-paying clients + pub enforce_zk_nyms: bool, + + /// Socket address this node will use for binding its client websocket API. + /// default: `0.0.0.0:9000` + pub bind_address: SocketAddr, + + /// Custom announced port for listening for websocket client traffic. + /// If unspecified, the value from the `bind_address` will be used instead + /// default: None + #[serde(deserialize_with = "de_maybe_port")] + pub announce_ws_port: Option, + + /// If applicable, announced port for listening for secure websocket client traffic. + /// (default: None) + #[serde(deserialize_with = "de_maybe_port")] + pub announce_wss_port: Option, + + #[serde(default)] + pub debug: EntryGatewayConfigDebugV4, +} + +#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)] +#[serde(deny_unknown_fields)] +pub struct NetworkRequesterPathsV4 { + /// Path to file containing network requester ed25519 identity private key. + pub private_ed25519_identity_key_file: PathBuf, + + /// Path to file containing network requester ed25519 identity public key. + pub public_ed25519_identity_key_file: PathBuf, + + /// Path to file containing network requester x25519 diffie hellman private key. + pub private_x25519_diffie_hellman_key_file: PathBuf, + + /// Path to file containing network requester x25519 diffie hellman public key. + pub public_x25519_diffie_hellman_key_file: PathBuf, + + /// Path to file containing key used for encrypting and decrypting the content of an + /// acknowledgement so that nobody besides the client knows which packet it refers to. + pub ack_key_file: PathBuf, + + /// Path to the persistent store for received reply surbs, unused encryption keys and used sender tags. + pub reply_surb_database: PathBuf, + + /// Normally this is a path to the file containing information about gateways used by this client, + /// i.e. details such as their public keys, owner addresses or the network information. + /// but in this case it just has the basic information of "we're using custom gateway". + /// Due to how clients are started up, this file has to exist. + pub gateway_registrations: PathBuf, + // it's possible we might have to add credential storage here for return tickets +} + +#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)] +#[serde(deny_unknown_fields)] +pub struct IpPacketRouterPathsV4 { + /// Path to file containing ip packet router ed25519 identity private key. + pub private_ed25519_identity_key_file: PathBuf, + + /// Path to file containing ip packet router ed25519 identity public key. + pub public_ed25519_identity_key_file: PathBuf, + + /// Path to file containing ip packet router x25519 diffie hellman private key. + pub private_x25519_diffie_hellman_key_file: PathBuf, + + /// Path to file containing ip packet router x25519 diffie hellman public key. + pub public_x25519_diffie_hellman_key_file: PathBuf, + + /// Path to file containing key used for encrypting and decrypting the content of an + /// acknowledgement so that nobody besides the client knows which packet it refers to. + pub ack_key_file: PathBuf, + + /// Path to the persistent store for received reply surbs, unused encryption keys and used sender tags. + pub reply_surb_database: PathBuf, + + /// Normally this is a path to the file containing information about gateways used by this client, + /// i.e. details such as their public keys, owner addresses or the network information. + /// but in this case it just has the basic information of "we're using custom gateway". + /// Due to how clients are started up, this file has to exist. + pub gateway_registrations: PathBuf, + // it's possible we might have to add credential storage here for return tickets +} + +#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)] +#[serde(deny_unknown_fields)] +pub struct AuthenticatorPathsV4 { + /// Path to file containing authenticator ed25519 identity private key. + pub private_ed25519_identity_key_file: PathBuf, + + /// Path to file containing authenticator ed25519 identity public key. + pub public_ed25519_identity_key_file: PathBuf, + + /// Path to file containing authenticator x25519 diffie hellman private key. + pub private_x25519_diffie_hellman_key_file: PathBuf, + + /// Path to file containing authenticator x25519 diffie hellman public key. + pub public_x25519_diffie_hellman_key_file: PathBuf, + + /// Path to file containing key used for encrypting and decrypting the content of an + /// acknowledgement so that nobody besides the client knows which packet it refers to. + pub ack_key_file: PathBuf, + + /// Path to the persistent store for received reply surbs, unused encryption keys and used sender tags. + pub reply_surb_database: PathBuf, + + /// Normally this is a path to the file containing information about gateways used by this client, + /// i.e. details such as their public keys, owner addresses or the network information. + /// but in this case it just has the basic information of "we're using custom gateway". + /// Due to how clients are started up, this file has to exist. + pub gateway_registrations: PathBuf, + // it's possible we might have to add credential storage here for return tickets +} + +impl AuthenticatorPathsV4 { + pub fn new>(data_dir: P) -> Self { + let data_dir = data_dir.as_ref(); + AuthenticatorPathsV4 { + private_ed25519_identity_key_file: data_dir + .join(DEFAULT_ED25519_AUTH_PRIVATE_IDENTITY_KEY_FILENAME), + public_ed25519_identity_key_file: data_dir + .join(DEFAULT_ED25519_AUTH_PUBLIC_IDENTITY_KEY_FILENAME), + private_x25519_diffie_hellman_key_file: data_dir + .join(DEFAULT_X25519_AUTH_PRIVATE_DH_KEY_FILENAME), + public_x25519_diffie_hellman_key_file: data_dir + .join(DEFAULT_X25519_AUTH_PUBLIC_DH_KEY_FILENAME), + ack_key_file: data_dir.join(DEFAULT_AUTH_ACK_KEY_FILENAME), + reply_surb_database: data_dir.join(DEFAULT_AUTH_REPLY_SURB_DB_FILENAME), + gateway_registrations: data_dir.join(DEFAULT_AUTH_GATEWAYS_DB_FILENAME), + } + } + + pub fn to_common_client_paths(&self) -> CommonClientPaths { + CommonClientPaths { + keys: ClientKeysPaths { + private_identity_key_file: self.private_ed25519_identity_key_file.clone(), + public_identity_key_file: self.public_ed25519_identity_key_file.clone(), + private_encryption_key_file: self.private_x25519_diffie_hellman_key_file.clone(), + public_encryption_key_file: self.public_x25519_diffie_hellman_key_file.clone(), + ack_key_file: self.ack_key_file.clone(), + }, + gateway_registrations: self.gateway_registrations.clone(), + + // not needed for embedded providers + credentials_database: Default::default(), + reply_surb_database: self.reply_surb_database.clone(), + } + } + + pub fn ed25519_identity_storage_paths(&self) -> nym_pemstore::KeyPairPath { + nym_pemstore::KeyPairPath::new( + &self.private_ed25519_identity_key_file, + &self.public_ed25519_identity_key_file, + ) + } + + pub fn x25519_diffie_hellman_storage_paths(&self) -> nym_pemstore::KeyPairPath { + nym_pemstore::KeyPairPath::new( + &self.private_x25519_diffie_hellman_key_file, + &self.public_x25519_diffie_hellman_key_file, + ) + } +} + +#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)] +#[serde(deny_unknown_fields)] +pub struct ExitGatewayPathsV4 { + pub clients_storage: PathBuf, + + pub network_requester: NetworkRequesterPathsV4, + + pub ip_packet_router: IpPacketRouterPathsV4, + + pub authenticator: AuthenticatorPathsV4, +} + +#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)] +pub struct AuthenticatorV4 { + #[serde(default)] + pub debug: AuthenticatorDebugV4, +} + +#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)] +#[serde(default)] +pub struct AuthenticatorDebugV4 { + /// Specifies whether authenticator service is enabled in this process. + /// This is only here for debugging purposes as exit gateway should always run + /// the authenticator. + pub enabled: bool, + + /// Disable Poisson sending rate. + /// This is equivalent to setting client_debug.traffic.disable_main_poisson_packet_distribution = true + /// (or is it (?)) + pub disable_poisson_rate: bool, + + /// Shared detailed client configuration options + #[serde(flatten)] + pub client_debug: ClientDebugConfig, +} + +impl Default for AuthenticatorDebugV4 { + fn default() -> Self { + AuthenticatorDebugV4 { + enabled: true, + disable_poisson_rate: true, + client_debug: Default::default(), + } + } +} + +#[allow(clippy::derivable_impls)] +impl Default for AuthenticatorV4 { + fn default() -> Self { + AuthenticatorV4 { + debug: Default::default(), + } + } +} + +#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)] +#[serde(default)] +pub struct IpPacketRouterDebugV4 { + /// Specifies whether ip packet routing service is enabled in this process. + /// This is only here for debugging purposes as exit gateway should always run **both** + /// network requester and an ip packet router. + pub enabled: bool, + + /// Disable Poisson sending rate. + /// This is equivalent to setting client_debug.traffic.disable_main_poisson_packet_distribution = true + /// (or is it (?)) + pub disable_poisson_rate: bool, + + /// Shared detailed client configuration options + #[serde(flatten)] + pub client_debug: ClientDebugConfig, +} + +impl Default for IpPacketRouterDebugV4 { + fn default() -> Self { + IpPacketRouterDebugV4 { + enabled: true, + disable_poisson_rate: true, + client_debug: Default::default(), + } + } +} + +#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)] +pub struct IpPacketRouterV4 { + #[serde(default)] + pub debug: IpPacketRouterDebugV4, +} + +#[allow(clippy::derivable_impls)] +impl Default for IpPacketRouterV4 { + fn default() -> Self { + IpPacketRouterV4 { + debug: Default::default(), + } + } +} + +#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)] +pub struct NetworkRequesterDebugV4 { + /// Specifies whether network requester service is enabled in this process. + /// This is only here for debugging purposes as exit gateway should always run **both** + /// network requester and an ip packet router. + pub enabled: bool, + + /// Disable Poisson sending rate. + /// This is equivalent to setting client_debug.traffic.disable_main_poisson_packet_distribution = true + /// (or is it (?)) + pub disable_poisson_rate: bool, + + /// Shared detailed client configuration options + #[serde(flatten)] + pub client_debug: ClientDebugConfig, +} + +impl Default for NetworkRequesterDebugV4 { + fn default() -> Self { + NetworkRequesterDebugV4 { + enabled: true, + disable_poisson_rate: true, + client_debug: Default::default(), + } + } +} + +#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)] +pub struct NetworkRequesterV4 { + #[serde(default)] + pub debug: NetworkRequesterDebugV4, +} + +#[allow(clippy::derivable_impls)] +impl Default for NetworkRequesterV4 { + fn default() -> Self { + NetworkRequesterV4 { + debug: Default::default(), + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct ExitGatewayDebugV4 { + /// Number of messages from offline client that can be pulled at once (i.e. with a single SQL query) from the storage. + pub message_retrieval_limit: i64, +} + +impl ExitGatewayDebugV4 { + const DEFAULT_MESSAGE_RETRIEVAL_LIMIT: i64 = 100; +} + +impl Default for ExitGatewayDebugV4 { + fn default() -> Self { + ExitGatewayDebugV4 { + message_retrieval_limit: Self::DEFAULT_MESSAGE_RETRIEVAL_LIMIT, + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct ExitGatewayConfigV4 { + pub storage_paths: ExitGatewayPathsV4, + + /// specifies whether this exit node should run in 'open-proxy' mode + /// and thus would attempt to resolve **ANY** request it receives. + pub open_proxy: bool, + + /// Specifies the url for an upstream source of the exit policy used by this node. + pub upstream_exit_policy_url: Url, + + pub network_requester: NetworkRequesterV4, + + pub ip_packet_router: IpPacketRouterV4, + + #[serde(default)] + pub debug: ExitGatewayDebugV4, +} + +#[derive(Debug, Default, Copy, Clone, Deserialize, PartialEq, Eq, Serialize)] +#[serde(deny_unknown_fields)] +pub struct LoggingSettingsV4 { + // well, we need to implement something here at some point... +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct ConfigV4 { + // additional metadata holding on-disk location of this config file + #[serde(skip)] + pub(crate) save_path: Option, + + /// Human-readable ID of this particular node. + pub id: String, + + /// Current mode of this nym-node. + /// Expect this field to be changed in the future to allow running the node in multiple modes (i.e. mixnode + gateway) + pub mode: NodeModeV4, + + pub host: HostV4, + + pub mixnet: MixnetV4, + + /// Storage paths to persistent nym-node data, such as its long term keys. + pub storage_paths: NymNodePathsV4, + + #[serde(default)] + pub http: HttpV4, + + pub wireguard: WireguardV4, + + pub mixnode: MixnodeConfigV4, + + pub entry_gateway: EntryGatewayConfigV4, + + pub exit_gateway: ExitGatewayConfigV4, + + pub authenticator: AuthenticatorV4, + + #[serde(default)] + pub logging: LoggingSettingsV4, +} + +impl NymConfigTemplate for ConfigV4 { + fn template(&self) -> &'static str { + CONFIG_TEMPLATE + } +} + +impl ConfigV4 { + pub fn save(&self) -> Result<(), NymNodeError> { + let save_location = self.save_location(); + debug!( + "attempting to save config file to '{}'", + save_location.display() + ); + save_formatted_config_to_file(self, &save_location).map_err(|source| { + NymNodeError::ConfigSaveFailure { + id: self.id.clone(), + path: save_location, + source, + } + }) + } + + pub fn save_location(&self) -> PathBuf { + self.save_path + .clone() + .unwrap_or(self.default_save_location()) + } + + pub fn default_save_location(&self) -> PathBuf { + default_config_filepath(&self.id) + } + + pub fn default_data_directory>(config_path: P) -> Result { + let config_path = config_path.as_ref(); + + // we got a proper path to the .toml file + let Some(config_dir) = config_path.parent() else { + error!( + "'{}' does not have a parent directory. Have you pointed to the fs root?", + config_path.display() + ); + return Err(NymNodeError::DataDirDerivationFailure); + }; + + let Some(config_dir_name) = config_dir.file_name() else { + error!( + "could not obtain parent directory name of '{}'. Have you used relative paths?", + config_path.display() + ); + return Err(NymNodeError::DataDirDerivationFailure); + }; + + if config_dir_name != DEFAULT_CONFIG_DIR { + error!( + "the parent directory of '{}' ({}) is not {DEFAULT_CONFIG_DIR}. currently this is not supported", + config_path.display(), config_dir_name.to_str().unwrap_or("UNKNOWN") + ); + return Err(NymNodeError::DataDirDerivationFailure); + } + + let Some(node_dir) = config_dir.parent() else { + error!( + "'{}' does not have a parent directory. Have you pointed to the fs root?", + config_dir.display() + ); + return Err(NymNodeError::DataDirDerivationFailure); + }; + + Ok(node_dir.join(DEFAULT_DATA_DIR)) + } + + // simple wrapper that reads config file and assigns path location + fn read_from_path>(path: P) -> Result { + let path = path.as_ref(); + let mut loaded: ConfigV4 = + read_config_from_toml_file(path).map_err(|source| NymNodeError::ConfigLoadFailure { + path: path.to_path_buf(), + source, + })?; + loaded.save_path = Some(path.to_path_buf()); + debug!("loaded config file from {}", path.display()); + Ok(loaded) + } + + pub fn read_from_toml_file>(path: P) -> Result { + Self::read_from_path(path) + } +} + +pub async fn initialise( + paths: &AuthenticatorPaths, + public_key: nym_crypto::asymmetric::identity::PublicKey, +) -> Result<(), NymNodeError> { + let mut rng = OsRng; + let ed25519_keys = ed25519::KeyPair::new(&mut rng); + let x25519_keys = x25519::KeyPair::new(&mut rng); + let aes128ctr_key = AckKey::new(&mut rng); + let gateway_details = GatewayDetails::Custom(CustomGatewayDetails::new(public_key)).into(); + + store_keypair(&ed25519_keys, &paths.ed25519_identity_storage_paths()).map_err(|e| { + KeyIOFailure::KeyPairStoreFailure { + keys: "ed25519-identity".to_string(), + paths: paths.ed25519_identity_storage_paths(), + err: e, + } + })?; + store_keypair(&x25519_keys, &paths.x25519_diffie_hellman_storage_paths()).map_err(|e| { + KeyIOFailure::KeyPairStoreFailure { + keys: "x25519-dh".to_string(), + paths: paths.x25519_diffie_hellman_storage_paths(), + err: e, + } + })?; + store_key(&aes128ctr_key, &paths.ack_key_file).map_err(|e| KeyIOFailure::KeyStoreFailure { + key: "ack".to_string(), + path: paths.ack_key_file.clone(), + err: e, + })?; + + // insert all required information into the gateways store + // (I hate that we have to do it, but that's currently the simplest thing to do) + let storage = setup_fs_gateways_storage(&paths.gateway_registrations).await?; + store_gateway_details(&storage, &gateway_details).await?; + set_active_gateway(&storage, &gateway_details.gateway_id().to_base58_string()).await?; + + Ok(()) +} + +pub async fn try_upgrade_config_v4>( + path: P, + prev_config: Option, +) -> Result { + tracing::debug!("Updating from 1.1.5"); + let old_cfg = if let Some(prev_config) = prev_config { + prev_config + } else { + ConfigV4::read_from_path(&path)? + }; + + let exit_gateway_paths = ExitGatewayPaths::new( + old_cfg + .exit_gateway + .storage_paths + .clients_storage + .parent() + .ok_or(NymNodeError::DataDirDerivationFailure)?, + ); + + let entry_gateway_paths = EntryGatewayPaths::new( + old_cfg + .entry_gateway + .storage_paths + .clients_storage + .parent() + .ok_or(NymNodeError::DataDirDerivationFailure)?, + ); + + let cfg = Config { + save_path: old_cfg.save_path, + id: old_cfg.id, + mode: old_cfg.mode.into(), + host: Host { + public_ips: old_cfg.host.public_ips, + hostname: old_cfg.host.hostname, + location: old_cfg.host.location, + }, + mixnet: Mixnet { + bind_address: old_cfg.mixnet.bind_address, + announce_port: old_cfg.mixnet.announce_port, + nym_api_urls: old_cfg.mixnet.nym_api_urls, + nyxd_urls: old_cfg.mixnet.nyxd_urls, + debug: MixnetDebug { + packet_forwarding_initial_backoff: old_cfg + .mixnet + .debug + .packet_forwarding_initial_backoff, + packet_forwarding_maximum_backoff: old_cfg + .mixnet + .debug + .packet_forwarding_maximum_backoff, + initial_connection_timeout: old_cfg.mixnet.debug.initial_connection_timeout, + maximum_connection_buffer_size: old_cfg.mixnet.debug.maximum_connection_buffer_size, + unsafe_disable_noise: old_cfg.mixnet.debug.unsafe_disable_noise, + }, + }, + storage_paths: NymNodePaths { + keys: KeysPaths { + private_ed25519_identity_key_file: old_cfg + .storage_paths + .keys + .private_ed25519_identity_key_file, + public_ed25519_identity_key_file: old_cfg + .storage_paths + .keys + .public_ed25519_identity_key_file, + private_x25519_sphinx_key_file: old_cfg + .storage_paths + .keys + .private_x25519_sphinx_key_file, + public_x25519_sphinx_key_file: old_cfg + .storage_paths + .keys + .public_x25519_sphinx_key_file, + private_x25519_noise_key_file: old_cfg + .storage_paths + .keys + .private_x25519_noise_key_file, + public_x25519_noise_key_file: old_cfg + .storage_paths + .keys + .public_x25519_noise_key_file, + }, + description: old_cfg.storage_paths.description, + }, + http: Http { + bind_address: old_cfg.http.bind_address, + landing_page_assets_path: old_cfg.http.landing_page_assets_path, + access_token: old_cfg.http.access_token, + expose_system_info: old_cfg.http.expose_system_info, + expose_system_hardware: old_cfg.http.expose_system_hardware, + expose_crypto_hardware: old_cfg.http.expose_crypto_hardware, + }, + wireguard: Wireguard { + enabled: old_cfg.wireguard.enabled, + bind_address: old_cfg.wireguard.bind_address, + private_ip: old_cfg.wireguard.private_ip, + announced_port: old_cfg.wireguard.announced_port, + private_network_prefix: old_cfg.wireguard.private_network_prefix, + storage_paths: WireguardPaths { + private_diffie_hellman_key_file: old_cfg + .wireguard + .storage_paths + .private_diffie_hellman_key_file, + public_diffie_hellman_key_file: old_cfg + .wireguard + .storage_paths + .public_diffie_hellman_key_file, + }, + }, + mixnode: MixnodeConfig { + storage_paths: MixnodePaths {}, + verloc: Verloc { + bind_address: old_cfg.mixnode.verloc.bind_address, + announce_port: old_cfg.mixnode.verloc.announce_port, + debug: VerlocDebug { + packets_per_node: old_cfg.mixnode.verloc.debug.packets_per_node, + connection_timeout: old_cfg.mixnode.verloc.debug.connection_timeout, + packet_timeout: old_cfg.mixnode.verloc.debug.packet_timeout, + delay_between_packets: old_cfg.mixnode.verloc.debug.delay_between_packets, + tested_nodes_batch_size: old_cfg.mixnode.verloc.debug.tested_nodes_batch_size, + testing_interval: old_cfg.mixnode.verloc.debug.testing_interval, + retry_timeout: old_cfg.mixnode.verloc.debug.retry_timeout, + }, + }, + debug: mixnode::Debug { + node_stats_logging_delay: old_cfg.mixnode.debug.node_stats_logging_delay, + node_stats_updating_delay: old_cfg.mixnode.debug.node_stats_updating_delay, + }, + }, + entry_gateway: EntryGatewayConfig { + storage_paths: EntryGatewayPaths { + clients_storage: old_cfg.entry_gateway.storage_paths.clients_storage, + stats_storage: entry_gateway_paths.stats_storage, + cosmos_mnemonic: old_cfg.entry_gateway.storage_paths.cosmos_mnemonic, + authenticator: AuthenticatorPaths { + private_ed25519_identity_key_file: old_cfg + .entry_gateway + .storage_paths + .authenticator + .private_ed25519_identity_key_file, + public_ed25519_identity_key_file: old_cfg + .entry_gateway + .storage_paths + .authenticator + .public_ed25519_identity_key_file, + private_x25519_diffie_hellman_key_file: old_cfg + .entry_gateway + .storage_paths + .authenticator + .private_x25519_diffie_hellman_key_file, + public_x25519_diffie_hellman_key_file: old_cfg + .entry_gateway + .storage_paths + .authenticator + .public_x25519_diffie_hellman_key_file, + ack_key_file: old_cfg + .entry_gateway + .storage_paths + .authenticator + .ack_key_file, + reply_surb_database: old_cfg + .entry_gateway + .storage_paths + .authenticator + .reply_surb_database, + gateway_registrations: old_cfg + .entry_gateway + .storage_paths + .authenticator + .gateway_registrations, + }, + }, + enforce_zk_nyms: old_cfg.entry_gateway.enforce_zk_nyms, + bind_address: old_cfg.entry_gateway.bind_address, + announce_ws_port: old_cfg.entry_gateway.announce_ws_port, + announce_wss_port: old_cfg.entry_gateway.announce_wss_port, + debug: EntryGatewayConfigDebug { + message_retrieval_limit: old_cfg.entry_gateway.debug.message_retrieval_limit, + zk_nym_tickets: ZkNymTicketHandlerDebug { + revocation_bandwidth_penalty: old_cfg + .entry_gateway + .debug + .zk_nym_tickets + .revocation_bandwidth_penalty, + pending_poller: old_cfg.entry_gateway.debug.zk_nym_tickets.pending_poller, + minimum_api_quorum: old_cfg + .entry_gateway + .debug + .zk_nym_tickets + .minimum_api_quorum, + minimum_redemption_tickets: old_cfg + .entry_gateway + .debug + .zk_nym_tickets + .minimum_redemption_tickets, + maximum_time_between_redemption: old_cfg + .entry_gateway + .debug + .zk_nym_tickets + .maximum_time_between_redemption, + }, + }, + }, + exit_gateway: ExitGatewayConfig { + storage_paths: ExitGatewayPaths { + clients_storage: old_cfg.exit_gateway.storage_paths.clients_storage, + stats_storage: exit_gateway_paths.stats_storage, + network_requester: NetworkRequesterPaths { + private_ed25519_identity_key_file: old_cfg + .exit_gateway + .storage_paths + .network_requester + .private_ed25519_identity_key_file, + public_ed25519_identity_key_file: old_cfg + .exit_gateway + .storage_paths + .network_requester + .public_ed25519_identity_key_file, + private_x25519_diffie_hellman_key_file: old_cfg + .exit_gateway + .storage_paths + .network_requester + .private_x25519_diffie_hellman_key_file, + public_x25519_diffie_hellman_key_file: old_cfg + .exit_gateway + .storage_paths + .network_requester + .public_x25519_diffie_hellman_key_file, + ack_key_file: old_cfg + .exit_gateway + .storage_paths + .network_requester + .ack_key_file, + reply_surb_database: old_cfg + .exit_gateway + .storage_paths + .network_requester + .reply_surb_database, + gateway_registrations: old_cfg + .exit_gateway + .storage_paths + .network_requester + .gateway_registrations, + }, + ip_packet_router: IpPacketRouterPaths { + private_ed25519_identity_key_file: old_cfg + .exit_gateway + .storage_paths + .ip_packet_router + .private_ed25519_identity_key_file, + public_ed25519_identity_key_file: old_cfg + .exit_gateway + .storage_paths + .ip_packet_router + .public_ed25519_identity_key_file, + private_x25519_diffie_hellman_key_file: old_cfg + .exit_gateway + .storage_paths + .ip_packet_router + .private_x25519_diffie_hellman_key_file, + public_x25519_diffie_hellman_key_file: old_cfg + .exit_gateway + .storage_paths + .ip_packet_router + .public_x25519_diffie_hellman_key_file, + ack_key_file: old_cfg + .exit_gateway + .storage_paths + .ip_packet_router + .ack_key_file, + reply_surb_database: old_cfg + .exit_gateway + .storage_paths + .ip_packet_router + .reply_surb_database, + gateway_registrations: old_cfg + .exit_gateway + .storage_paths + .ip_packet_router + .gateway_registrations, + }, + authenticator: AuthenticatorPaths { + private_ed25519_identity_key_file: old_cfg + .exit_gateway + .storage_paths + .authenticator + .private_ed25519_identity_key_file, + public_ed25519_identity_key_file: old_cfg + .exit_gateway + .storage_paths + .authenticator + .public_ed25519_identity_key_file, + private_x25519_diffie_hellman_key_file: old_cfg + .exit_gateway + .storage_paths + .authenticator + .private_x25519_diffie_hellman_key_file, + public_x25519_diffie_hellman_key_file: old_cfg + .exit_gateway + .storage_paths + .authenticator + .public_x25519_diffie_hellman_key_file, + ack_key_file: old_cfg + .exit_gateway + .storage_paths + .authenticator + .ack_key_file, + reply_surb_database: old_cfg + .exit_gateway + .storage_paths + .authenticator + .reply_surb_database, + gateway_registrations: old_cfg + .exit_gateway + .storage_paths + .authenticator + .gateway_registrations, + }, + }, + open_proxy: old_cfg.exit_gateway.open_proxy, + upstream_exit_policy_url: old_cfg.exit_gateway.upstream_exit_policy_url, + network_requester: NetworkRequester { + debug: NetworkRequesterDebug { + enabled: old_cfg.exit_gateway.network_requester.debug.enabled, + disable_poisson_rate: old_cfg + .exit_gateway + .network_requester + .debug + .disable_poisson_rate, + client_debug: old_cfg.exit_gateway.network_requester.debug.client_debug, + }, + }, + ip_packet_router: IpPacketRouter { + debug: IpPacketRouterDebug { + enabled: old_cfg.exit_gateway.ip_packet_router.debug.enabled, + disable_poisson_rate: old_cfg + .exit_gateway + .ip_packet_router + .debug + .disable_poisson_rate, + client_debug: old_cfg.exit_gateway.ip_packet_router.debug.client_debug, + }, + }, + debug: ExitGatewayConfigDebug { + message_retrieval_limit: old_cfg.exit_gateway.debug.message_retrieval_limit, + }, + }, + authenticator: Default::default(), + logging: LoggingSettings {}, + }; + + Ok(cfg) +} diff --git a/nym-node/src/config/persistence.rs b/nym-node/src/config/persistence.rs index 3ae8595fe8..4106156aff 100644 --- a/nym-node/src/config/persistence.rs +++ b/nym-node/src/config/persistence.rs @@ -24,6 +24,7 @@ pub const DEFAULT_NYMNODE_DESCRIPTION_FILENAME: &str = "description.toml"; // Entry Gateway: pub const DEFAULT_CLIENTS_STORAGE_FILENAME: &str = "clients.sqlite"; +pub const DEFAULT_STATS_STORAGE_FILENAME: &str = "stats.sqlite"; pub const DEFAULT_MNEMONIC_FILENAME: &str = "cosmos_mnemonic"; // Exit Gateway: @@ -147,6 +148,9 @@ pub struct EntryGatewayPaths { /// derived shared keys, available client bandwidths and wireguard peers. pub clients_storage: PathBuf, + /// Path to sqlite database containing all persistent stats data. + pub stats_storage: PathBuf, + /// Path to file containing cosmos account mnemonic used for zk-nym redemption. pub cosmos_mnemonic: PathBuf, @@ -157,6 +161,7 @@ impl EntryGatewayPaths { pub fn new>(data_dir: P) -> Self { EntryGatewayPaths { clients_storage: data_dir.as_ref().join(DEFAULT_CLIENTS_STORAGE_FILENAME), + stats_storage: data_dir.as_ref().join(DEFAULT_STATS_STORAGE_FILENAME), cosmos_mnemonic: data_dir.as_ref().join(DEFAULT_MNEMONIC_FILENAME), authenticator: AuthenticatorPaths::new(data_dir), } @@ -207,6 +212,9 @@ pub struct ExitGatewayPaths { /// derived shared keys, available client bandwidths and wireguard peers. pub clients_storage: PathBuf, + /// Path to sqlite database containing all persistent stats data. + pub stats_storage: PathBuf, + pub network_requester: NetworkRequesterPaths, pub ip_packet_router: IpPacketRouterPaths, @@ -459,6 +467,7 @@ impl ExitGatewayPaths { let data_dir = data_dir.as_ref(); ExitGatewayPaths { clients_storage: data_dir.join(DEFAULT_CLIENTS_STORAGE_FILENAME), + stats_storage: data_dir.join(DEFAULT_STATS_STORAGE_FILENAME), network_requester: NetworkRequesterPaths::new(data_dir), ip_packet_router: IpPacketRouterPaths::new(data_dir), authenticator: AuthenticatorPaths::new(data_dir), diff --git a/nym-node/src/config/template.rs b/nym-node/src/config/template.rs index 83588a0639..a43b942caf 100644 --- a/nym-node/src/config/template.rs +++ b/nym-node/src/config/template.rs @@ -185,6 +185,9 @@ announce_wss_port = {{#if entry_gateway.announce_wss_port }} {{ entry_gateway.an # derived shared keys, available client bandwidths and wireguard peers. clients_storage = '{{ entry_gateway.storage_paths.clients_storage }}' +# Path to sqlite database containing all persistent stats data. +stats_storage = '{{ entry_gateway.storage_paths.stats_storage }}' + # Path to file containing cosmos account mnemonic used for zk-nym redemption. cosmos_mnemonic = '{{ entry_gateway.storage_paths.cosmos_mnemonic }}' @@ -237,6 +240,10 @@ upstream_exit_policy_url = '{{ exit_gateway.upstream_exit_policy_url }}' # derived shared keys, available client bandwidths and wireguard peers. clients_storage = '{{ exit_gateway.storage_paths.clients_storage }}' +# Path to sqlite database containing all persistent stats data. +stats_storage = '{{ exit_gateway.storage_paths.stats_storage }}' + + [exit_gateway.storage_paths.network_requester] # Path to file containing network requester ed25519 identity private key. private_ed25519_identity_key_file = '{{ exit_gateway.storage_paths.network_requester.private_ed25519_identity_key_file }}' diff --git a/nym-node/src/config/upgrade_helpers.rs b/nym-node/src/config/upgrade_helpers.rs index 4645b1a285..29352ec525 100644 --- a/nym-node/src/config/upgrade_helpers.rs +++ b/nym-node/src/config/upgrade_helpers.rs @@ -10,7 +10,8 @@ use std::path::Path; async fn try_upgrade_config(path: &Path) -> Result<(), NymNodeError> { let cfg = try_upgrade_config_v1(path, None).await.ok(); let cfg = try_upgrade_config_v2(path, cfg).await.ok(); - match try_upgrade_config_v3(path, cfg).await { + let cfg = try_upgrade_config_v3(path, cfg).await.ok(); + match try_upgrade_config_v4(path, cfg).await { Ok(cfg) => cfg.save(), Err(e) => { tracing::error!("Failed to finish upgrade - {e}"); diff --git a/nym-node/src/node/mod.rs b/nym-node/src/node/mod.rs index 5a7bc0c419..4b455cf6a5 100644 --- a/nym-node/src/node/mod.rs +++ b/nym-node/src/node/mod.rs @@ -67,6 +67,7 @@ impl MixnodeData { pub struct EntryGatewayData { mnemonic: Zeroizing, client_storage: nym_gateway::node::PersistentStorage, + stats_storage: nym_gateway::node::PersistentStatsStorage, sessions_stats: SharedSessionStats, } @@ -94,6 +95,11 @@ impl EntryGatewayData { ) .await .map_err(nym_gateway::GatewayError::from)?, + stats_storage: nym_gateway::node::PersistentStatsStorage::init( + &config.storage_paths.stats_storage, + ) + .await + .map_err(nym_gateway::GatewayError::from)?, sessions_stats: SharedSessionStats::new(), }) } @@ -114,6 +120,7 @@ pub struct ExitGatewayData { auth_x25519: x25519::PublicKey, client_storage: nym_gateway::node::PersistentStorage, + stats_storage: nym_gateway::node::PersistentStatsStorage, } impl ExitGatewayData { @@ -262,6 +269,11 @@ impl ExitGatewayData { .await .map_err(nym_gateway::GatewayError::from)?; + let stats_storage = + nym_gateway::node::PersistentStatsStorage::init(&config.storage_paths.stats_storage) + .await + .map_err(nym_gateway::GatewayError::from)?; + Ok(ExitGatewayData { nr_ed25519, nr_x25519, @@ -270,6 +282,7 @@ impl ExitGatewayData { auth_ed25519, auth_x25519, client_storage, + stats_storage, }) } } @@ -580,6 +593,7 @@ impl NymNode { self.ed25519_identity_keys.clone(), self.x25519_sphinx_keys.clone(), self.entry_gateway.client_storage.clone(), + self.entry_gateway.stats_storage.clone(), ); entry_gateway.disable_http_server(); entry_gateway.set_task_client(task_client); @@ -610,6 +624,7 @@ impl NymNode { self.ed25519_identity_keys.clone(), self.x25519_sphinx_keys.clone(), self.exit_gateway.client_storage.clone(), + self.exit_gateway.stats_storage.clone(), ); exit_gateway.disable_http_server(); exit_gateway.set_task_client(task_client); From fa392169c1ddb4b3920570e24be96a8b7179e5f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Stuczy=C5=84ski?= Date: Mon, 28 Oct 2024 09:08:17 +0000 Subject: [PATCH 19/48] bugfix: use human readable roles for annotations (#5036) * bugfix: use human readable roles for annotations * update the wallet code to use 'DisplayRole' --- nym-api/nym-api-requests/src/models.rs | 44 ++++++++++++++++++- .../src/node_status_api/cache/node_sets.rs | 6 +-- .../src/operations/nym_api/status.rs | 5 +-- nym-wallet/src/types/global.ts | 2 +- 4 files changed, 49 insertions(+), 8 deletions(-) diff --git a/nym-api/nym-api-requests/src/models.rs b/nym-api/nym-api-requests/src/models.rs index b16c22f467..93e9be195d 100644 --- a/nym-api/nym-api-requests/src/models.rs +++ b/nym-api/nym-api-requests/src/models.rs @@ -138,6 +138,48 @@ pub struct NodePerformance { pub last_24h: Performance, } +#[derive(Serialize, Deserialize, Debug, Clone, Copy, Hash, JsonSchema)] +#[serde(rename_all = "camelCase")] +#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))] +#[cfg_attr( + feature = "generate-ts", + ts(export, export_to = "ts-packages/types/src/types/rust/DisplayRole.ts") +)] +pub enum DisplayRole { + EntryGateway, + Layer1, + Layer2, + Layer3, + ExitGateway, + Standby, +} + +impl From for DisplayRole { + fn from(role: Role) -> Self { + match role { + Role::EntryGateway => DisplayRole::EntryGateway, + Role::Layer1 => DisplayRole::Layer1, + Role::Layer2 => DisplayRole::Layer2, + Role::Layer3 => DisplayRole::Layer3, + Role::ExitGateway => DisplayRole::ExitGateway, + Role::Standby => DisplayRole::Standby, + } + } +} + +impl From for Role { + fn from(role: DisplayRole) -> Self { + match role { + DisplayRole::EntryGateway => Role::EntryGateway, + DisplayRole::Layer1 => Role::Layer1, + DisplayRole::Layer2 => Role::Layer2, + DisplayRole::Layer3 => Role::Layer3, + DisplayRole::ExitGateway => Role::ExitGateway, + DisplayRole::Standby => Role::Standby, + } + } +} + // imo for now there's no point in exposing more than that, // nym-api shouldn't be calculating apy or stake saturation for you. // it should just return its own metrics (performance) and then you can do with it as you wish @@ -153,7 +195,7 @@ pub struct NodePerformance { pub struct NodeAnnotation { #[cfg_attr(feature = "generate-ts", ts(type = "string"))] pub last_24h_performance: Performance, - pub current_role: Option, + pub current_role: Option, } #[derive(Clone, Copy, Debug, Serialize, Deserialize, JsonSchema, ToSchema)] diff --git a/nym-api/src/node_status_api/cache/node_sets.rs b/nym-api/src/node_status_api/cache/node_sets.rs index d532308c68..a13508b9df 100644 --- a/nym-api/src/node_status_api/cache/node_sets.rs +++ b/nym-api/src/node_status_api/cache/node_sets.rs @@ -209,7 +209,7 @@ pub(crate) async fn produce_node_annotations( legacy_mix.mix_id(), NodeAnnotation { last_24h_performance: perf, - current_role: rewarded_set.role(legacy_mix.mix_id()), + current_role: rewarded_set.role(legacy_mix.mix_id()).map(|r| r.into()), }, ); } @@ -229,7 +229,7 @@ pub(crate) async fn produce_node_annotations( legacy_gateway.node_id, NodeAnnotation { last_24h_performance: perf, - current_role: rewarded_set.role(legacy_gateway.node_id), + current_role: rewarded_set.role(legacy_gateway.node_id).map(|r| r.into()), }, ); } @@ -249,7 +249,7 @@ pub(crate) async fn produce_node_annotations( nym_node.node_id(), NodeAnnotation { last_24h_performance: perf, - current_role: rewarded_set.role(nym_node.node_id()), + current_role: rewarded_set.role(nym_node.node_id()).map(|r| r.into()), }, ); } diff --git a/nym-wallet/src-tauri/src/operations/nym_api/status.rs b/nym-wallet/src-tauri/src/operations/nym_api/status.rs index 1fa8dc70c4..8e2ce5a327 100644 --- a/nym-wallet/src-tauri/src/operations/nym_api/status.rs +++ b/nym-wallet/src-tauri/src/operations/nym_api/status.rs @@ -4,13 +4,12 @@ use crate::api_client; use crate::error::BackendError; use crate::state::WalletState; -use nym_mixnet_contract_common::nym_node::Role; use nym_mixnet_contract_common::{ reward_params::Performance, Coin, IdentityKeyRef, NodeId, Percent, }; use nym_validator_client::client::NymApiClientExt; use nym_validator_client::models::{ - AnnotationResponse, ComputeRewardEstParam, GatewayCoreStatusResponse, + AnnotationResponse, ComputeRewardEstParam, DisplayRole, GatewayCoreStatusResponse, GatewayStatusReportResponse, InclusionProbabilityResponse, MixnodeCoreStatusResponse, MixnodeStatusResponse, RewardEstimationResponse, StakeSaturationResponse, }; @@ -110,7 +109,7 @@ pub async fn mixnode_inclusion_probability( pub async fn get_nymnode_role( node_id: NodeId, state: tauri::State<'_, WalletState>, -) -> Result, BackendError> { +) -> Result, BackendError> { let annotation = get_nymnode_annotation(node_id, state).await?; Ok(annotation.annotation.and_then(|n| n.current_role)) } diff --git a/nym-wallet/src/types/global.ts b/nym-wallet/src/types/global.ts index ff35bd2b9b..23144fd871 100644 --- a/nym-wallet/src/types/global.ts +++ b/nym-wallet/src/types/global.ts @@ -110,7 +110,7 @@ export type TGatewayReport = { most_recent: number; }; -export type TNodeRole = 'entry' | 'exit' | 'layer1' | 'layer2' | 'layer3' | 'standby'; +export type TNodeRole = 'entryGateway' | 'exitGateway' | 'layer1' | 'layer2' | 'layer3' | 'standby'; export type MixnodeSaturationResponse = { saturation: string; From cb13be27f8f61d9ae74d924e85d2e6787895eb14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Stuczy=C5=84ski?= Date: Mon, 28 Oct 2024 09:12:40 +0000 Subject: [PATCH 20/48] bugfix: fix ecash handlers routes (#5043) --- nym-api/src/ecash/api_routes/aggregation.rs | 6 +++--- nym-api/src/ecash/api_routes/partial_signing.rs | 4 ++-- nym-api/src/ecash/api_routes/spending.rs | 2 ++ 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/nym-api/src/ecash/api_routes/aggregation.rs b/nym-api/src/ecash/api_routes/aggregation.rs index d956f2897d..92fb198d30 100644 --- a/nym-api/src/ecash/api_routes/aggregation.rs +++ b/nym-api/src/ecash/api_routes/aggregation.rs @@ -24,21 +24,21 @@ use utoipa::IntoParams; pub(crate) fn aggregation_routes(ecash_state: Arc) -> Router { Router::new() .route( - "/master-verification-key:epoch_id", + "/master-verification-key/:epoch_id", axum::routing::get({ let ecash_state = Arc::clone(&ecash_state); |epoch_id| master_verification_key(epoch_id, ecash_state) }), ) .route( - "/aggregated-expiration-date-signatures:expiration_date", + "/aggregated-expiration-date-signatures/:expiration_date", axum::routing::get({ let ecash_state = Arc::clone(&ecash_state); |expiration_date| expiration_date_signatures(expiration_date, ecash_state) }), ) .route( - "/aggregated-coin-indices-signatures:epoch_id", + "/aggregated-coin-indices-signatures/:epoch_id", axum::routing::get({ let ecash_state = Arc::clone(&ecash_state); |epoch_id| coin_indices_signatures(epoch_id, ecash_state) diff --git a/nym-api/src/ecash/api_routes/partial_signing.rs b/nym-api/src/ecash/api_routes/partial_signing.rs index 72e6b50fec..93a13a1122 100644 --- a/nym-api/src/ecash/api_routes/partial_signing.rs +++ b/nym-api/src/ecash/api_routes/partial_signing.rs @@ -32,14 +32,14 @@ pub(crate) fn partial_signing_routes(ecash_state: Arc) -> Router) -> Router { Router::new() .route( @@ -242,6 +243,7 @@ async fn batch_redeem_tickets( (status = 500, body = ErrorResponse, description = "bloomfilters got disabled"), ) )] +#[deprecated] async fn double_spending_filter_v1( _state: Arc, ) -> AxumResult> { From 4d08047c570594fa9792af4b9b0da77ebd628e35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Stuczy=C5=84ski?= Date: Mon, 28 Oct 2024 09:28:47 +0000 Subject: [PATCH 21/48] bugfix: restore default http port for nym-api (#5045) when it was run under 'rocket' server the port used was 8000. let's restore that value --- nym-api/src/support/config/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nym-api/src/support/config/mod.rs b/nym-api/src/support/config/mod.rs index 8d1809668f..36f17593ae 100644 --- a/nym-api/src/support/config/mod.rs +++ b/nym-api/src/support/config/mod.rs @@ -234,9 +234,9 @@ impl Config { fn default_http_socket_addr() -> SocketAddr { cfg_if::cfg_if! { if #[cfg(debug_assertions)] { - SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 8080) + SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 8000) } else { - SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 8080) + SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 8000) } } } From a56a318a7f92002a416dd27c727a1e71701ddca6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Stuczy=C5=84ski?= Date: Mon, 28 Oct 2024 09:57:14 +0000 Subject: [PATCH 22/48] bugfix: supersede 'cb13be27f8f61d9ae74d924e85d2e6787895eb14' by using query parameters (#5046) --- nym-api/src/ecash/api_routes/aggregation.rs | 12 ++++++------ nym-api/src/ecash/api_routes/partial_signing.rs | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/nym-api/src/ecash/api_routes/aggregation.rs b/nym-api/src/ecash/api_routes/aggregation.rs index 92fb198d30..89478fbd09 100644 --- a/nym-api/src/ecash/api_routes/aggregation.rs +++ b/nym-api/src/ecash/api_routes/aggregation.rs @@ -24,21 +24,21 @@ use utoipa::IntoParams; pub(crate) fn aggregation_routes(ecash_state: Arc) -> Router { Router::new() .route( - "/master-verification-key/:epoch_id", + "/master-verification-key", axum::routing::get({ let ecash_state = Arc::clone(&ecash_state); |epoch_id| master_verification_key(epoch_id, ecash_state) }), ) .route( - "/aggregated-expiration-date-signatures/:expiration_date", + "/aggregated-expiration-date-signatures", axum::routing::get({ let ecash_state = Arc::clone(&ecash_state); |expiration_date| expiration_date_signatures(expiration_date, ecash_state) }), ) .route( - "/aggregated-coin-indices-signatures/:epoch_id", + "/aggregated-coin-indices-signatures", axum::routing::get({ let ecash_state = Arc::clone(&ecash_state); |epoch_id| coin_indices_signatures(epoch_id, ecash_state) @@ -52,7 +52,7 @@ pub(crate) fn aggregation_routes(ecash_state: Arc) -> Router) -> Router Date: Mon, 28 Oct 2024 10:07:51 +0000 Subject: [PATCH 23/48] bugfix: adjust runtime storage migration (#5047) --- nym-api/src/v3_migration.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/nym-api/src/v3_migration.rs b/nym-api/src/v3_migration.rs index c4e920247b..42401ad6fd 100644 --- a/nym-api/src/v3_migration.rs +++ b/nym-api/src/v3_migration.rs @@ -30,10 +30,6 @@ pub async fn migrate_v3_database( let contract_gateways = nyxd_client.get_gateways().await?; let nym_nodes = nyxd_client.get_nymnodes().await?; - if preassigned_ids.len() != contract_gateways.len() { - bail!("CONTRACT DATA CORRUPTION: THE NUMBER OF PREASSIGNED GATEWAY IDS IS DIFFERENT THAN THE NUMBER OF GATEWAYS") - } - // assign node_id to every gateway let all_known = storage.get_all_known_gateways().await?; for gateway in all_known { From c03cf86000617431d7d48319bebe46addc7520c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bogdan-=C8=98tefan=20Neac=C5=9Fu?= Date: Mon, 28 Oct 2024 16:42:05 +0200 Subject: [PATCH 24/48] Authenticator CLI client mode (#5044) --- .../authenticator/src/cli/mod.rs | 5 + .../authenticator/src/cli/request.rs | 142 ++++++++++++++++++ service-providers/authenticator/src/error.rs | 12 ++ .../authenticator/src/mixnet_client.rs | 2 +- 4 files changed, 160 insertions(+), 1 deletion(-) create mode 100644 service-providers/authenticator/src/cli/request.rs diff --git a/service-providers/authenticator/src/cli/mod.rs b/service-providers/authenticator/src/cli/mod.rs index 22c88b2710..e8d6e1d123 100644 --- a/service-providers/authenticator/src/cli/mod.rs +++ b/service-providers/authenticator/src/cli/mod.rs @@ -19,6 +19,7 @@ pub mod ecash; mod init; mod list_gateways; mod peer_handler; +mod request; mod run; mod sign; mod switch_gateway; @@ -69,6 +70,9 @@ pub(crate) enum Commands { /// parameters. Run(run::Run), + /// Make a dummy request to a running authenticator + Request(request::Request), + /// Ecash-related functionalities Ecash(Ecash), @@ -127,6 +131,7 @@ pub(crate) async fn execute(args: Cli) -> Result<(), AuthenticatorError> { match args.command { Commands::Init(m) => init::execute(m).await?, Commands::Run(m) => run::execute(&m).await?, + Commands::Request(r) => request::execute(&r).await?, Commands::Ecash(ecash) => ecash.execute().await?, Commands::ListGateways(args) => list_gateways::execute(args).await?, Commands::AddGateway(args) => add_gateway::execute(args).await?, diff --git a/service-providers/authenticator/src/cli/request.rs b/service-providers/authenticator/src/cli/request.rs new file mode 100644 index 0000000000..2400e9fc2f --- /dev/null +++ b/service-providers/authenticator/src/cli/request.rs @@ -0,0 +1,142 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::cli::AuthenticatorError; +use crate::cli::{override_config, OverrideConfig}; +use crate::cli::{try_load_current_config, version_check}; +use clap::{Args, Subcommand}; +use nym_authenticator_requests::latest::{ + registration::{ClientMac, FinalMessage, GatewayClient, InitMessage}, + request::{AuthenticatorRequest, AuthenticatorRequestData}, +}; +use nym_client_core::cli_helpers::client_run::CommonClientRunArgs; +use nym_sdk::mixnet::{MixnetMessageSender, Recipient, TransmissionLane}; +use nym_task::TaskHandle; +use nym_wireguard_types::PeerPublicKey; +use std::net::IpAddr; +use std::str::FromStr; +use std::time::Duration; +use tokio::time::sleep; + +#[allow(clippy::struct_excessive_bools)] +#[derive(Args, Clone)] +pub(crate) struct Request { + #[command(flatten)] + common_args: CommonClientRunArgs, + + #[command(subcommand)] + request: RequestType, + + authenticator_recipient: String, +} + +impl From for OverrideConfig { + fn from(request_config: Request) -> Self { + OverrideConfig { + nym_apis: None, + nyxd_urls: request_config.common_args.nyxd_urls, + enabled_credentials_mode: request_config.common_args.enabled_credentials_mode, + } + } +} + +#[derive(Clone, Subcommand)] +pub(crate) enum RequestType { + Initial(Initial), + Final(Final), + QueryBandwidth(QueryBandwidth), +} + +#[derive(Args, Clone, Debug)] +pub(crate) struct Initial { + pub_key: String, +} + +#[derive(Args, Clone, Debug)] +pub(crate) struct Final { + pub_key: String, + private_ip: String, + mac: String, +} + +#[derive(Args, Clone, Debug)] +pub(crate) struct QueryBandwidth { + pub_key: String, +} + +impl TryFrom for AuthenticatorRequestData { + type Error = AuthenticatorError; + + fn try_from(value: RequestType) -> Result { + let ret = match value { + RequestType::Initial(req) => AuthenticatorRequestData::Initial(InitMessage::new( + PeerPublicKey::from_str(&req.pub_key)?, + )), + RequestType::Final(req) => AuthenticatorRequestData::Final(Box::new(FinalMessage { + gateway_client: GatewayClient { + pub_key: PeerPublicKey::from_str(&req.pub_key)?, + private_ip: IpAddr::from_str(&req.private_ip)?, + mac: ClientMac::from_str(&req.mac)?, + }, + credential: None, + })), + RequestType::QueryBandwidth(req) => { + AuthenticatorRequestData::QueryBandwidth(PeerPublicKey::from_str(&req.pub_key)?) + } + }; + Ok(ret) + } +} + +pub(crate) async fn execute(args: &Request) -> Result<(), AuthenticatorError> { + let mut config = try_load_current_config(&args.common_args.id).await?; + config = override_config(config, OverrideConfig::from(args.clone())); + + if !version_check(&config) { + log::error!("failed the local version check"); + return Err(AuthenticatorError::FailedLocalVersionCheck); + } + + let shutdown = TaskHandle::default(); + let mixnet_client = nym_authenticator::mixnet_client::create_mixnet_client( + &config.base, + shutdown.get_handle().named("nym_sdk::MixnetClient"), + None, + None, + false, + &config.storage_paths.common_paths, + ) + .await?; + + let request_data = AuthenticatorRequestData::try_from(args.request.clone())?; + let authenticator_recipient = Recipient::from_str(&args.authenticator_recipient)?; + let (request, _) = match request_data { + AuthenticatorRequestData::Initial(init_message) => { + AuthenticatorRequest::new_initial_request(init_message, *mixnet_client.nym_address()) + } + AuthenticatorRequestData::Final(final_message) => { + AuthenticatorRequest::new_final_request(*final_message, *mixnet_client.nym_address()) + } + AuthenticatorRequestData::QueryBandwidth(query_message) => { + AuthenticatorRequest::new_query_request(query_message, *mixnet_client.nym_address()) + } + AuthenticatorRequestData::TopUpBandwidth(top_up_message) => { + AuthenticatorRequest::new_topup_request(*top_up_message, *mixnet_client.nym_address()) + } + }; + mixnet_client + .split_sender() + .send(nym_sdk::mixnet::InputMessage::new_regular( + authenticator_recipient, + request.to_bytes().unwrap(), + TransmissionLane::General, + None, + )) + .await + .map_err(|source| AuthenticatorError::FailedToSendPacketToMixnet { source })?; + + log::info!("Sent request, sleeping 60 seconds or until killed"); + sleep(Duration::from_secs(60)).await; + + Ok(()) +} diff --git a/service-providers/authenticator/src/error.rs b/service-providers/authenticator/src/error.rs index bfc04c3fbc..07713eb3c3 100644 --- a/service-providers/authenticator/src/error.rs +++ b/service-providers/authenticator/src/error.rs @@ -88,6 +88,18 @@ pub enum AuthenticatorError { #[error("storage should have the requested bandwidht entry")] MissingClientBandwidthEntry, + + #[error("{0}")] + PublicKey(#[from] nym_wireguard_types::Error), + + #[error("{0}")] + IpAddr(#[from] std::net::AddrParseError), + + #[error("{0}")] + AuthenticatorRequests(#[from] nym_authenticator_requests::Error), + + #[error("{0}")] + RecipientFormatting(#[from] nym_sdk::mixnet::RecipientFormattingError), } pub type Result = std::result::Result; diff --git a/service-providers/authenticator/src/mixnet_client.rs b/service-providers/authenticator/src/mixnet_client.rs index ba3ad22852..2222ffa93c 100644 --- a/service-providers/authenticator/src/mixnet_client.rs +++ b/service-providers/authenticator/src/mixnet_client.rs @@ -11,7 +11,7 @@ use crate::{config::BaseClientConfig, error::AuthenticatorError}; // This is NOT in the SDK since we don't want to expose any of the client-core config types. // We could however consider moving it to a crate in common in the future. // TODO: refactor this function and its arguments -pub(crate) async fn create_mixnet_client( +pub async fn create_mixnet_client( config: &BaseClientConfig, shutdown: TaskClient, custom_transceiver: Option>, From 2f051fd943664392ebab0cd0b0eb740901398e59 Mon Sep 17 00:00:00 2001 From: Dinko Zdravac <173912580+dynco-nym@users.noreply.github.com> Date: Fri, 13 Sep 2024 17:14:07 +0200 Subject: [PATCH 25/48] Node Status API background task (#4854) * Setup new package * Setup DB * Fetch & store mixnodes/GWs - refactor db package structure - finally solve DATABASE_URL: absolute path works best * Additional query functionality - missing only daily summary, which requires type refactoring * Replace type alias tuples with structs * Insert summary * Add github job to build package * Build script for sqlx * Remove data dir - useless now that sqlx DB sits in OUT_DIR * PR feedback --- Cargo.lock | 23 ++ Cargo.toml | 2 + nym-node-status-api/.gitignore | 1 + nym-node-status-api/Cargo.toml | 37 ++ nym-node-status-api/build.rs | 33 ++ nym-node-status-api/launch_node_status_api.sh | 64 +++ nym-node-status-api/migrations/000_init.sql | 59 +++ nym-node-status-api/src/cli/mod.rs | 17 + nym-node-status-api/src/db/mod.rs | 40 ++ nym-node-status-api/src/db/models.rs | 128 ++++++ .../src/db/queries/gateways.rs | 116 ++++++ nym-node-status-api/src/db/queries/misc.rs | 86 ++++ .../src/db/queries/mixnodes.rs | 101 +++++ nym-node-status-api/src/db/queries/mod.rs | 9 + nym-node-status-api/src/logging.rs | 41 ++ nym-node-status-api/src/main.rs | 38 ++ nym-node-status-api/src/monitor/mod.rs | 374 ++++++++++++++++++ 17 files changed, 1169 insertions(+) create mode 100644 nym-node-status-api/.gitignore create mode 100644 nym-node-status-api/Cargo.toml create mode 100644 nym-node-status-api/build.rs create mode 100755 nym-node-status-api/launch_node_status_api.sh create mode 100644 nym-node-status-api/migrations/000_init.sql create mode 100644 nym-node-status-api/src/cli/mod.rs create mode 100644 nym-node-status-api/src/db/mod.rs create mode 100644 nym-node-status-api/src/db/models.rs create mode 100644 nym-node-status-api/src/db/queries/gateways.rs create mode 100644 nym-node-status-api/src/db/queries/misc.rs create mode 100644 nym-node-status-api/src/db/queries/mixnodes.rs create mode 100644 nym-node-status-api/src/db/queries/mod.rs create mode 100644 nym-node-status-api/src/logging.rs create mode 100644 nym-node-status-api/src/main.rs create mode 100644 nym-node-status-api/src/monitor/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 0a402bea90..a1b8a47c19 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5878,6 +5878,29 @@ dependencies = [ "utoipa", ] +[[package]] +name = "nym-node-status-api" +version = "0.1.0" +dependencies = [ + "anyhow", + "axum 0.7.5", + "chrono", + "clap 4.5.17", + "cosmwasm-std", + "futures-util", + "nym-bin-common", + "nym-explorer-client", + "nym-network-defaults", + "nym-validator-client", + "serde", + "serde_json", + "sqlx", + "thiserror", + "tokio", + "tracing", + "tracing-subscriber", +] + [[package]] name = "nym-node-tester-utils" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 6303eccca0..8f2bc57f13 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -120,6 +120,7 @@ members = [ "nym-node", "nym-node/nym-node-http-api", "nym-node/nym-node-requests", + "nym-node-status-api", "nym-outfox", "nym-validator-rewarder", "tools/echo-server", @@ -238,6 +239,7 @@ eyre = "0.6.9" fastrand = "2.1.1" flate2 = "1.0.34" futures = "0.3.28" +futures-util = "0.3" generic-array = "0.14.7" getrandom = "0.2.10" getset = "0.1.3" diff --git a/nym-node-status-api/.gitignore b/nym-node-status-api/.gitignore new file mode 100644 index 0000000000..8fce603003 --- /dev/null +++ b/nym-node-status-api/.gitignore @@ -0,0 +1 @@ +data/ diff --git a/nym-node-status-api/Cargo.toml b/nym-node-status-api/Cargo.toml new file mode 100644 index 0000000000..f670afc8d8 --- /dev/null +++ b/nym-node-status-api/Cargo.toml @@ -0,0 +1,37 @@ +# Copyright 2024 - Nym Technologies SA +# SPDX-License-Identifier: Apache-2.0 + +[package] +name = "nym-node-status-api" +version = "0.1.0" +authors.workspace = true +repository.workspace = true +homepage.workspace = true +documentation.workspace = true +edition.workspace = true +license.workspace = true +rust-version.workspace = true + +[dependencies] +anyhow = { workspace = true } +axum = { workspace = true } +chrono = { workspace = true } +clap = { workspace = true, features = ["cargo", "derive"] } +cosmwasm-std = { workspace = true } +futures-util = { workspace = true } +nym-bin-common = { path = "../common/bin-common" } +nym-explorer-client = { path = "../explorer-api/explorer-client" } +nym-network-defaults = { path = "../common/network-defaults" } +nym-validator-client = { path = "../common/client-libs/validator-client" } +serde = { workspace = true, features = ["derive"] } +serde_json = { workspace = true } +sqlx = { workspace = true, features = ["runtime-tokio-rustls", "sqlite"] } +thiserror = { workspace = true } +tokio = { workspace = true, features = ["full"] } +tracing = { workspace = true } +tracing-subscriber = { workspace = true, features = ["env-filter"] } + +[build-dependencies] +anyhow = { workspace = true } +tokio = { workspace = true, features = ["macros" ] } +sqlx = { workspace = true, features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"] } diff --git a/nym-node-status-api/build.rs b/nym-node-status-api/build.rs new file mode 100644 index 0000000000..74c6767c6f --- /dev/null +++ b/nym-node-status-api/build.rs @@ -0,0 +1,33 @@ +use anyhow::{anyhow, Result}; +use sqlx::{Connection, SqliteConnection}; + +const SQLITE_DB_FILENAME: &str = "nym-node-status-api.sqlite"; + +#[tokio::main] +async fn main() -> Result<()> { + let out_dir = read_env_var("OUT_DIR")?; + let database_path = format!("sqlite://{}/{}?mode=rwc", out_dir, SQLITE_DB_FILENAME); + + let mut conn = SqliteConnection::connect(&database_path).await?; + sqlx::migrate!("./migrations").run(&mut conn).await?; + + #[cfg(target_family = "unix")] + println!("cargo::rustc-env=DATABASE_URL=sqlite://{}", &database_path); + + #[cfg(target_family = "windows")] + // for some strange reason we need to add a leading `/` to the windows path even though it's + // not a valid windows path... but hey, it works... + println!("cargo::rustc-env=DATABASE_URL=sqlite:///{}", &database_path); + + rerun_if_changed(); + Ok(()) +} + +fn read_env_var(var: &str) -> Result { + std::env::var(var).map_err(|_| anyhow!("You need to set {} env var", var)) +} + +fn rerun_if_changed() { + println!("cargo::rerun-if-changed=migrations"); + println!("cargo::rerun-if-changed=src/db/queries"); +} diff --git a/nym-node-status-api/launch_node_status_api.sh b/nym-node-status-api/launch_node_status_api.sh new file mode 100755 index 0000000000..8af370232f --- /dev/null +++ b/nym-node-status-api/launch_node_status_api.sh @@ -0,0 +1,64 @@ +#!/bin/bash + +set -e + +function usage() { + echo "Usage: $0 [-ci]" + echo " -c Clear DB and re-initialize it before launching the binary." + echo " -i Only initialize and prepare database, env vars then exit without" + echo " launching" + exit 0 +} + +function init_db() { + rm -rf data/* + # https://github.com/launchbadge/sqlx/blob/main/sqlx-cli/README.md + cargo sqlx database drop -y + + cargo sqlx database create + cargo sqlx migrate run + cargo sqlx prepare + + echo "Fresh database ready!" +} + +# export DATABASE_URL as absolute path due to this +# https://github.com/launchbadge/sqlx/issues/3099 +db_filename="nym-node-status-api.sqlite" +script_abs_path=$(realpath "$0") +package_dir=$(dirname "$script_abs_path") +db_abs_path="$package_dir/data/$db_filename" +dotenv_file="$package_dir/.env" +echo "DATABASE_URL=sqlite://$db_abs_path" > "$dotenv_file" + +export RUST_LOG=${RUST_LOG:-debug} + +# export DATABASE_URL from .env file +set -a && source "$dotenv_file" && set +a + +clear_db=false +init_only=false + +while getopts "ci" opt; do + case ${opt} in + c) + clear_db=true + ;; + i) + init_only=true + ;; + \?) + usage + ;; + esac +done + +if [ "$clear_db" = true ] || [ "$init_only" = true ]; then + init_db +fi + +if [ "$init_only" = true ]; then + exit 0 +fi + +cargo run --package nym-node-status-api diff --git a/nym-node-status-api/migrations/000_init.sql b/nym-node-status-api/migrations/000_init.sql new file mode 100644 index 0000000000..f950b49229 --- /dev/null +++ b/nym-node-status-api/migrations/000_init.sql @@ -0,0 +1,59 @@ +CREATE TABLE gateways +( + id INTEGER PRIMARY KEY AUTOINCREMENT, + gateway_identity_key VARCHAR NOT NULL UNIQUE, + self_described VARCHAR, + explorer_pretty_bond VARCHAR, + last_probe_result VARCHAR, + last_probe_log VARCHAR, + config_score INTEGER NOT NULL DEFAULT (0), + config_score_successes REAL NOT NULL DEFAULT (0), + config_score_samples REAL NOT NULL DEFAULT (0), + routing_score INTEGER NOT NULL DEFAULT (0), + routing_score_successes REAL NOT NULL DEFAULT (0), + routing_score_samples REAL NOT NULL DEFAULT (0), + test_run_samples REAL NOT NULL DEFAULT (0), + last_testrun_utc INTEGER, + last_updated_utc INTEGER NOT NULL, + bonded INTEGER CHECK (bonded in (0, 1)) NOT NULL DEFAULT 0, + blacklisted INTEGER CHECK (bonded in (0, 1)) NOT NULL DEFAULT 0, + performance INTEGER NOT NULL DEFAULT 0 +); + +CREATE INDEX idx_gateway_description_gateway_identity_key ON gateways (gateway_identity_key); + + +CREATE TABLE mixnodes ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + identity_key VARCHAR NOT NULL UNIQUE, + mix_id INTEGER NOT NULL UNIQUE, + bonded INTEGER CHECK (bonded in (0, 1)) NOT NULL DEFAULT 0, + total_stake INTEGER NOT NULL, + host VARCHAR NOT NULL, + http_api_port INTEGER NOT NULL, + blacklisted INTEGER CHECK (blacklisted in (0, 1)) NOT NULL DEFAULT 0, + full_details VARCHAR, + self_described VARCHAR, + last_updated_utc INTEGER NOT NULL + , is_dp_delegatee INTEGER CHECK (is_dp_delegatee IN (0, 1)) NOT NULL DEFAULT 0); +CREATE INDEX idx_mixnodes_mix_id ON mixnodes (mix_id); +CREATE INDEX idx_mixnodes_identity_key ON mixnodes (identity_key); + + +CREATE TABLE summary +( + key VARCHAR PRIMARY KEY, + value_json VARCHAR, + last_updated_utc INTEGER NOT NULL +); + + +CREATE TABLE summary_history +( + id INTEGER PRIMARY KEY AUTOINCREMENT, + date VARCHAR UNIQUE NOT NULL, + timestamp_utc INTEGER NOT NULL, + value_json VARCHAR +); +CREATE INDEX idx_summary_history_timestamp_utc ON summary_history (timestamp_utc); +CREATE INDEX idx_summary_history_date ON summary_history (date); diff --git a/nym-node-status-api/src/cli/mod.rs b/nym-node-status-api/src/cli/mod.rs new file mode 100644 index 0000000000..7f46578863 --- /dev/null +++ b/nym-node-status-api/src/cli/mod.rs @@ -0,0 +1,17 @@ +use clap::Parser; +use nym_bin_common::bin_info; +use std::sync::OnceLock; + +// Helper for passing LONG_VERSION to clap +fn pretty_build_info_static() -> &'static str { + static PRETTY_BUILD_INFORMATION: OnceLock = OnceLock::new(); + PRETTY_BUILD_INFORMATION.get_or_init(|| bin_info!().pretty_print()) +} + +#[derive(Parser, Debug)] +#[clap(author = "Nymtech", version, long_version = pretty_build_info_static(), about)] +pub(crate) struct Cli { + /// Path pointing to an env file that configures the Nym API. + #[clap(short, long)] + pub(crate) config_env_file: Option, +} diff --git a/nym-node-status-api/src/db/mod.rs b/nym-node-status-api/src/db/mod.rs new file mode 100644 index 0000000000..d9850abf7d --- /dev/null +++ b/nym-node-status-api/src/db/mod.rs @@ -0,0 +1,40 @@ +use std::str::FromStr; + +use crate::read_env_var; +use anyhow::{anyhow, Result}; +use sqlx::{migrate::Migrator, sqlite::SqliteConnectOptions, ConnectOptions, SqlitePool}; +pub(crate) const DATABASE_URL_ENV_VAR: &str = "DATABASE_URL"; +static MIGRATOR: Migrator = sqlx::migrate!("./migrations"); + +pub(crate) mod models; +pub(crate) mod queries; + +pub(crate) type DbPool = SqlitePool; + +pub(crate) struct Storage { + pool: DbPool, +} + +impl Storage { + pub async fn init() -> Result { + let connection_url = read_env_var(DATABASE_URL_ENV_VAR)?; + let connect_options = { + let connect_options = SqliteConnectOptions::from_str(&connection_url)?; + let mut connect_options = connect_options.create_if_missing(true); + let connect_options = connect_options.disable_statement_logging(); + (*connect_options).clone() + }; + + let pool = sqlx::SqlitePool::connect_with(connect_options) + .await + .map_err(|err| anyhow!("Failed to connect to {}: {}", &connection_url, err))?; + + MIGRATOR.run(&pool).await?; + + Ok(Storage { pool }) + } + + pub async fn pool(&self) -> &DbPool { + &self.pool + } +} diff --git a/nym-node-status-api/src/db/models.rs b/nym-node-status-api/src/db/models.rs new file mode 100644 index 0000000000..83e04d99ee --- /dev/null +++ b/nym-node-status-api/src/db/models.rs @@ -0,0 +1,128 @@ +use serde::{Deserialize, Serialize}; + +pub(crate) struct GatewayRecord { + pub(crate) identity_key: String, + pub(crate) bonded: bool, + pub(crate) blacklisted: bool, + pub(crate) self_described: Option, + pub(crate) explorer_pretty_bond: Option, + pub(crate) last_updated_utc: i64, + pub(crate) performance: u8, +} + +pub(crate) struct MixnodeRecord { + pub(crate) mix_id: u32, + pub(crate) identity_key: String, + pub(crate) bonded: bool, + pub(crate) total_stake: i64, + pub(crate) host: String, + pub(crate) http_port: u16, + pub(crate) blacklisted: bool, + pub(crate) full_details: String, + pub(crate) self_described: Option, + pub(crate) last_updated_utc: i64, + pub(crate) is_dp_delegatee: bool, +} + +#[allow(unused)] +#[derive(Debug, Clone)] +pub(crate) struct BondedStatusDto { + pub(crate) id: i64, + pub(crate) identity_key: String, + pub(crate) bonded: bool, +} + +#[allow(unused)] +#[derive(Debug, Clone, Default)] +pub(crate) struct SummaryDto { + pub(crate) key: String, + pub(crate) value_json: String, + pub(crate) last_updated_utc: i64, +} + +pub(crate) const MIXNODES_BONDED_COUNT: &str = "mixnodes.bonded.count"; +pub(crate) const MIXNODES_BONDED_ACTIVE: &str = "mixnodes.bonded.active"; +pub(crate) const MIXNODES_BONDED_INACTIVE: &str = "mixnodes.bonded.inactive"; +pub(crate) const MIXNODES_BONDED_RESERVE: &str = "mixnodes.bonded.reserve"; +pub(crate) const MIXNODES_BLACKLISTED_COUNT: &str = "mixnodes.blacklisted.count"; + +pub(crate) const GATEWAYS_BONDED_COUNT: &str = "gateways.bonded.count"; +pub(crate) const GATEWAYS_EXPLORER_COUNT: &str = "gateways.explorer.count"; +pub(crate) const GATEWAYS_BLACKLISTED_COUNT: &str = "gateways.blacklisted.count"; + +pub(crate) const MIXNODES_HISTORICAL_COUNT: &str = "mixnodes.historical.count"; +pub(crate) const GATEWAYS_HISTORICAL_COUNT: &str = "gateways.historical.count"; + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub(crate) struct NetworkSummary { + pub(crate) mixnodes: mixnode::MixnodeSummary, + pub(crate) gateways: gateway::GatewaySummary, +} + +pub(crate) mod mixnode { + use super::*; + + #[derive(Debug, Clone, Deserialize, Serialize)] + pub(crate) struct MixnodeSummary { + pub(crate) bonded: MixnodeSummaryBonded, + pub(crate) blacklisted: MixnodeSummaryBlacklisted, + pub(crate) historical: MixnodeSummaryHistorical, + } + + #[derive(Debug, Clone, Deserialize, Serialize)] + pub(crate) struct MixnodeSummaryBonded { + pub(crate) count: i32, + pub(crate) active: i32, + pub(crate) inactive: i32, + pub(crate) reserve: i32, + pub(crate) last_updated_utc: String, + } + + #[derive(Debug, Clone, Deserialize, Serialize)] + pub(crate) struct MixnodeSummaryBlacklisted { + pub(crate) count: i32, + pub(crate) last_updated_utc: String, + } + + #[derive(Debug, Clone, Deserialize, Serialize)] + pub(crate) struct MixnodeSummaryHistorical { + pub(crate) count: i32, + pub(crate) last_updated_utc: String, + } +} + +pub(crate) mod gateway { + use super::*; + + #[derive(Debug, Clone, Deserialize, Serialize)] + pub(crate) struct GatewaySummary { + pub(crate) bonded: GatewaySummaryBonded, + pub(crate) blacklisted: GatewaySummaryBlacklisted, + pub(crate) historical: GatewaySummaryHistorical, + pub(crate) explorer: GatewaySummaryExplorer, + } + + #[derive(Debug, Clone, Deserialize, Serialize)] + pub(crate) struct GatewaySummaryExplorer { + pub(crate) count: i32, + pub(crate) last_updated_utc: String, + } + + #[derive(Debug, Clone, Deserialize, Serialize)] + pub(crate) struct GatewaySummaryBonded { + pub(crate) count: i32, + pub(crate) last_updated_utc: String, + } + + #[derive(Debug, Clone, Deserialize, Serialize)] + pub(crate) struct GatewaySummaryHistorical { + pub(crate) count: i32, + pub(crate) last_updated_utc: String, + } + + #[derive(Debug, Clone, Deserialize, Serialize)] + pub(crate) struct GatewaySummaryBlacklisted { + pub(crate) count: i32, + pub(crate) last_updated_utc: String, + } +} diff --git a/nym-node-status-api/src/db/queries/gateways.rs b/nym-node-status-api/src/db/queries/gateways.rs new file mode 100644 index 0000000000..69f77a97fd --- /dev/null +++ b/nym-node-status-api/src/db/queries/gateways.rs @@ -0,0 +1,116 @@ +use crate::db::{ + models::{BondedStatusDto, GatewayRecord}, + DbPool, +}; +use futures_util::TryStreamExt; +use nym_validator_client::models::DescribedGateway; + +pub(crate) async fn insert_gateways( + pool: &DbPool, + gateways: Vec, +) -> anyhow::Result<()> { + let mut db = pool.acquire().await?; + for record in gateways { + sqlx::query!( + "INSERT INTO gateways + (gateway_identity_key, bonded, blacklisted, + self_described, explorer_pretty_bond, + last_updated_utc, performance) + VALUES (?, ?, ?, ?, ?, ?, ?) + ON CONFLICT(gateway_identity_key) DO UPDATE SET + bonded=excluded.bonded, + blacklisted=excluded.blacklisted, + self_described=excluded.self_described, + explorer_pretty_bond=excluded.explorer_pretty_bond, + last_updated_utc=excluded.last_updated_utc, + performance = excluded.performance;", + record.identity_key, + record.bonded, + record.blacklisted, + record.self_described, + record.explorer_pretty_bond, + record.last_updated_utc, + record.performance + ) + .execute(&mut *db) + .await?; + } + + Ok(()) +} + +pub(crate) async fn write_blacklisted_gateways_to_db<'a, I>( + pool: &DbPool, + gateways: I, +) -> anyhow::Result<()> +where + I: Iterator, +{ + let mut conn = pool.acquire().await?; + for gateway_identity_key in gateways { + sqlx::query!( + "UPDATE gateways + SET blacklisted = true + WHERE gateway_identity_key = ?;", + gateway_identity_key, + ) + .execute(&mut *conn) + .await?; + } + + Ok(()) +} + +/// Ensure all gateways that are set as bonded, are still bonded +pub(crate) async fn ensure_gateways_still_bonded( + pool: &DbPool, + gateways: &[DescribedGateway], +) -> anyhow::Result { + let bonded_gateways_rows = get_all_bonded_gateways_row_ids_by_status(pool, true).await?; + let unbonded_gateways_rows = bonded_gateways_rows.iter().filter(|v| { + !gateways + .iter() + .any(|bonded| *bonded.bond.identity() == v.identity_key) + }); + + let recently_unbonded_gateways = unbonded_gateways_rows.to_owned().count(); + let last_updated_utc = chrono::offset::Utc::now().timestamp(); + let mut transaction = pool.begin().await?; + for row in unbonded_gateways_rows { + sqlx::query!( + "UPDATE gateways + SET bonded = ?, last_updated_utc = ? + WHERE id = ?;", + false, + last_updated_utc, + row.id, + ) + .execute(&mut *transaction) + .await?; + } + transaction.commit().await?; + + Ok(recently_unbonded_gateways) +} + +async fn get_all_bonded_gateways_row_ids_by_status( + pool: &DbPool, + status: bool, +) -> anyhow::Result> { + let mut conn = pool.acquire().await?; + let items = sqlx::query_as!( + BondedStatusDto, + r#"SELECT + id as "id!", + gateway_identity_key as "identity_key!", + bonded as "bonded: bool" + FROM gateways + WHERE bonded = ?"#, + status, + ) + .fetch(&mut *conn) + .try_collect::>() + .await?; + + Ok(items) +} diff --git a/nym-node-status-api/src/db/queries/misc.rs b/nym-node-status-api/src/db/queries/misc.rs new file mode 100644 index 0000000000..c2c7b6d9eb --- /dev/null +++ b/nym-node-status-api/src/db/queries/misc.rs @@ -0,0 +1,86 @@ +use crate::db::{models::NetworkSummary, DbPool}; +use chrono::{DateTime, Utc}; + +/// take `last_updated` instead of calculating it so that `summary` matches +/// `daily_summary` +pub(crate) async fn insert_summaries( + pool: &DbPool, + summaries: &[(&str, &usize)], + summary: &NetworkSummary, + last_updated: DateTime, +) -> anyhow::Result<()> { + insert_summary(pool, summaries, last_updated).await?; + + insert_summary_history(pool, summary, last_updated).await?; + + Ok(()) +} + +async fn insert_summary( + pool: &DbPool, + summaries: &[(&str, &usize)], + last_updated: DateTime, +) -> anyhow::Result<()> { + let timestamp = last_updated.timestamp(); + let mut tx = pool.begin().await?; + + for (kind, value) in summaries { + let value = value.to_string(); + sqlx::query!( + "INSERT INTO summary + (key, value_json, last_updated_utc) + VALUES (?, ?, ?) + ON CONFLICT(key) DO UPDATE SET + value_json=excluded.value_json, + last_updated_utc=excluded.last_updated_utc;", + kind, + value, + timestamp + ) + .execute(&mut tx) + .await + .map_err(|err| { + tracing::error!("Failed to insert data for {kind}: {err}, aborting transaction",); + err + })?; + } + + Ok(()) +} + +/// For ``, `summary_history` is updated with fresh data on every +/// iteration. +/// +/// After UTC midnight, summary is inserted for `` and last entry for +/// `` stays there forever. +/// +/// This is not aggregate data, it's a set of latest data points +async fn insert_summary_history( + pool: &DbPool, + summary: &NetworkSummary, + last_updated: DateTime, +) -> anyhow::Result<()> { + let mut conn = pool.acquire().await?; + + let value_json = serde_json::to_string(&summary)?; + let timestamp = last_updated.timestamp(); + let now_rfc3339 = last_updated.to_rfc3339(); + // YYYY-MM-DD, without time + let date = &now_rfc3339[..10]; + + sqlx::query!( + "INSERT INTO summary_history + (date, timestamp_utc, value_json) + VALUES (?, ?, ?) + ON CONFLICT(date) DO UPDATE SET + timestamp_utc=excluded.timestamp_utc, + value_json=excluded.value_json;", + date, + timestamp, + value_json + ) + .execute(&mut *conn) + .await?; + + Ok(()) +} diff --git a/nym-node-status-api/src/db/queries/mixnodes.rs b/nym-node-status-api/src/db/queries/mixnodes.rs new file mode 100644 index 0000000000..aed48ed4d7 --- /dev/null +++ b/nym-node-status-api/src/db/queries/mixnodes.rs @@ -0,0 +1,101 @@ +use futures_util::TryStreamExt; +use nym_validator_client::models::MixNodeBondAnnotated; + +use crate::db::{ + models::{BondedStatusDto, MixnodeRecord}, + DbPool, +}; + +pub(crate) async fn insert_mixnodes( + pool: &DbPool, + mixnodes: Vec, +) -> anyhow::Result<()> { + let mut conn = pool.acquire().await?; + + for record in mixnodes.iter() { + // https://www.sqlite.org/lang_upsert.html + sqlx::query!( + "INSERT INTO mixnodes + (mix_id, identity_key, bonded, total_stake, + host, http_api_port, blacklisted, full_details, + self_described, last_updated_utc, is_dp_delegatee) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + ON CONFLICT(mix_id) DO UPDATE SET + bonded=excluded.bonded, + total_stake=excluded.total_stake, host=excluded.host, + http_api_port=excluded.http_api_port,blacklisted=excluded.blacklisted, + full_details=excluded.full_details,self_described=excluded.self_described, + last_updated_utc=excluded.last_updated_utc, + is_dp_delegatee = excluded.is_dp_delegatee;", + record.mix_id, + record.identity_key, + record.bonded, + record.total_stake, + record.host, + record.http_port, + record.blacklisted, + record.full_details, + record.self_described, + record.last_updated_utc, + record.is_dp_delegatee + ) + .execute(&mut *conn) + .await?; + } + + Ok(()) +} + +/// Ensure all mixnodes that are set as bonded, are still bonded +pub(crate) async fn ensure_mixnodes_still_bonded( + pool: &DbPool, + mixnodes: &[MixNodeBondAnnotated], +) -> anyhow::Result { + let bonded_mixnodes_rows = get_all_bonded_mixnodes_row_ids_by_status(pool, true).await?; + let unbonded_mixnodes_rows = bonded_mixnodes_rows.iter().filter(|v| { + !mixnodes + .iter() + .any(|bonded| *bonded.mixnode_details.bond_information.identity() == v.identity_key) + }); + + let recently_unbonded_mixnodes = unbonded_mixnodes_rows.to_owned().count(); + let last_updated_utc = chrono::offset::Utc::now().timestamp(); + let mut transaction = pool.begin().await?; + for row in unbonded_mixnodes_rows { + sqlx::query!( + "UPDATE mixnodes + SET bonded = ?, last_updated_utc = ? + WHERE id = ?;", + false, + last_updated_utc, + row.id, + ) + .execute(&mut *transaction) + .await?; + } + transaction.commit().await?; + + Ok(recently_unbonded_mixnodes) +} + +async fn get_all_bonded_mixnodes_row_ids_by_status( + pool: &DbPool, + status: bool, +) -> anyhow::Result> { + let mut conn = pool.acquire().await?; + let items = sqlx::query_as!( + BondedStatusDto, + r#"SELECT + id as "id!", + identity_key as "identity_key!", + bonded as "bonded: bool" + FROM mixnodes + WHERE bonded = ?"#, + status, + ) + .fetch(&mut *conn) + .try_collect::>() + .await?; + + Ok(items) +} diff --git a/nym-node-status-api/src/db/queries/mod.rs b/nym-node-status-api/src/db/queries/mod.rs new file mode 100644 index 0000000000..38f1faab61 --- /dev/null +++ b/nym-node-status-api/src/db/queries/mod.rs @@ -0,0 +1,9 @@ +mod gateways; +mod misc; +mod mixnodes; + +pub(crate) use gateways::{ + ensure_gateways_still_bonded, insert_gateways, write_blacklisted_gateways_to_db, +}; +pub(crate) use misc::insert_summaries; +pub(crate) use mixnodes::{ensure_mixnodes_still_bonded, insert_mixnodes}; diff --git a/nym-node-status-api/src/logging.rs b/nym-node-status-api/src/logging.rs new file mode 100644 index 0000000000..d61cd78ee1 --- /dev/null +++ b/nym-node-status-api/src/logging.rs @@ -0,0 +1,41 @@ +use tracing::level_filters::LevelFilter; +use tracing_subscriber::{filter::Directive, EnvFilter}; + +pub(crate) fn setup_tracing_logger() { + fn directive_checked(directive: String) -> Directive { + directive.parse().expect("Failed to parse log directive") + } + + let log_builder = tracing_subscriber::fmt() + // Use a more compact, abbreviated log format + .compact() + // Display source code file paths + .with_file(true) + // Display source code line numbers + .with_line_number(true) + // Don't display the event's target (module path) + .with_target(false); + + let mut filter = EnvFilter::builder() + // if RUST_LOG isn't set, set default level + .with_default_directive(LevelFilter::INFO.into()) + .from_env_lossy(); + // these crates are more granularly filtered + let filter_crates = [ + "nym_bin_common", + "nym_explorer_client", + "nym_network_defaults", + "nym_validator_client", + "reqwest", + "rustls", + "hyper", + "sqlx", + "h2", + "tendermint_rpc", + ]; + for crate_name in filter_crates { + filter = filter.add_directive(directive_checked(format!("{}=warn", crate_name))); + } + + log_builder.with_env_filter(filter).init(); +} diff --git a/nym-node-status-api/src/main.rs b/nym-node-status-api/src/main.rs new file mode 100644 index 0000000000..6296406701 --- /dev/null +++ b/nym-node-status-api/src/main.rs @@ -0,0 +1,38 @@ +use anyhow::anyhow; +use clap::Parser; +use nym_network_defaults::setup_env; + +mod cli; +mod db; +mod logging; +mod monitor; + +#[tokio::main] +async fn main() -> anyhow::Result<()> { + logging::setup_tracing_logger(); + + let args = cli::Cli::parse(); + // if dotenv file is present, load its values + // otherwise, default to mainnet + setup_env(args.config_env_file.as_ref()); + tracing::debug!("{:?}", std::env::var("NETWORK_NAME")); + tracing::debug!("{:?}", std::env::var("EXPLORER_API")); + tracing::debug!("{:?}", std::env::var("NYM_API")); + + let storage = db::Storage::init().await?; + monitor::spawn_in_background(storage) + .await + .expect("Monitor task failed"); + tracing::info!("Started server"); + + Ok(()) +} + +pub(crate) fn read_env_var(env_var: &str) -> anyhow::Result { + std::env::var(env_var) + .map(|value| { + tracing::trace!("{}={}", env_var, value); + value + }) + .map_err(|_| anyhow!("You need to set {}", env_var)) +} diff --git a/nym-node-status-api/src/monitor/mod.rs b/nym-node-status-api/src/monitor/mod.rs new file mode 100644 index 0000000000..810320bdd0 --- /dev/null +++ b/nym-node-status-api/src/monitor/mod.rs @@ -0,0 +1,374 @@ +use crate::db::models::{ + gateway, mixnode, GatewayRecord, MixnodeRecord, NetworkSummary, GATEWAYS_BLACKLISTED_COUNT, + GATEWAYS_BONDED_COUNT, GATEWAYS_EXPLORER_COUNT, GATEWAYS_HISTORICAL_COUNT, + MIXNODES_BLACKLISTED_COUNT, MIXNODES_BONDED_ACTIVE, MIXNODES_BONDED_COUNT, + MIXNODES_BONDED_INACTIVE, MIXNODES_BONDED_RESERVE, MIXNODES_HISTORICAL_COUNT, +}; +use crate::db::{queries, DbPool, Storage}; +use anyhow::anyhow; +use cosmwasm_std::Decimal; +use nym_explorer_client::{ExplorerClient, PrettyDetailedGatewayBond}; +use nym_network_defaults::NymNetworkDetails; +use nym_validator_client::client::NymApiClientExt; +use nym_validator_client::models::{DescribedGateway, DescribedMixNode, MixNodeBondAnnotated}; +use nym_validator_client::nym_nodes::SkimmedNode; +use nym_validator_client::nyxd::contract_traits::PagedMixnetQueryClient; +use nym_validator_client::nyxd::{AccountId, NyxdClient}; +use nym_validator_client::NymApiClient; +use std::collections::HashSet; +use std::str::FromStr; +use tokio::task::JoinHandle; +use tokio::time::Duration; + +const REFRESH_DELAY: Duration = Duration::from_secs(60 * 5); +const FAILURE_RETRY_DELAY: Duration = Duration::from_secs(60); +static DELEGATION_PROGRAM_WALLET: &str = "n1rnxpdpx3kldygsklfft0gech7fhfcux4zst5lw"; + +// TODO dz: query many NYM APIs: +// multiple instances running directory cache, ask sachin +pub(crate) fn spawn_in_background(storage: Storage) -> JoinHandle<()> { + tokio::spawn(async move { + let db_pool = storage.pool().await; + let network_defaults = nym_network_defaults::NymNetworkDetails::new_from_env(); + + loop { + tracing::info!("Refreshing node info..."); + + if let Err(e) = run(db_pool, &network_defaults).await { + tracing::error!( + "Monitor run failed: {e}, retrying in {}s...", + FAILURE_RETRY_DELAY.as_secs() + ); + tokio::time::sleep(FAILURE_RETRY_DELAY).await; + } else { + tracing::info!( + "Info successfully collected, sleeping for {}s...", + REFRESH_DELAY.as_secs() + ); + tokio::time::sleep(REFRESH_DELAY).await; + } + } + }) +} + +async fn run(pool: &DbPool, network_details: &NymNetworkDetails) -> anyhow::Result<()> { + let default_api_url = network_details + .endpoints + .first() + .expect("rust sdk mainnet default incorrectly configured") + .api_url() + .clone() + .expect("rust sdk mainnet default missing api_url"); + let default_explorer_url = network_details.explorer_api.clone().map(|url| { + url.parse() + .expect("rust sdk mainnet default explorer url not parseable") + }); + + let default_explorer_url = + default_explorer_url.expect("explorer url missing in network config"); + let explorer_client = ExplorerClient::new(default_explorer_url)?; + let explorer_gateways = explorer_client.get_gateways().await?; + + let api_client = NymApiClient::new(default_api_url); + let gateways = api_client.get_cached_described_gateways().await?; + tracing::debug!("Fetched {} gateways", gateways.len()); + let skimmed_gateways = api_client.get_basic_gateways(None).await?; + + let mixnodes = api_client.get_cached_mixnodes().await?; + tracing::debug!("Fetched {} mixnodes", mixnodes.len()); + + // TODO dz can we calculate blacklisted GWs from their performance? + // where do we get their performance? + let gateways_blacklisted = api_client + .nym_api + .get_gateways_blacklisted() + .await + .map(|vec| vec.into_iter().collect::>())?; + + // Cached mixnodes don't include blacklisted nodes + // We need that to calculate the total locked tokens later + let mixnodes = api_client + .nym_api + .get_mixnodes_detailed_unfiltered() + .await?; + let mixnodes_described = api_client.nym_api.get_mixnodes_described().await?; + let mixnodes_active = api_client.nym_api.get_active_mixnodes().await?; + let delegation_program_members = get_delegation_program_details(network_details).await?; + + // keep stats for later + let count_bonded_mixnodes = mixnodes.len(); + let count_bonded_gateways = gateways.len(); + let count_explorer_gateways = explorer_gateways.len(); + let count_bonded_mixnodes_active = mixnodes_active.len(); + + let gateway_records = prepare_gateway_data( + &gateways, + &gateways_blacklisted, + explorer_gateways, + skimmed_gateways, + )?; + queries::insert_gateways(pool, gateway_records) + .await + .map(|_| { + tracing::debug!("Gateway info written to DB!"); + })?; + + // instead of counting blacklisted GWs returned from API cache, count from the active set + let count_gateways_blacklisted = gateways + .iter() + .filter(|gw| { + let gw_identity = gw.bond.identity(); + gateways_blacklisted.contains(gw_identity) + }) + .count(); + + if count_gateways_blacklisted > 0 { + queries::write_blacklisted_gateways_to_db(pool, gateways_blacklisted.iter()) + .await + .map(|_| { + tracing::debug!( + "Gateway blacklist info written to DB! {} blacklisted by Nym API", + count_gateways_blacklisted + ) + })?; + } + + let mixnode_records = + prepare_mixnode_data(&mixnodes, mixnodes_described, delegation_program_members)?; + queries::insert_mixnodes(pool, mixnode_records) + .await + .map(|_| { + tracing::debug!("Mixnode info written to DB!"); + })?; + + let count_mixnodes_blacklisted = mixnodes.iter().filter(|elem| elem.blacklisted).count(); + + let recently_unbonded_gateways = queries::ensure_gateways_still_bonded(pool, &gateways).await?; + let recently_unbonded_mixnodes = queries::ensure_mixnodes_still_bonded(pool, &mixnodes).await?; + + let count_bonded_mixnodes_reserve = 0; // TODO: NymAPI doesn't report the reserve set size + let count_bonded_mixnodes_inactive = count_bonded_mixnodes - count_bonded_mixnodes_active; + + let (all_historical_gateways, all_historical_mixnodes) = calculate_stats(pool).await?; + + // + // write summary keys and values to table + // + + let nodes_summary = vec![ + (MIXNODES_BONDED_COUNT, &count_bonded_mixnodes), + (MIXNODES_BONDED_ACTIVE, &count_bonded_mixnodes_active), + (MIXNODES_BONDED_INACTIVE, &count_bonded_mixnodes_inactive), + (MIXNODES_BONDED_RESERVE, &count_bonded_mixnodes_reserve), + (MIXNODES_BLACKLISTED_COUNT, &count_mixnodes_blacklisted), + (GATEWAYS_BONDED_COUNT, &count_bonded_gateways), + (GATEWAYS_EXPLORER_COUNT, &count_explorer_gateways), + (MIXNODES_HISTORICAL_COUNT, &all_historical_mixnodes), + (GATEWAYS_HISTORICAL_COUNT, &all_historical_gateways), + (GATEWAYS_BLACKLISTED_COUNT, &count_gateways_blacklisted), + ]; + + // TODO dz do we need signed int in type definition? maybe because of API? + let last_updated = chrono::offset::Utc::now(); + let last_updated_utc = last_updated.timestamp().to_string(); + let network_summary = NetworkSummary { + mixnodes: mixnode::MixnodeSummary { + bonded: mixnode::MixnodeSummaryBonded { + count: count_bonded_mixnodes as i32, + active: count_bonded_mixnodes_active as i32, + inactive: count_bonded_mixnodes_inactive as i32, + reserve: count_bonded_mixnodes_reserve as i32, + last_updated_utc: last_updated_utc.to_owned(), + }, + blacklisted: mixnode::MixnodeSummaryBlacklisted { + count: count_mixnodes_blacklisted as i32, + last_updated_utc: last_updated_utc.to_owned(), + }, + historical: mixnode::MixnodeSummaryHistorical { + count: all_historical_mixnodes as i32, + last_updated_utc: last_updated_utc.to_owned(), + }, + }, + gateways: gateway::GatewaySummary { + bonded: gateway::GatewaySummaryBonded { + count: count_bonded_gateways as i32, + last_updated_utc: last_updated_utc.to_owned(), + }, + blacklisted: gateway::GatewaySummaryBlacklisted { + count: count_gateways_blacklisted as i32, + last_updated_utc: last_updated_utc.to_owned(), + }, + historical: gateway::GatewaySummaryHistorical { + count: all_historical_gateways as i32, + last_updated_utc: last_updated_utc.to_owned(), + }, + explorer: gateway::GatewaySummaryExplorer { + count: count_explorer_gateways as i32, + last_updated_utc: last_updated_utc.to_owned(), + }, + }, + }; + + queries::insert_summaries(pool, &nodes_summary, &network_summary, last_updated).await?; + + let mut log_lines: Vec = vec![]; + for (key, value) in nodes_summary.iter() { + log_lines.push(format!("{} = {}", key, value)); + } + log_lines.push(format!( + "recently_unbonded_mixnodes = {}", + recently_unbonded_mixnodes + )); + log_lines.push(format!( + "recently_unbonded_gateways = {}", + recently_unbonded_gateways + )); + + tracing::info!("Directory summary: \n{}", log_lines.join("\n")); + + Ok(()) +} + +fn prepare_gateway_data( + gateways: &[DescribedGateway], + gateways_blacklisted: &HashSet, + explorer_gateways: Vec, + skimmed_gateways: Vec, +) -> anyhow::Result> { + let mut gateway_records = Vec::new(); + + for gateway in gateways { + let identity_key = gateway.bond.identity(); + let bonded = true; + let last_updated_utc = chrono::offset::Utc::now().timestamp(); + let blacklisted = gateways_blacklisted.contains(identity_key); + + let self_described = gateway + .self_described + .as_ref() + .and_then(|v| serde_json::to_string(&v).ok()); + + let explorer_pretty_bond = explorer_gateways + .iter() + .find(|g| g.gateway.identity_key.eq(identity_key)); + let explorer_pretty_bond = explorer_pretty_bond.and_then(|g| serde_json::to_string(g).ok()); + + let performance = skimmed_gateways + .iter() + .find(|g| g.ed25519_identity_pubkey.eq(identity_key)) + .map(|g| g.performance) + .unwrap_or_default() + .round_to_integer(); + + gateway_records.push(GatewayRecord { + identity_key: identity_key.to_owned(), + bonded, + blacklisted, + self_described, + explorer_pretty_bond, + last_updated_utc, + performance, + }); + } + + Ok(gateway_records) +} + +fn prepare_mixnode_data( + mixnodes: &[MixNodeBondAnnotated], + mixnodes_described: Vec, + delegation_program_members: Vec, +) -> anyhow::Result> { + let mut mixnode_records = Vec::new(); + + for mixnode in mixnodes { + let mix_id = mixnode.mix_id(); + let identity_key = mixnode.identity_key(); + let bonded = true; + let total_stake = decimal_to_i64(mixnode.mixnode_details.total_stake()); + let blacklisted = mixnode.blacklisted; + let node_info = mixnode.mix_node(); + let host = node_info.host.clone(); + let http_port = node_info.http_api_port; + // Contains all the information including what's above + let full_details = serde_json::to_string(&mixnode)?; + + let mixnode_described = mixnodes_described.iter().find(|m| m.bond.mix_id == mix_id); + let self_described = mixnode_described.and_then(|v| serde_json::to_string(v).ok()); + let is_dp_delegatee = delegation_program_members.contains(&mix_id); + + let last_updated_utc = chrono::offset::Utc::now().timestamp(); + + mixnode_records.push(MixnodeRecord { + mix_id, + identity_key: identity_key.to_owned(), + bonded, + total_stake, + host, + http_port, + blacklisted, + full_details, + self_described, + last_updated_utc, + is_dp_delegatee, + }); + } + + Ok(mixnode_records) +} + +async fn calculate_stats(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"#) + .fetch_one(&mut *conn) + .await? as usize; + + let all_historical_mixnodes = sqlx::query_scalar!(r#"SELECT count(id) FROM mixnodes"#) + .fetch_one(&mut *conn) + .await? as usize; + + Ok((all_historical_gateways, all_historical_mixnodes)) +} + +async fn get_delegation_program_details( + network_details: &NymNetworkDetails, +) -> anyhow::Result> { + let config = nym_validator_client::nyxd::Config::try_from_nym_network_details(network_details)?; + + // TODO dz should this be configurable? + let client = NyxdClient::connect(config, "https://rpc.nymtech.net") + .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 = delegations + .iter() + .map(|delegation| delegation.mix_id) + .collect(); + + Ok(mix_ids) +} + +fn decimal_to_i64(decimal: Decimal) -> i64 { + // Convert the underlying Uint128 to a u128 + let atomics = decimal.atomics().u128(); + let precision = 1_000_000_000_000_000_000u128; + + // Get the fractional part + let fractional = atomics % precision; + + // Get the integer part + let integer = atomics / precision; + + // Combine them into a float + let float_value = integer as f64 + (fractional as f64 / 1_000_000_000_000_000_000_f64); + + // Limit to 6 decimal places + let rounded_value = (float_value * 1_000_000.0).round() / 1_000_000.0; + + rounded_value as i64 +} From 56c55f6b95fab734e49f7f146a57e389c2d9f46a Mon Sep 17 00:00:00 2001 From: Dinko Zdravac <173912580+dynco-nym@users.noreply.github.com> Date: Wed, 9 Oct 2024 11:33:12 +0200 Subject: [PATCH 26/48] Working HTTP server (#4941) * Server file structure * Create HTTP server - graceful shutdown - routes - logging, CORS * gateways WIP * gateways API + swagger docs complete * Mixnodes API + swagger docs complete * Services API + swagger docs complete * Commit summary insert * Make troubleshooting DB easier * Summary API + swagger docs * Client log changes * QOL improvements - remove implicit panics via `as` - safer DTO conversions - add logging - new config --- Cargo.lock | 180 +++++++++++++- Cargo.toml | 4 + .../client-libs/validator-client/Cargo.toml | 2 +- .../validator-client/src/client.rs | 7 + .../validator-client/src/connection_tester.rs | 18 +- .../validator-client/src/nym_api/mod.rs | 42 ++++ .../nyxd/contract_traits/dkg_query_client.rs | 2 +- .../client_traits/query_client.rs | 7 +- .../client_traits/signing_client.rs | 2 +- .../src/nyxd/cosmwasm_client/helpers.rs | 2 +- common/http-api-client/src/lib.rs | 7 +- explorer-api/explorer-client/Cargo.toml | 2 +- explorer-api/explorer-client/src/lib.rs | 12 +- nym-node-status-api/.gitignore | 1 + nym-node-status-api/Cargo.toml | 20 +- nym-node-status-api/build.rs | 13 + nym-node-status-api/launch_node_status_api.sh | 60 +---- nym-node-status-api/migrations/000_init.sql | 41 ++++ nym-node-status-api/src/config.rs | 79 +++++++ nym-node-status-api/src/db/mod.rs | 10 +- nym-node-status-api/src/db/models.rs | 196 ++++++++++++++- .../src/db/queries/gateways.rs | 50 +++- nym-node-status-api/src/db/queries/misc.rs | 2 + .../src/db/queries/mixnodes.rs | 82 ++++++- nym-node-status-api/src/db/queries/mod.rs | 9 +- nym-node-status-api/src/db/queries/summary.rs | 209 ++++++++++++++++ nym-node-status-api/src/http/api/gateways.rs | 110 +++++++++ nym-node-status-api/src/http/api/mixnodes.rs | 91 +++++++ nym-node-status-api/src/http/api/mod.rs | 85 +++++++ .../src/http/api/services/json_path.rs | 58 +++++ .../src/http/api/services/mod.rs | 134 +++++++++++ nym-node-status-api/src/http/api/summary.rs | 43 ++++ nym-node-status-api/src/http/api/testruns.rs | 7 + nym-node-status-api/src/http/api_docs.rs | 15 ++ nym-node-status-api/src/http/error.rs | 28 +++ nym-node-status-api/src/http/mod.rs | 71 ++++++ nym-node-status-api/src/http/models.rs | 74 ++++++ nym-node-status-api/src/http/server.rs | 92 ++++++++ nym-node-status-api/src/http/state.rs | 223 ++++++++++++++++++ nym-node-status-api/src/logging.rs | 19 +- nym-node-status-api/src/main.rs | 50 ++-- nym-node-status-api/src/monitor/mod.rs | 174 ++++++++++---- 42 files changed, 2150 insertions(+), 183 deletions(-) create mode 100644 nym-node-status-api/src/config.rs create mode 100644 nym-node-status-api/src/db/queries/summary.rs create mode 100644 nym-node-status-api/src/http/api/gateways.rs create mode 100644 nym-node-status-api/src/http/api/mixnodes.rs create mode 100644 nym-node-status-api/src/http/api/mod.rs create mode 100644 nym-node-status-api/src/http/api/services/json_path.rs create mode 100644 nym-node-status-api/src/http/api/services/mod.rs create mode 100644 nym-node-status-api/src/http/api/summary.rs create mode 100644 nym-node-status-api/src/http/api/testruns.rs create mode 100644 nym-node-status-api/src/http/api_docs.rs create mode 100644 nym-node-status-api/src/http/error.rs create mode 100644 nym-node-status-api/src/http/mod.rs create mode 100644 nym-node-status-api/src/http/models.rs create mode 100644 nym-node-status-api/src/http/server.rs create mode 100644 nym-node-status-api/src/http/state.rs diff --git a/Cargo.lock b/Cargo.lock index a1b8a47c19..0cab5a15ee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -318,10 +318,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" dependencies = [ "concurrent-queue", - "event-listener", + "event-listener 2.5.3", "futures-core", ] +[[package]] +name = "async-lock" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" +dependencies = [ + "event-listener 5.3.1", + "event-listener-strategy", + "pin-project-lite", +] + [[package]] name = "async-stream" version = "0.3.5" @@ -2320,6 +2331,15 @@ dependencies = [ "termcolor", ] +[[package]] +name = "envy" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f47e0157f2cb54f5ae1bd371b30a2ae4311e1c028f575cd4e81de7353215965" +dependencies = [ + "serde", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -2362,6 +2382,27 @@ version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" +[[package]] +name = "event-listener" +version = "5.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" +dependencies = [ + "event-listener 5.3.1", + "pin-project-lite", +] + [[package]] name = "explorer-api" version = "1.1.41" @@ -3572,7 +3613,7 @@ dependencies = [ "crossbeam-utils", "curl", "curl-sys", - "event-listener", + "event-listener 2.5.3", "futures-lite", "http 0.2.12", "log", @@ -4054,6 +4095,30 @@ dependencies = [ "wasm-utils", ] +[[package]] +name = "moka" +version = "0.12.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32cf62eb4dd975d2dde76432fb1075c49e3ee2331cf36f1f8fd4b66550d32b6f" +dependencies = [ + "async-lock", + "async-trait", + "crossbeam-channel", + "crossbeam-epoch", + "crossbeam-utils", + "event-listener 5.3.1", + "futures-util", + "once_cell", + "parking_lot", + "quanta", + "rustc_version 0.4.0", + "smallvec", + "tagptr", + "thiserror", + "triomphe", + "uuid", +] + [[package]] name = "multer" version = "2.1.0" @@ -5208,12 +5273,12 @@ dependencies = [ name = "nym-explorer-client" version = "0.1.0" dependencies = [ - "log", "nym-explorer-api-requests", "reqwest 0.12.4", "serde", "thiserror", "tokio", + "tracing", "url", ] @@ -5883,22 +5948,34 @@ name = "nym-node-status-api" version = "0.1.0" dependencies = [ "anyhow", - "axum 0.7.5", + "axum 0.7.7", "chrono", - "clap 4.5.17", + "clap 4.5.20", "cosmwasm-std", + "envy", "futures-util", + "moka", "nym-bin-common", "nym-explorer-client", "nym-network-defaults", + "nym-node-requests", + "nym-task", "nym-validator-client", + "reqwest 0.12.4", "serde", "serde_json", + "serde_json_path", "sqlx", "thiserror", "tokio", + "tokio-util", + "tower-http", "tracing", + "tracing-log 0.2.0", "tracing-subscriber", + "utoipa", + "utoipa-swagger-ui", + "utoipauto", ] [[package]] @@ -6495,7 +6572,6 @@ dependencies = [ "flate2", "futures", "itertools 0.13.0", - "log", "nym-api-requests", "nym-coconut-bandwidth-contract-common", "nym-coconut-dkg-common", @@ -6519,6 +6595,7 @@ dependencies = [ "thiserror", "time", "tokio", + "tracing", "ts-rs", "url", "wasmtimer", @@ -7428,6 +7505,21 @@ dependencies = [ "psl-types", ] +[[package]] +name = "quanta" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5167a477619228a0b284fac2674e3c388cba90631d7b7de620e6f1fcd08da5" +dependencies = [ + "crossbeam-utils", + "libc", + "once_cell", + "raw-cpuid", + "wasi", + "web-sys", + "winapi", +] + [[package]] name = "quick-error" version = "1.2.3" @@ -7519,6 +7611,15 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "raw-cpuid" +version = "11.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb9ee317cfe3fbd54b36a511efc1edd42e216903c9cd575e686dd68a2ba90d8d" +dependencies = [ + "bitflags 2.5.0", +] + [[package]] name = "rayon" version = "1.10.0" @@ -8378,6 +8479,59 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_json_path" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bc0207b6351893eafa1e39aa9aea452abb6425ca7b02dd64faf29109e7a33ba" +dependencies = [ + "inventory", + "nom", + "once_cell", + "regex", + "serde", + "serde_json", + "serde_json_path_core", + "serde_json_path_macros", + "thiserror", +] + +[[package]] +name = "serde_json_path_core" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3d64fe53ce1aaa31bea2b2b46d3b6ab6a37e61854bedcbd9f174e188f3f7d79" +dependencies = [ + "inventory", + "once_cell", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "serde_json_path_macros" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a31e8177a443fd3e94917f12946ae7891dfb656e6d4c5e79b8c5d202fbcb723" +dependencies = [ + "inventory", + "once_cell", + "serde_json_path_core", + "serde_json_path_macros_internal", +] + +[[package]] +name = "serde_json_path_macros_internal" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75dde5a1d2ed78dfc411fc45592f72d3694436524d3353683ecb3d22009731dc" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.82", +] + [[package]] name = "serde_path_to_error" version = "0.1.16" @@ -8742,7 +8896,7 @@ dependencies = [ "crc", "crossbeam-queue", "either", - "event-listener", + "event-listener 2.5.3", "futures-channel", "futures-core", "futures-intrusive", @@ -9152,6 +9306,12 @@ dependencies = [ "libc", ] +[[package]] +name = "tagptr" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417" + [[package]] name = "tap" version = "1.0.1" @@ -9924,6 +10084,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "triomphe" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "859eb650cfee7434994602c3a68b25d77ad9e68c8a6cd491616ef86661382eb3" + [[package]] name = "try-lock" version = "0.2.5" diff --git a/Cargo.toml b/Cargo.toml index 8f2bc57f13..68c9476f5e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -235,6 +235,7 @@ dotenvy = "0.15.6" ecdsa = "0.16" ed25519-dalek = "2.1" etherparse = "0.13.0" +envy = "0.4" eyre = "0.6.9" fastrand = "2.1.1" flate2 = "1.0.34" @@ -269,6 +270,7 @@ ledger-transport-hid = "0.10.0" log = "0.4" maxminddb = "0.23.0" mime = "0.3.17" +moka = { version = "0.12", features = ["future"] } nix = "0.27.1" notify = "5.1.0" okapi = "0.7.0" @@ -302,6 +304,7 @@ serde = "1.0.211" serde_bytes = "0.11.15" serde_derive = "1.0" serde_json = "1.0.132" +serde_json_path = "0.6.7" serde_repr = "0.1" serde_with = "3.9.0" serde_yaml = "0.9.25" @@ -331,6 +334,7 @@ tracing = "0.1.37" tracing-opentelemetry = "0.19.0" tracing-subscriber = "0.3.16" tracing-tree = "0.2.2" +tracing-log = "0.2" ts-rs = "10.0.0" tungstenite = { version = "0.20.1", default-features = false } url = "2.5" diff --git a/common/client-libs/validator-client/Cargo.toml b/common/client-libs/validator-client/Cargo.toml index d17b100672..5cd86e2dba 100644 --- a/common/client-libs/validator-client/Cargo.toml +++ b/common/client-libs/validator-client/Cargo.toml @@ -25,7 +25,7 @@ serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } nym-http-api-client = { path = "../../../common/http-api-client" } thiserror = { workspace = true } -log = { workspace = true } +tracing = { workspace = true } url = { workspace = true, features = ["serde"] } tokio = { workspace = true, features = ["sync", "time"] } time = { workspace = true, features = ["formatting"] } diff --git a/common/client-libs/validator-client/src/client.rs b/common/client-libs/validator-client/src/client.rs index b8375abdbd..472a6a9933 100644 --- a/common/client-libs/validator-client/src/client.rs +++ b/common/client-libs/validator-client/src/client.rs @@ -265,6 +265,13 @@ impl NymApiClient { NymApiClient { nym_api } } + #[cfg(not(target_arch = "wasm32"))] + pub fn new_with_timeout(api_url: Url, timeout: std::time::Duration) -> Self { + let nym_api = nym_api::Client::new(api_url, Some(timeout)); + + NymApiClient { nym_api } + } + pub fn new_with_user_agent(api_url: Url, user_agent: UserAgent) -> Self { let nym_api = nym_api::Client::builder::<_, ValidatorClientError>(api_url) .expect("invalid api url") diff --git a/common/client-libs/validator-client/src/connection_tester.rs b/common/client-libs/validator-client/src/connection_tester.rs index 15d2efe250..c9b4b3e435 100644 --- a/common/client-libs/validator-client/src/connection_tester.rs +++ b/common/client-libs/validator-client/src/connection_tester.rs @@ -121,36 +121,36 @@ async fn test_nyxd_connection( { Ok(Err(NyxdError::TendermintErrorRpc(e))) => { // If we get a tendermint-rpc error, we classify the node as not contactable - log::warn!("Checking: nyxd url: {url}: {}: {}", "failed".red(), e); + tracing::warn!("Checking: nyxd url: {url}: {}: {}", "failed".red(), e); false } Ok(Err(NyxdError::AbciError { code, log, .. })) => { // We accept the mixnet contract not found as ok from a connection standpoint. This happens // for example on a pre-launch network. - log::debug!( + tracing::debug!( "Checking: nyxd url: {url}: {}, but with abci error: {code}: {log}", "success".green() ); code == 18 } Ok(Err(error @ NyxdError::NoContractAddressAvailable(_))) => { - log::warn!("Checking: nyxd url: {url}: {}: {error}", "failed".red()); + tracing::warn!("Checking: nyxd url: {url}: {}: {error}", "failed".red()); false } Ok(Err(e)) => { // For any other error, we're optimistic and just try anyway. - log::warn!( + tracing::warn!( "Checking: nyxd_url: {url}: {}, but with error: {e}", "success".green() ); true } Ok(Ok(_)) => { - log::debug!("Checking: nyxd_url: {url}: {}", "success".green()); + tracing::debug!("Checking: nyxd_url: {url}: {}", "success".green()); true } Err(e) => { - log::warn!("Checking: nyxd_url: {url}: {}: {e}", "failed".red()); + tracing::warn!("Checking: nyxd_url: {url}: {}: {e}", "failed".red()); false } }; @@ -169,15 +169,15 @@ async fn test_nym_api_connection( .await { Ok(Ok(_)) => { - log::debug!("Checking: api_url: {url}: {}", "success".green()); + tracing::debug!("Checking: api_url: {url}: {}", "success".green()); true } Ok(Err(e)) => { - log::debug!("Checking: api_url: {url}: {}: {e}", "failed".red()); + tracing::debug!("Checking: api_url: {url}: {}: {e}", "failed".red()); false } Err(e) => { - log::debug!("Checking: api_url: {url}: {}: {e}", "failed".red()); + tracing::debug!("Checking: api_url: {url}: {}: {e}", "failed".red()); false } }; diff --git a/common/client-libs/validator-client/src/nym_api/mod.rs b/common/client-libs/validator-client/src/nym_api/mod.rs index 9660e470c7..2e5ce565f9 100644 --- a/common/client-libs/validator-client/src/nym_api/mod.rs +++ b/common/client-libs/validator-client/src/nym_api/mod.rs @@ -41,6 +41,7 @@ use nym_mixnet_contract_common::mixnode::MixNodeDetails; use nym_mixnet_contract_common::{GatewayBond, IdentityKeyRef, NodeId}; use time::format_description::BorrowedFormatItem; use time::Date; +use tracing::instrument; pub mod error; pub mod routes; @@ -52,11 +53,13 @@ pub fn rfc_3339_date() -> Vec> { #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] #[cfg_attr(not(target_arch = "wasm32"), async_trait)] pub trait NymApiClientExt: ApiClient { + #[instrument(level = "debug", skip(self))] async fn get_mixnodes(&self) -> Result, NymAPIError> { self.get_json(&[routes::API_VERSION, routes::MIXNODES], NO_PARAMS) .await } + #[instrument(level = "debug", skip(self))] async fn get_mixnodes_detailed(&self) -> Result, NymAPIError> { self.get_json( &[ @@ -70,6 +73,7 @@ pub trait NymApiClientExt: ApiClient { .await } + #[instrument(level = "debug", skip(self))] async fn get_gateways_detailed(&self) -> Result, NymAPIError> { self.get_json( &[ @@ -83,6 +87,7 @@ pub trait NymApiClientExt: ApiClient { .await } + #[instrument(level = "debug", skip(self))] async fn get_mixnodes_detailed_unfiltered( &self, ) -> Result, NymAPIError> { @@ -98,11 +103,13 @@ pub trait NymApiClientExt: ApiClient { .await } + #[instrument(level = "debug", skip(self))] async fn get_gateways(&self) -> Result, NymAPIError> { self.get_json(&[routes::API_VERSION, routes::GATEWAYS], NO_PARAMS) .await } + #[instrument(level = "debug", skip(self))] async fn get_gateways_described(&self) -> Result, NymAPIError> { self.get_json( &[routes::API_VERSION, routes::GATEWAYS, routes::DESCRIBED], @@ -111,6 +118,7 @@ pub trait NymApiClientExt: ApiClient { .await } + #[instrument(level = "debug", skip(self))] async fn get_mixnodes_described(&self) -> Result, NymAPIError> { self.get_json( &[routes::API_VERSION, routes::MIXNODES, routes::DESCRIBED], @@ -119,6 +127,7 @@ pub trait NymApiClientExt: ApiClient { .await } + #[tracing::instrument(level = "debug", skip_all)] async fn get_basic_mixnodes( &self, semver_compatibility: Option, @@ -142,6 +151,7 @@ pub trait NymApiClientExt: ApiClient { .await } + #[instrument(level = "debug", skip(self))] async fn get_basic_gateways( &self, semver_compatibility: Option, @@ -167,6 +177,7 @@ pub trait NymApiClientExt: ApiClient { /// retrieve basic information for nodes are capable of operating as an entry gateway /// this includes legacy gateways and nym-nodes + #[instrument(level = "debug", skip(self))] async fn get_all_basic_entry_assigned_nodes( &self, semver_compatibility: Option, @@ -208,6 +219,7 @@ pub trait NymApiClientExt: ApiClient { /// retrieve basic information for nodes that got assigned 'mixing' node in this epoch /// this includes legacy mixnodes and nym-nodes + #[instrument(level = "debug", skip(self))] async fn get_basic_active_mixing_assigned_nodes( &self, semver_compatibility: Option, @@ -247,6 +259,7 @@ pub trait NymApiClientExt: ApiClient { .await } + #[instrument(level = "debug", skip(self))] async fn get_active_mixnodes(&self) -> Result, NymAPIError> { self.get_json( &[routes::API_VERSION, routes::MIXNODES, routes::ACTIVE], @@ -255,6 +268,7 @@ pub trait NymApiClientExt: ApiClient { .await } + #[instrument(level = "debug", skip(self))] async fn get_active_mixnodes_detailed(&self) -> Result, NymAPIError> { self.get_json( &[ @@ -269,6 +283,7 @@ pub trait NymApiClientExt: ApiClient { .await } + #[instrument(level = "debug", skip(self))] async fn get_rewarded_mixnodes(&self) -> Result, NymAPIError> { self.get_json( &[routes::API_VERSION, routes::MIXNODES, routes::REWARDED], @@ -277,6 +292,7 @@ pub trait NymApiClientExt: ApiClient { .await } + #[instrument(level = "debug", skip(self))] async fn get_mixnode_report( &self, mix_id: NodeId, @@ -294,6 +310,7 @@ pub trait NymApiClientExt: ApiClient { .await } + #[instrument(level = "debug", skip(self))] async fn get_gateway_report( &self, identity: IdentityKeyRef<'_>, @@ -311,6 +328,7 @@ pub trait NymApiClientExt: ApiClient { .await } + #[instrument(level = "debug", skip(self))] async fn get_mixnode_history( &self, mix_id: NodeId, @@ -328,6 +346,7 @@ pub trait NymApiClientExt: ApiClient { .await } + #[instrument(level = "debug", skip(self))] async fn get_gateway_history( &self, identity: IdentityKeyRef<'_>, @@ -345,6 +364,7 @@ pub trait NymApiClientExt: ApiClient { .await } + #[instrument(level = "debug", skip(self))] async fn get_rewarded_mixnodes_detailed( &self, ) -> Result, NymAPIError> { @@ -361,6 +381,7 @@ pub trait NymApiClientExt: ApiClient { .await } + #[instrument(level = "debug", skip(self))] async fn get_gateway_core_status_count( &self, identity: IdentityKeyRef<'_>, @@ -392,6 +413,7 @@ pub trait NymApiClientExt: ApiClient { } } + #[instrument(level = "debug", skip(self))] async fn get_mixnode_core_status_count( &self, mix_id: NodeId, @@ -424,6 +446,7 @@ pub trait NymApiClientExt: ApiClient { } } + #[instrument(level = "debug", skip(self))] async fn get_mixnode_status( &self, mix_id: NodeId, @@ -441,6 +464,7 @@ pub trait NymApiClientExt: ApiClient { .await } + #[instrument(level = "debug", skip(self))] async fn get_mixnode_reward_estimation( &self, mix_id: NodeId, @@ -458,6 +482,7 @@ pub trait NymApiClientExt: ApiClient { .await } + #[instrument(level = "debug", skip(self))] async fn compute_mixnode_reward_estimation( &self, mix_id: NodeId, @@ -477,6 +502,7 @@ pub trait NymApiClientExt: ApiClient { .await } + #[instrument(level = "debug", skip(self))] async fn get_mixnode_stake_saturation( &self, mix_id: NodeId, @@ -494,6 +520,7 @@ pub trait NymApiClientExt: ApiClient { .await } + #[instrument(level = "debug", skip(self))] async fn get_mixnode_inclusion_probability( &self, mix_id: NodeId, @@ -511,6 +538,7 @@ pub trait NymApiClientExt: ApiClient { .await } + #[instrument(level = "debug", skip(self))] async fn get_current_node_performance( &self, node_id: NodeId, @@ -541,6 +569,7 @@ pub trait NymApiClientExt: ApiClient { .await } + #[instrument(level = "debug", skip(self))] async fn get_mixnodes_blacklisted(&self) -> Result, NymAPIError> { self.get_json( &[routes::API_VERSION, routes::MIXNODES, routes::BLACKLISTED], @@ -549,6 +578,7 @@ pub trait NymApiClientExt: ApiClient { .await } + #[instrument(level = "debug", skip(self))] async fn get_gateways_blacklisted(&self) -> Result, NymAPIError> { self.get_json( &[routes::API_VERSION, routes::GATEWAYS, routes::BLACKLISTED], @@ -557,6 +587,7 @@ pub trait NymApiClientExt: ApiClient { .await } + #[instrument(level = "debug", skip(self, request_body))] async fn blind_sign( &self, request_body: &BlindSignRequestBody, @@ -573,6 +604,7 @@ pub trait NymApiClientExt: ApiClient { .await } + #[instrument(level = "debug", skip(self, request_body))] async fn verify_ecash_ticket( &self, request_body: &VerifyEcashTicketBody, @@ -589,6 +621,7 @@ pub trait NymApiClientExt: ApiClient { .await } + #[instrument(level = "debug", skip(self, request_body))] async fn batch_redeem_ecash_tickets( &self, request_body: &BatchRedeemTicketsBody, @@ -605,6 +638,7 @@ pub trait NymApiClientExt: ApiClient { .await } + #[instrument(level = "debug", skip(self))] async fn double_spending_filter_v1(&self) -> Result { self.get_json( &[ @@ -617,6 +651,7 @@ pub trait NymApiClientExt: ApiClient { .await } + #[instrument(level = "debug", skip(self))] async fn partial_expiration_date_signatures( &self, expiration_date: Option, @@ -640,6 +675,7 @@ pub trait NymApiClientExt: ApiClient { .await } + #[instrument(level = "debug", skip(self))] async fn partial_coin_indices_signatures( &self, epoch_id: Option, @@ -660,6 +696,7 @@ pub trait NymApiClientExt: ApiClient { .await } + #[instrument(level = "debug", skip(self))] async fn global_expiration_date_signatures( &self, expiration_date: Option, @@ -683,6 +720,7 @@ pub trait NymApiClientExt: ApiClient { .await } + #[instrument(level = "debug", skip(self))] async fn global_coin_indices_signatures( &self, epoch_id: Option, @@ -703,6 +741,7 @@ pub trait NymApiClientExt: ApiClient { .await } + #[instrument(level = "debug", skip(self))] async fn master_verification_key( &self, epoch_id: Option, @@ -722,6 +761,7 @@ pub trait NymApiClientExt: ApiClient { .await } + #[instrument(level = "debug", skip(self))] async fn epoch_credentials( &self, dkg_epoch: EpochId, @@ -738,6 +778,7 @@ pub trait NymApiClientExt: ApiClient { .await } + #[instrument(level = "debug", skip(self))] async fn issued_credential( &self, credential_id: i64, @@ -754,6 +795,7 @@ pub trait NymApiClientExt: ApiClient { .await } + #[instrument(level = "debug", skip(self))] async fn issued_credentials( &self, credential_ids: Vec, diff --git a/common/client-libs/validator-client/src/nyxd/contract_traits/dkg_query_client.rs b/common/client-libs/validator-client/src/nyxd/contract_traits/dkg_query_client.rs index 4d8fd3237c..8674d7cb0a 100644 --- a/common/client-libs/validator-client/src/nyxd/contract_traits/dkg_query_client.rs +++ b/common/client-libs/validator-client/src/nyxd/contract_traits/dkg_query_client.rs @@ -8,9 +8,9 @@ use crate::nyxd::CosmWasmClient; use async_trait::async_trait; use cosmrs::AccountId; use cosmwasm_std::Addr; -use log::trace; use nym_coconut_dkg_common::types::{ChunkIndex, NodeIndex, StateAdvanceResponse}; use serde::Deserialize; +use tracing::trace; use nym_coconut_dkg_common::dealer::RegisteredDealerDetails; pub use nym_coconut_dkg_common::{ diff --git a/common/client-libs/validator-client/src/nyxd/cosmwasm_client/client_traits/query_client.rs b/common/client-libs/validator-client/src/nyxd/cosmwasm_client/client_traits/query_client.rs index 5cea0a1ba2..8feceebdbf 100644 --- a/common/client-libs/validator-client/src/nyxd/cosmwasm_client/client_traits/query_client.rs +++ b/common/client-libs/validator-client/src/nyxd/cosmwasm_client/client_traits/query_client.rs @@ -29,7 +29,6 @@ use cosmrs::proto::cosmwasm::wasm::v1::{ }; use cosmrs::tendermint::{block, chain, Hash}; use cosmrs::{AccountId, Coin as CosmosCoin, Tx}; -use log::trace; use prost::Message; use serde::{Deserialize, Serialize}; @@ -68,7 +67,7 @@ pub trait CosmWasmClient: TendermintRpcClient { Res: Message + Default, { if let Some(ref abci_path) = path { - trace!("performing query on abci path {abci_path}") + tracing::trace!("performing query on abci path {abci_path}") } let mut buf = Vec::with_capacity(req.encoded_len()); req.encode(&mut buf)?; @@ -297,7 +296,7 @@ pub trait CosmWasmClient: TendermintRpcClient { let start = Instant::now(); loop { - log::debug!( + tracing::debug!( "Polling for result of including {} in a block...", broadcasted.hash ); @@ -522,7 +521,7 @@ pub trait CosmWasmClient: TendermintRpcClient { .make_abci_query::<_, QuerySmartContractStateResponse>(path, req) .await?; - trace!("raw query response: {}", String::from_utf8_lossy(&res.data)); + tracing::trace!("raw query response: {}", String::from_utf8_lossy(&res.data)); Ok(serde_json::from_slice(&res.data)?) } diff --git a/common/client-libs/validator-client/src/nyxd/cosmwasm_client/client_traits/signing_client.rs b/common/client-libs/validator-client/src/nyxd/cosmwasm_client/client_traits/signing_client.rs index 8d0bb0fb4f..cec29e9c50 100644 --- a/common/client-libs/validator-client/src/nyxd/cosmwasm_client/client_traits/signing_client.rs +++ b/common/client-libs/validator-client/src/nyxd/cosmwasm_client/client_traits/signing_client.rs @@ -25,12 +25,12 @@ use cosmrs::proto::cosmos::tx::signing::v1beta1::SignMode; use cosmrs::staking::{MsgDelegate, MsgUndelegate}; use cosmrs::tx::{self, Msg}; use cosmrs::{cosmwasm, AccountId, Any, Tx}; -use log::debug; use serde::Serialize; use sha2::Digest; use sha2::Sha256; use std::time::SystemTime; use tendermint_rpc::endpoint::broadcast; +use tracing::debug; fn empty_fee() -> tx::Fee { tx::Fee { diff --git a/common/client-libs/validator-client/src/nyxd/cosmwasm_client/helpers.rs b/common/client-libs/validator-client/src/nyxd/cosmwasm_client/helpers.rs index d6e42daac7..559ad434a2 100644 --- a/common/client-libs/validator-client/src/nyxd/cosmwasm_client/helpers.rs +++ b/common/client-libs/validator-client/src/nyxd/cosmwasm_client/helpers.rs @@ -7,9 +7,9 @@ use base64::Engine; use cosmrs::abci::TxMsgData; use cosmrs::cosmwasm::MsgExecuteContractResponse; use cosmrs::proto::cosmos::base::query::v1beta1::{PageRequest, PageResponse}; -use log::error; use prost::bytes::Bytes; use tendermint_rpc::endpoint::broadcast; +use tracing::error; pub use cosmrs::abci::MsgResponse; diff --git a/common/http-api-client/src/lib.rs b/common/http-api-client/src/lib.rs index a8ad8e64d1..c90d731adb 100644 --- a/common/http-api-client/src/lib.rs +++ b/common/http-api-client/src/lib.rs @@ -9,7 +9,7 @@ use serde::{Deserialize, Serialize}; use std::fmt::Display; use std::time::Duration; use thiserror::Error; -use tracing::warn; +use tracing::{instrument, warn}; use url::Url; pub use reqwest::IntoUrl; @@ -202,6 +202,7 @@ impl Client { self.reqwest_client.get(url) } + #[instrument(level = "debug", skip_all, fields(path=?path))] async fn send_get_request( &self, path: PathSegments<'_>, @@ -212,6 +213,7 @@ impl Client { V: AsRef, E: Display, { + tracing::trace!("Sending GET request"); let url = sanitize_url(&self.base_url, path, params); #[cfg(target_arch = "wasm32")] @@ -277,6 +279,7 @@ impl Client { } } + #[instrument(level = "debug", skip_all)] pub async fn get_json( &self, path: PathSegments<'_>, @@ -512,12 +515,14 @@ pub fn sanitize_url, V: AsRef>( url } +#[tracing::instrument(level = "debug", skip_all)] pub async fn parse_response(res: Response, allow_empty: bool) -> Result> where T: DeserializeOwned, E: DeserializeOwned + Display, { let status = res.status(); + tracing::debug!("Status: {} (success: {})", &status, status.is_success()); if !allow_empty { if let Some(0) = res.content_length() { diff --git a/explorer-api/explorer-client/Cargo.toml b/explorer-api/explorer-client/Cargo.toml index 2397c83f7b..d32429e9a1 100644 --- a/explorer-api/explorer-client/Cargo.toml +++ b/explorer-api/explorer-client/Cargo.toml @@ -7,12 +7,12 @@ license.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -log.workspace = true nym-explorer-api-requests = { path = "../explorer-api-requests" } reqwest = { workspace = true, features = ["json"] } serde.workspace = true thiserror.workspace = true url.workspace = true +tracing = {workspace = true, features = ["attributes"]} [dev-dependencies] tokio = { workspace = true, features = ["full"] } diff --git a/explorer-api/explorer-client/src/lib.rs b/explorer-api/explorer-client/src/lib.rs index 3ff4807b2b..2415c90c69 100644 --- a/explorer-api/explorer-client/src/lib.rs +++ b/explorer-api/explorer-client/src/lib.rs @@ -3,6 +3,7 @@ use std::time::Duration; use reqwest::StatusCode; use thiserror::Error; +use tracing::instrument; use url::Url; // Re-export request types @@ -47,6 +48,12 @@ impl ExplorerClient { Ok(Self { client, url }) } + #[cfg(not(target_arch = "wasm32"))] + pub fn new_with_timeout(url: url::Url, timeout: Duration) -> Result { + let client = reqwest::Client::builder().timeout(timeout).build()?; + Ok(Self { client, url }) + } + #[cfg(target_arch = "wasm32")] pub fn new(url: url::Url) -> Result { let client = reqwest::Client::builder().build()?; @@ -58,10 +65,11 @@ impl ExplorerClient { paths: &[&str], ) -> Result { let url = combine_url(self.url.clone(), paths)?; - log::trace!("Sending GET request {url:?}"); + tracing::debug!("Sending GET request"); Ok(self.client.get(url).send().await?) } + #[instrument(level = "trace", skip_all, fields(paths=?paths))] async fn query_explorer_api(&self, paths: &[&str]) -> Result where T: std::fmt::Debug, @@ -70,7 +78,7 @@ impl ExplorerClient { let response = self.send_get_request(paths).await?; if response.status().is_success() { let res = response.json::().await?; - log::trace!("Got response: {res:?}"); + tracing::trace!("Got response: {res:?}"); Ok(res) } else if response.status() == StatusCode::NOT_FOUND { Err(ExplorerApiError::NotFound) diff --git a/nym-node-status-api/.gitignore b/nym-node-status-api/.gitignore index 8fce603003..b2a9b208f0 100644 --- a/nym-node-status-api/.gitignore +++ b/nym-node-status-api/.gitignore @@ -1 +1,2 @@ data/ +enter_db.sh diff --git a/nym-node-status-api/Cargo.toml b/nym-node-status-api/Cargo.toml index f670afc8d8..3b8d315ccb 100644 --- a/nym-node-status-api/Cargo.toml +++ b/nym-node-status-api/Cargo.toml @@ -14,22 +14,38 @@ rust-version.workspace = true [dependencies] anyhow = { workspace = true } -axum = { workspace = true } +axum = { workspace = true, features = ["tokio"] } chrono = { workspace = true } clap = { workspace = true, features = ["cargo", "derive"] } cosmwasm-std = { workspace = true } +envy = { workspace = true } futures-util = { workspace = true } +moka = { workspace = true, features = ["future"] } nym-bin-common = { path = "../common/bin-common" } nym-explorer-client = { path = "../explorer-api/explorer-client" } nym-network-defaults = { path = "../common/network-defaults" } nym-validator-client = { path = "../common/client-libs/validator-client" } +nym-task = { path = "../common/task" } +nym-node-requests = { path = "../nym-node/nym-node-requests", features = ["openapi"] } +reqwest = { workspace = true } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } +serde_json_path = { workspace = true } sqlx = { workspace = true, features = ["runtime-tokio-rustls", "sqlite"] } thiserror = { workspace = true } -tokio = { workspace = true, features = ["full"] } +tokio = { workspace = true, features = ["rt-multi-thread"] } +tokio-util = { workspace = true } tracing = { workspace = true } tracing-subscriber = { workspace = true, features = ["env-filter"] } +tracing-log = { workspace = true } +tower-http = { workspace = true, features = ["cors", "trace"] } +utoipa = { workspace = true, features = ["axum_extras", "time"] } +utoipa-swagger-ui = { workspace = true, features = ["axum"] } +# TODO dz `cargo update async-trait` +# for automatic schema detection, which was merged, but not released yet +# https://github.com/ProbablyClem/utoipauto/pull/38 +# utoipauto = { git = "https://github.com/ProbablyClem/utoipauto", rev = "eb04cba" } +utoipauto = { workspace = true } [build-dependencies] anyhow = { workspace = true } diff --git a/nym-node-status-api/build.rs b/nym-node-status-api/build.rs index 74c6767c6f..394083f8be 100644 --- a/nym-node-status-api/build.rs +++ b/nym-node-status-api/build.rs @@ -1,13 +1,17 @@ use anyhow::{anyhow, Result}; use sqlx::{Connection, SqliteConnection}; +use tokio::{fs::File, io::AsyncWriteExt}; const SQLITE_DB_FILENAME: &str = "nym-node-status-api.sqlite"; +/// If you need to re-run migrations or reset the db, just run +/// cargo clean -p nym-node-status-api #[tokio::main] async fn main() -> Result<()> { let out_dir = read_env_var("OUT_DIR")?; let database_path = format!("sqlite://{}/{}?mode=rwc", out_dir, SQLITE_DB_FILENAME); + write_db_path_to_file(&out_dir, SQLITE_DB_FILENAME).await?; let mut conn = SqliteConnection::connect(&database_path).await?; sqlx::migrate!("./migrations").run(&mut conn).await?; @@ -31,3 +35,12 @@ fn rerun_if_changed() { println!("cargo::rerun-if-changed=migrations"); println!("cargo::rerun-if-changed=src/db/queries"); } + +/// use `./enter_db.sh` to inspect DB +async fn write_db_path_to_file(out_dir: &str, db_filename: &str) -> anyhow::Result<()> { + let mut file = File::create("enter_db.sh").await?; + let _ = file.write(b"#!/bin/bash\n").await?; + file.write_all(format!("sqlite3 {}/{}", out_dir, db_filename).as_bytes()) + .await + .map_err(From::from) +} diff --git a/nym-node-status-api/launch_node_status_api.sh b/nym-node-status-api/launch_node_status_api.sh index 8af370232f..e5059c5011 100755 --- a/nym-node-status-api/launch_node_status_api.sh +++ b/nym-node-status-api/launch_node_status_api.sh @@ -2,63 +2,9 @@ set -e -function usage() { - echo "Usage: $0 [-ci]" - echo " -c Clear DB and re-initialize it before launching the binary." - echo " -i Only initialize and prepare database, env vars then exit without" - echo " launching" - exit 0 -} - -function init_db() { - rm -rf data/* - # https://github.com/launchbadge/sqlx/blob/main/sqlx-cli/README.md - cargo sqlx database drop -y - - cargo sqlx database create - cargo sqlx migrate run - cargo sqlx prepare - - echo "Fresh database ready!" -} - -# export DATABASE_URL as absolute path due to this -# https://github.com/launchbadge/sqlx/issues/3099 -db_filename="nym-node-status-api.sqlite" -script_abs_path=$(realpath "$0") -package_dir=$(dirname "$script_abs_path") -db_abs_path="$package_dir/data/$db_filename" -dotenv_file="$package_dir/.env" -echo "DATABASE_URL=sqlite://$db_abs_path" > "$dotenv_file" - export RUST_LOG=${RUST_LOG:-debug} -# export DATABASE_URL from .env file -set -a && source "$dotenv_file" && set +a +export NYM_API_CLIENT_TIMEOUT=60; +export EXPLORER_CLIENT_TIMEOUT=60; -clear_db=false -init_only=false - -while getopts "ci" opt; do - case ${opt} in - c) - clear_db=true - ;; - i) - init_only=true - ;; - \?) - usage - ;; - esac -done - -if [ "$clear_db" = true ] || [ "$init_only" = true ]; then - init_db -fi - -if [ "$init_only" = true ]; then - exit 0 -fi - -cargo run --package nym-node-status-api +cargo run --package nym-node-status-api --release -- --config-env-file ../envs/mainnet.env diff --git a/nym-node-status-api/migrations/000_init.sql b/nym-node-status-api/migrations/000_init.sql index f950b49229..35aaa40654 100644 --- a/nym-node-status-api/migrations/000_init.sql +++ b/nym-node-status-api/migrations/000_init.sql @@ -39,6 +39,21 @@ CREATE TABLE mixnodes ( CREATE INDEX idx_mixnodes_mix_id ON mixnodes (mix_id); CREATE INDEX idx_mixnodes_identity_key ON mixnodes (identity_key); +CREATE TABLE + mixnode_description ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + mix_id INTEGER UNIQUE NOT NULL, + moniker VARCHAR, + website VARCHAR, + security_contact VARCHAR, + details VARCHAR, + last_updated_utc INTEGER NOT NULL, + FOREIGN KEY (mix_id) REFERENCES mixnodes (mix_id) + ); + +-- Indexes for description table +CREATE INDEX idx_mixnode_description_mix_id ON mixnode_description (mix_id); + CREATE TABLE summary ( @@ -57,3 +72,29 @@ CREATE TABLE summary_history ); CREATE INDEX idx_summary_history_timestamp_utc ON summary_history (timestamp_utc); CREATE INDEX idx_summary_history_date ON summary_history (date); + + +CREATE TABLE gateway_description ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + gateway_identity_key VARCHAR UNIQUE NOT NULL, + moniker VARCHAR, + website VARCHAR, + security_contact VARCHAR, + details VARCHAR, + last_updated_utc INTEGER NOT NULL, + FOREIGN KEY (gateway_identity_key) REFERENCES gateways (gateway_identity_key) + ); + + +CREATE TABLE + mixnode_daily_stats ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + mix_id INTEGER NOT NULL, + total_stake BIGINT NOT NULL, + date_utc VARCHAR NOT NULL, + packets_received INTEGER DEFAULT 0, + packets_sent INTEGER DEFAULT 0, + packets_dropped INTEGER DEFAULT 0, + FOREIGN KEY (mix_id) REFERENCES mixnodes (mix_id), + UNIQUE (mix_id, date_utc) -- This constraint automatically creates an index + ); diff --git a/nym-node-status-api/src/config.rs b/nym-node-status-api/src/config.rs new file mode 100644 index 0000000000..24e966a53f --- /dev/null +++ b/nym-node-status-api/src/config.rs @@ -0,0 +1,79 @@ +use anyhow::anyhow; +use reqwest::Url; +use serde::Deserialize; +use std::time::Duration; + +#[derive(Debug, Clone, Deserialize)] +pub(crate) struct Config { + #[serde(default = "Config::default_http_cache_seconds")] + nym_http_cache_ttl: u64, + #[serde(default = "Config::default_http_port")] + http_port: u16, + #[serde(rename = "nyxd")] + nyxd_addr: Url, + #[serde(default = "Config::default_client_timeout")] + #[serde(deserialize_with = "parse_duration")] + nym_api_client_timeout: Duration, + #[serde(default = "Config::default_client_timeout")] + #[serde(deserialize_with = "parse_duration")] + explorer_client_timeout: Duration, +} + +impl Config { + pub(crate) fn from_env() -> anyhow::Result { + envy::from_env::().map_err(|e| { + tracing::error!("Failed to load config from env: {e}"); + anyhow::Error::from(e) + }) + } + + fn default_client_timeout() -> Duration { + Duration::from_secs(15) + } + + fn default_http_port() -> u16 { + 8000 + } + + fn default_http_cache_seconds() -> u64 { + 30 + } + + pub(crate) fn nym_http_cache_ttl(&self) -> u64 { + self.nym_http_cache_ttl + } + + pub(crate) fn http_port(&self) -> u16 { + self.http_port + } + + pub(crate) fn nyxd_addr(&self) -> &Url { + &self.nyxd_addr + } + + pub(crate) fn nym_api_client_timeout(&self) -> Duration { + self.nym_api_client_timeout.to_owned() + } + + pub(crate) fn nym_explorer_client_timeout(&self) -> Duration { + self.explorer_client_timeout.to_owned() + } +} + +fn parse_duration<'de, D>(deserializer: D) -> Result +where + D: serde::Deserializer<'de>, +{ + let s: String = Deserialize::deserialize(deserializer)?; + let secs: u64 = s.parse().map_err(serde::de::Error::custom)?; + Ok(Duration::from_secs(secs)) +} + +pub(super) fn read_env_var(env_var: &str) -> anyhow::Result { + std::env::var(env_var) + .map_err(|_| anyhow!("You need to set {}", env_var)) + .map(|value| { + tracing::trace!("{}={}", env_var, value); + value + }) +} diff --git a/nym-node-status-api/src/db/mod.rs b/nym-node-status-api/src/db/mod.rs index d9850abf7d..784a35f17a 100644 --- a/nym-node-status-api/src/db/mod.rs +++ b/nym-node-status-api/src/db/mod.rs @@ -3,12 +3,13 @@ use std::str::FromStr; use crate::read_env_var; use anyhow::{anyhow, Result}; use sqlx::{migrate::Migrator, sqlite::SqliteConnectOptions, ConnectOptions, SqlitePool}; -pub(crate) const DATABASE_URL_ENV_VAR: &str = "DATABASE_URL"; -static MIGRATOR: Migrator = sqlx::migrate!("./migrations"); pub(crate) mod models; pub(crate) mod queries; +pub(crate) const DATABASE_URL_ENV_VAR: &str = "DATABASE_URL"; +static MIGRATOR: Migrator = sqlx::migrate!("./migrations"); + pub(crate) type DbPool = SqlitePool; pub(crate) struct Storage { @@ -34,7 +35,8 @@ impl Storage { Ok(Storage { pool }) } - pub async fn pool(&self) -> &DbPool { - &self.pool + /// Cloning pool is cheap, it's the same underlying set of connections + pub async fn pool_owned(&self) -> DbPool { + self.pool.clone() } } diff --git a/nym-node-status-api/src/db/models.rs b/nym-node-status-api/src/db/models.rs index 83e04d99ee..54164b34e3 100644 --- a/nym-node-status-api/src/db/models.rs +++ b/nym-node-status-api/src/db/models.rs @@ -1,4 +1,10 @@ +use crate::{ + http::{self, models::SummaryHistory}, + monitor::NumericalCheckedCast, +}; +use nym_node_requests::api::v1::node::models::NodeDescription; use serde::{Deserialize, Serialize}; +use utoipa::ToSchema; pub(crate) struct GatewayRecord { pub(crate) identity_key: String, @@ -10,6 +16,88 @@ pub(crate) struct GatewayRecord { pub(crate) performance: u8, } +#[derive(Debug, Clone)] +pub(crate) struct GatewayDto { + pub(crate) gateway_identity_key: String, + pub(crate) bonded: bool, + pub(crate) blacklisted: bool, + pub(crate) performance: i64, + pub(crate) self_described: Option, + pub(crate) explorer_pretty_bond: Option, + pub(crate) last_probe_result: Option, + pub(crate) last_probe_log: Option, + pub(crate) last_testrun_utc: Option, + pub(crate) last_updated_utc: i64, + pub(crate) moniker: String, + pub(crate) security_contact: String, + pub(crate) details: String, + pub(crate) website: String, +} + +impl TryFrom for http::models::Gateway { + type Error = anyhow::Error; + + fn try_from(value: GatewayDto) -> Result { + // Instead of using routing_score_successes / routing_score_samples, we use the + // number of successful testruns in the last 24h. + let routing_score = 0f32; + let config_score = 0u32; + let last_updated_utc = + timestamp_as_utc(value.last_updated_utc.cast_checked()?).to_rfc3339(); + let last_testrun_utc = value + .last_testrun_utc + .and_then(|i| i.cast_checked().ok()) + .map(|t| timestamp_as_utc(t).to_rfc3339()); + + let self_described = value.self_described.clone().unwrap_or("null".to_string()); + let explorer_pretty_bond = value + .explorer_pretty_bond + .clone() + .unwrap_or("null".to_string()); + let last_probe_result = value + .last_probe_result + .clone() + .unwrap_or("null".to_string()); + let last_probe_log = value.last_probe_log.clone(); + + let self_described = serde_json::from_str(&self_described).unwrap_or(None); + let explorer_pretty_bond = serde_json::from_str(&explorer_pretty_bond).unwrap_or(None); + let last_probe_result = serde_json::from_str(&last_probe_result).unwrap_or(None); + + let bonded = value.bonded; + let blacklisted = value.blacklisted; + let performance = value.performance as u8; + + let description = NodeDescription { + moniker: value.moniker.clone(), + website: value.website.clone(), + security_contact: value.security_contact.clone(), + details: value.details.clone(), + }; + + Ok(http::models::Gateway { + gateway_identity_key: value.gateway_identity_key.clone(), + bonded, + blacklisted, + performance, + self_described, + explorer_pretty_bond, + description, + last_probe_result, + last_probe_log, + routing_score, + config_score, + last_testrun_utc, + last_updated_utc, + }) + } +} + +fn timestamp_as_utc(unix_timestamp: u64) -> chrono::DateTime { + let d = std::time::UNIX_EPOCH + std::time::Duration::from_secs(unix_timestamp); + d.into() +} + pub(crate) struct MixnodeRecord { pub(crate) mix_id: u32, pub(crate) identity_key: String, @@ -24,6 +112,63 @@ pub(crate) struct MixnodeRecord { pub(crate) is_dp_delegatee: bool, } +#[derive(Debug, Clone)] +pub(crate) struct MixnodeDto { + pub(crate) mix_id: i64, + pub(crate) bonded: bool, + pub(crate) blacklisted: bool, + pub(crate) is_dp_delegatee: bool, + pub(crate) total_stake: i64, + pub(crate) full_details: String, + pub(crate) self_described: Option, + pub(crate) last_updated_utc: i64, + pub(crate) moniker: String, + pub(crate) website: String, + pub(crate) security_contact: String, + pub(crate) details: String, +} + +impl TryFrom for http::models::Mixnode { + type Error = anyhow::Error; + + fn try_from(value: MixnodeDto) -> Result { + let mix_id = value.mix_id.cast_checked()?; + let full_details = value.full_details.clone(); + let full_details = serde_json::from_str(&full_details).unwrap_or(None); + + let self_described = value + .self_described + .clone() + .map(|v| serde_json::from_str(&v).unwrap_or(serde_json::Value::Null)); + + let last_updated_utc = + timestamp_as_utc(value.last_updated_utc.cast_checked()?).to_rfc3339(); + let blacklisted = value.blacklisted; + let is_dp_delegatee = value.is_dp_delegatee; + let moniker = value.moniker.clone(); + let website = value.website.clone(); + let security_contact = value.security_contact.clone(); + let details = value.details.clone(); + + Ok(http::models::Mixnode { + mix_id, + bonded: value.bonded, + blacklisted, + is_dp_delegatee, + total_stake: value.total_stake, + full_details, + description: NodeDescription { + moniker, + website, + security_contact, + details, + }, + self_described, + last_updated_utc, + }) + } +} + #[allow(unused)] #[derive(Debug, Clone)] pub(crate) struct BondedStatusDto { @@ -40,6 +185,28 @@ pub(crate) struct SummaryDto { pub(crate) last_updated_utc: i64, } +#[derive(Debug, Clone, Default)] +pub(crate) struct SummaryHistoryDto { + #[allow(dead_code)] + pub id: i64, + pub date: String, + pub value_json: String, + pub timestamp_utc: i64, +} + +impl TryFrom for SummaryHistory { + type Error = anyhow::Error; + + fn try_from(value: SummaryHistoryDto) -> Result { + let value_json = serde_json::from_str(&value.value_json).unwrap_or_default(); + Ok(SummaryHistory { + value_json, + date: value.date.clone(), + timestamp_utc: timestamp_as_utc(value.timestamp_utc.cast_checked()?).to_rfc3339(), + }) + } +} + pub(crate) const MIXNODES_BONDED_COUNT: &str = "mixnodes.bonded.count"; pub(crate) const MIXNODES_BONDED_ACTIVE: &str = "mixnodes.bonded.active"; pub(crate) const MIXNODES_BONDED_INACTIVE: &str = "mixnodes.bonded.inactive"; @@ -53,23 +220,28 @@ pub(crate) const GATEWAYS_BLACKLISTED_COUNT: &str = "gateways.blacklisted.count" pub(crate) const MIXNODES_HISTORICAL_COUNT: &str = "mixnodes.historical.count"; pub(crate) const GATEWAYS_HISTORICAL_COUNT: &str = "gateways.historical.count"; -#[derive(Debug, Clone, Deserialize, Serialize)] +// `utoipa`` goes crazy if you use module-qualified prefix as field type so we +// have to import it +use gateway::GatewaySummary; +use mixnode::MixnodeSummary; + +#[derive(Debug, Clone, Deserialize, Serialize, ToSchema)] pub(crate) struct NetworkSummary { - pub(crate) mixnodes: mixnode::MixnodeSummary, - pub(crate) gateways: gateway::GatewaySummary, + pub(crate) mixnodes: MixnodeSummary, + pub(crate) gateways: GatewaySummary, } pub(crate) mod mixnode { use super::*; - #[derive(Debug, Clone, Deserialize, Serialize)] + #[derive(Debug, Clone, Deserialize, Serialize, ToSchema)] pub(crate) struct MixnodeSummary { pub(crate) bonded: MixnodeSummaryBonded, pub(crate) blacklisted: MixnodeSummaryBlacklisted, pub(crate) historical: MixnodeSummaryHistorical, } - #[derive(Debug, Clone, Deserialize, Serialize)] + #[derive(Debug, Clone, Deserialize, Serialize, ToSchema)] pub(crate) struct MixnodeSummaryBonded { pub(crate) count: i32, pub(crate) active: i32, @@ -78,13 +250,13 @@ pub(crate) mod mixnode { pub(crate) last_updated_utc: String, } - #[derive(Debug, Clone, Deserialize, Serialize)] + #[derive(Debug, Clone, Deserialize, Serialize, ToSchema)] pub(crate) struct MixnodeSummaryBlacklisted { pub(crate) count: i32, pub(crate) last_updated_utc: String, } - #[derive(Debug, Clone, Deserialize, Serialize)] + #[derive(Debug, Clone, Deserialize, Serialize, ToSchema)] pub(crate) struct MixnodeSummaryHistorical { pub(crate) count: i32, pub(crate) last_updated_utc: String, @@ -94,7 +266,7 @@ pub(crate) mod mixnode { pub(crate) mod gateway { use super::*; - #[derive(Debug, Clone, Deserialize, Serialize)] + #[derive(Debug, Clone, Deserialize, Serialize, ToSchema)] pub(crate) struct GatewaySummary { pub(crate) bonded: GatewaySummaryBonded, pub(crate) blacklisted: GatewaySummaryBlacklisted, @@ -102,25 +274,25 @@ pub(crate) mod gateway { pub(crate) explorer: GatewaySummaryExplorer, } - #[derive(Debug, Clone, Deserialize, Serialize)] + #[derive(Debug, Clone, Deserialize, Serialize, ToSchema)] pub(crate) struct GatewaySummaryExplorer { pub(crate) count: i32, pub(crate) last_updated_utc: String, } - #[derive(Debug, Clone, Deserialize, Serialize)] + #[derive(Debug, Clone, Deserialize, Serialize, ToSchema)] pub(crate) struct GatewaySummaryBonded { pub(crate) count: i32, pub(crate) last_updated_utc: String, } - #[derive(Debug, Clone, Deserialize, Serialize)] + #[derive(Debug, Clone, Deserialize, Serialize, ToSchema)] pub(crate) struct GatewaySummaryHistorical { pub(crate) count: i32, pub(crate) last_updated_utc: String, } - #[derive(Debug, Clone, Deserialize, Serialize)] + #[derive(Debug, Clone, Deserialize, Serialize, ToSchema)] pub(crate) struct GatewaySummaryBlacklisted { pub(crate) count: i32, pub(crate) last_updated_utc: String, diff --git a/nym-node-status-api/src/db/queries/gateways.rs b/nym-node-status-api/src/db/queries/gateways.rs index 69f77a97fd..92a599154f 100644 --- a/nym-node-status-api/src/db/queries/gateways.rs +++ b/nym-node-status-api/src/db/queries/gateways.rs @@ -1,9 +1,13 @@ -use crate::db::{ - models::{BondedStatusDto, GatewayRecord}, - DbPool, +use crate::{ + db::{ + models::{BondedStatusDto, GatewayDto, GatewayRecord}, + DbPool, + }, + http::models::Gateway, }; use futures_util::TryStreamExt; use nym_validator_client::models::DescribedGateway; +use tracing::error; pub(crate) async fn insert_gateways( pool: &DbPool, @@ -114,3 +118,43 @@ async fn get_all_bonded_gateways_row_ids_by_status( Ok(items) } + +pub(crate) async fn get_all_gateways(pool: &DbPool) -> anyhow::Result> { + let mut conn = pool.acquire().await?; + let items = sqlx::query_as!( + GatewayDto, + r#"SELECT + gw.gateway_identity_key as "gateway_identity_key!", + gw.bonded as "bonded: bool", + gw.blacklisted as "blacklisted: bool", + gw.performance as "performance!", + gw.self_described as "self_described?", + gw.explorer_pretty_bond as "explorer_pretty_bond?", + gw.last_probe_result as "last_probe_result?", + gw.last_probe_log as "last_probe_log?", + gw.last_testrun_utc as "last_testrun_utc?", + gw.last_updated_utc as "last_updated_utc!", + COALESCE(gd.moniker, "NA") as "moniker!", + COALESCE(gd.website, "NA") as "website!", + COALESCE(gd.security_contact, "NA") as "security_contact!", + COALESCE(gd.details, "NA") as "details!" + FROM gateways gw + LEFT JOIN gateway_description gd + ON gw.gateway_identity_key = gd.gateway_identity_key + ORDER BY gw.gateway_identity_key"#, + ) + .fetch(&mut conn) + .try_collect::>() + .await?; + + let items: Vec = items + .into_iter() + .map(|item| item.try_into()) + .collect::>>() + .map_err(|e| { + error!("Conversion from DTO failed: {e}. Invalidly stored data?"); + e + })?; + tracing::trace!("Fetched {} gateways from DB", items.len()); + Ok(items) +} diff --git a/nym-node-status-api/src/db/queries/misc.rs b/nym-node-status-api/src/db/queries/misc.rs index c2c7b6d9eb..64b2f3cd24 100644 --- a/nym-node-status-api/src/db/queries/misc.rs +++ b/nym-node-status-api/src/db/queries/misc.rs @@ -45,6 +45,8 @@ async fn insert_summary( })?; } + tx.commit().await?; + Ok(()) } diff --git a/nym-node-status-api/src/db/queries/mixnodes.rs b/nym-node-status-api/src/db/queries/mixnodes.rs index aed48ed4d7..8bc8020ef9 100644 --- a/nym-node-status-api/src/db/queries/mixnodes.rs +++ b/nym-node-status-api/src/db/queries/mixnodes.rs @@ -1,9 +1,13 @@ use futures_util::TryStreamExt; use nym_validator_client::models::MixNodeBondAnnotated; +use tracing::error; -use crate::db::{ - models::{BondedStatusDto, MixnodeRecord}, - DbPool, +use crate::{ + db::{ + models::{BondedStatusDto, MixnodeDto, MixnodeRecord}, + DbPool, + }, + http::models::{DailyStats, Mixnode}, }; pub(crate) async fn insert_mixnodes( @@ -46,6 +50,78 @@ pub(crate) async fn insert_mixnodes( Ok(()) } +pub(crate) async fn get_all_mixnodes(pool: &DbPool) -> anyhow::Result> { + let mut conn = pool.acquire().await?; + let items = sqlx::query_as!( + MixnodeDto, + r#"SELECT + mn.mix_id as "mix_id!", + mn.bonded as "bonded: bool", + mn.blacklisted as "blacklisted: bool", + mn.is_dp_delegatee as "is_dp_delegatee: bool", + mn.total_stake as "total_stake!", + mn.full_details as "full_details!", + mn.self_described as "self_described", + mn.last_updated_utc as "last_updated_utc!", + COALESCE(md.moniker, "NA") as "moniker!", + COALESCE(md.website, "NA") as "website!", + COALESCE(md.security_contact, "NA") as "security_contact!", + COALESCE(md.details, "NA") as "details!" + FROM mixnodes mn + LEFT JOIN mixnode_description md ON mn.mix_id = md.mix_id + ORDER BY mn.mix_id"# + ) + .fetch(&mut conn) + .try_collect::>() + .await?; + + let items = items + .into_iter() + .map(|item| item.try_into()) + .collect::>>() + .map_err(|e| { + error!("Conversion from DTO failed: {e}. Invalidly stored data?"); + e + })?; + Ok(items) +} + +/// We fetch the latest 30 days of data as a subquery and then +/// return it in ascending order, so we don't break existing UI +pub(crate) async fn get_daily_stats(pool: &DbPool) -> anyhow::Result> { + let mut conn = pool.acquire().await?; + let items = sqlx::query_as!( + DailyStats, + r#" + SELECT + date_utc as "date_utc!", + packets_received as "total_packets_received!: i64", + packets_sent as "total_packets_sent!: i64", + packets_dropped as "total_packets_dropped!: i64", + total_stake as "total_stake!: i64" + FROM ( + SELECT + date_utc, + SUM(packets_received) as packets_received, + SUM(packets_sent) as packets_sent, + SUM(packets_dropped) as packets_dropped, + SUM(total_stake) as total_stake + FROM mixnode_daily_stats + GROUP BY date_utc + ORDER BY date_utc DESC + LIMIT 30 + ) + GROUP BY date_utc + ORDER BY date_utc + "# + ) + .fetch(&mut conn) + .try_collect::>() + .await?; + + Ok(items) +} + /// Ensure all mixnodes that are set as bonded, are still bonded pub(crate) async fn ensure_mixnodes_still_bonded( pool: &DbPool, diff --git a/nym-node-status-api/src/db/queries/mod.rs b/nym-node-status-api/src/db/queries/mod.rs index 38f1faab61..279d31dc34 100644 --- a/nym-node-status-api/src/db/queries/mod.rs +++ b/nym-node-status-api/src/db/queries/mod.rs @@ -1,9 +1,14 @@ mod gateways; mod misc; mod mixnodes; +mod summary; pub(crate) use gateways::{ - ensure_gateways_still_bonded, insert_gateways, write_blacklisted_gateways_to_db, + ensure_gateways_still_bonded, get_all_gateways, insert_gateways, + write_blacklisted_gateways_to_db, }; pub(crate) use misc::insert_summaries; -pub(crate) use mixnodes::{ensure_mixnodes_still_bonded, insert_mixnodes}; +pub(crate) use mixnodes::{ + ensure_mixnodes_still_bonded, get_all_mixnodes, get_daily_stats, insert_mixnodes, +}; +pub(crate) use summary::{get_summary, get_summary_history}; diff --git a/nym-node-status-api/src/db/queries/summary.rs b/nym-node-status-api/src/db/queries/summary.rs new file mode 100644 index 0000000000..d3855639f6 --- /dev/null +++ b/nym-node-status-api/src/db/queries/summary.rs @@ -0,0 +1,209 @@ +use chrono::{DateTime, Utc}; +use futures_util::TryStreamExt; +use std::collections::HashMap; +use tracing::error; + +use crate::{ + db::{ + models::{ + gateway::{ + GatewaySummary, GatewaySummaryBlacklisted, GatewaySummaryBonded, + GatewaySummaryExplorer, GatewaySummaryHistorical, + }, + mixnode::{ + MixnodeSummary, MixnodeSummaryBlacklisted, MixnodeSummaryBonded, + MixnodeSummaryHistorical, + }, + NetworkSummary, SummaryDto, SummaryHistoryDto, + }, + DbPool, + }, + http::{ + error::{HttpError, HttpResult}, + models::SummaryHistory, + }, +}; + +pub(crate) async fn get_summary_history(pool: &DbPool) -> anyhow::Result> { + let mut conn = pool.acquire().await?; + let items = sqlx::query_as!( + SummaryHistoryDto, + r#"SELECT + id as "id!", + date as "date!", + timestamp_utc as "timestamp_utc!", + value_json as "value_json!" + FROM summary_history + ORDER BY date DESC + LIMIT 30"#, + ) + .fetch(&mut conn) + .try_collect::>() + .await?; + + let items = items + .into_iter() + .map(|item| item.try_into()) + .collect::>>() + .map_err(|e| { + error!("Conversion from DTO failed: {e}. Invalidly stored data?"); + e + })?; + Ok(items) +} + +async fn get_summary_dto(pool: &DbPool) -> anyhow::Result> { + let mut conn = pool.acquire().await?; + Ok(sqlx::query_as!( + SummaryDto, + r#"SELECT + key as "key!", + value_json as "value_json!", + last_updated_utc as "last_updated_utc!" + FROM summary"# + ) + .fetch(&mut conn) + .try_collect::>() + .await?) +} + +pub(crate) async fn get_summary(pool: &DbPool) -> HttpResult { + let items = get_summary_dto(pool).await.map_err(|err| { + tracing::error!("Couldn't get Summary from DB: {err}"); + HttpError::internal() + })?; + from_summary_dto(items).await +} + +async fn from_summary_dto(items: Vec) -> HttpResult { + const MIXNODES_BONDED_COUNT: &str = "mixnodes.bonded.count"; + const MIXNODES_BONDED_ACTIVE: &str = "mixnodes.bonded.active"; + const MIXNODES_BONDED_INACTIVE: &str = "mixnodes.bonded.inactive"; + const MIXNODES_BONDED_RESERVE: &str = "mixnodes.bonded.reserve"; + const MIXNODES_BLACKLISTED_COUNT: &str = "mixnodes.blacklisted.count"; + const GATEWAYS_BONDED_COUNT: &str = "gateways.bonded.count"; + const GATEWAYS_EXPLORER_COUNT: &str = "gateways.explorer.count"; + const GATEWAYS_BLACKLISTED_COUNT: &str = "gateways.blacklisted.count"; + const MIXNODES_HISTORICAL_COUNT: &str = "mixnodes.historical.count"; + const GATEWAYS_HISTORICAL_COUNT: &str = "gateways.historical.count"; + + // convert database rows into a map by key + let mut map = HashMap::new(); + for item in items { + map.insert(item.key.clone(), item); + } + + // check we have all the keys we are expecting, and build up a map of errors for missing one + let keys = [ + GATEWAYS_BONDED_COUNT, + GATEWAYS_EXPLORER_COUNT, + GATEWAYS_HISTORICAL_COUNT, + GATEWAYS_BLACKLISTED_COUNT, + MIXNODES_BLACKLISTED_COUNT, + MIXNODES_BONDED_ACTIVE, + MIXNODES_BONDED_COUNT, + MIXNODES_BONDED_INACTIVE, + MIXNODES_BONDED_RESERVE, + MIXNODES_HISTORICAL_COUNT, + ]; + + let mut errors: Vec<&str> = vec![]; + for key in keys { + if !map.contains_key(key) { + errors.push(key); + } + } + + // return an error if anything is missing, with a nice list + if !errors.is_empty() { + tracing::error!("Summary value missing: {}", errors.join(", ")); + return Err(HttpError::internal()); + } + + // strip the options and use default values (anything missing is trapped above) + let mixnodes_bonded_count: SummaryDto = + map.get(MIXNODES_BONDED_COUNT).cloned().unwrap_or_default(); + let mixnodes_bonded_active: SummaryDto = + map.get(MIXNODES_BONDED_ACTIVE).cloned().unwrap_or_default(); + let mixnodes_bonded_inactive: SummaryDto = map + .get(MIXNODES_BONDED_INACTIVE) + .cloned() + .unwrap_or_default(); + let mixnodes_bonded_reserve: SummaryDto = map + .get(MIXNODES_BONDED_RESERVE) + .cloned() + .unwrap_or_default(); + let mixnodes_blacklisted_count: SummaryDto = map + .get(MIXNODES_BLACKLISTED_COUNT) + .cloned() + .unwrap_or_default(); + let gateways_bonded_count: SummaryDto = + map.get(GATEWAYS_BONDED_COUNT).cloned().unwrap_or_default(); + let gateways_explorer_count: SummaryDto = map + .get(GATEWAYS_EXPLORER_COUNT) + .cloned() + .unwrap_or_default(); + let mixnodes_historical_count: SummaryDto = map + .get(MIXNODES_HISTORICAL_COUNT) + .cloned() + .unwrap_or_default(); + let gateways_historical_count: SummaryDto = map + .get(GATEWAYS_HISTORICAL_COUNT) + .cloned() + .unwrap_or_default(); + let gateways_blacklisted_count: SummaryDto = map + .get(GATEWAYS_BLACKLISTED_COUNT) + .cloned() + .unwrap_or_default(); + + Ok(NetworkSummary { + mixnodes: MixnodeSummary { + bonded: MixnodeSummaryBonded { + count: to_count_i32(&mixnodes_bonded_count), + active: to_count_i32(&mixnodes_bonded_active), + reserve: to_count_i32(&mixnodes_bonded_reserve), + inactive: to_count_i32(&mixnodes_bonded_inactive), + last_updated_utc: to_timestamp(&mixnodes_bonded_count), + }, + blacklisted: MixnodeSummaryBlacklisted { + count: to_count_i32(&mixnodes_blacklisted_count), + last_updated_utc: to_timestamp(&mixnodes_blacklisted_count), + }, + historical: MixnodeSummaryHistorical { + count: to_count_i32(&mixnodes_historical_count), + last_updated_utc: to_timestamp(&mixnodes_historical_count), + }, + }, + gateways: GatewaySummary { + bonded: GatewaySummaryBonded { + count: to_count_i32(&gateways_bonded_count), + last_updated_utc: to_timestamp(&gateways_bonded_count), + }, + blacklisted: GatewaySummaryBlacklisted { + count: to_count_i32(&gateways_blacklisted_count), + last_updated_utc: to_timestamp(&gateways_blacklisted_count), + }, + historical: GatewaySummaryHistorical { + count: to_count_i32(&gateways_historical_count), + last_updated_utc: to_timestamp(&gateways_historical_count), + }, + explorer: GatewaySummaryExplorer { + count: to_count_i32(&gateways_explorer_count), + last_updated_utc: to_timestamp(&gateways_explorer_count), + }, + }, + }) +} + +fn to_count_i32(value: &SummaryDto) -> i32 { + value.value_json.parse::().unwrap_or_default() +} + +fn to_timestamp(value: &SummaryDto) -> String { + timestamp_as_utc(value.last_updated_utc as u64).to_rfc3339() +} + +fn timestamp_as_utc(unix_timestamp: u64) -> DateTime { + let d = std::time::UNIX_EPOCH + std::time::Duration::from_secs(unix_timestamp); + d.into() +} diff --git a/nym-node-status-api/src/http/api/gateways.rs b/nym-node-status-api/src/http/api/gateways.rs new file mode 100644 index 0000000000..9dec3134e4 --- /dev/null +++ b/nym-node-status-api/src/http/api/gateways.rs @@ -0,0 +1,110 @@ +use axum::{ + extract::{Path, Query, State}, + Json, Router, +}; +use serde::Deserialize; +use utoipa::IntoParams; + +use crate::http::{ + error::{HttpError, HttpResult}, + models::{Gateway, GatewaySkinny}, + state::AppState, + PagedResult, Pagination, +}; + +pub(crate) fn routes() -> Router { + Router::new() + .route("/", axum::routing::get(gateways)) + .route("/skinny", axum::routing::get(gateways_skinny)) + .route("/skinny/:identity_key", axum::routing::get(get_gateway)) +} + +#[utoipa::path( + tag = "Gateways", + get, + params( + Pagination + ), + path = "/v2/gateways", + responses( + (status = 200, body = PagedGateway) + ) +)] +async fn gateways( + Query(pagination): Query, + State(state): State, +) -> HttpResult>> { + let db = state.db_pool(); + let res = state.cache().get_gateway_list(db).await; + + Ok(Json(PagedResult::paginate(pagination, res))) +} + +#[utoipa::path( + tag = "Gateways", + get, + params( + Pagination + ), + path = "/v2/gateways/skinny", + responses( + (status = 200, body = PagedGatewaySkinny) + ) +)] +async fn gateways_skinny( + Query(pagination): Query, + State(state): State, +) -> HttpResult>> { + let db = state.db_pool(); + let res = state.cache().get_gateway_list(db).await; + let res: Vec = res + .iter() + .filter(|g| g.bonded) + .map(|g| GatewaySkinny { + gateway_identity_key: g.gateway_identity_key.clone(), + self_described: g.self_described.clone(), + performance: g.performance, + explorer_pretty_bond: g.explorer_pretty_bond.clone(), + last_probe_result: g.last_probe_result.clone(), + last_testrun_utc: g.last_testrun_utc.clone(), + last_updated_utc: g.last_updated_utc.clone(), + routing_score: g.routing_score, + config_score: g.config_score, + }) + .collect(); + + Ok(Json(PagedResult::paginate(pagination, res))) +} + +#[derive(Deserialize, IntoParams)] +#[into_params(parameter_in = Path)] +struct IdentityKeyParam { + identity_key: String, +} + +#[utoipa::path( + tag = "Gateways", + get, + params( + IdentityKeyParam + ), + path = "/v2/gateways/{identity_key}", + responses( + (status = 200, body = Gateway) + ) +)] +async fn get_gateway( + Path(IdentityKeyParam { identity_key }): Path, + State(state): State, +) -> HttpResult> { + let db = state.db_pool(); + let res = state.cache().get_gateway_list(db).await; + + match res + .iter() + .find(|item| item.gateway_identity_key == identity_key) + { + Some(res) => Ok(Json(res.clone())), + None => Err(HttpError::invalid_input(identity_key)), + } +} diff --git a/nym-node-status-api/src/http/api/mixnodes.rs b/nym-node-status-api/src/http/api/mixnodes.rs new file mode 100644 index 0000000000..f42d0bf91c --- /dev/null +++ b/nym-node-status-api/src/http/api/mixnodes.rs @@ -0,0 +1,91 @@ +use axum::{ + extract::{Path, Query, State}, + Json, Router, +}; +use serde::Deserialize; +use tracing::instrument; +use utoipa::IntoParams; + +use crate::http::{ + error::{HttpError, HttpResult}, + models::{DailyStats, Mixnode}, + state::AppState, + PagedResult, Pagination, +}; + +pub(crate) fn routes() -> Router { + Router::new() + .route("/", axum::routing::get(mixnodes)) + .route("/:mix_id", axum::routing::get(get_mixnodes)) + .route("/stats", axum::routing::get(get_stats)) +} + +#[utoipa::path( + tag = "Mixnodes", + get, + params( + Pagination + ), + path = "/v2/mixnodes", + responses( + (status = 200, body = PagedMixnode) + ) +)] +#[instrument(level = tracing::Level::DEBUG, skip_all, fields(page=pagination.page, size=pagination.size))] +async fn mixnodes( + Query(pagination): Query, + State(state): State, +) -> HttpResult>> { + let db = state.db_pool(); + let res = state.cache().get_mixnodes_list(db).await; + + Ok(Json(PagedResult::paginate(pagination, res))) +} + +#[derive(Deserialize, IntoParams)] +#[into_params(parameter_in = Path)] +struct MixIdParam { + mix_id: String, +} + +#[utoipa::path( + tag = "Mixnodes", + get, + params( + MixIdParam + ), + path = "/v2/mixnodes/{mix_id}", + responses( + (status = 200, body = Mixnode) + ) +)] +#[instrument(level = tracing::Level::DEBUG, skip_all, fields(mix_id = mix_id))] +async fn get_mixnodes( + Path(MixIdParam { mix_id }): Path, + State(state): State, +) -> HttpResult> { + match mix_id.parse::() { + Ok(parsed_mix_id) => { + let res = state.cache().get_mixnodes_list(state.db_pool()).await; + + match res.iter().find(|item| item.mix_id == parsed_mix_id) { + Some(res) => Ok(Json(res.clone())), + None => Err(HttpError::invalid_input(mix_id)), + } + } + Err(_e) => Err(HttpError::invalid_input(mix_id)), + } +} + +#[utoipa::path( + tag = "Mixnodes", + get, + path = "/v2/mixnodes/stats", + responses( + (status = 200, body = Vec) + ) +)] +async fn get_stats(State(state): State) -> HttpResult>> { + let stats = state.cache().get_mixnode_stats(state.db_pool()).await; + Ok(Json(stats)) +} diff --git a/nym-node-status-api/src/http/api/mod.rs b/nym-node-status-api/src/http/api/mod.rs new file mode 100644 index 0000000000..4d385904b5 --- /dev/null +++ b/nym-node-status-api/src/http/api/mod.rs @@ -0,0 +1,85 @@ +use anyhow::anyhow; +use axum::{response::Redirect, Router}; +use tokio::net::ToSocketAddrs; +use tower_http::{cors::CorsLayer, trace::TraceLayer}; +use utoipa::OpenApi; +use utoipa_swagger_ui::SwaggerUi; + +use crate::http::{server::HttpServer, state::AppState}; + +pub(crate) mod gateways; +pub(crate) mod mixnodes; +pub(crate) mod services; +pub(crate) mod summary; +pub(crate) mod testruns; + +pub(crate) struct RouterBuilder { + unfinished_router: Router, +} + +impl RouterBuilder { + pub(crate) fn with_default_routes() -> Self { + let router = Router::new() + .merge( + SwaggerUi::new("/swagger") + .url("/api-docs/openapi.json", super::api_docs::ApiDoc::openapi()), + ) + .route( + "/", + axum::routing::get(|| async { Redirect::permanent("/swagger") }), + ) + .nest( + "/v2", + Router::new() + .nest("/gateways", gateways::routes()) + .nest("/mixnodes", mixnodes::routes()) + .nest("/services", services::routes()) + .nest("/summary", summary::routes()), + // .nest("/testruns", testruns::_routes()), + ); + + Self { + unfinished_router: router, + } + } + + pub(crate) fn with_state(self, state: AppState) -> RouterWithState { + RouterWithState { + router: self.finalize_routes().with_state(state), + } + } + + fn finalize_routes(self) -> Router { + // layers added later wrap earlier layers + self.unfinished_router + // CORS layer needs to wrap other API layers + .layer(setup_cors()) + // logger should be outermost layer + .layer(TraceLayer::new_for_http()) + } +} + +pub(crate) struct RouterWithState { + router: Router, +} + +impl RouterWithState { + pub(crate) async fn build_server( + self, + bind_address: A, + ) -> anyhow::Result { + tokio::net::TcpListener::bind(bind_address) + .await + .map(|listener| HttpServer::new(self.router, listener)) + .map_err(|err| anyhow!("Couldn't bind to address due to {}", err)) + } +} + +fn setup_cors() -> CorsLayer { + use axum::http::Method; + CorsLayer::new() + .allow_origin(tower_http::cors::Any) + .allow_methods([Method::POST, Method::GET, Method::PATCH, Method::OPTIONS]) + .allow_headers(tower_http::cors::Any) + .allow_credentials(false) +} diff --git a/nym-node-status-api/src/http/api/services/json_path.rs b/nym-node-status-api/src/http/api/services/json_path.rs new file mode 100644 index 0000000000..caefc6489d --- /dev/null +++ b/nym-node-status-api/src/http/api/services/json_path.rs @@ -0,0 +1,58 @@ +use serde_json_path::JsonPath; + +use crate::http::models::Gateway; + +pub(super) struct ParseJsonPaths { + pub(super) path_ip_address: JsonPath, + pub(super) path_hostname: JsonPath, + pub(super) path_service_provider_client_id: JsonPath, +} + +impl ParseJsonPaths { + pub fn new() -> Result { + Ok(ParseJsonPaths { + path_ip_address: JsonPath::parse("$.host_information.ip_address[0]")?, + path_hostname: JsonPath::parse("$.host_information.hostname")?, + path_service_provider_client_id: JsonPath::parse("$.network_requester.address")?, + }) + } +} + +pub(super) struct ParsedDetails { + pub(super) ip_address: Option, + pub(super) hostname: Option, + pub(super) service_provider_client_id: Option, +} + +impl ParsedDetails { + fn get_string_from_json_path( + value: &Option, + path: &JsonPath, + ) -> Option { + match value { + Some(value) => path + .query(value) + .exactly_one() + .map(|v2| v2.as_str().map(|v3| v3.to_string())) + .ok() + .flatten(), + None => None, + } + } + pub fn new(paths: &ParseJsonPaths, g: &Gateway) -> ParsedDetails { + ParsedDetails { + hostname: ParsedDetails::get_string_from_json_path( + &g.self_described, + &paths.path_hostname, + ), + ip_address: ParsedDetails::get_string_from_json_path( + &g.self_described, + &paths.path_ip_address, + ), + service_provider_client_id: ParsedDetails::get_string_from_json_path( + &g.self_described, + &paths.path_service_provider_client_id, + ), + } + } +} diff --git a/nym-node-status-api/src/http/api/services/mod.rs b/nym-node-status-api/src/http/api/services/mod.rs new file mode 100644 index 0000000000..5650684c43 --- /dev/null +++ b/nym-node-status-api/src/http/api/services/mod.rs @@ -0,0 +1,134 @@ +use crate::http::{ + error::{HttpError, HttpResult}, + models::Service, + state::AppState, + PagedResult, Pagination, +}; +use axum::{ + extract::{Query, State}, + Json, Router, +}; +use json_path::{ParseJsonPaths, ParsedDetails}; +use tracing::instrument; + +mod json_path; + +pub(crate) fn routes() -> Router { + Router::new().route("/", axum::routing::get(mixnodes)) +} + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, utoipa::IntoParams)] +#[into_params(parameter_in = Query)] +pub(crate) struct ServicesQueryParams { + size: Option, + page: Option, + wss: Option, + hostname: Option, + entry: Option, +} + +#[utoipa::path( + tag = "Services", + get, + params( + ServicesQueryParams, + ), + path = "/v2/services", + responses( + (status = 200, body = PagedService) + ) +)] +#[instrument(level = tracing::Level::DEBUG, skip(state))] +async fn mixnodes( + Query(ServicesQueryParams { + size, + page, + wss, + hostname, + entry, + }): Query, + State(state): State, +) -> HttpResult>> { + let db = state.db_pool(); + let cache = state.cache(); + + let show_only_wss = wss.unwrap_or(false); + let show_only_with_hostname = hostname.unwrap_or(false); + let show_entry_gateways_only = entry.unwrap_or(false); + + let paths = ParseJsonPaths::new().map_err(|e| { + tracing::error!("Invalidly configured ParseJsonPaths: {e}"); + HttpError::internal() + })?; + let res = cache.get_gateway_list(db).await; + let res: Vec = res + .iter() + .map(|g| { + let details = ParsedDetails::new(&paths, g); + + let s = Service { + gateway_identity_key: g.gateway_identity_key.clone(), + ip_address: details.ip_address, + service_provider_client_id: details.service_provider_client_id, + hostname: details.hostname, + last_successful_ping_utc: g.last_testrun_utc.clone(), + last_updated_utc: g.last_updated_utc.clone(), + // routing_score: g.routing_score, + routing_score: 1f32, + mixnet_websockets: g + .self_described + .clone() + .and_then(|s| s.get("mixnet_websockets").cloned()), + }; + + let f = ServiceFilter::new(&s); + + (s, f) + }) + .filter(|(_, f)| { + let mut keep = f.has_network_requester_sp; + + if show_entry_gateways_only { + keep = true; + } + + if show_only_wss { + keep &= f.has_wss; + } + if show_only_with_hostname { + keep &= f.has_hostname; + } + + keep + }) + .map(|(s, _)| s) + .collect(); + + Ok(Json(PagedResult::paginate(Pagination { size, page }, res))) +} + +struct ServiceFilter { + has_wss: bool, + has_network_requester_sp: bool, + has_hostname: bool, +} + +impl ServiceFilter { + fn new(s: &Service) -> Self { + let has_wss = match &s.mixnet_websockets { + Some(v) => v.get("wss_port").map(|v2| !v2.is_null()).unwrap_or(false), + None => false, + }; + let has_hostname = s.hostname.is_some(); + let has_network_requester_sp = match &s.service_provider_client_id { + Some(v) => !v.is_empty(), + None => false, + }; + + ServiceFilter { + has_wss, + has_hostname, + has_network_requester_sp, + } + } +} diff --git a/nym-node-status-api/src/http/api/summary.rs b/nym-node-status-api/src/http/api/summary.rs new file mode 100644 index 0000000000..729141509c --- /dev/null +++ b/nym-node-status-api/src/http/api/summary.rs @@ -0,0 +1,43 @@ +use axum::{extract::State, Json, Router}; +use tracing::instrument; + +use crate::{ + db::models::NetworkSummary, + http::{error::HttpResult, models::SummaryHistory, state::AppState}, +}; + +pub(crate) fn routes() -> Router { + Router::new() + .route("/", axum::routing::get(summary)) + .route("/history", axum::routing::get(summary_history)) +} + +#[utoipa::path( + tag = "Summary", + get, + path = "/v2/summary", + responses( + (status = 200, body = NetworkSummary) + ) +)] +#[instrument(level = tracing::Level::DEBUG, skip_all)] +async fn summary(State(state): State) -> HttpResult> { + crate::db::queries::get_summary(state.db_pool()) + .await + .map(Json) +} + +#[utoipa::path( + tag = "Summary", + get, + path = "/v2/summary/history", + responses( + (status = 200, body = Vec) + ) +)] +#[instrument(level = tracing::Level::DEBUG, skip_all)] +async fn summary_history(State(state): State) -> HttpResult>> { + Ok(Json( + state.cache().get_summary_history(state.db_pool()).await, + )) +} diff --git a/nym-node-status-api/src/http/api/testruns.rs b/nym-node-status-api/src/http/api/testruns.rs new file mode 100644 index 0000000000..875012b0fb --- /dev/null +++ b/nym-node-status-api/src/http/api/testruns.rs @@ -0,0 +1,7 @@ +use axum::Router; + +use crate::http::state::AppState; + +pub(crate) fn _routes() -> Router { + unimplemented!() +} diff --git a/nym-node-status-api/src/http/api_docs.rs b/nym-node-status-api/src/http/api_docs.rs new file mode 100644 index 0000000000..172aa899a3 --- /dev/null +++ b/nym-node-status-api/src/http/api_docs.rs @@ -0,0 +1,15 @@ +use crate::http::{Gateway, GatewaySkinny, Mixnode, Service}; +use utoipa::OpenApi; +use utoipauto::utoipauto; + +// manually import external structs which are behind feature flags because they +// can't be automatically discovered +// https://github.com/ProbablyClem/utoipauto/issues/13#issuecomment-1974911829 +#[utoipauto(paths = "./nym-node-status-api/src")] +#[derive(OpenApi)] +#[openapi( + info(title = "Nym API"), + tags(), + components(schemas(nym_node_requests::api::v1::node::models::NodeDescription,)) +)] +pub(super) struct ApiDoc; diff --git a/nym-node-status-api/src/http/error.rs b/nym-node-status-api/src/http/error.rs new file mode 100644 index 0000000000..8bbd59e095 --- /dev/null +++ b/nym-node-status-api/src/http/error.rs @@ -0,0 +1,28 @@ +pub(crate) type HttpResult = Result; + +pub(crate) struct HttpError { + message: String, + status: axum::http::StatusCode, +} + +impl HttpError { + pub(crate) fn invalid_input(message: String) -> Self { + Self { + message, + status: axum::http::StatusCode::BAD_REQUEST, + } + } + + pub(crate) fn internal() -> Self { + Self { + message: serde_json::json!({"message": "Internal server error"}).to_string(), + status: axum::http::StatusCode::INTERNAL_SERVER_ERROR, + } + } +} + +impl axum::response::IntoResponse for HttpError { + fn into_response(self) -> axum::response::Response { + (self.status, self.message).into_response() + } +} diff --git a/nym-node-status-api/src/http/mod.rs b/nym-node-status-api/src/http/mod.rs new file mode 100644 index 0000000000..1cc317337f --- /dev/null +++ b/nym-node-status-api/src/http/mod.rs @@ -0,0 +1,71 @@ +use models::{Gateway, GatewaySkinny, Mixnode, Service}; + +pub(crate) mod api; +pub(crate) mod api_docs; +pub(crate) mod error; +pub(crate) mod models; +pub(crate) mod server; +pub(crate) mod state; + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, utoipa::ToSchema)] +// exclude generic from auto-discovery +#[utoipauto::utoipa_ignore] +// https://docs.rs/utoipa/latest/utoipa/derive.ToSchema.html#generic-schemas-with-aliases +// Generic structs can only be included via aliases, not directly, because they +// it would cause an error in generated Swagger docs. +// Instead, you have to manually monomorphize each generic struct that +// you wish to document +#[aliases( + PagedGateway = PagedResult, + PagedGatewaySkinny = PagedResult, + PagedMixnode = PagedResult, + PagedService = PagedResult, +)] +pub struct PagedResult { + pub page: usize, + pub size: usize, + pub total: usize, + pub items: Vec, +} + +impl PagedResult { + pub fn paginate(pagination: Pagination, res: Vec) -> Self { + let total = res.len(); + let (size, mut page) = pagination.intoto_inner_values(); + + if page * size > total { + page = total / size; + } + + let chunks: Vec<&[T]> = res.chunks(size).collect(); + + PagedResult { + page, + size, + total, + items: chunks.get(page).cloned().unwrap_or_default().into(), + } + } +} + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, utoipa::IntoParams)] +#[into_params(parameter_in = Query)] +pub(crate) struct Pagination { + size: Option, + page: Option, +} + +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; + + ( + self.size.unwrap_or(SIZE_DEFAULT).min(SIZE_MAX), + self.page.unwrap_or(PAGE_DEFAULT), + ) + } +} diff --git a/nym-node-status-api/src/http/models.rs b/nym-node-status-api/src/http/models.rs new file mode 100644 index 0000000000..3a4c348b25 --- /dev/null +++ b/nym-node-status-api/src/http/models.rs @@ -0,0 +1,74 @@ +use nym_node_requests::api::v1::node::models::NodeDescription; +use serde::{Deserialize, Serialize}; +use utoipa::ToSchema; + +#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] +pub struct Gateway { + pub gateway_identity_key: String, + pub bonded: bool, + pub blacklisted: bool, + pub performance: u8, + pub self_described: Option, + pub explorer_pretty_bond: Option, + pub description: NodeDescription, + pub last_probe_result: Option, + pub last_probe_log: Option, + pub last_testrun_utc: Option, + pub last_updated_utc: String, + pub routing_score: f32, + pub config_score: u32, +} + +#[derive(Debug, Clone, Deserialize, Serialize, ToSchema)] +pub struct GatewaySkinny { + pub gateway_identity_key: String, + pub self_described: Option, + pub explorer_pretty_bond: Option, + pub last_probe_result: Option, + pub last_testrun_utc: Option, + pub last_updated_utc: String, + pub routing_score: f32, + pub config_score: u32, + pub performance: u8, +} + +#[derive(Debug, Clone, Deserialize, Serialize, ToSchema)] +pub struct Mixnode { + pub mix_id: u32, + pub bonded: bool, + pub blacklisted: bool, + pub is_dp_delegatee: bool, + pub total_stake: i64, + pub full_details: Option, + pub self_described: Option, + pub description: NodeDescription, + pub last_updated_utc: String, +} + +#[derive(Debug, Clone, Deserialize, Serialize, ToSchema)] +pub struct DailyStats { + pub date_utc: String, + pub total_packets_received: i64, + pub total_packets_sent: i64, + pub total_packets_dropped: i64, + pub total_stake: i64, +} + +#[derive(Debug, Clone, Deserialize, Serialize, ToSchema)] +pub struct Service { + pub gateway_identity_key: String, + pub last_updated_utc: String, + pub routing_score: f32, + pub service_provider_client_id: Option, + pub ip_address: Option, + pub hostname: Option, + pub mixnet_websockets: Option, + pub last_successful_ping_utc: Option, +} + +#[derive(Debug, Clone, Deserialize, Serialize, ToSchema)] +pub(crate) struct SummaryHistory { + pub date: String, + pub value_json: serde_json::Value, + pub timestamp_utc: String, +} diff --git a/nym-node-status-api/src/http/server.rs b/nym-node-status-api/src/http/server.rs new file mode 100644 index 0000000000..694f5fa79a --- /dev/null +++ b/nym-node-status-api/src/http/server.rs @@ -0,0 +1,92 @@ +use axum::Router; +use core::net::SocketAddr; +use tokio::{net::TcpListener, task::JoinHandle}; +use tokio_util::sync::{CancellationToken, WaitForCancellationFutureOwned}; + +use crate::{ + db::DbPool, + http::{api::RouterBuilder, state::AppState}, +}; + +/// Return handles that allow for graceful shutdown of server + awaiting its +/// background tokio task +pub(crate) async fn start_http_api( + db_pool: DbPool, + http_port: u16, + nym_http_cache_ttl: u64, +) -> anyhow::Result { + let router_builder = RouterBuilder::with_default_routes(); + + let state = AppState::new(db_pool, nym_http_cache_ttl); + let router = router_builder.with_state(state); + + // TODO dz do we need this to be configurable? + let bind_addr = format!("0.0.0.0:{}", http_port); + let server = router.build_server(bind_addr).await?; + + Ok(start_server(server)) +} + +fn start_server(server: HttpServer) -> ShutdownHandles { + // one copy is stored to trigger a graceful shutdown later + let shutdown_button = CancellationToken::new(); + // other copy is given to server to listen for a shutdown + let shutdown_receiver = shutdown_button.clone(); + let shutdown_receiver = shutdown_receiver.cancelled_owned(); + + let server_handle = tokio::spawn(async move { server.run(shutdown_receiver).await }); + + ShutdownHandles { + server_handle, + shutdown_button, + } +} + +pub(crate) struct ShutdownHandles { + server_handle: JoinHandle>, + shutdown_button: CancellationToken, +} + +impl ShutdownHandles { + /// Send graceful shutdown signal to server and wait for server task to complete + pub(crate) async fn shutdown(self) -> anyhow::Result<()> { + self.shutdown_button.cancel(); + + match self.server_handle.await { + Ok(Ok(_)) => { + tracing::info!("HTTP server shut down without errors"); + } + Ok(Err(err)) => { + tracing::error!("HTTP server terminated with: {err}"); + anyhow::bail!(err) + } + Err(err) => { + tracing::error!("Server task panicked: {err}"); + } + }; + + Ok(()) + } +} + +pub(crate) struct HttpServer { + router: Router, + listener: TcpListener, +} + +impl HttpServer { + pub(crate) fn new(router: Router, listener: TcpListener) -> Self { + Self { router, listener } + } + + pub(crate) async fn run(self, receiver: WaitForCancellationFutureOwned) -> std::io::Result<()> { + // 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::(), + ) + .with_graceful_shutdown(receiver) + .await + } +} diff --git a/nym-node-status-api/src/http/state.rs b/nym-node-status-api/src/http/state.rs new file mode 100644 index 0000000000..6bccea39a1 --- /dev/null +++ b/nym-node-status-api/src/http/state.rs @@ -0,0 +1,223 @@ +use std::{sync::Arc, time::Duration}; + +use moka::{future::Cache, Entry}; +use tokio::sync::RwLock; + +use crate::{ + db::DbPool, + http::models::{DailyStats, Gateway, Mixnode, SummaryHistory}, +}; + +#[derive(Debug, Clone)] +pub(crate) struct AppState { + db_pool: DbPool, + cache: HttpCache, +} + +impl AppState { + pub(crate) fn new(db_pool: DbPool, cache_ttl: u64) -> Self { + Self { + db_pool, + cache: HttpCache::new(cache_ttl), + } + } + + pub(crate) fn db_pool(&self) -> &DbPool { + &self.db_pool + } + + pub(crate) fn cache(&self) -> &HttpCache { + &self.cache + } +} + +static GATEWAYS_LIST_KEY: &str = "gateways"; +static MIXNODES_LIST_KEY: &str = "mixnodes"; +static MIXSTATS_LIST_KEY: &str = "mixstats"; +static SUMMARY_HISTORY_LIST_KEY: &str = "summary-history"; + +#[derive(Debug, Clone)] +pub(crate) struct HttpCache { + gateways: Cache>>>, + mixnodes: Cache>>>, + mixstats: Cache>>>, + history: Cache>>>, +} + +impl HttpCache { + pub fn new(ttl_seconds: u64) -> Self { + HttpCache { + gateways: Cache::builder() + .max_capacity(2) + .time_to_live(Duration::from_secs(ttl_seconds)) + .build(), + mixnodes: Cache::builder() + .max_capacity(2) + .time_to_live(Duration::from_secs(ttl_seconds)) + .build(), + mixstats: Cache::builder() + .max_capacity(2) + .time_to_live(Duration::from_secs(ttl_seconds)) + .build(), + history: Cache::builder() + .max_capacity(2) + .time_to_live(Duration::from_secs(ttl_seconds)) + .build(), + } + } + + pub async fn upsert_gateway_list( + &self, + new_gateway_list: Vec, + ) -> Entry>>> { + self.gateways + .entry_by_ref(GATEWAYS_LIST_KEY) + .and_upsert_with(|maybe_entry| async { + if let Some(entry) = maybe_entry { + let v = entry.into_value(); + let mut guard = v.write().await; + *guard = new_gateway_list; + v.clone() + } else { + Arc::new(RwLock::new(new_gateway_list)) + } + }) + .await + } + + pub async fn get_gateway_list(&self, db: &DbPool) -> Vec { + match self.gateways.get(GATEWAYS_LIST_KEY).await { + Some(guard) => { + let read_lock = guard.read().await; + read_lock.clone() + } + None => { + // the key is missing so populate it + tracing::warn!("No gateways in cache, refreshing cache from DB..."); + + let gateways = crate::db::queries::get_all_gateways(db) + .await + .unwrap_or_default(); + self.upsert_gateway_list(gateways.clone()).await; + + if gateways.is_empty() { + tracing::warn!("Database contains 0 gateways"); + } + + gateways + } + } + } + + pub async fn upsert_mixnode_list( + &self, + new_mixnode_list: Vec, + ) -> Entry>>> { + self.mixnodes + .entry_by_ref(MIXNODES_LIST_KEY) + .and_upsert_with(|maybe_entry| async { + if let Some(entry) = maybe_entry { + let v = entry.into_value(); + let mut guard = v.write().await; + *guard = new_mixnode_list; + v.clone() + } else { + Arc::new(RwLock::new(new_mixnode_list)) + } + }) + .await + } + + pub async fn get_mixnodes_list(&self, db: &DbPool) -> Vec { + match self.mixnodes.get(MIXNODES_LIST_KEY).await { + Some(guard) => { + let read_lock = guard.read().await; + read_lock.clone() + } + None => { + tracing::warn!("No mixnodes in cache, refreshing cache from DB..."); + + let mixnodes = crate::db::queries::get_all_mixnodes(db) + .await + .unwrap_or_default(); + self.upsert_mixnode_list(mixnodes.clone()).await; + + if mixnodes.is_empty() { + tracing::warn!("Database contains 0 mixnodes"); + } + + mixnodes + } + } + } + + pub async fn upsert_mixnode_stats( + &self, + mixnode_stats: Vec, + ) -> Entry>>> { + self.mixstats + .entry_by_ref(MIXSTATS_LIST_KEY) + .and_upsert_with(|maybe_entry| async { + if let Some(entry) = maybe_entry { + let v = entry.into_value(); + let mut guard = v.write().await; + *guard = mixnode_stats; + v.clone() + } else { + Arc::new(RwLock::new(mixnode_stats)) + } + }) + .await + } + + pub async fn get_mixnode_stats(&self, db: &DbPool) -> Vec { + match self.mixstats.get(MIXSTATS_LIST_KEY).await { + Some(guard) => { + let read_lock = guard.read().await; + read_lock.to_vec() + } + None => { + let mixnode_stats = crate::db::queries::get_daily_stats(db) + .await + .unwrap_or_default(); + self.upsert_mixnode_stats(mixnode_stats.clone()).await; + mixnode_stats + } + } + } + + pub async fn get_summary_history(&self, db: &DbPool) -> Vec { + match self.history.get(SUMMARY_HISTORY_LIST_KEY).await { + Some(guard) => { + let read_lock = guard.read().await; + read_lock.to_vec() + } + None => { + let summary_history = crate::db::queries::get_summary_history(db) + .await + .unwrap_or(vec![]); + self.upsert_summary_history(summary_history.clone()).await; + summary_history + } + } + } + + pub async fn upsert_summary_history( + &self, + summary_history: Vec, + ) -> Entry>>> { + self.history + .entry_by_ref(SUMMARY_HISTORY_LIST_KEY) + .and_upsert_with(|maybe_entry| async { + if let Some(entry) = maybe_entry { + let v = entry.into_value(); + let mut guard = v.write().await; + *guard = summary_history; + v.clone() + } else { + Arc::new(RwLock::new(summary_history)) + } + }) + .await + } +} diff --git a/nym-node-status-api/src/logging.rs b/nym-node-status-api/src/logging.rs index d61cd78ee1..01dd31562e 100644 --- a/nym-node-status-api/src/logging.rs +++ b/nym-node-status-api/src/logging.rs @@ -2,8 +2,11 @@ use tracing::level_filters::LevelFilter; use tracing_subscriber::{filter::Directive, EnvFilter}; pub(crate) fn setup_tracing_logger() { - fn directive_checked(directive: String) -> Directive { - directive.parse().expect("Failed to parse log directive") + fn directive_checked(directive: impl Into) -> Directive { + directive + .into() + .parse() + .expect("Failed to parse log directive") } let log_builder = tracing_subscriber::fmt() @@ -13,6 +16,7 @@ pub(crate) fn setup_tracing_logger() { .with_file(true) // Display source code line numbers .with_line_number(true) + .with_thread_ids(true) // Don't display the event's target (module path) .with_target(false); @@ -22,20 +26,23 @@ pub(crate) fn setup_tracing_logger() { .from_env_lossy(); // these crates are more granularly filtered let filter_crates = [ - "nym_bin_common", - "nym_explorer_client", - "nym_network_defaults", - "nym_validator_client", "reqwest", "rustls", "hyper", "sqlx", "h2", "tendermint_rpc", + "tower_http", + "axum", ]; for crate_name in filter_crates { filter = filter.add_directive(directive_checked(format!("{}=warn", crate_name))); } + filter = filter.add_directive(directive_checked("nym_bin_common=debug")); + filter = filter.add_directive(directive_checked("nym_explorer_client=debug")); + filter = filter.add_directive(directive_checked("nym_network_defaults=debug")); + filter = filter.add_directive(directive_checked("nym_validator_client=debug")); + log_builder.with_env_filter(filter).init(); } diff --git a/nym-node-status-api/src/main.rs b/nym-node-status-api/src/main.rs index 6296406701..0fb1d0e1d2 100644 --- a/nym-node-status-api/src/main.rs +++ b/nym-node-status-api/src/main.rs @@ -1,9 +1,13 @@ -use anyhow::anyhow; use clap::Parser; use nym_network_defaults::setup_env; +use nym_task::signal::wait_for_signal; + +use crate::config::read_env_var; mod cli; +mod config; mod db; +mod http; mod logging; mod monitor; @@ -15,24 +19,36 @@ async fn main() -> anyhow::Result<()> { // if dotenv file is present, load its values // otherwise, default to mainnet setup_env(args.config_env_file.as_ref()); - tracing::debug!("{:?}", std::env::var("NETWORK_NAME")); - tracing::debug!("{:?}", std::env::var("EXPLORER_API")); - tracing::debug!("{:?}", std::env::var("NYM_API")); + tracing::debug!("{:?}", read_env_var("NETWORK_NAME")); + tracing::debug!("{:?}", read_env_var("EXPLORER_API")); + tracing::debug!("{:?}", read_env_var("NYM_API")); + + let conf = config::Config::from_env()?; + tracing::debug!("Using config:\n{:#?}", conf); let storage = db::Storage::init().await?; - monitor::spawn_in_background(storage) - .await - .expect("Monitor task failed"); - tracing::info!("Started server"); + let db_pool = storage.pool_owned().await; + let conf_clone = conf.clone(); + tokio::spawn(async move { + monitor::spawn_in_background(db_pool, conf_clone).await; + }); + tracing::info!("Started monitor task"); + + let shutdown_handles = http::server::start_http_api( + storage.pool_owned().await, + conf.http_port(), + conf.nym_http_cache_ttl(), + ) + .await + .expect("Failed to start server"); + + tracing::info!("Started HTTP server on port {}", conf.http_port()); + + wait_for_signal().await; + + if let Err(err) = shutdown_handles.shutdown().await { + tracing::error!("{err}"); + }; Ok(()) } - -pub(crate) fn read_env_var(env_var: &str) -> anyhow::Result { - std::env::var(env_var) - .map(|value| { - tracing::trace!("{}={}", env_var, value); - value - }) - .map_err(|_| anyhow!("You need to set {}", env_var)) -} diff --git a/nym-node-status-api/src/monitor/mod.rs b/nym-node-status-api/src/monitor/mod.rs index 810320bdd0..4215e297cc 100644 --- a/nym-node-status-api/src/monitor/mod.rs +++ b/nym-node-status-api/src/monitor/mod.rs @@ -1,10 +1,11 @@ +use crate::config::Config; use crate::db::models::{ gateway, mixnode, GatewayRecord, MixnodeRecord, NetworkSummary, GATEWAYS_BLACKLISTED_COUNT, GATEWAYS_BONDED_COUNT, GATEWAYS_EXPLORER_COUNT, GATEWAYS_HISTORICAL_COUNT, MIXNODES_BLACKLISTED_COUNT, MIXNODES_BONDED_ACTIVE, MIXNODES_BONDED_COUNT, MIXNODES_BONDED_INACTIVE, MIXNODES_BONDED_RESERVE, MIXNODES_HISTORICAL_COUNT, }; -use crate::db::{queries, DbPool, Storage}; +use crate::db::{queries, DbPool}; use anyhow::anyhow; use cosmwasm_std::Decimal; use nym_explorer_client::{ExplorerClient, PrettyDetailedGatewayBond}; @@ -15,43 +16,46 @@ use nym_validator_client::nym_nodes::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::HashSet; use std::str::FromStr; use tokio::task::JoinHandle; use tokio::time::Duration; const REFRESH_DELAY: Duration = Duration::from_secs(60 * 5); -const FAILURE_RETRY_DELAY: Duration = Duration::from_secs(60); +const FAILURE_RETRY_DELAY: Duration = Duration::from_secs(15); + static DELEGATION_PROGRAM_WALLET: &str = "n1rnxpdpx3kldygsklfft0gech7fhfcux4zst5lw"; // TODO dz: query many NYM APIs: // multiple instances running directory cache, ask sachin -pub(crate) fn spawn_in_background(storage: Storage) -> JoinHandle<()> { - tokio::spawn(async move { - let db_pool = storage.pool().await; - let network_defaults = nym_network_defaults::NymNetworkDetails::new_from_env(); +pub(crate) async fn spawn_in_background(db_pool: DbPool, config: Config) -> JoinHandle<()> { + let network_defaults = nym_network_defaults::NymNetworkDetails::new_from_env(); - loop { - tracing::info!("Refreshing node info..."); + loop { + tracing::info!("Refreshing node info..."); - if let Err(e) = run(db_pool, &network_defaults).await { - tracing::error!( - "Monitor run failed: {e}, retrying in {}s...", - FAILURE_RETRY_DELAY.as_secs() - ); - tokio::time::sleep(FAILURE_RETRY_DELAY).await; - } else { - tracing::info!( - "Info successfully collected, sleeping for {}s...", - REFRESH_DELAY.as_secs() - ); - tokio::time::sleep(REFRESH_DELAY).await; - } + if let Err(e) = run(&db_pool, &network_defaults, &config).await { + tracing::error!( + "Monitor run failed: {e}, retrying in {}s...", + FAILURE_RETRY_DELAY.as_secs() + ); + tokio::time::sleep(FAILURE_RETRY_DELAY).await; + } else { + tracing::info!( + "Info successfully collected, sleeping for {}s...", + REFRESH_DELAY.as_secs() + ); + tokio::time::sleep(REFRESH_DELAY).await; } - }) + } } -async fn run(pool: &DbPool, network_details: &NymNetworkDetails) -> anyhow::Result<()> { +async fn run( + pool: &DbPool, + network_details: &NymNetworkDetails, + config: &Config, +) -> anyhow::Result<()> { let default_api_url = network_details .endpoints .first() @@ -66,15 +70,32 @@ async fn run(pool: &DbPool, network_details: &NymNetworkDetails) -> anyhow::Resu let default_explorer_url = default_explorer_url.expect("explorer url missing in network config"); - let explorer_client = ExplorerClient::new(default_explorer_url)?; - let explorer_gateways = explorer_client.get_gateways().await?; + let explorer_client = ExplorerClient::new_with_timeout( + default_explorer_url, + config.nym_explorer_client_timeout(), + )?; + let explorer_gateways = explorer_client + .get_gateways() + .await + .log_error("get_gateways")?; + tracing::debug!("6"); - let api_client = NymApiClient::new(default_api_url); - let gateways = api_client.get_cached_described_gateways().await?; + let api_client = + NymApiClient::new_with_timeout(default_api_url, config.nym_api_client_timeout()); + let gateways = api_client + .get_cached_described_gateways() + .await + .log_error("get_described_gateways")?; tracing::debug!("Fetched {} gateways", gateways.len()); - let skimmed_gateways = api_client.get_basic_gateways(None).await?; + let skimmed_gateways = api_client + .get_basic_gateways(None) + .await + .log_error("get_basic_gateways")?; - let mixnodes = api_client.get_cached_mixnodes().await?; + let mixnodes = api_client + .get_cached_mixnodes() + .await + .log_error("get_cached_mixnodes")?; tracing::debug!("Fetched {} mixnodes", mixnodes.len()); // TODO dz can we calculate blacklisted GWs from their performance? @@ -83,17 +104,28 @@ async fn run(pool: &DbPool, network_details: &NymNetworkDetails) -> anyhow::Resu .nym_api .get_gateways_blacklisted() .await - .map(|vec| vec.into_iter().collect::>())?; + .map(|vec| vec.into_iter().collect::>()) + .log_error("get_gateways_blacklisted")?; // Cached mixnodes don't include blacklisted nodes // We need that to calculate the total locked tokens later let mixnodes = api_client .nym_api .get_mixnodes_detailed_unfiltered() - .await?; - let mixnodes_described = api_client.nym_api.get_mixnodes_described().await?; - let mixnodes_active = api_client.nym_api.get_active_mixnodes().await?; - let delegation_program_members = get_delegation_program_details(network_details).await?; + .await + .log_error("get_mixnodes_detailed_unfiltered")?; + let mixnodes_described = api_client + .nym_api + .get_mixnodes_described() + .await + .log_error("get_mixnodes_described")?; + let mixnodes_active = api_client + .nym_api + .get_active_mixnodes() + .await + .log_error("get_active_mixnodes")?; + let delegation_program_members = + get_delegation_program_details(network_details, config.nyxd_addr()).await?; // keep stats for later let count_bonded_mixnodes = mixnodes.len(); @@ -168,42 +200,41 @@ async fn run(pool: &DbPool, network_details: &NymNetworkDetails) -> anyhow::Resu (GATEWAYS_BLACKLISTED_COUNT, &count_gateways_blacklisted), ]; - // TODO dz do we need signed int in type definition? maybe because of API? let last_updated = chrono::offset::Utc::now(); let last_updated_utc = last_updated.timestamp().to_string(); let network_summary = NetworkSummary { mixnodes: mixnode::MixnodeSummary { bonded: mixnode::MixnodeSummaryBonded { - count: count_bonded_mixnodes as i32, - active: count_bonded_mixnodes_active as i32, - inactive: count_bonded_mixnodes_inactive as i32, - reserve: count_bonded_mixnodes_reserve as i32, + count: count_bonded_mixnodes.cast_checked()?, + active: count_bonded_mixnodes_active.cast_checked()?, + inactive: count_bonded_mixnodes_inactive.cast_checked()?, + reserve: count_bonded_mixnodes_reserve.cast_checked()?, last_updated_utc: last_updated_utc.to_owned(), }, blacklisted: mixnode::MixnodeSummaryBlacklisted { - count: count_mixnodes_blacklisted as i32, + count: count_mixnodes_blacklisted.cast_checked()?, last_updated_utc: last_updated_utc.to_owned(), }, historical: mixnode::MixnodeSummaryHistorical { - count: all_historical_mixnodes as i32, + count: all_historical_mixnodes.cast_checked()?, last_updated_utc: last_updated_utc.to_owned(), }, }, gateways: gateway::GatewaySummary { bonded: gateway::GatewaySummaryBonded { - count: count_bonded_gateways as i32, + count: count_bonded_gateways.cast_checked()?, last_updated_utc: last_updated_utc.to_owned(), }, blacklisted: gateway::GatewaySummaryBlacklisted { - count: count_gateways_blacklisted as i32, + count: count_gateways_blacklisted.cast_checked()?, last_updated_utc: last_updated_utc.to_owned(), }, historical: gateway::GatewaySummaryHistorical { - count: all_historical_gateways as i32, + count: all_historical_gateways.cast_checked()?, last_updated_utc: last_updated_utc.to_owned(), }, explorer: gateway::GatewaySummaryExplorer { - count: count_explorer_gateways as i32, + count: count_explorer_gateways.cast_checked()?, last_updated_utc: last_updated_utc.to_owned(), }, }, @@ -317,27 +348,56 @@ fn prepare_mixnode_data( Ok(mixnode_records) } +// TODO dz is there a common monorepo place this can be put? +pub trait NumericalCheckedCast +where + T: TryFrom, + >::Error: std::error::Error, + Self: std::fmt::Display + Copy, +{ + fn cast_checked(self) -> anyhow::Result { + T::try_from(self).map_err(|e| { + anyhow::anyhow!( + "Couldn't cast {} to {}: {}", + self, + std::any::type_name::(), + e + ) + }) + } +} + +impl NumericalCheckedCast for T +where + U: TryFrom, + >::Error: std::error::Error, + T: std::fmt::Display + Copy, +{ +} + async fn calculate_stats(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"#) .fetch_one(&mut *conn) - .await? as usize; + .await? + .cast_checked()?; let all_historical_mixnodes = sqlx::query_scalar!(r#"SELECT count(id) FROM mixnodes"#) .fetch_one(&mut *conn) - .await? as usize; + .await? + .cast_checked()?; Ok((all_historical_gateways, all_historical_mixnodes)) } async fn get_delegation_program_details( network_details: &NymNetworkDetails, + nyxd_addr: &Url, ) -> anyhow::Result> { let config = nym_validator_client::nyxd::Config::try_from_nym_network_details(network_details)?; - // TODO dz should this be configurable? - let client = NyxdClient::connect(config, "https://rpc.nymtech.net") + 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) @@ -372,3 +432,19 @@ fn decimal_to_i64(decimal: Decimal) -> i64 { rounded_value as i64 } + +trait LogError { + fn log_error(self, msg: &str) -> Result; +} + +impl LogError for anyhow::Result +where + E: std::error::Error, +{ + fn log_error(self, msg: &str) -> Result { + if let Err(e) = &self { + tracing::error!("[{msg}]:\t{e}"); + } + self + } +} From e5a29cc76e9c91ae0e4ea36a9f89c2944d3e96f3 Mon Sep 17 00:00:00 2001 From: dynco-nym <173912580+dynco-nym@users.noreply.github.com> Date: Mon, 14 Oct 2024 16:32:39 +0200 Subject: [PATCH 27/48] Work with directory pre-v2.1 Rebase + point to earlier network client code Adjust to new Nym API types Refer to earlier client code Revert "Rebase + point to earlier network client code" This reverts commit dd75e7dc0695c25b0883e2f5dd15b7d70165e9e8. Point to earlier commit --- Cargo.lock | 1028 ++++++++++++----- Cargo.toml | 1 + nym-node-status-api/Cargo.toml | 10 +- nym-node-status-api/src/config.rs | 7 - nym-node-status-api/src/db/mod.rs | 9 +- .../src/db/queries/gateways.rs | 2 +- nym-node-status-api/src/db/queries/misc.rs | 2 +- .../src/db/queries/mixnodes.rs | 4 +- nym-node-status-api/src/db/queries/summary.rs | 4 +- nym-node-status-api/src/monitor/mod.rs | 3 +- 10 files changed, 727 insertions(+), 343 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0cab5a15ee..b9d06b3a8e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2153,8 +2153,8 @@ dependencies = [ "cosmwasm-std", "cosmwasm-storage", "cw-storage-plus", - "nym-coconut-dkg-common", - "nym-contracts-common", + "nym-coconut-dkg-common 0.1.0", + "nym-contracts-common 0.5.0", ] [[package]] @@ -2415,13 +2415,13 @@ dependencies = [ "itertools 0.13.0", "log", "maxminddb", - "nym-bin-common", - "nym-contracts-common", + "nym-bin-common 0.6.0", + "nym-contracts-common 0.5.0", "nym-explorer-api-requests", - "nym-mixnet-contract-common", - "nym-network-defaults", + "nym-mixnet-contract-common 0.6.0", + "nym-network-defaults 0.1.0", "nym-task", - "nym-validator-client", + "nym-validator-client 0.1.0", "okapi", "pretty_env_logger", "rand", @@ -3404,11 +3404,11 @@ dependencies = [ "clap 4.5.20", "dirs", "importer-contract", - "nym-bin-common", - "nym-mixnet-contract-common", - "nym-network-defaults", - "nym-validator-client", - "nym-vesting-contract-common", + "nym-bin-common 0.6.0", + "nym-mixnet-contract-common 0.6.0", + "nym-network-defaults 0.1.0", + "nym-validator-client 0.1.0", + "nym-vesting-contract-common 0.7.0", "serde", "serde_json", "tokio", @@ -4077,8 +4077,8 @@ dependencies = [ "async-trait", "futures", "js-sys", - "nym-bin-common", - "nym-http-api-client", + "nym-bin-common 0.6.0", + "nym-http-api-client 0.1.0", "nym-ordered-buffer", "nym-service-providers-common", "nym-socks5-requests", @@ -4437,37 +4437,37 @@ dependencies = [ "humantime-serde", "itertools 0.13.0", "k256", - "nym-api-requests", + "nym-api-requests 0.1.0", "nym-bandwidth-controller", - "nym-bin-common", + "nym-bin-common 0.6.0", "nym-coconut", - "nym-coconut-dkg-common", - "nym-compact-ecash", - "nym-config", - "nym-contracts-common", + "nym-coconut-dkg-common 0.1.0", + "nym-compact-ecash 0.1.0", + "nym-config 0.1.0", + "nym-contracts-common 0.5.0", "nym-credential-storage", "nym-credentials", - "nym-credentials-interface", - "nym-crypto", + "nym-credentials-interface 0.1.0", + "nym-crypto 0.4.0", "nym-dkg", - "nym-ecash-contract-common", + "nym-ecash-contract-common 0.1.0", "nym-ecash-double-spending", - "nym-ecash-time", + "nym-ecash-time 0.1.0", "nym-gateway-client", "nym-http-api-common", "nym-inclusion-probability", - "nym-mixnet-contract-common", - "nym-multisig-contract-common", - "nym-node-requests", + "nym-mixnet-contract-common 0.6.0", + "nym-multisig-contract-common 0.1.0", + "nym-node-requests 0.1.0", "nym-node-tester-utils", - "nym-pemstore", - "nym-serde-helpers", + "nym-pemstore 0.3.0", + "nym-serde-helpers 0.1.0", "nym-sphinx", "nym-task", "nym-topology", "nym-types", - "nym-validator-client", - "nym-vesting-contract-common", + "nym-validator-client 0.1.0", + "nym-vesting-contract-common 0.7.0", "pin-project", "rand", "rand_chacha", @@ -4502,14 +4502,14 @@ dependencies = [ "cosmwasm-std", "ecdsa", "getset", - "nym-compact-ecash", - "nym-credentials-interface", - "nym-crypto", - "nym-ecash-time", - "nym-mixnet-contract-common", - "nym-network-defaults", - "nym-node-requests", - "nym-serde-helpers", + "nym-compact-ecash 0.1.0", + "nym-credentials-interface 0.1.0", + "nym-crypto 0.4.0", + "nym-ecash-time 0.1.0", + "nym-mixnet-contract-common 0.6.0", + "nym-network-defaults 0.1.0", + "nym-node-requests 0.1.0", + "nym-serde-helpers 0.1.0", "schemars", "serde", "serde_json", @@ -4521,6 +4521,32 @@ dependencies = [ "utoipa", ] +[[package]] +name = "nym-api-requests" +version = "0.1.0" +source = "git+https://github.com/nymtech/nym?rev=f86e08866#f86e0886631a98b0638fe09e6fcbe5458d47adc1" +dependencies = [ + "bs58", + "cosmrs 0.17.0-pre", + "cosmwasm-std", + "ecdsa", + "getset", + "nym-compact-ecash 0.1.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", + "nym-credentials-interface 0.1.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", + "nym-crypto 0.4.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", + "nym-ecash-time 0.1.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", + "nym-mixnet-contract-common 0.6.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", + "nym-node-requests 0.1.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", + "nym-serde-helpers 0.1.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", + "schemars", + "serde", + "sha2 0.10.8", + "tendermint 0.37.0", + "thiserror", + "time", + "utoipa", +] + [[package]] name = "nym-async-file-watcher" version = "0.1.0" @@ -4546,16 +4572,16 @@ dependencies = [ "ipnetwork 0.20.0", "log", "nym-authenticator-requests", - "nym-bin-common", + "nym-bin-common 0.6.0", "nym-client-core", - "nym-config", + "nym-config 0.1.0", "nym-credential-verification", - "nym-credentials-interface", - "nym-crypto", + "nym-credentials-interface 0.1.0", + "nym-crypto 0.4.0", "nym-gateway-requests", "nym-gateway-storage", "nym-id", - "nym-network-defaults", + "nym-network-defaults 0.1.0", "nym-sdk", "nym-service-provider-requests-common", "nym-service-providers-common", @@ -4563,7 +4589,7 @@ dependencies = [ "nym-task", "nym-types", "nym-wireguard", - "nym-wireguard-types", + "nym-wireguard-types 0.1.0", "rand", "serde", "serde_json", @@ -4581,11 +4607,11 @@ dependencies = [ "base64 0.22.1", "bincode", "hmac", - "nym-credentials-interface", - "nym-crypto", + "nym-credentials-interface 0.1.0", + "nym-crypto 0.4.0", "nym-service-provider-requests-common", "nym-sphinx", - "nym-wireguard-types", + "nym-wireguard-types 0.1.0", "rand", "serde", "sha2 0.10.8", @@ -4601,12 +4627,12 @@ dependencies = [ "log", "nym-credential-storage", "nym-credentials", - "nym-credentials-interface", - "nym-crypto", - "nym-ecash-contract-common", - "nym-ecash-time", - "nym-network-defaults", - "nym-validator-client", + "nym-credentials-interface 0.1.0", + "nym-crypto 0.4.0", + "nym-ecash-contract-common 0.1.0", + "nym-ecash-time 0.1.0", + "nym-network-defaults 0.1.0", + "nym-validator-client 0.1.0", "rand", "thiserror", "url", @@ -4636,6 +4662,21 @@ dependencies = [ "vergen", ] +[[package]] +name = "nym-bin-common" +version = "0.6.0" +source = "git+https://github.com/nymtech/nym?rev=f86e08866#f86e0886631a98b0638fe09e6fcbe5458d47adc1" +dependencies = [ + "const-str", + "log", + "pretty_env_logger", + "schemars", + "semver 1.0.23", + "serde", + "utoipa", + "vergen", +] + [[package]] name = "nym-bity-integration" version = "0.1.0" @@ -4645,7 +4686,7 @@ dependencies = [ "eyre", "k256", "nym-cli-commands", - "nym-validator-client", + "nym-validator-client 0.1.0", "serde", "serde_json", "thiserror", @@ -4665,10 +4706,10 @@ dependencies = [ "dotenvy", "inquire", "log", - "nym-bin-common", + "nym-bin-common 0.6.0", "nym-cli-commands", - "nym-network-defaults", - "nym-validator-client", + "nym-network-defaults 0.1.0", + "nym-validator-client 0.1.0", "pretty_env_logger", "serde", "serde_json", @@ -4699,26 +4740,26 @@ dependencies = [ "k256", "log", "nym-bandwidth-controller", - "nym-bin-common", + "nym-bin-common 0.6.0", "nym-client-core", - "nym-coconut-dkg-common", - "nym-config", - "nym-contracts-common", + "nym-coconut-dkg-common 0.1.0", + "nym-config 0.1.0", + "nym-contracts-common 0.5.0", "nym-credential-storage", "nym-credential-utils", "nym-credentials", - "nym-credentials-interface", - "nym-crypto", - "nym-ecash-contract-common", + "nym-credentials-interface 0.1.0", + "nym-crypto 0.4.0", + "nym-ecash-contract-common 0.1.0", "nym-id", - "nym-mixnet-contract-common", - "nym-multisig-contract-common", - "nym-network-defaults", - "nym-pemstore", + "nym-mixnet-contract-common 0.6.0", + "nym-multisig-contract-common 0.1.0", + "nym-network-defaults 0.1.0", + "nym-pemstore 0.3.0", "nym-sphinx", "nym-types", - "nym-validator-client", - "nym-vesting-contract-common", + "nym-validator-client 0.1.0", + "nym-vesting-contract-common 0.7.0", "rand", "serde", "serde_json", @@ -4742,21 +4783,21 @@ dependencies = [ "futures", "log", "nym-bandwidth-controller", - "nym-bin-common", + "nym-bin-common 0.6.0", "nym-client-core", "nym-client-websocket-requests", - "nym-config", + "nym-config 0.1.0", "nym-credential-storage", "nym-credentials", - "nym-crypto", + "nym-crypto 0.4.0", "nym-gateway-requests", "nym-id", - "nym-network-defaults", - "nym-pemstore", + "nym-network-defaults 0.1.0", + "nym-pemstore 0.3.0", "nym-sphinx", "nym-task", "nym-topology", - "nym-validator-client", + "nym-validator-client 0.1.0", "rand", "serde", "serde_json", @@ -4790,24 +4831,24 @@ dependencies = [ "nym-client-core-config-types", "nym-client-core-gateways-storage", "nym-client-core-surb-storage", - "nym-config", + "nym-config 0.1.0", "nym-country-group", "nym-credential-storage", - "nym-credentials-interface", - "nym-crypto", - "nym-ecash-time", + "nym-credentials-interface 0.1.0", + "nym-crypto 0.4.0", + "nym-ecash-time 0.1.0", "nym-explorer-client", "nym-gateway-client", "nym-gateway-requests", "nym-id", "nym-metrics", - "nym-network-defaults", + "nym-network-defaults 0.1.0", "nym-nonexhaustive-delayqueue", - "nym-pemstore", + "nym-pemstore 0.3.0", "nym-sphinx", "nym-task", "nym-topology", - "nym-validator-client", + "nym-validator-client 0.1.0", "rand", "rand_chacha", "serde", @@ -4835,9 +4876,9 @@ name = "nym-client-core-config-types" version = "0.1.0" dependencies = [ "humantime-serde", - "nym-config", + "nym-config 0.1.0", "nym-country-group", - "nym-pemstore", + "nym-pemstore 0.3.0", "nym-sphinx-addressing", "nym-sphinx-params", "serde", @@ -4852,7 +4893,7 @@ dependencies = [ "async-trait", "cosmrs 0.17.0-pre", "log", - "nym-crypto", + "nym-crypto 0.4.0", "nym-gateway-requests", "serde", "sqlx", @@ -4870,7 +4911,7 @@ dependencies = [ "async-trait", "dashmap", "log", - "nym-crypto", + "nym-crypto 0.4.0", "nym-sphinx", "nym-task", "sqlx", @@ -4886,7 +4927,7 @@ dependencies = [ "anyhow", "futures", "js-sys", - "nym-bin-common", + "nym-bin-common 0.6.0", "nym-node-tester-utils", "nym-node-tester-wasm", "rand", @@ -4925,7 +4966,7 @@ dependencies = [ "group", "itertools 0.13.0", "nym-dkg", - "nym-pemstore", + "nym-pemstore 0.3.0", "rand", "rand_chacha", "serde", @@ -4942,7 +4983,17 @@ dependencies = [ "cosmwasm-schema", "cosmwasm-std", "cw2", - "nym-multisig-contract-common", + "nym-multisig-contract-common 0.1.0", +] + +[[package]] +name = "nym-coconut-bandwidth-contract-common" +version = "0.1.0" +source = "git+https://github.com/nymtech/nym?rev=f86e08866#f86e0886631a98b0638fe09e6fcbe5458d47adc1" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "nym-multisig-contract-common 0.1.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", ] [[package]] @@ -4954,8 +5005,22 @@ dependencies = [ "cw-utils", "cw2", "cw4", - "nym-contracts-common", - "nym-multisig-contract-common", + "nym-contracts-common 0.5.0", + "nym-multisig-contract-common 0.1.0", +] + +[[package]] +name = "nym-coconut-dkg-common" +version = "0.1.0" +source = "git+https://github.com/nymtech/nym?rev=f86e08866#f86e0886631a98b0638fe09e6fcbe5458d47adc1" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-utils", + "cw2", + "cw4", + "nym-contracts-common 0.5.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", + "nym-multisig-contract-common 0.1.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", ] [[package]] @@ -4971,8 +5036,8 @@ dependencies = [ "ff", "group", "itertools 0.13.0", - "nym-network-defaults", - "nym-pemstore", + "nym-network-defaults 0.1.0", + "nym-pemstore 0.3.0", "rand", "rayon", "serde", @@ -4982,6 +5047,28 @@ dependencies = [ "zeroize", ] +[[package]] +name = "nym-compact-ecash" +version = "0.1.0" +source = "git+https://github.com/nymtech/nym?rev=f86e08866#f86e0886631a98b0638fe09e6fcbe5458d47adc1" +dependencies = [ + "bincode", + "bls12_381", + "bs58", + "cfg-if", + "digest 0.9.0", + "ff", + "group", + "itertools 0.12.1", + "nym-network-defaults 0.1.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", + "nym-pemstore 0.3.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", + "rand", + "serde", + "sha2 0.9.9", + "thiserror", + "zeroize", +] + [[package]] name = "nym-config" version = "0.1.0" @@ -4989,7 +5076,21 @@ dependencies = [ "dirs", "handlebars", "log", - "nym-network-defaults", + "nym-network-defaults 0.1.0", + "serde", + "toml 0.8.14", + "url", +] + +[[package]] +name = "nym-config" +version = "0.1.0" +source = "git+https://github.com/nymtech/nym?rev=f86e08866#f86e0886631a98b0638fe09e6fcbe5458d47adc1" +dependencies = [ + "dirs", + "handlebars", + "log", + "nym-network-defaults 0.1.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", "serde", "toml 0.8.14", "url", @@ -5010,6 +5111,21 @@ dependencies = [ "vergen", ] +[[package]] +name = "nym-contracts-common" +version = "0.5.0" +source = "git+https://github.com/nymtech/nym?rev=f86e08866#f86e0886631a98b0638fe09e6fcbe5458d47adc1" +dependencies = [ + "bs58", + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus", + "schemars", + "serde", + "thiserror", + "vergen", +] + [[package]] name = "nym-country-group" version = "0.1.0" @@ -5025,7 +5141,7 @@ dependencies = [ "anyhow", "bs58", "lazy_static", - "nym-bin-common", + "nym-bin-common 0.6.0", "nym-ffi-shared", "nym-sdk", "nym-sphinx-anonymous-replies", @@ -5039,9 +5155,9 @@ dependencies = [ "async-trait", "bincode", "log", - "nym-compact-ecash", + "nym-compact-ecash 0.1.0", "nym-credentials", - "nym-ecash-time", + "nym-ecash-time 0.1.0", "serde", "sqlx", "thiserror", @@ -5056,12 +5172,12 @@ dependencies = [ "log", "nym-bandwidth-controller", "nym-client-core", - "nym-config", + "nym-config 0.1.0", "nym-credential-storage", "nym-credentials", - "nym-credentials-interface", - "nym-ecash-time", - "nym-validator-client", + "nym-credentials-interface 0.1.0", + "nym-ecash-time 0.1.0", + "nym-validator-client 0.1.0", "thiserror", "time", "tokio", @@ -5075,15 +5191,15 @@ dependencies = [ "cosmwasm-std", "cw-utils", "futures", - "nym-api-requests", + "nym-api-requests 0.1.0", "nym-credentials", - "nym-credentials-interface", - "nym-ecash-contract-common", + "nym-credentials-interface 0.1.0", + "nym-ecash-contract-common 0.1.0", "nym-ecash-double-spending", "nym-gateway-requests", "nym-gateway-storage", "nym-task", - "nym-validator-client", + "nym-validator-client 0.1.0", "rand", "si-scale", "thiserror", @@ -5100,14 +5216,14 @@ dependencies = [ "bls12_381", "cosmrs 0.17.0-pre", "log", - "nym-api-requests", - "nym-credentials-interface", - "nym-crypto", - "nym-ecash-contract-common", - "nym-ecash-time", - "nym-network-defaults", - "nym-serde-helpers", - "nym-validator-client", + "nym-api-requests 0.1.0", + "nym-credentials-interface 0.1.0", + "nym-crypto 0.4.0", + "nym-ecash-contract-common 0.1.0", + "nym-ecash-time 0.1.0", + "nym-network-defaults 0.1.0", + "nym-serde-helpers 0.1.0", + "nym-validator-client 0.1.0", "rand", "serde", "thiserror", @@ -5120,9 +5236,25 @@ name = "nym-credentials-interface" version = "0.1.0" dependencies = [ "bls12_381", - "nym-compact-ecash", - "nym-ecash-time", - "nym-network-defaults", + "nym-compact-ecash 0.1.0", + "nym-ecash-time 0.1.0", + "nym-network-defaults 0.1.0", + "rand", + "serde", + "strum 0.26.3", + "thiserror", + "time", +] + +[[package]] +name = "nym-credentials-interface" +version = "0.1.0" +source = "git+https://github.com/nymtech/nym?rev=f86e08866#f86e0886631a98b0638fe09e6fcbe5458d47adc1" +dependencies = [ + "bls12_381", + "nym-compact-ecash 0.1.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", + "nym-ecash-time 0.1.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", + "nym-network-defaults 0.1.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", "rand", "serde", "strum 0.26.3", @@ -5146,8 +5278,8 @@ dependencies = [ "generic-array 0.14.7", "hkdf", "hmac", - "nym-pemstore", - "nym-sphinx-types", + "nym-pemstore 0.3.0", + "nym-sphinx-types 0.2.0", "rand", "rand_chacha", "serde", @@ -5158,6 +5290,23 @@ dependencies = [ "zeroize", ] +[[package]] +name = "nym-crypto" +version = "0.4.0" +source = "git+https://github.com/nymtech/nym?rev=f86e08866#f86e0886631a98b0638fe09e6fcbe5458d47adc1" +dependencies = [ + "bs58", + "ed25519-dalek", + "nym-pemstore 0.3.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", + "nym-sphinx-types 0.2.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", + "serde", + "serde_bytes", + "subtle-encoding", + "thiserror", + "x25519-dalek", + "zeroize", +] + [[package]] name = "nym-data-observatory" version = "0.1.0" @@ -5166,9 +5315,9 @@ dependencies = [ "axum 0.7.7", "chrono", "clap 4.5.20", - "nym-bin-common", - "nym-network-defaults", - "nym-node-requests", + "nym-bin-common 0.6.0", + "nym-network-defaults 0.1.0", + "nym-node-requests 0.1.0", "nym-task", "serde", "serde_json", @@ -5194,8 +5343,8 @@ dependencies = [ "ff", "group", "lazy_static", - "nym-contracts-common", - "nym-pemstore", + "nym-contracts-common 0.5.0", + "nym-pemstore 0.3.0", "rand", "rand_chacha", "rand_core 0.6.4", @@ -5216,7 +5365,21 @@ dependencies = [ "cw-controllers", "cw-utils", "cw2", - "nym-multisig-contract-common", + "nym-multisig-contract-common 0.1.0", + "thiserror", +] + +[[package]] +name = "nym-ecash-contract-common" +version = "0.1.0" +source = "git+https://github.com/nymtech/nym?rev=f86e08866#f86e0886631a98b0638fe09e6fcbe5458d47adc1" +dependencies = [ + "bs58", + "cosmwasm-schema", + "cosmwasm-std", + "cw-controllers", + "cw-utils", + "nym-multisig-contract-common 0.1.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", "thiserror", ] @@ -5226,14 +5389,22 @@ version = "0.1.0" dependencies = [ "bit-vec", "bloomfilter", - "nym-network-defaults", + "nym-network-defaults 0.1.0", ] [[package]] name = "nym-ecash-time" version = "0.1.0" dependencies = [ - "nym-compact-ecash", + "nym-compact-ecash 0.1.0", + "time", +] + +[[package]] +name = "nym-ecash-time" +version = "0.1.0" +source = "git+https://github.com/nymtech/nym?rev=f86e08866#f86e0886631a98b0638fe09e6fcbe5458d47adc1" +dependencies = [ "time", ] @@ -5257,13 +5428,25 @@ dependencies = [ "utoipa", ] +[[package]] +name = "nym-exit-policy" +version = "0.1.0" +source = "git+https://github.com/nymtech/nym?rev=f86e08866#f86e0886631a98b0638fe09e6fcbe5458d47adc1" +dependencies = [ + "serde", + "serde_json", + "thiserror", + "tracing", + "utoipa", +] + [[package]] name = "nym-explorer-api-requests" version = "0.1.0" dependencies = [ - "nym-api-requests", - "nym-contracts-common", - "nym-mixnet-contract-common", + "nym-api-requests 0.1.0", + "nym-contracts-common 0.5.0", + "nym-mixnet-contract-common 0.6.0", "schemars", "serde", "ts-rs", @@ -5289,7 +5472,7 @@ dependencies = [ "anyhow", "bs58", "lazy_static", - "nym-bin-common", + "nym-bin-common 0.6.0", "nym-sdk", "nym-sphinx-anonymous-replies", "tokio", @@ -5314,31 +5497,31 @@ dependencies = [ "futures", "humantime-serde", "ipnetwork 0.20.0", - "nym-api-requests", + "nym-api-requests 0.1.0", "nym-authenticator", - "nym-bin-common", - "nym-config", + "nym-bin-common 0.6.0", + "nym-config 0.1.0", "nym-credential-verification", "nym-credentials", - "nym-credentials-interface", - "nym-crypto", + "nym-credentials-interface 0.1.0", + "nym-crypto 0.4.0", "nym-gateway-requests", "nym-gateway-stats-storage", "nym-gateway-storage", "nym-ip-packet-router", "nym-mixnet-client", "nym-mixnode-common", - "nym-network-defaults", + "nym-network-defaults 0.1.0", "nym-network-requester", "nym-node-http-api", - "nym-pemstore", + "nym-pemstore 0.3.0", "nym-sphinx", "nym-statistics-common", "nym-task", "nym-types", - "nym-validator-client", + "nym-validator-client 0.1.0", "nym-wireguard", - "nym-wireguard-types", + "nym-wireguard-types 0.1.0", "once_cell", "rand", "serde", @@ -5367,14 +5550,14 @@ dependencies = [ "nym-bandwidth-controller", "nym-credential-storage", "nym-credentials", - "nym-credentials-interface", - "nym-crypto", + "nym-credentials-interface 0.1.0", + "nym-crypto 0.4.0", "nym-gateway-requests", - "nym-network-defaults", - "nym-pemstore", + "nym-network-defaults 0.1.0", + "nym-pemstore 0.3.0", "nym-sphinx", "nym-task", - "nym-validator-client", + "nym-validator-client 0.1.0", "rand", "serde", "si-scale", @@ -5400,11 +5583,11 @@ dependencies = [ "bs58", "futures", "generic-array 0.14.7", - "nym-compact-ecash", + "nym-compact-ecash 0.1.0", "nym-credentials", - "nym-credentials-interface", - "nym-crypto", - "nym-pemstore", + "nym-credentials-interface 0.1.0", + "nym-crypto 0.4.0", + "nym-pemstore 0.3.0", "nym-sphinx", "nym-task", "rand", @@ -5423,7 +5606,7 @@ dependencies = [ name = "nym-gateway-stats-storage" version = "0.1.0" dependencies = [ - "nym-credentials-interface", + "nym-credentials-interface 0.1.0", "nym-sphinx", "sqlx", "thiserror", @@ -5440,7 +5623,7 @@ dependencies = [ "bincode", "defguard_wireguard_rs", "log", - "nym-credentials-interface", + "nym-credentials-interface 0.1.0", "nym-gateway-requests", "nym-sphinx", "sqlx", @@ -5456,7 +5639,7 @@ version = "0.2.0" dependencies = [ "anyhow", "lazy_static", - "nym-bin-common", + "nym-bin-common 0.6.0", "nym-ffi-shared", "nym-sdk", "nym-sphinx-anonymous-replies", @@ -5477,13 +5660,42 @@ dependencies = [ "serde", ] +[[package]] +name = "nym-group-contract-common" +version = "0.1.0" +source = "git+https://github.com/nymtech/nym?rev=f86e08866#f86e0886631a98b0638fe09e6fcbe5458d47adc1" +dependencies = [ + "cosmwasm-schema", + "cw-controllers", + "cw4", + "schemars", + "serde", +] + [[package]] name = "nym-http-api-client" version = "0.1.0" dependencies = [ "async-trait", "http 1.1.0", - "nym-bin-common", + "nym-bin-common 0.6.0", + "reqwest 0.12.4", + "serde", + "serde_json", + "thiserror", + "tracing", + "url", + "wasmtimer", +] + +[[package]] +name = "nym-http-api-client" +version = "0.1.0" +source = "git+https://github.com/nymtech/nym?rev=f86e08866#f86e0886631a98b0638fe09e6fcbe5458d47adc1" +dependencies = [ + "async-trait", + "http 1.1.0", + "nym-bin-common 0.6.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", "reqwest 0.12.4", "serde", "serde_json", @@ -5527,7 +5739,7 @@ dependencies = [ "anyhow", "bs58", "clap 4.5.20", - "nym-bin-common", + "nym-bin-common 0.6.0", "nym-credential-storage", "nym-id", "tokio", @@ -5549,8 +5761,8 @@ version = "0.1.0" dependencies = [ "bincode", "bytes", - "nym-bin-common", - "nym-crypto", + "nym-bin-common 0.6.0", + "nym-crypto 0.4.0", "nym-sphinx", "rand", "serde", @@ -5572,14 +5784,14 @@ dependencies = [ "etherparse", "futures", "log", - "nym-bin-common", + "nym-bin-common 0.6.0", "nym-client-core", - "nym-config", - "nym-crypto", - "nym-exit-policy", + "nym-config 0.1.0", + "nym-crypto 0.4.0", + "nym-exit-policy 0.1.0", "nym-id", "nym-ip-packet-requests", - "nym-network-defaults", + "nym-network-defaults 0.1.0", "nym-network-requester", "nym-sdk", "nym-service-providers-common", @@ -5588,7 +5800,7 @@ dependencies = [ "nym-tun", "nym-types", "nym-wireguard", - "nym-wireguard-types", + "nym-wireguard-types 0.1.0", "rand", "reqwest 0.12.4", "serde", @@ -5647,7 +5859,7 @@ dependencies = [ "cw2", "humantime-serde", "log", - "nym-contracts-common", + "nym-contracts-common 0.5.0", "rand_chacha", "schemars", "serde", @@ -5659,6 +5871,26 @@ dependencies = [ "utoipa", ] +[[package]] +name = "nym-mixnet-contract-common" +version = "0.6.0" +source = "git+https://github.com/nymtech/nym?rev=f86e08866#f86e0886631a98b0638fe09e6fcbe5458d47adc1" +dependencies = [ + "bs58", + "cosmwasm-schema", + "cosmwasm-std", + "cw-controllers", + "humantime-serde", + "log", + "nym-contracts-common 0.5.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", + "schemars", + "serde", + "serde-json-wasm", + "serde_repr", + "thiserror", + "time", +] + [[package]] name = "nym-mixnode" version = "1.1.37" @@ -5674,24 +5906,24 @@ dependencies = [ "humantime-serde", "lazy_static", "log", - "nym-bin-common", - "nym-config", - "nym-contracts-common", - "nym-crypto", + "nym-bin-common 0.6.0", + "nym-config 0.1.0", + "nym-contracts-common 0.5.0", + "nym-crypto 0.4.0", "nym-http-api-common", "nym-metrics", "nym-mixnet-client", "nym-mixnode-common", "nym-node-http-api", "nym-nonexhaustive-delayqueue", - "nym-pemstore", + "nym-pemstore 0.3.0", "nym-sphinx", "nym-sphinx-params", - "nym-sphinx-types", + "nym-sphinx-types 0.2.0", "nym-task", "nym-topology", "nym-types", - "nym-validator-client", + "nym-validator-client 0.1.0", "rand", "serde", "serde_json", @@ -5712,19 +5944,19 @@ dependencies = [ "futures", "humantime-serde", "log", - "nym-bin-common", - "nym-crypto", + "nym-bin-common 0.6.0", + "nym-crypto 0.4.0", "nym-metrics", - "nym-network-defaults", + "nym-network-defaults 0.1.0", "nym-node-http-api", "nym-sphinx-acknowledgements", "nym-sphinx-addressing", "nym-sphinx-forwarding", "nym-sphinx-framing", "nym-sphinx-params", - "nym-sphinx-types", + "nym-sphinx-types 0.2.0", "nym-task", - "nym-validator-client", + "nym-validator-client 0.1.0", "rand", "serde", "thiserror", @@ -5749,6 +5981,22 @@ dependencies = [ "thiserror", ] +[[package]] +name = "nym-multisig-contract-common" +version = "0.1.0" +source = "git+https://github.com/nymtech/nym?rev=f86e08866#f86e0886631a98b0638fe09e6fcbe5458d47adc1" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus", + "cw-utils", + "cw3", + "cw4", + "schemars", + "serde", + "thiserror", +] + [[package]] name = "nym-network-defaults" version = "0.1.0" @@ -5761,6 +6009,19 @@ dependencies = [ "utoipa", ] +[[package]] +name = "nym-network-defaults" +version = "0.1.0" +source = "git+https://github.com/nymtech/nym?rev=f86e08866#f86e0886631a98b0638fe09e6fcbe5458d47adc1" +dependencies = [ + "dotenvy", + "log", + "schemars", + "serde", + "url", + "utoipa", +] + [[package]] name = "nym-network-monitor" version = "0.1.0" @@ -5771,14 +6032,14 @@ dependencies = [ "dashmap", "futures", "log", - "nym-bin-common", - "nym-crypto", - "nym-network-defaults", + "nym-bin-common 0.6.0", + "nym-crypto 0.4.0", + "nym-network-defaults 0.1.0", "nym-sdk", "nym-sphinx", "nym-topology", "nym-types", - "nym-validator-client", + "nym-validator-client 0.1.0", "petgraph", "rand", "rand_chacha", @@ -5806,16 +6067,16 @@ dependencies = [ "ipnetwork 0.20.0", "log", "nym-async-file-watcher", - "nym-bin-common", + "nym-bin-common 0.6.0", "nym-client-core", "nym-client-websocket-requests", - "nym-config", + "nym-config 0.1.0", "nym-credential-storage", "nym-credentials", - "nym-crypto", - "nym-exit-policy", + "nym-crypto 0.4.0", + "nym-exit-policy 0.1.0", "nym-id", - "nym-network-defaults", + "nym-network-defaults 0.1.0", "nym-ordered-buffer", "nym-sdk", "nym-service-providers-common", @@ -5857,22 +6118,22 @@ dependencies = [ "humantime-serde", "ipnetwork 0.20.0", "nym-authenticator", - "nym-bin-common", + "nym-bin-common 0.6.0", "nym-client-core-config-types", - "nym-config", - "nym-crypto", + "nym-config 0.1.0", + "nym-crypto 0.4.0", "nym-gateway", "nym-ip-packet-router", "nym-mixnode", "nym-network-requester", "nym-node-http-api", - "nym-pemstore", + "nym-pemstore 0.3.0", "nym-sphinx-acknowledgements", "nym-sphinx-addressing", "nym-task", "nym-types", "nym-wireguard", - "nym-wireguard-types", + "nym-wireguard-types 0.1.0", "rand", "semver 1.0.23", "serde", @@ -5900,10 +6161,10 @@ dependencies = [ "hmac", "hyper 1.4.1", "ipnetwork 0.20.0", - "nym-crypto", + "nym-crypto 0.4.0", "nym-http-api-common", "nym-metrics", - "nym-node-requests", + "nym-node-requests 0.1.0", "nym-task", "nym-wireguard", "rand", @@ -5928,11 +6189,11 @@ dependencies = [ "celes", "humantime 2.1.0", "humantime-serde", - "nym-bin-common", - "nym-crypto", - "nym-exit-policy", - "nym-http-api-client", - "nym-wireguard-types", + "nym-bin-common 0.6.0", + "nym-crypto 0.4.0", + "nym-exit-policy 0.1.0", + "nym-http-api-client 0.1.0", + "nym-wireguard-types 0.1.0", "rand_chacha", "schemars", "serde", @@ -5943,6 +6204,29 @@ dependencies = [ "utoipa", ] +[[package]] +name = "nym-node-requests" +version = "0.1.0" +source = "git+https://github.com/nymtech/nym?rev=f86e08866#f86e0886631a98b0638fe09e6fcbe5458d47adc1" +dependencies = [ + "async-trait", + "base64 0.22.1", + "celes", + "humantime 2.1.0", + "humantime-serde", + "nym-bin-common 0.6.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", + "nym-crypto 0.4.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", + "nym-exit-policy 0.1.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", + "nym-http-api-client 0.1.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", + "nym-wireguard-types 0.1.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", + "schemars", + "serde", + "serde_json", + "thiserror", + "time", + "utoipa", +] + [[package]] name = "nym-node-status-api" version = "0.1.0" @@ -5955,12 +6239,12 @@ dependencies = [ "envy", "futures-util", "moka", - "nym-bin-common", + "nym-bin-common 0.6.0", "nym-explorer-client", - "nym-network-defaults", - "nym-node-requests", + "nym-network-defaults 0.1.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", + "nym-node-requests 0.1.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", "nym-task", - "nym-validator-client", + "nym-validator-client 0.1.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", "reqwest 0.12.4", "serde", "serde_json", @@ -5984,7 +6268,7 @@ version = "0.1.0" dependencies = [ "futures", "log", - "nym-crypto", + "nym-crypto 0.4.0", "nym-sphinx", "nym-sphinx-params", "nym-task", @@ -6035,8 +6319,8 @@ dependencies = [ "anyhow", "clap 4.5.20", "log", - "nym-bin-common", - "nym-network-defaults", + "nym-bin-common 0.6.0", + "nym-network-defaults 0.1.0", "nym-sdk", "nym-service-providers-common", "nym-socks5-requests", @@ -6078,6 +6362,14 @@ dependencies = [ "pem", ] +[[package]] +name = "nym-pemstore" +version = "0.3.0" +source = "git+https://github.com/nymtech/nym?rev=f86e08866#f86e0886631a98b0638fe09e6fcbe5458d47adc1" +dependencies = [ + "pem", +] + [[package]] name = "nym-sdk" version = "0.1.0" @@ -6097,15 +6389,15 @@ dependencies = [ "httpcodec", "log", "nym-bandwidth-controller", - "nym-bin-common", + "nym-bin-common 0.6.0", "nym-client-core", "nym-credential-storage", "nym-credential-utils", "nym-credentials", - "nym-credentials-interface", - "nym-crypto", + "nym-credentials-interface 0.1.0", + "nym-crypto 0.4.0", "nym-gateway-requests", - "nym-network-defaults", + "nym-network-defaults 0.1.0", "nym-ordered-buffer", "nym-service-providers-common", "nym-socks5-client-core", @@ -6113,7 +6405,7 @@ dependencies = [ "nym-sphinx", "nym-task", "nym-topology", - "nym-validator-client", + "nym-validator-client 0.1.0", "parking_lot", "pretty_env_logger", "rand", @@ -6143,6 +6435,17 @@ dependencies = [ "time", ] +[[package]] +name = "nym-serde-helpers" +version = "0.1.0" +source = "git+https://github.com/nymtech/nym?rev=f86e08866#f86e0886631a98b0638fe09e6fcbe5458d47adc1" +dependencies = [ + "base64 0.22.1", + "bs58", + "serde", + "time", +] + [[package]] name = "nym-service-provider-requests-common" version = "0.1.0" @@ -6157,7 +6460,7 @@ dependencies = [ "anyhow", "async-trait", "log", - "nym-bin-common", + "nym-bin-common 0.6.0", "nym-sdk", "nym-socks5-requests", "nym-sphinx-anonymous-replies", @@ -6174,21 +6477,21 @@ dependencies = [ "bs58", "clap 4.5.20", "log", - "nym-bin-common", + "nym-bin-common 0.6.0", "nym-client-core", - "nym-config", + "nym-config 0.1.0", "nym-credential-storage", "nym-credentials", - "nym-crypto", + "nym-crypto 0.4.0", "nym-gateway-requests", "nym-id", - "nym-network-defaults", + "nym-network-defaults 0.1.0", "nym-ordered-buffer", - "nym-pemstore", + "nym-pemstore 0.3.0", "nym-socks5-client-core", "nym-sphinx", "nym-topology", - "nym-validator-client", + "nym-validator-client 0.1.0", "rand", "serde", "serde_json", @@ -6210,17 +6513,17 @@ dependencies = [ "log", "nym-bandwidth-controller", "nym-client-core", - "nym-config", - "nym-contracts-common", + "nym-config 0.1.0", + "nym-contracts-common 0.5.0", "nym-credential-storage", - "nym-mixnet-contract-common", - "nym-network-defaults", + "nym-mixnet-contract-common 0.6.0", + "nym-network-defaults 0.1.0", "nym-service-providers-common", "nym-socks5-proxy-helpers", "nym-socks5-requests", "nym-sphinx", "nym-task", - "nym-validator-client", + "nym-validator-client 0.1.0", "pin-project", "rand", "reqwest 0.12.4", @@ -6242,11 +6545,11 @@ dependencies = [ "jni", "lazy_static", "log", - "nym-bin-common", + "nym-bin-common 0.6.0", "nym-client-core", - "nym-config", + "nym-config 0.1.0", "nym-credential-storage", - "nym-crypto", + "nym-crypto 0.4.0", "nym-socks5-client-core", "rand", "safer-ffi", @@ -6275,7 +6578,7 @@ version = "0.1.0" dependencies = [ "bincode", "log", - "nym-exit-policy", + "nym-exit-policy 0.1.0", "nym-service-providers-common", "nym-sphinx-addressing", "serde", @@ -6289,9 +6592,9 @@ name = "nym-sphinx" version = "0.1.0" dependencies = [ "log", - "nym-crypto", + "nym-crypto 0.4.0", "nym-metrics", - "nym-mixnet-contract-common", + "nym-mixnet-contract-common 0.6.0", "nym-sphinx-acknowledgements", "nym-sphinx-addressing", "nym-sphinx-anonymous-replies", @@ -6301,7 +6604,7 @@ dependencies = [ "nym-sphinx-framing", "nym-sphinx-params", "nym-sphinx-routing", - "nym-sphinx-types", + "nym-sphinx-types 0.2.0", "nym-topology", "rand", "rand_chacha", @@ -6315,12 +6618,12 @@ name = "nym-sphinx-acknowledgements" version = "0.1.0" dependencies = [ "generic-array 0.14.7", - "nym-crypto", - "nym-pemstore", + "nym-crypto 0.4.0", + "nym-pemstore 0.3.0", "nym-sphinx-addressing", "nym-sphinx-params", "nym-sphinx-routing", - "nym-sphinx-types", + "nym-sphinx-types 0.2.0", "nym-topology", "rand", "serde", @@ -6332,8 +6635,8 @@ dependencies = [ name = "nym-sphinx-addressing" version = "0.1.0" dependencies = [ - "nym-crypto", - "nym-sphinx-types", + "nym-crypto 0.4.0", + "nym-sphinx-types 0.2.0", "rand", "serde", "thiserror", @@ -6344,11 +6647,11 @@ name = "nym-sphinx-anonymous-replies" version = "0.1.0" dependencies = [ "bs58", - "nym-crypto", + "nym-crypto 0.4.0", "nym-sphinx-addressing", "nym-sphinx-params", "nym-sphinx-routing", - "nym-sphinx-types", + "nym-sphinx-types 0.2.0", "nym-topology", "rand", "rand_chacha", @@ -6363,11 +6666,11 @@ version = "0.1.0" dependencies = [ "dashmap", "log", - "nym-crypto", + "nym-crypto 0.4.0", "nym-metrics", "nym-sphinx-addressing", "nym-sphinx-params", - "nym-sphinx-types", + "nym-sphinx-types 0.2.0", "rand", "serde", "thiserror", @@ -6378,14 +6681,14 @@ dependencies = [ name = "nym-sphinx-cover" version = "0.1.0" dependencies = [ - "nym-crypto", + "nym-crypto 0.4.0", "nym-sphinx-acknowledgements", "nym-sphinx-addressing", "nym-sphinx-chunking", "nym-sphinx-forwarding", "nym-sphinx-params", "nym-sphinx-routing", - "nym-sphinx-types", + "nym-sphinx-types 0.2.0", "nym-topology", "rand", "thiserror", @@ -6398,7 +6701,7 @@ dependencies = [ "nym-outfox", "nym-sphinx-addressing", "nym-sphinx-params", - "nym-sphinx-types", + "nym-sphinx-types 0.2.0", "thiserror", ] @@ -6413,7 +6716,7 @@ dependencies = [ "nym-sphinx-addressing", "nym-sphinx-forwarding", "nym-sphinx-params", - "nym-sphinx-types", + "nym-sphinx-types 0.2.0", "thiserror", "tokio", "tokio-util", @@ -6423,8 +6726,8 @@ dependencies = [ name = "nym-sphinx-params" version = "0.1.0" dependencies = [ - "nym-crypto", - "nym-sphinx-types", + "nym-crypto 0.4.0", + "nym-sphinx-types 0.2.0", "serde", "thiserror", ] @@ -6434,7 +6737,7 @@ name = "nym-sphinx-routing" version = "0.1.0" dependencies = [ "nym-sphinx-addressing", - "nym-sphinx-types", + "nym-sphinx-types 0.2.0", "thiserror", ] @@ -6447,12 +6750,21 @@ dependencies = [ "thiserror", ] +[[package]] +name = "nym-sphinx-types" +version = "0.2.0" +source = "git+https://github.com/nymtech/nym?rev=f86e08866#f86e0886631a98b0638fe09e6fcbe5458d47adc1" +dependencies = [ + "sphinx-packet", + "thiserror", +] + [[package]] name = "nym-statistics-common" version = "0.1.0" dependencies = [ "futures", - "nym-credentials-interface", + "nym-credentials-interface 0.1.0", "nym-sphinx", "time", ] @@ -6492,14 +6804,14 @@ dependencies = [ "async-trait", "bs58", "log", - "nym-api-requests", - "nym-bin-common", - "nym-config", - "nym-crypto", - "nym-mixnet-contract-common", + "nym-api-requests 0.1.0", + "nym-bin-common 0.6.0", + "nym-config 0.1.0", + "nym-crypto 0.4.0", + "nym-mixnet-contract-common 0.6.0", "nym-sphinx-addressing", "nym-sphinx-routing", - "nym-sphinx-types", + "nym-sphinx-types 0.2.0", "rand", "reqwest 0.12.4", "semver 1.0.23", @@ -6517,7 +6829,7 @@ version = "0.1.0" dependencies = [ "etherparse", "log", - "nym-wireguard-types", + "nym-wireguard-types 0.1.0", "thiserror", "tokio", "tokio-tun", @@ -6534,11 +6846,11 @@ dependencies = [ "hmac", "itertools 0.13.0", "log", - "nym-config", - "nym-crypto", - "nym-mixnet-contract-common", - "nym-validator-client", - "nym-vesting-contract-common", + "nym-config 0.1.0", + "nym-crypto 0.4.0", + "nym-mixnet-contract-common 0.6.0", + "nym-validator-client 0.1.0", + "nym-vesting-contract-common 0.7.0", "reqwest 0.12.4", "schemars", "serde", @@ -6572,20 +6884,20 @@ dependencies = [ "flate2", "futures", "itertools 0.13.0", - "nym-api-requests", - "nym-coconut-bandwidth-contract-common", - "nym-coconut-dkg-common", - "nym-compact-ecash", - "nym-config", - "nym-contracts-common", - "nym-ecash-contract-common", - "nym-group-contract-common", - "nym-http-api-client", - "nym-mixnet-contract-common", - "nym-multisig-contract-common", - "nym-network-defaults", - "nym-serde-helpers", - "nym-vesting-contract-common", + "nym-api-requests 0.1.0", + "nym-coconut-bandwidth-contract-common 0.1.0", + "nym-coconut-dkg-common 0.1.0", + "nym-compact-ecash 0.1.0", + "nym-config 0.1.0", + "nym-contracts-common 0.5.0", + "nym-ecash-contract-common 0.1.0", + "nym-group-contract-common 0.1.0", + "nym-http-api-client 0.1.0", + "nym-mixnet-contract-common 0.6.0", + "nym-multisig-contract-common 0.1.0", + "nym-network-defaults 0.1.0", + "nym-serde-helpers 0.1.0", + "nym-vesting-contract-common 0.7.0", "prost 0.12.6", "reqwest 0.12.4", "serde", @@ -6602,6 +6914,55 @@ dependencies = [ "zeroize", ] +[[package]] +name = "nym-validator-client" +version = "0.1.0" +source = "git+https://github.com/nymtech/nym?rev=f86e08866#f86e0886631a98b0638fe09e6fcbe5458d47adc1" +dependencies = [ + "async-trait", + "base64 0.22.1", + "bip32", + "bip39", + "colored", + "cosmrs 0.17.0-pre", + "cosmwasm-std", + "cw-controllers", + "cw-utils", + "cw2", + "cw3", + "cw4", + "eyre", + "flate2", + "futures", + "itertools 0.13.0", + "log", + "nym-api-requests 0.1.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", + "nym-coconut-bandwidth-contract-common 0.1.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", + "nym-coconut-dkg-common 0.1.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", + "nym-compact-ecash 0.1.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", + "nym-config 0.1.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", + "nym-contracts-common 0.5.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", + "nym-ecash-contract-common 0.1.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", + "nym-group-contract-common 0.1.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", + "nym-http-api-client 0.1.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", + "nym-mixnet-contract-common 0.6.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", + "nym-multisig-contract-common 0.1.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", + "nym-network-defaults 0.1.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", + "nym-vesting-contract-common 0.7.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", + "prost 0.12.6", + "reqwest 0.12.4", + "serde", + "serde_json", + "sha2 0.9.9", + "tendermint-rpc", + "thiserror", + "time", + "tokio", + "url", + "wasmtimer", + "zeroize", +] + [[package]] name = "nym-validator-rewarder" version = "0.1.0" @@ -6613,18 +6974,18 @@ dependencies = [ "futures", "humantime 2.1.0", "humantime-serde", - "nym-bin-common", - "nym-coconut-bandwidth-contract-common", - "nym-coconut-dkg-common", - "nym-compact-ecash", - "nym-config", + "nym-bin-common 0.6.0", + "nym-coconut-bandwidth-contract-common 0.1.0", + "nym-coconut-dkg-common 0.1.0", + "nym-compact-ecash 0.1.0", + "nym-config 0.1.0", "nym-credentials", - "nym-credentials-interface", - "nym-crypto", - "nym-ecash-time", - "nym-network-defaults", + "nym-credentials-interface 0.1.0", + "nym-crypto 0.4.0", + "nym-ecash-time 0.1.0", + "nym-network-defaults 0.1.0", "nym-task", - "nym-validator-client", + "nym-validator-client 0.1.0", "nyxd-scraper", "rand_chacha", "serde", @@ -6646,13 +7007,26 @@ dependencies = [ "cosmwasm-schema", "cosmwasm-std", "cw2", - "nym-contracts-common", - "nym-mixnet-contract-common", + "nym-contracts-common 0.5.0", + "nym-mixnet-contract-common 0.6.0", "serde", "thiserror", "ts-rs", ] +[[package]] +name = "nym-vesting-contract-common" +version = "0.7.0" +source = "git+https://github.com/nymtech/nym?rev=f86e08866#f86e0886631a98b0638fe09e6fcbe5458d47adc1" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "nym-contracts-common 0.5.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", + "nym-mixnet-contract-common 0.6.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", + "serde", + "thiserror", +] + [[package]] name = "nym-wallet-types" version = "1.0.0" @@ -6660,12 +7034,12 @@ dependencies = [ "cosmrs 0.15.0", "cosmwasm-std", "hex-literal", - "nym-config", - "nym-mixnet-contract-common", - "nym-network-defaults", + "nym-config 0.1.0", + "nym-mixnet-contract-common 0.6.0", + "nym-network-defaults 0.1.0", "nym-types", - "nym-validator-client", - "nym-vesting-contract-common", + "nym-validator-client 0.1.0", + "nym-vesting-contract-common 0.7.0", "serde", "serde_json", "strum 0.23.0", @@ -6686,11 +7060,11 @@ dependencies = [ "log", "nym-authenticator-requests", "nym-credential-verification", - "nym-crypto", + "nym-crypto 0.4.0", "nym-gateway-storage", - "nym-network-defaults", + "nym-network-defaults 0.1.0", "nym-task", - "nym-wireguard-types", + "nym-wireguard-types 0.1.0", "thiserror", "tokio", "tokio-stream", @@ -6703,15 +7077,29 @@ version = "0.1.0" dependencies = [ "base64 0.22.1", "log", - "nym-config", - "nym-crypto", - "nym-network-defaults", + "nym-config 0.1.0", + "nym-crypto 0.4.0", + "nym-network-defaults 0.1.0", "rand", "serde", "thiserror", "x25519-dalek", ] +[[package]] +name = "nym-wireguard-types" +version = "0.1.0" +source = "git+https://github.com/nymtech/nym?rev=f86e08866#f86e0886631a98b0638fe09e6fcbe5458d47adc1" +dependencies = [ + "base64 0.22.1", + "log", + "nym-config 0.1.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", + "nym-network-defaults 0.1.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", + "serde", + "thiserror", + "x25519-dalek", +] + [[package]] name = "nymvisor" version = "0.1.8" @@ -6727,8 +7115,8 @@ dependencies = [ "humantime-serde", "nix 0.27.1", "nym-async-file-watcher", - "nym-bin-common", - "nym-config", + "nym-bin-common 0.6.0", + "nym-config 0.1.0", "nym-task", "reqwest 0.12.4", "serde", @@ -9506,19 +9894,19 @@ dependencies = [ "cw-utils", "dkg-bypass-contract", "indicatif", - "nym-bin-common", - "nym-coconut-dkg-common", - "nym-compact-ecash", - "nym-config", - "nym-contracts-common", - "nym-crypto", - "nym-ecash-contract-common", - "nym-group-contract-common", - "nym-mixnet-contract-common", - "nym-multisig-contract-common", - "nym-pemstore", - "nym-validator-client", - "nym-vesting-contract-common", + "nym-bin-common 0.6.0", + "nym-coconut-dkg-common 0.1.0", + "nym-compact-ecash 0.1.0", + "nym-config 0.1.0", + "nym-contracts-common 0.5.0", + "nym-crypto 0.4.0", + "nym-ecash-contract-common 0.1.0", + "nym-group-contract-common 0.1.0", + "nym-mixnet-contract-common 0.6.0", + "nym-multisig-contract-common 0.1.0", + "nym-pemstore 0.3.0", + "nym-validator-client 0.1.0", + "nym-vesting-contract-common 0.7.0", "rand", "serde", "serde_json", @@ -10112,11 +10500,11 @@ name = "ts-rs-cli" version = "0.1.0" dependencies = [ "anyhow", - "nym-api-requests", - "nym-mixnet-contract-common", + "nym-api-requests 0.1.0", + "nym-mixnet-contract-common 0.6.0", "nym-types", - "nym-validator-client", - "nym-vesting-contract-common", + "nym-validator-client 0.1.0", + "nym-vesting-contract-common 0.7.0", "nym-wallet-types", "ts-rs", "walkdir", @@ -10760,15 +11148,15 @@ dependencies = [ "js-sys", "nym-bandwidth-controller", "nym-client-core", - "nym-config", + "nym-config 0.1.0", "nym-credential-storage", - "nym-crypto", + "nym-crypto 0.4.0", "nym-gateway-client", "nym-sphinx", "nym-sphinx-acknowledgements", "nym-task", "nym-topology", - "nym-validator-client", + "nym-validator-client 0.1.0", "rand", "serde", "serde-wasm-bindgen 0.6.5", @@ -11323,12 +11711,12 @@ dependencies = [ "bs58", "getrandom", "js-sys", - "nym-bin-common", + "nym-bin-common 0.6.0", "nym-coconut", - "nym-compact-ecash", + "nym-compact-ecash 0.1.0", "nym-credentials", - "nym-crypto", - "nym-http-api-client", + "nym-crypto 0.4.0", + "nym-http-api-client 0.1.0", "rand", "reqwest 0.12.4", "serde", diff --git a/Cargo.toml b/Cargo.toml index 68c9476f5e..d431d4567b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -155,6 +155,7 @@ default-members = [ "nym-data-observatory", "nym-node", "nym-validator-rewarder", + "nym-node-status-api", "service-providers/authenticator", "service-providers/ip-packet-router", "service-providers/network-requester", diff --git a/nym-node-status-api/Cargo.toml b/nym-node-status-api/Cargo.toml index 3b8d315ccb..03e6fe60a7 100644 --- a/nym-node-status-api/Cargo.toml +++ b/nym-node-status-api/Cargo.toml @@ -23,10 +23,14 @@ futures-util = { workspace = true } moka = { workspace = true, features = ["future"] } nym-bin-common = { path = "../common/bin-common" } nym-explorer-client = { path = "../explorer-api/explorer-client" } -nym-network-defaults = { path = "../common/network-defaults" } -nym-validator-client = { path = "../common/client-libs/validator-client" } +# TODO dz: ref before Nym API client changes. Update to latest develop once new Nym API is live +nym-network-defaults = { git = "https://github.com/nymtech/nym", rev = "f86e08866" } +nym-validator-client = { git = "https://github.com/nymtech/nym", rev = "f86e08866" } +# nym-network-defaults = { path = "../common/network-defaults" } +# nym-validator-client = { path = "../common/client-libs/validator-client" } nym-task = { path = "../common/task" } -nym-node-requests = { path = "../nym-node/nym-node-requests", features = ["openapi"] } +nym-node-requests = { git = "https://github.com/nymtech/nym", rev = "f86e08866" } +# nym-node-requests = { path = "../nym-node/nym-node-requests", features = ["openapi"] } reqwest = { workspace = true } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } diff --git a/nym-node-status-api/src/config.rs b/nym-node-status-api/src/config.rs index 24e966a53f..c499ef3c65 100644 --- a/nym-node-status-api/src/config.rs +++ b/nym-node-status-api/src/config.rs @@ -13,9 +13,6 @@ pub(crate) struct Config { nyxd_addr: Url, #[serde(default = "Config::default_client_timeout")] #[serde(deserialize_with = "parse_duration")] - nym_api_client_timeout: Duration, - #[serde(default = "Config::default_client_timeout")] - #[serde(deserialize_with = "parse_duration")] explorer_client_timeout: Duration, } @@ -51,10 +48,6 @@ impl Config { &self.nyxd_addr } - pub(crate) fn nym_api_client_timeout(&self) -> Duration { - self.nym_api_client_timeout.to_owned() - } - pub(crate) fn nym_explorer_client_timeout(&self) -> Duration { self.explorer_client_timeout.to_owned() } diff --git a/nym-node-status-api/src/db/mod.rs b/nym-node-status-api/src/db/mod.rs index 784a35f17a..2df2483687 100644 --- a/nym-node-status-api/src/db/mod.rs +++ b/nym-node-status-api/src/db/mod.rs @@ -19,12 +19,9 @@ pub(crate) struct Storage { impl Storage { pub async fn init() -> Result { let connection_url = read_env_var(DATABASE_URL_ENV_VAR)?; - let connect_options = { - let connect_options = SqliteConnectOptions::from_str(&connection_url)?; - let mut connect_options = connect_options.create_if_missing(true); - let connect_options = connect_options.disable_statement_logging(); - (*connect_options).clone() - }; + let connect_options = SqliteConnectOptions::from_str(&connection_url)? + .create_if_missing(true) + .disable_statement_logging(); let pool = sqlx::SqlitePool::connect_with(connect_options) .await diff --git a/nym-node-status-api/src/db/queries/gateways.rs b/nym-node-status-api/src/db/queries/gateways.rs index 92a599154f..02b5d05dc3 100644 --- a/nym-node-status-api/src/db/queries/gateways.rs +++ b/nym-node-status-api/src/db/queries/gateways.rs @@ -143,7 +143,7 @@ pub(crate) async fn get_all_gateways(pool: &DbPool) -> anyhow::Result>() .await?; diff --git a/nym-node-status-api/src/db/queries/misc.rs b/nym-node-status-api/src/db/queries/misc.rs index 64b2f3cd24..2aa6356051 100644 --- a/nym-node-status-api/src/db/queries/misc.rs +++ b/nym-node-status-api/src/db/queries/misc.rs @@ -37,7 +37,7 @@ async fn insert_summary( value, timestamp ) - .execute(&mut tx) + .execute(&mut *tx) .await .map_err(|err| { tracing::error!("Failed to insert data for {kind}: {err}, aborting transaction",); diff --git a/nym-node-status-api/src/db/queries/mixnodes.rs b/nym-node-status-api/src/db/queries/mixnodes.rs index 8bc8020ef9..58af9bd429 100644 --- a/nym-node-status-api/src/db/queries/mixnodes.rs +++ b/nym-node-status-api/src/db/queries/mixnodes.rs @@ -71,7 +71,7 @@ pub(crate) async fn get_all_mixnodes(pool: &DbPool) -> anyhow::Result>() .await?; @@ -115,7 +115,7 @@ pub(crate) async fn get_daily_stats(pool: &DbPool) -> anyhow::Result>() .await?; diff --git a/nym-node-status-api/src/db/queries/summary.rs b/nym-node-status-api/src/db/queries/summary.rs index d3855639f6..103712a9a4 100644 --- a/nym-node-status-api/src/db/queries/summary.rs +++ b/nym-node-status-api/src/db/queries/summary.rs @@ -37,7 +37,7 @@ pub(crate) async fn get_summary_history(pool: &DbPool) -> anyhow::Result>() .await?; @@ -62,7 +62,7 @@ async fn get_summary_dto(pool: &DbPool) -> anyhow::Result> { last_updated_utc as "last_updated_utc!" FROM summary"# ) - .fetch(&mut conn) + .fetch(&mut *conn) .try_collect::>() .await?) } diff --git a/nym-node-status-api/src/monitor/mod.rs b/nym-node-status-api/src/monitor/mod.rs index 4215e297cc..5079af3b5a 100644 --- a/nym-node-status-api/src/monitor/mod.rs +++ b/nym-node-status-api/src/monitor/mod.rs @@ -81,7 +81,8 @@ async fn run( tracing::debug!("6"); let api_client = - NymApiClient::new_with_timeout(default_api_url, config.nym_api_client_timeout()); + // TODO dz introduce timeout ? + NymApiClient::new(default_api_url); let gateways = api_client .get_cached_described_gateways() .await From 40d9321aecc68d4d58d027610570366b35c79260 Mon Sep 17 00:00:00 2001 From: Fran Arbanas Date: Fri, 18 Oct 2024 17:14:44 +0200 Subject: [PATCH 28/48] Node status API dockerfile and env vars (#4986) * feat: add dockerfile and env variables * Added workflow for pushing node status api on harbor * Misc changes to pathing and using yq instead of jq * fix: change the way we read env vars for nyxd, nym api and explorer * fix: docker build workflow * Remove config in favor of clap args * Added naming and tags * change from value to result --------- Co-authored-by: Lawrence Stalder Co-authored-by: dynco-nym <173912580+dynco-nym@users.noreply.github.com> --- .github/workflows/push-node-status-api.yaml | 54 ++++++++++++-- envs/mainnet.env | 6 +- nym-node-status-api/Cargo.toml | 11 ++- nym-node-status-api/Dockerfile | 15 ++++ nym-node-status-api/launch_node_status_api.sh | 14 +++- nym-node-status-api/src/cli/mod.rs | 62 ++++++++++++++-- nym-node-status-api/src/config.rs | 72 ------------------- nym-node-status-api/src/db/mod.rs | 5 +- nym-node-status-api/src/main.rs | 26 +++---- nym-node-status-api/src/monitor/mod.rs | 14 ++-- 10 files changed, 158 insertions(+), 121 deletions(-) create mode 100644 nym-node-status-api/Dockerfile delete mode 100644 nym-node-status-api/src/config.rs diff --git a/.github/workflows/push-node-status-api.yaml b/.github/workflows/push-node-status-api.yaml index 1a082a1ad2..941a8619f0 100644 --- a/.github/workflows/push-node-status-api.yaml +++ b/.github/workflows/push-node-status-api.yaml @@ -1,11 +1,55 @@ name: Build and upload Node Status API container to harbor.nymte.ch - on: workflow_dispatch: +env: + WORKING_DIRECTORY: "nym-node-status-api" + CONTAINER_NAME: "node-status-api" + jobs: - my-job: - runs-on: arc-ubuntu-22.04 + build-container: + runs-on: arc-ubuntu-22.04-dind steps: - - name: my-step - run: echo "Hello World!" + - name: Login to Harbor + uses: docker/login-action@v3 + with: + registry: harbor.nymte.ch + username: ${{ secrets.HARBOR_ROBOT_USERNAME }} + password: ${{ secrets.HARBOR_ROBOT_SECRET }} + + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Configure git identity + run: | + git config --global user.email "lawrence@nymtech.net" + git config --global user.name "Lawrence Stalder" + + - name: Get version from cargo.toml + uses: mikefarah/yq@v4.44.3 + id: get_version + with: + cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml + + - name: Check if tag exists + run: | + if git rev-parse ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }} >/dev/null 2>&1; then + echo "Tag ${{ steps.get_version.outputs.result }} already exists" + fi + + - name: Remove existing tag if exists + run: | + if git rev-parse ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }} >/dev/null 2>&1; then + git push --delete origin ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }} + git tag -d ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }} + fi + + - name: Create tag + run: | + git tag -a ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }} -m "Version ${{ steps.get_version.outputs.result }}" + git push origin ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }} + + - name: BuildAndPushImageOnHarbor + run: | + docker build -f ${{ env.WORKING_DIRECTORY }}/Dockerfile . -t harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }}:${{ steps.get_version.outputs.result }} -t harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }}:latest + docker push harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }} --all-tags diff --git a/envs/mainnet.env b/envs/mainnet.env index e40e54a471..419547bf97 100644 --- a/envs/mainnet.env +++ b/envs/mainnet.env @@ -21,8 +21,8 @@ COCONUT_DKG_CONTRACT_ADDRESS=n19604yflqggs9mk2z26mqygq43q2kr3n932egxx630svywd5mp REWARDING_VALIDATOR_ADDRESS=n10yyd98e2tuwu0f7ypz9dy3hhjw7v772q6287gy STATISTICS_SERVICE_DOMAIN_ADDRESS="https://mainnet-stats.nymte.ch:8090" -NYXD="https://rpc.nymtech.net" -NYM_API="https://validator.nymtech.net/api/" +NYXD=https://rpc.nymtech.net +NYM_API=https://validator.nymtech.net/api/ NYXD_WS="wss://rpc.nymtech.net/websocket" -EXPLORER_API="https://explorer.nymtech.net/api/" +EXPLORER_API=https://explorer.nymtech.net/api/ NYM_VPN_API="https://nymvpn.com/api" diff --git a/nym-node-status-api/Cargo.toml b/nym-node-status-api/Cargo.toml index 03e6fe60a7..056271e41d 100644 --- a/nym-node-status-api/Cargo.toml +++ b/nym-node-status-api/Cargo.toml @@ -16,7 +16,7 @@ rust-version.workspace = true anyhow = { workspace = true } axum = { workspace = true, features = ["tokio"] } chrono = { workspace = true } -clap = { workspace = true, features = ["cargo", "derive"] } +clap = { workspace = true, features = ["cargo", "derive", "env", "string"] } cosmwasm-std = { workspace = true } envy = { workspace = true } futures-util = { workspace = true } @@ -53,5 +53,10 @@ utoipauto = { workspace = true } [build-dependencies] anyhow = { workspace = true } -tokio = { workspace = true, features = ["macros" ] } -sqlx = { workspace = true, features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"] } +tokio = { workspace = true, features = ["macros"] } +sqlx = { workspace = true, features = [ + "runtime-tokio-rustls", + "sqlite", + "macros", + "migrate", +] } diff --git a/nym-node-status-api/Dockerfile b/nym-node-status-api/Dockerfile new file mode 100644 index 0000000000..ceab7c9392 --- /dev/null +++ b/nym-node-status-api/Dockerfile @@ -0,0 +1,15 @@ +FROM rust:latest AS builder + +COPY ./ /usr/src/nym +WORKDIR /usr/src/nym/nym-node-status-api + +RUN cargo build --release + +FROM ubuntu:24.04 + +RUN apt-get update && apt-get install -y ca-certificates + +WORKDIR /nym + +COPY --from=builder /usr/src/nym/target/release/nym-node-status-api ./ +ENTRYPOINT [ "/nym/nym-node-status-api" ] diff --git a/nym-node-status-api/launch_node_status_api.sh b/nym-node-status-api/launch_node_status_api.sh index e5059c5011..1db6bcc1b2 100755 --- a/nym-node-status-api/launch_node_status_api.sh +++ b/nym-node-status-api/launch_node_status_api.sh @@ -4,7 +4,15 @@ set -e export RUST_LOG=${RUST_LOG:-debug} -export NYM_API_CLIENT_TIMEOUT=60; -export EXPLORER_CLIENT_TIMEOUT=60; +export NYM_API_CLIENT_TIMEOUT=60 +export EXPLORER_CLIENT_TIMEOUT=60 +#export NYXD=https://rpc.nymtech.net +#export NYM_API=https://validator.nymtech.net/api/ +#export EXPLORER_API=https://explorer.nymtech.net/api/ +#export NETWORK_NAME=mainnet -cargo run --package nym-node-status-api --release -- --config-env-file ../envs/mainnet.env +#cargo run --package nym-node-status-api --release -- --connection-url "sqlite://node-status-api.sqlite?mode=rwc" + +cd .. +docker build -t node-status-api -f nym-node-status-api/Dockerfile . +docker run --env-file envs/mainnet.env -e NYM_NODE_STATUS_API_CONNECTION_URL="sqlite://node-status-api.sqlite?mode=rwc" node-status-api diff --git a/nym-node-status-api/src/cli/mod.rs b/nym-node-status-api/src/cli/mod.rs index 7f46578863..ad214b6a23 100644 --- a/nym-node-status-api/src/cli/mod.rs +++ b/nym-node-status-api/src/cli/mod.rs @@ -1,6 +1,7 @@ use clap::Parser; use nym_bin_common::bin_info; -use std::sync::OnceLock; +use reqwest::Url; +use std::{sync::OnceLock, time::Duration}; // Helper for passing LONG_VERSION to clap fn pretty_build_info_static() -> &'static str { @@ -8,10 +9,61 @@ fn pretty_build_info_static() -> &'static str { PRETTY_BUILD_INFORMATION.get_or_init(|| bin_info!().pretty_print()) } -#[derive(Parser, Debug)] +#[derive(Clone, Debug, Parser)] #[clap(author = "Nymtech", version, long_version = pretty_build_info_static(), about)] pub(crate) struct Cli { - /// Path pointing to an env file that configures the Nym API. - #[clap(short, long)] - pub(crate) config_env_file: Option, + /// Network name for the network to which we're connecting. + #[clap(long, env = "NETWORK_NAME")] + pub(crate) network_name: String, + + /// Explorer api url. + #[clap(short, long, env = "EXPLORER_API")] + pub(crate) explorer_api: String, + + /// Nym api url. + #[clap(short, long, env = "NYM_API")] + pub(crate) nym_api: String, + + /// TTL for the http cache. + #[clap( + long, + default_value_t = 30, + env = "NYM_NODE_STATUS_API_NYM_HTTP_CACHE_TTL" + )] + pub(crate) nym_http_cache_ttl: u64, + + /// HTTP port on which to run node status api. + #[clap(long, default_value_t = 8000, env = "NYM_NODE_STATUS_API_HTTP_PORT")] + pub(crate) http_port: u16, + + /// Nyxd address. + #[clap(long, env = "NYXD")] + pub(crate) nyxd_addr: Url, + + /// Nym api client timeout. + #[clap( + long, + default_value = "15", + env = "NYM_NODE_STATUS_API_NYM_API_CLIENT_TIMEOUT" + )] + #[arg(value_parser = parse_duration)] + pub(crate) nym_api_client_timeout: Duration, + + /// Explorer api client timeout. + #[clap( + long, + default_value = "15", + env = "NYM_NODE_STATUS_API_EXPLORER_CLIENT_TIMEOUT" + )] + #[arg(value_parser = parse_duration)] + pub(crate) explorer_client_timeout: Duration, + + /// Connection url for the database. + #[clap(long, env = "NYM_NODE_STATUS_API_CONNECTION_URL")] + pub(crate) connection_url: String, +} + +fn parse_duration(arg: &str) -> Result { + let seconds = arg.parse()?; + Ok(std::time::Duration::from_secs(seconds)) } diff --git a/nym-node-status-api/src/config.rs b/nym-node-status-api/src/config.rs deleted file mode 100644 index c499ef3c65..0000000000 --- a/nym-node-status-api/src/config.rs +++ /dev/null @@ -1,72 +0,0 @@ -use anyhow::anyhow; -use reqwest::Url; -use serde::Deserialize; -use std::time::Duration; - -#[derive(Debug, Clone, Deserialize)] -pub(crate) struct Config { - #[serde(default = "Config::default_http_cache_seconds")] - nym_http_cache_ttl: u64, - #[serde(default = "Config::default_http_port")] - http_port: u16, - #[serde(rename = "nyxd")] - nyxd_addr: Url, - #[serde(default = "Config::default_client_timeout")] - #[serde(deserialize_with = "parse_duration")] - explorer_client_timeout: Duration, -} - -impl Config { - pub(crate) fn from_env() -> anyhow::Result { - envy::from_env::().map_err(|e| { - tracing::error!("Failed to load config from env: {e}"); - anyhow::Error::from(e) - }) - } - - fn default_client_timeout() -> Duration { - Duration::from_secs(15) - } - - fn default_http_port() -> u16 { - 8000 - } - - fn default_http_cache_seconds() -> u64 { - 30 - } - - pub(crate) fn nym_http_cache_ttl(&self) -> u64 { - self.nym_http_cache_ttl - } - - pub(crate) fn http_port(&self) -> u16 { - self.http_port - } - - pub(crate) fn nyxd_addr(&self) -> &Url { - &self.nyxd_addr - } - - pub(crate) fn nym_explorer_client_timeout(&self) -> Duration { - self.explorer_client_timeout.to_owned() - } -} - -fn parse_duration<'de, D>(deserializer: D) -> Result -where - D: serde::Deserializer<'de>, -{ - let s: String = Deserialize::deserialize(deserializer)?; - let secs: u64 = s.parse().map_err(serde::de::Error::custom)?; - Ok(Duration::from_secs(secs)) -} - -pub(super) fn read_env_var(env_var: &str) -> anyhow::Result { - std::env::var(env_var) - .map_err(|_| anyhow!("You need to set {}", env_var)) - .map(|value| { - tracing::trace!("{}={}", env_var, value); - value - }) -} diff --git a/nym-node-status-api/src/db/mod.rs b/nym-node-status-api/src/db/mod.rs index 2df2483687..8e840252f6 100644 --- a/nym-node-status-api/src/db/mod.rs +++ b/nym-node-status-api/src/db/mod.rs @@ -1,13 +1,11 @@ use std::str::FromStr; -use crate::read_env_var; use anyhow::{anyhow, Result}; use sqlx::{migrate::Migrator, sqlite::SqliteConnectOptions, ConnectOptions, SqlitePool}; pub(crate) mod models; pub(crate) mod queries; -pub(crate) const DATABASE_URL_ENV_VAR: &str = "DATABASE_URL"; static MIGRATOR: Migrator = sqlx::migrate!("./migrations"); pub(crate) type DbPool = SqlitePool; @@ -17,8 +15,7 @@ pub(crate) struct Storage { } impl Storage { - pub async fn init() -> Result { - let connection_url = read_env_var(DATABASE_URL_ENV_VAR)?; + pub async fn init(connection_url: String) -> Result { let connect_options = SqliteConnectOptions::from_str(&connection_url)? .create_if_missing(true) .disable_statement_logging(); diff --git a/nym-node-status-api/src/main.rs b/nym-node-status-api/src/main.rs index 0fb1d0e1d2..a15cfe68a4 100644 --- a/nym-node-status-api/src/main.rs +++ b/nym-node-status-api/src/main.rs @@ -1,11 +1,7 @@ use clap::Parser; -use nym_network_defaults::setup_env; use nym_task::signal::wait_for_signal; -use crate::config::read_env_var; - mod cli; -mod config; mod db; mod http; mod logging; @@ -16,33 +12,27 @@ async fn main() -> anyhow::Result<()> { logging::setup_tracing_logger(); let args = cli::Cli::parse(); - // if dotenv file is present, load its values - // otherwise, default to mainnet - setup_env(args.config_env_file.as_ref()); - tracing::debug!("{:?}", read_env_var("NETWORK_NAME")); - tracing::debug!("{:?}", read_env_var("EXPLORER_API")); - tracing::debug!("{:?}", read_env_var("NYM_API")); - let conf = config::Config::from_env()?; - tracing::debug!("Using config:\n{:#?}", conf); + let connection_url = args.connection_url.clone(); + tracing::debug!("Using config:\n{:#?}", args); - let storage = db::Storage::init().await?; + let storage = db::Storage::init(connection_url).await?; let db_pool = storage.pool_owned().await; - let conf_clone = conf.clone(); + let args_clone = args.clone(); tokio::spawn(async move { - monitor::spawn_in_background(db_pool, conf_clone).await; + monitor::spawn_in_background(db_pool, args_clone).await; }); tracing::info!("Started monitor task"); let shutdown_handles = http::server::start_http_api( storage.pool_owned().await, - conf.http_port(), - conf.nym_http_cache_ttl(), + args.http_port, + args.nym_http_cache_ttl, ) .await .expect("Failed to start server"); - tracing::info!("Started HTTP server on port {}", conf.http_port()); + tracing::info!("Started HTTP server on port {}", args.http_port); wait_for_signal().await; diff --git a/nym-node-status-api/src/monitor/mod.rs b/nym-node-status-api/src/monitor/mod.rs index 5079af3b5a..3c5172c8a9 100644 --- a/nym-node-status-api/src/monitor/mod.rs +++ b/nym-node-status-api/src/monitor/mod.rs @@ -1,4 +1,4 @@ -use crate::config::Config; +use crate::cli::Cli; use crate::db::models::{ gateway, mixnode, GatewayRecord, MixnodeRecord, NetworkSummary, GATEWAYS_BLACKLISTED_COUNT, GATEWAYS_BONDED_COUNT, GATEWAYS_EXPLORER_COUNT, GATEWAYS_HISTORICAL_COUNT, @@ -29,7 +29,7 @@ static DELEGATION_PROGRAM_WALLET: &str = "n1rnxpdpx3kldygsklfft0gech7fhfcux4zst5 // TODO dz: query many NYM APIs: // multiple instances running directory cache, ask sachin -pub(crate) async fn spawn_in_background(db_pool: DbPool, config: Config) -> JoinHandle<()> { +pub(crate) async fn spawn_in_background(db_pool: DbPool, config: Cli) -> JoinHandle<()> { let network_defaults = nym_network_defaults::NymNetworkDetails::new_from_env(); loop { @@ -54,7 +54,7 @@ pub(crate) async fn spawn_in_background(db_pool: DbPool, config: Config) -> Join async fn run( pool: &DbPool, network_details: &NymNetworkDetails, - config: &Config, + config: &Cli, ) -> anyhow::Result<()> { let default_api_url = network_details .endpoints @@ -70,10 +70,8 @@ async fn run( let default_explorer_url = default_explorer_url.expect("explorer url missing in network config"); - let explorer_client = ExplorerClient::new_with_timeout( - default_explorer_url, - config.nym_explorer_client_timeout(), - )?; + let explorer_client = + ExplorerClient::new_with_timeout(default_explorer_url, config.explorer_client_timeout)?; let explorer_gateways = explorer_client .get_gateways() .await @@ -126,7 +124,7 @@ async fn run( .await .log_error("get_active_mixnodes")?; let delegation_program_members = - get_delegation_program_details(network_details, config.nyxd_addr()).await?; + get_delegation_program_details(network_details, &config.nyxd_addr).await?; // keep stats for later let count_bonded_mixnodes = mixnodes.len(); From cc983963d42c1241760eee68d4be6c5feeabbd60 Mon Sep 17 00:00:00 2001 From: Dinko Zdravac <173912580+dynco-nym@users.noreply.github.com> Date: Mon, 28 Oct 2024 16:53:36 +0100 Subject: [PATCH 29/48] Fully functional network scores (#5048) * Compile & copy wg probe * Node status agent WIP * Enable debug logging * Agent submits results - add clap to agent - agent runs network probe - /submit endpoint on NS API * Build clients with timeouts * Update logging and dev scripts * Replace /blaclisted endpoint * Testruns fully functional - task that queues testruns periodically - testruns read/write in DB * Probe scores fully working - testruns are assigned on API - submit updates testruns correctly on NS API side - agent registers with API - agent submits results correctly * Clippy fixes * PR feedback * Clippy again * PR feedback * Run clippy earlier in CI * Make refresh delay configurable in server & agent --- .github/workflows/ci-build.yml | 12 +- Cargo.lock | 185 +++++++++++------- Cargo.toml | 5 + common/bin-common/Cargo.toml | 1 + common/http-api-client/src/lib.rs | 12 ++ common/models/Cargo.toml | 14 ++ common/models/src/lib.rs | 1 + common/models/src/ns_api.rs | 8 + envs/canary.env | 4 +- envs/qa.env | 4 +- envs/sandbox.env | 6 +- explorer-api/explorer-client/src/lib.rs | 4 +- nym-data-observatory/README_SQLX.md | 1 + nym-node-status-agent/.gitignore | 1 + nym-node-status-agent/Cargo.toml | 27 +++ nym-node-status-agent/run.sh | 49 +++++ nym-node-status-agent/src/cli.rs | 109 +++++++++++ nym-node-status-agent/src/main.rs | 78 ++++++++ nym-node-status-agent/src/probe.rs | 54 +++++ nym-node-status-api/.gitignore | 4 + nym-node-status-api/Cargo.toml | 16 +- nym-node-status-api/Dockerfile.dev | 8 + nym-node-status-api/build.rs | 5 + nym-node-status-api/launch_node_status_api.sh | 37 +++- nym-node-status-api/migrations/000_init.sql | 12 ++ nym-node-status-api/src/cli/mod.rs | 32 +-- nym-node-status-api/src/db/mod.rs | 5 +- nym-node-status-api/src/db/models.rs | 34 ++++ nym-node-status-api/src/db/queries/mod.rs | 1 + .../src/db/queries/testruns.rs | 126 ++++++++++++ nym-node-status-api/src/http/api/gateways.rs | 2 +- nym-node-status-api/src/http/api/mod.rs | 5 +- nym-node-status-api/src/http/api/testruns.rs | 117 ++++++++++- nym-node-status-api/src/http/api_docs.rs | 2 +- nym-node-status-api/src/http/error.rs | 14 ++ nym-node-status-api/src/http/models.rs | 12 ++ nym-node-status-api/src/http/server.rs | 1 + nym-node-status-api/src/logging.rs | 49 +++-- nym-node-status-api/src/main.rs | 21 +- nym-node-status-api/src/monitor/mod.rs | 63 +++--- nym-node-status-api/src/testruns/mod.rs | 76 +++++++ nym-node-status-api/src/testruns/models.rs | 16 ++ nym-node-status-api/src/testruns/queue.rs | 118 +++++++++++ 43 files changed, 1185 insertions(+), 166 deletions(-) create mode 100644 common/models/Cargo.toml create mode 100644 common/models/src/lib.rs create mode 100644 common/models/src/ns_api.rs create mode 100644 nym-node-status-agent/.gitignore create mode 100644 nym-node-status-agent/Cargo.toml create mode 100755 nym-node-status-agent/run.sh create mode 100644 nym-node-status-agent/src/cli.rs create mode 100644 nym-node-status-agent/src/main.rs create mode 100644 nym-node-status-agent/src/probe.rs create mode 100644 nym-node-status-api/Dockerfile.dev create mode 100644 nym-node-status-api/src/db/queries/testruns.rs create mode 100644 nym-node-status-api/src/testruns/mod.rs create mode 100644 nym-node-status-api/src/testruns/models.rs create mode 100644 nym-node-status-api/src/testruns/queue.rs diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml index 77d220e633..d7a76f2673 100644 --- a/.github/workflows/ci-build.yml +++ b/.github/workflows/ci-build.yml @@ -57,6 +57,12 @@ jobs: command: fmt args: --all -- --check + - name: Clippy + uses: actions-rs/cargo@v1 + with: + command: clippy + args: --workspace --all-targets -- -D warnings + - name: Build all binaries uses: actions-rs/cargo@v1 with: @@ -82,9 +88,3 @@ jobs: with: command: test args: --workspace -- --ignored - - - name: Clippy - uses: actions-rs/cargo@v1 - with: - command: clippy - args: --workspace --all-targets -- -D warnings diff --git a/Cargo.lock b/Cargo.lock index b9d06b3a8e..def19342de 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -466,6 +466,7 @@ checksum = "504e3947307ac8326a5437504c517c4b56716c9d98fac0028c2acc7ca47d70ae" dependencies = [ "async-trait", "axum-core 0.4.5", + "axum-macros", "bytes", "futures-util", "http 1.1.0", @@ -553,6 +554,17 @@ dependencies = [ "tracing", ] +[[package]] +name = "axum-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d123550fa8d071b7255cb0cc04dc302baa6c8c4a79f55701552684d8399bce" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.82", +] + [[package]] name = "axum-test" version = "16.2.0" @@ -4524,20 +4536,20 @@ dependencies = [ [[package]] name = "nym-api-requests" version = "0.1.0" -source = "git+https://github.com/nymtech/nym?rev=f86e08866#f86e0886631a98b0638fe09e6fcbe5458d47adc1" +source = "git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork#8d68cf88dac1adf1f68726a9b6e0740f8cddcf32" dependencies = [ "bs58", "cosmrs 0.17.0-pre", "cosmwasm-std", "ecdsa", "getset", - "nym-compact-ecash 0.1.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", - "nym-credentials-interface 0.1.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", - "nym-crypto 0.4.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", - "nym-ecash-time 0.1.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", - "nym-mixnet-contract-common 0.6.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", - "nym-node-requests 0.1.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", - "nym-serde-helpers 0.1.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", + "nym-compact-ecash 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", + "nym-credentials-interface 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", + "nym-crypto 0.4.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", + "nym-ecash-time 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", + "nym-mixnet-contract-common 0.6.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", + "nym-node-requests 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", + "nym-serde-helpers 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", "schemars", "serde", "sha2 0.10.8", @@ -4665,7 +4677,7 @@ dependencies = [ [[package]] name = "nym-bin-common" version = "0.6.0" -source = "git+https://github.com/nymtech/nym?rev=f86e08866#f86e0886631a98b0638fe09e6fcbe5458d47adc1" +source = "git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork#8d68cf88dac1adf1f68726a9b6e0740f8cddcf32" dependencies = [ "const-str", "log", @@ -4989,11 +5001,11 @@ dependencies = [ [[package]] name = "nym-coconut-bandwidth-contract-common" version = "0.1.0" -source = "git+https://github.com/nymtech/nym?rev=f86e08866#f86e0886631a98b0638fe09e6fcbe5458d47adc1" +source = "git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork#8d68cf88dac1adf1f68726a9b6e0740f8cddcf32" dependencies = [ "cosmwasm-schema", "cosmwasm-std", - "nym-multisig-contract-common 0.1.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", + "nym-multisig-contract-common 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", ] [[package]] @@ -5012,15 +5024,22 @@ dependencies = [ [[package]] name = "nym-coconut-dkg-common" version = "0.1.0" -source = "git+https://github.com/nymtech/nym?rev=f86e08866#f86e0886631a98b0638fe09e6fcbe5458d47adc1" +source = "git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork#8d68cf88dac1adf1f68726a9b6e0740f8cddcf32" dependencies = [ "cosmwasm-schema", "cosmwasm-std", "cw-utils", "cw2", "cw4", - "nym-contracts-common 0.5.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", - "nym-multisig-contract-common 0.1.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", + "nym-contracts-common 0.5.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", + "nym-multisig-contract-common 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", +] + +[[package]] +name = "nym-common-models" +version = "0.1.0" +dependencies = [ + "serde", ] [[package]] @@ -5050,7 +5069,7 @@ dependencies = [ [[package]] name = "nym-compact-ecash" version = "0.1.0" -source = "git+https://github.com/nymtech/nym?rev=f86e08866#f86e0886631a98b0638fe09e6fcbe5458d47adc1" +source = "git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork#8d68cf88dac1adf1f68726a9b6e0740f8cddcf32" dependencies = [ "bincode", "bls12_381", @@ -5060,8 +5079,8 @@ dependencies = [ "ff", "group", "itertools 0.12.1", - "nym-network-defaults 0.1.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", - "nym-pemstore 0.3.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", + "nym-network-defaults 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", + "nym-pemstore 0.3.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", "rand", "serde", "sha2 0.9.9", @@ -5085,12 +5104,12 @@ dependencies = [ [[package]] name = "nym-config" version = "0.1.0" -source = "git+https://github.com/nymtech/nym?rev=f86e08866#f86e0886631a98b0638fe09e6fcbe5458d47adc1" +source = "git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork#8d68cf88dac1adf1f68726a9b6e0740f8cddcf32" dependencies = [ "dirs", "handlebars", "log", - "nym-network-defaults 0.1.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", + "nym-network-defaults 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", "serde", "toml 0.8.14", "url", @@ -5114,7 +5133,7 @@ dependencies = [ [[package]] name = "nym-contracts-common" version = "0.5.0" -source = "git+https://github.com/nymtech/nym?rev=f86e08866#f86e0886631a98b0638fe09e6fcbe5458d47adc1" +source = "git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork#8d68cf88dac1adf1f68726a9b6e0740f8cddcf32" dependencies = [ "bs58", "cosmwasm-schema", @@ -5249,12 +5268,12 @@ dependencies = [ [[package]] name = "nym-credentials-interface" version = "0.1.0" -source = "git+https://github.com/nymtech/nym?rev=f86e08866#f86e0886631a98b0638fe09e6fcbe5458d47adc1" +source = "git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork#8d68cf88dac1adf1f68726a9b6e0740f8cddcf32" dependencies = [ "bls12_381", - "nym-compact-ecash 0.1.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", - "nym-ecash-time 0.1.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", - "nym-network-defaults 0.1.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", + "nym-compact-ecash 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", + "nym-ecash-time 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", + "nym-network-defaults 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", "rand", "serde", "strum 0.26.3", @@ -5293,12 +5312,12 @@ dependencies = [ [[package]] name = "nym-crypto" version = "0.4.0" -source = "git+https://github.com/nymtech/nym?rev=f86e08866#f86e0886631a98b0638fe09e6fcbe5458d47adc1" +source = "git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork#8d68cf88dac1adf1f68726a9b6e0740f8cddcf32" dependencies = [ "bs58", "ed25519-dalek", - "nym-pemstore 0.3.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", - "nym-sphinx-types 0.2.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", + "nym-pemstore 0.3.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", + "nym-sphinx-types 0.2.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", "serde", "serde_bytes", "subtle-encoding", @@ -5372,14 +5391,14 @@ dependencies = [ [[package]] name = "nym-ecash-contract-common" version = "0.1.0" -source = "git+https://github.com/nymtech/nym?rev=f86e08866#f86e0886631a98b0638fe09e6fcbe5458d47adc1" +source = "git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork#8d68cf88dac1adf1f68726a9b6e0740f8cddcf32" dependencies = [ "bs58", "cosmwasm-schema", "cosmwasm-std", "cw-controllers", "cw-utils", - "nym-multisig-contract-common 0.1.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", + "nym-multisig-contract-common 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", "thiserror", ] @@ -5403,7 +5422,7 @@ dependencies = [ [[package]] name = "nym-ecash-time" version = "0.1.0" -source = "git+https://github.com/nymtech/nym?rev=f86e08866#f86e0886631a98b0638fe09e6fcbe5458d47adc1" +source = "git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork#8d68cf88dac1adf1f68726a9b6e0740f8cddcf32" dependencies = [ "time", ] @@ -5431,7 +5450,7 @@ dependencies = [ [[package]] name = "nym-exit-policy" version = "0.1.0" -source = "git+https://github.com/nymtech/nym?rev=f86e08866#f86e0886631a98b0638fe09e6fcbe5458d47adc1" +source = "git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork#8d68cf88dac1adf1f68726a9b6e0740f8cddcf32" dependencies = [ "serde", "serde_json", @@ -5663,7 +5682,7 @@ dependencies = [ [[package]] name = "nym-group-contract-common" version = "0.1.0" -source = "git+https://github.com/nymtech/nym?rev=f86e08866#f86e0886631a98b0638fe09e6fcbe5458d47adc1" +source = "git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork#8d68cf88dac1adf1f68726a9b6e0740f8cddcf32" dependencies = [ "cosmwasm-schema", "cw-controllers", @@ -5691,11 +5710,11 @@ dependencies = [ [[package]] name = "nym-http-api-client" version = "0.1.0" -source = "git+https://github.com/nymtech/nym?rev=f86e08866#f86e0886631a98b0638fe09e6fcbe5458d47adc1" +source = "git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork#8d68cf88dac1adf1f68726a9b6e0740f8cddcf32" dependencies = [ "async-trait", "http 1.1.0", - "nym-bin-common 0.6.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", + "nym-bin-common 0.6.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", "reqwest 0.12.4", "serde", "serde_json", @@ -5874,7 +5893,7 @@ dependencies = [ [[package]] name = "nym-mixnet-contract-common" version = "0.6.0" -source = "git+https://github.com/nymtech/nym?rev=f86e08866#f86e0886631a98b0638fe09e6fcbe5458d47adc1" +source = "git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork#8d68cf88dac1adf1f68726a9b6e0740f8cddcf32" dependencies = [ "bs58", "cosmwasm-schema", @@ -5882,7 +5901,7 @@ dependencies = [ "cw-controllers", "humantime-serde", "log", - "nym-contracts-common 0.5.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", + "nym-contracts-common 0.5.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", "schemars", "serde", "serde-json-wasm", @@ -5984,7 +6003,7 @@ dependencies = [ [[package]] name = "nym-multisig-contract-common" version = "0.1.0" -source = "git+https://github.com/nymtech/nym?rev=f86e08866#f86e0886631a98b0638fe09e6fcbe5458d47adc1" +source = "git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork#8d68cf88dac1adf1f68726a9b6e0740f8cddcf32" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -6012,7 +6031,7 @@ dependencies = [ [[package]] name = "nym-network-defaults" version = "0.1.0" -source = "git+https://github.com/nymtech/nym?rev=f86e08866#f86e0886631a98b0638fe09e6fcbe5458d47adc1" +source = "git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork#8d68cf88dac1adf1f68726a9b6e0740f8cddcf32" dependencies = [ "dotenvy", "log", @@ -6207,18 +6226,18 @@ dependencies = [ [[package]] name = "nym-node-requests" version = "0.1.0" -source = "git+https://github.com/nymtech/nym?rev=f86e08866#f86e0886631a98b0638fe09e6fcbe5458d47adc1" +source = "git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork#8d68cf88dac1adf1f68726a9b6e0740f8cddcf32" dependencies = [ "async-trait", "base64 0.22.1", "celes", "humantime 2.1.0", "humantime-serde", - "nym-bin-common 0.6.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", - "nym-crypto 0.4.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", - "nym-exit-policy 0.1.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", - "nym-http-api-client 0.1.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", - "nym-wireguard-types 0.1.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", + "nym-bin-common 0.6.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", + "nym-crypto 0.4.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", + "nym-exit-policy 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", + "nym-http-api-client 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", + "nym-wireguard-types 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", "schemars", "serde", "serde_json", @@ -6227,6 +6246,22 @@ dependencies = [ "utoipa", ] +[[package]] +name = "nym-node-status-agent" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap 4.5.20", + "nym-bin-common 0.6.0", + "nym-common-models", + "reqwest 0.12.4", + "serde_json", + "tokio", + "tokio-util", + "tracing", + "tracing-subscriber", +] + [[package]] name = "nym-node-status-api" version = "0.1.0" @@ -6240,16 +6275,20 @@ dependencies = [ "futures-util", "moka", "nym-bin-common 0.6.0", + "nym-common-models", "nym-explorer-client", - "nym-network-defaults 0.1.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", - "nym-node-requests 0.1.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", + "nym-network-defaults 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", + "nym-node-requests 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", "nym-task", - "nym-validator-client 0.1.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", + "nym-validator-client 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", + "regex", "reqwest 0.12.4", "serde", "serde_json", "serde_json_path", "sqlx", + "strum 0.26.3", + "strum_macros 0.26.4", "thiserror", "tokio", "tokio-util", @@ -6365,7 +6404,7 @@ dependencies = [ [[package]] name = "nym-pemstore" version = "0.3.0" -source = "git+https://github.com/nymtech/nym?rev=f86e08866#f86e0886631a98b0638fe09e6fcbe5458d47adc1" +source = "git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork#8d68cf88dac1adf1f68726a9b6e0740f8cddcf32" dependencies = [ "pem", ] @@ -6438,7 +6477,7 @@ dependencies = [ [[package]] name = "nym-serde-helpers" version = "0.1.0" -source = "git+https://github.com/nymtech/nym?rev=f86e08866#f86e0886631a98b0638fe09e6fcbe5458d47adc1" +source = "git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork#8d68cf88dac1adf1f68726a9b6e0740f8cddcf32" dependencies = [ "base64 0.22.1", "bs58", @@ -6753,7 +6792,7 @@ dependencies = [ [[package]] name = "nym-sphinx-types" version = "0.2.0" -source = "git+https://github.com/nymtech/nym?rev=f86e08866#f86e0886631a98b0638fe09e6fcbe5458d47adc1" +source = "git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork#8d68cf88dac1adf1f68726a9b6e0740f8cddcf32" dependencies = [ "sphinx-packet", "thiserror", @@ -6917,7 +6956,7 @@ dependencies = [ [[package]] name = "nym-validator-client" version = "0.1.0" -source = "git+https://github.com/nymtech/nym?rev=f86e08866#f86e0886631a98b0638fe09e6fcbe5458d47adc1" +source = "git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork#8d68cf88dac1adf1f68726a9b6e0740f8cddcf32" dependencies = [ "async-trait", "base64 0.22.1", @@ -6935,20 +6974,19 @@ dependencies = [ "flate2", "futures", "itertools 0.13.0", - "log", - "nym-api-requests 0.1.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", - "nym-coconut-bandwidth-contract-common 0.1.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", - "nym-coconut-dkg-common 0.1.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", - "nym-compact-ecash 0.1.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", - "nym-config 0.1.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", - "nym-contracts-common 0.5.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", - "nym-ecash-contract-common 0.1.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", - "nym-group-contract-common 0.1.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", - "nym-http-api-client 0.1.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", - "nym-mixnet-contract-common 0.6.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", - "nym-multisig-contract-common 0.1.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", - "nym-network-defaults 0.1.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", - "nym-vesting-contract-common 0.7.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", + "nym-api-requests 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", + "nym-coconut-bandwidth-contract-common 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", + "nym-coconut-dkg-common 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", + "nym-compact-ecash 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", + "nym-config 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", + "nym-contracts-common 0.5.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", + "nym-ecash-contract-common 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", + "nym-group-contract-common 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", + "nym-http-api-client 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", + "nym-mixnet-contract-common 0.6.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", + "nym-multisig-contract-common 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", + "nym-network-defaults 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", + "nym-vesting-contract-common 0.7.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", "prost 0.12.6", "reqwest 0.12.4", "serde", @@ -6958,6 +6996,7 @@ dependencies = [ "thiserror", "time", "tokio", + "tracing", "url", "wasmtimer", "zeroize", @@ -7017,12 +7056,12 @@ dependencies = [ [[package]] name = "nym-vesting-contract-common" version = "0.7.0" -source = "git+https://github.com/nymtech/nym?rev=f86e08866#f86e0886631a98b0638fe09e6fcbe5458d47adc1" +source = "git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork#8d68cf88dac1adf1f68726a9b6e0740f8cddcf32" dependencies = [ "cosmwasm-schema", "cosmwasm-std", - "nym-contracts-common 0.5.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", - "nym-mixnet-contract-common 0.6.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", + "nym-contracts-common 0.5.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", + "nym-mixnet-contract-common 0.6.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", "serde", "thiserror", ] @@ -7089,12 +7128,12 @@ dependencies = [ [[package]] name = "nym-wireguard-types" version = "0.1.0" -source = "git+https://github.com/nymtech/nym?rev=f86e08866#f86e0886631a98b0638fe09e6fcbe5458d47adc1" +source = "git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork#8d68cf88dac1adf1f68726a9b6e0740f8cddcf32" dependencies = [ "base64 0.22.1", "log", - "nym-config 0.1.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", - "nym-network-defaults 0.1.0 (git+https://github.com/nymtech/nym?rev=f86e08866)", + "nym-config 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", + "nym-network-defaults 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", "serde", "thiserror", "x25519-dalek", @@ -8001,9 +8040,9 @@ dependencies = [ [[package]] name = "raw-cpuid" -version = "11.1.0" +version = "11.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb9ee317cfe3fbd54b36a511efc1edd42e216903c9cd575e686dd68a2ba90d8d" +checksum = "1ab240315c661615f2ee9f0f2cd32d5a7343a84d5ebcccb99d46e6637565e7b0" dependencies = [ "bitflags 2.5.0", ] diff --git a/Cargo.toml b/Cargo.toml index d431d4567b..d99e3d7532 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,6 +61,7 @@ members = [ "common/ip-packet-requests", "common/ledger", "common/mixnode-common", + "common/models", "common/network-defaults", "common/node-tester-utils", "common/nonexhaustive-delayqueue", @@ -121,6 +122,7 @@ members = [ "nym-node/nym-node-http-api", "nym-node/nym-node-requests", "nym-node-status-api", + "nym-node-status-agent", "nym-outfox", "nym-validator-rewarder", "tools/echo-server", @@ -148,12 +150,14 @@ members = [ default-members = [ "clients/native", "clients/socks5", + "common/models", "explorer-api", "gateway", "mixnode", "nym-api", "nym-data-observatory", "nym-node", + "nym-node-status-api", "nym-validator-rewarder", "nym-node-status-api", "service-providers/authenticator", @@ -314,6 +318,7 @@ si-scale = "0.2.3" sphinx-packet = "0.1.1" sqlx = "0.7.4" strum = "0.26" +strum_macros = "0.26" subtle-encoding = "0.5" syn = "1" sysinfo = "0.30.13" diff --git a/common/bin-common/Cargo.toml b/common/bin-common/Cargo.toml index b63631ddaa..11a2c76f2b 100644 --- a/common/bin-common/Cargo.toml +++ b/common/bin-common/Cargo.toml @@ -45,3 +45,4 @@ tracing = [ "opentelemetry", ] clap = [ "dep:clap", "dep:clap_complete", "dep:clap_complete_fig" ] +models = [] diff --git a/common/http-api-client/src/lib.rs b/common/http-api-client/src/lib.rs index c90d731adb..f35c662e88 100644 --- a/common/http-api-client/src/lib.rs +++ b/common/http-api-client/src/lib.rs @@ -531,6 +531,18 @@ where } if res.status().is_success() { + #[cfg(debug_assertions)] + { + let text = res.text().await.inspect_err(|err| { + tracing::error!("Couldn't even get response text: {err}"); + })?; + tracing::trace!("Result:\n{:#?}", text); + + serde_json::from_str(&text) + .map_err(|err| HttpClientError::GenericRequestFailure(err.to_string())) + } + + #[cfg(not(debug_assertions))] Ok(res.json().await?) } else if res.status() == StatusCode::NOT_FOUND { Err(HttpClientError::NotFound) diff --git a/common/models/Cargo.toml b/common/models/Cargo.toml new file mode 100644 index 0000000000..acb6e35682 --- /dev/null +++ b/common/models/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "nym-common-models" +version = "0.1.0" +authors.workspace = true +repository.workspace = true +homepage.workspace = true +documentation.workspace = true +edition.workspace = true +license.workspace = true +rust-version.workspace = true +readme.workspace = true + +[dependencies] +serde = { workspace = true, features = ["derive"] } diff --git a/common/models/src/lib.rs b/common/models/src/lib.rs new file mode 100644 index 0000000000..3d85e66947 --- /dev/null +++ b/common/models/src/lib.rs @@ -0,0 +1 @@ +pub mod ns_api; diff --git a/common/models/src/ns_api.rs b/common/models/src/ns_api.rs new file mode 100644 index 0000000000..9c3373802a --- /dev/null +++ b/common/models/src/ns_api.rs @@ -0,0 +1,8 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct TestrunAssignment { + /// has nothing to do with GW identity key. This is PK from `gateways` table + pub testrun_id: i64, + pub gateway_pk_id: i64, +} diff --git a/envs/canary.env b/envs/canary.env index c4aac15082..db70a19fd0 100644 --- a/envs/canary.env +++ b/envs/canary.env @@ -19,5 +19,5 @@ MULTISIG_CONTRACT_ADDRESS=n1zwv6feuzhy6a9wekh96cd57lsarmqlwxdypdsplw6zhfncqw6ftq COCONUT_DKG_CONTRACT_ADDRESS=n1aakfpghcanxtc45gpqlx8j3rq0zcpyf49qmhm9mdjrfx036h4z5sy2vfh9 EXPLORER_API=https://canary-explorer.performance.nymte.ch/api -NYXD="https://canary-validator.performance.nymte.ch" -NYM_API="https://canary-api.performance.nymte.ch/api" +NYXD=https://canary-validator.performance.nymte.ch +NYM_API=https://canary-api.performance.nymte.ch/api diff --git a/envs/qa.env b/envs/qa.env index e88f845416..34306a62ea 100644 --- a/envs/qa.env +++ b/envs/qa.env @@ -19,5 +19,5 @@ VESTING_CONTRACT_ADDRESS=n1jlzdxnyces4hrhqz68dqk28mrw5jgwtcfq0c2funcwrmw0dx9l9s8 REWARDING_VALIDATOR_ADDRESS=n1rfvpsynktze6wvn6ldskj8xgwfzzk5v6pnff39 EXPLORER_API=https://qa-network-explorer.qa.nymte.ch/api -NYXD="https://qa-validator.qa.nymte.ch" -NYM_API="https://qa-nym-api.qa.nymte.ch/api" +NYXD=https://qa-validator.qa.nymte.ch +NYM_API=https://qa-nym-api.qa.nymte.ch/api diff --git a/envs/sandbox.env b/envs/sandbox.env index 4763269a6f..6310b8fa89 100644 --- a/envs/sandbox.env +++ b/envs/sandbox.env @@ -20,6 +20,6 @@ ECASH_CONTRACT_ADDRESS=n1v3vydvs2ued84yv3khqwtgldmgwn0elljsdh08dr5s2j9x4rc5fs9jl STATISTICS_SERVICE_DOMAIN_ADDRESS="http://0.0.0.0" EXPLORER_API=https://sandbox-explorer.nymtech.net/api -NYXD="https://rpc.sandbox.nymtech.net" -NYXD_WS="wss://rpc.sandbox.nymtech.net/websocket" -NYM_API="https://sandbox-nym-api1.nymtech.net/api" +NYXD=https://rpc.sandbox.nymtech.net +NYXD_WS=wss://rpc.sandbox.nymtech.net/websocket +NYM_API=https://sandbox-nym-api1.nymtech.net/api diff --git a/explorer-api/explorer-client/src/lib.rs b/explorer-api/explorer-client/src/lib.rs index 2415c90c69..50c5431c25 100644 --- a/explorer-api/explorer-client/src/lib.rs +++ b/explorer-api/explorer-client/src/lib.rs @@ -83,7 +83,9 @@ impl ExplorerClient { } else if response.status() == StatusCode::NOT_FOUND { Err(ExplorerApiError::NotFound) } else { - Err(ExplorerApiError::RequestFailure(response.text().await?)) + let status = response.status(); + let err_msg = format!("{}: {}", response.text().await?, status); + Err(ExplorerApiError::RequestFailure(err_msg)) } } diff --git a/nym-data-observatory/README_SQLX.md b/nym-data-observatory/README_SQLX.md index 3e1021c8eb..72cca4294f 100644 --- a/nym-data-observatory/README_SQLX.md +++ b/nym-data-observatory/README_SQLX.md @@ -68,6 +68,7 @@ warning: no queries found; do you have the `offline` feature enabled ### Possible solutions - does your `sqlx-cli` version match `sqlx` version from `Cargo.toml`? + + `cargo install -f sqlx-cli --version ` ``` cargo install sqlx-cli --version --force ``` diff --git a/nym-node-status-agent/.gitignore b/nym-node-status-agent/.gitignore new file mode 100644 index 0000000000..bf462f3e05 --- /dev/null +++ b/nym-node-status-agent/.gitignore @@ -0,0 +1 @@ +nym-gateway-probe diff --git a/nym-node-status-agent/Cargo.toml b/nym-node-status-agent/Cargo.toml new file mode 100644 index 0000000000..a2ce8cdf97 --- /dev/null +++ b/nym-node-status-agent/Cargo.toml @@ -0,0 +1,27 @@ +# Copyright 2024 - Nym Technologies SA +# SPDX-License-Identifier: Apache-2.0 + + +[package] +name = "nym-node-status-agent" +version = "0.1.0" +authors.workspace = true +repository.workspace = true +homepage.workspace = true +documentation.workspace = true +edition.workspace = true +license.workspace = true +rust-version.workspace = true +readme.workspace = true + +[dependencies] +anyhow = { workspace = true} +clap = { workspace = true, features = ["derive", "env"] } +nym-bin-common = { path = "../common/bin-common", features = ["models"]} +nym-common-models = { path = "../common/models" } +tokio = { workspace = true, features = ["macros", "rt-multi-thread", "process"] } +tokio-util = { workspace = true } +tracing = { workspace = true } +tracing-subscriber = { workspace = true, features = ["env-filter"] } +reqwest = { workspace = true, features = ["json"] } +serde_json = { workspace = true } diff --git a/nym-node-status-agent/run.sh b/nym-node-status-agent/run.sh new file mode 100755 index 0000000000..5054a90825 --- /dev/null +++ b/nym-node-status-agent/run.sh @@ -0,0 +1,49 @@ +#!/bin/bash + +set -eu + +export RUST_LOG=${RUST_LOG:-debug} + +crate_root=$(dirname $(realpath "$0")) +gateway_probe_src=$(dirname $(dirname "$crate_root"))/nym-vpn-client/nym-vpn-core +echo "gateway_probe_src=$gateway_probe_src" +echo "crate_root=$crate_root" + +export NODE_STATUS_AGENT_PROBE_PATH="$crate_root/nym-gateway-probe" + +# build & copy over GW probe +function copy_gw_probe() { + pushd $gateway_probe_src + cargo build --release --package nym-gateway-probe + cp target/release/nym-gateway-probe "$crate_root" + $crate_root/nym-gateway-probe --version + popd +} + +function build_agent() { + cargo build --package nym-node-status-agent --release +} + +function swarm() { + local workers=$1 + echo "Running $workers in parallel" + + build_agent + + for ((i=1; i<=$workers; i++)); do + ../target/release/nym-node-status-agent run-probe & + done + + wait + + echo "All agents completed" +} + +export NODE_STATUS_AGENT_SERVER_ADDRESS="http://127.0.0.1" +export NODE_STATUS_AGENT_SERVER_PORT="8000" + +copy_gw_probe + +swarm 30 + +# cargo run -- run-probe diff --git a/nym-node-status-agent/src/cli.rs b/nym-node-status-agent/src/cli.rs new file mode 100644 index 0000000000..c4465797d0 --- /dev/null +++ b/nym-node-status-agent/src/cli.rs @@ -0,0 +1,109 @@ +use clap::{Parser, Subcommand}; +use nym_bin_common::bin_info; +use nym_common_models::ns_api::TestrunAssignment; +use std::sync::OnceLock; +use tracing::instrument; + +use crate::probe::GwProbe; + +// Helper for passing LONG_VERSION to clap +fn pretty_build_info_static() -> &'static str { + static PRETTY_BUILD_INFORMATION: OnceLock = OnceLock::new(); + PRETTY_BUILD_INFORMATION.get_or_init(|| bin_info!().pretty_print()) +} + +#[derive(Parser, Debug)] +#[clap(author = "Nymtech", version, long_version = pretty_build_info_static(), about)] +pub(crate) struct Args { + #[command(subcommand)] + pub(crate) command: Command, + #[arg(short, long, env = "NODE_STATUS_AGENT_SERVER_ADDRESS")] + pub(crate) server_address: String, + + #[arg(short = 'p', long, env = "NODE_STATUS_AGENT_SERVER_PORT")] + pub(crate) server_port: u16, + // TODO dz accept keypair for identification / auth +} + +#[derive(Subcommand, Debug)] +pub(crate) enum Command { + RunProbe { + /// path of binary to run + #[arg(long, env = "NODE_STATUS_AGENT_PROBE_PATH")] + probe_path: String, + #[arg(short, long, env = "NODE_STATUS_AGENT_GATEWAY_ID")] + gateway_id: Option, + }, +} + +impl Args { + pub(crate) async fn execute(&self) -> anyhow::Result<()> { + match &self.command { + Command::RunProbe { + probe_path, + gateway_id, + } => self.run_probe(probe_path, gateway_id).await?, + } + + Ok(()) + } + + async fn run_probe(&self, probe_path: &str, gateway_id: &Option) -> anyhow::Result<()> { + let server_address = format!("{}:{}", &self.server_address, self.server_port); + + let probe = GwProbe::new(probe_path.to_string()); + + let version = probe.version().await; + tracing::info!("Probe version:\n{}", version); + + let testrun = request_testrun(&server_address).await?; + + let log = probe.run_and_get_log(gateway_id); + + submit_results(&server_address, testrun.testrun_id, log).await?; + + Ok(()) + } +} + +const URL_BASE: &str = "internal/testruns"; + +#[instrument(level = "debug", skip_all)] +async fn request_testrun(server_addr: &str) -> anyhow::Result { + let target_url = format!("{}/{}", server_addr, URL_BASE); + let client = reqwest::Client::new(); + let res = client + .get(target_url) + .send() + .await + .and_then(|response| response.error_for_status())?; + res.json() + .await + .map(|testrun| { + tracing::info!("Received testrun assignment: {:?}", testrun); + testrun + }) + .map_err(|err| { + tracing::error!("err"); + err.into() + }) +} + +#[instrument(level = "debug", skip(probe_outcome))] +async fn submit_results( + server_addr: &str, + testrun_id: i64, + probe_outcome: String, +) -> anyhow::Result<()> { + let target_url = format!("{}/{}/{}", server_addr, URL_BASE, testrun_id); + let client = reqwest::Client::new(); + let res = client + .post(target_url) + .body(probe_outcome) + .send() + .await + .and_then(|response| response.error_for_status())?; + + tracing::debug!("Submitted results: {})", res.status()); + Ok(()) +} diff --git a/nym-node-status-agent/src/main.rs b/nym-node-status-agent/src/main.rs new file mode 100644 index 0000000000..133828b7fa --- /dev/null +++ b/nym-node-status-agent/src/main.rs @@ -0,0 +1,78 @@ +use crate::cli::Args; +use clap::Parser; +use tracing::level_filters::LevelFilter; +use tracing_subscriber::{filter::Directive, EnvFilter}; + +mod cli; +mod probe; + +#[tokio::main] +async fn main() -> anyhow::Result<()> { + setup_tracing(); + let args = Args::parse(); + + let server_addr = format!("{}:{}", args.server_address, args.server_port); + test_ns_api_conn(&server_addr).await?; + + args.execute().await?; + + Ok(()) +} + +async fn test_ns_api_conn(server_addr: &str) -> anyhow::Result<()> { + reqwest::get(server_addr) + .await + .map(|res| { + tracing::info!( + "Testing connection to NS API at {server_addr}: {}", + res.status() + ); + }) + .map_err(|err| anyhow::anyhow!("Couldn't connect to server on {}: {}", server_addr, err)) +} + +pub(crate) fn setup_tracing() { + fn directive_checked(directive: impl Into) -> Directive { + directive + .into() + .parse() + .expect("Failed to parse log directive") + } + + let log_builder = tracing_subscriber::fmt() + // Use a more compact, abbreviated log format + .compact() + // Display source code file paths + .with_file(true) + // Display source code line numbers + .with_line_number(true) + .with_thread_ids(true) + // Don't display the event's target (module path) + .with_target(false); + + let mut filter = EnvFilter::builder() + // if RUST_LOG isn't set, set default level + .with_default_directive(LevelFilter::INFO.into()) + .from_env_lossy(); + // these crates are more granularly filtered + let filter_crates = [ + "reqwest", + "rustls", + "hyper", + "sqlx", + "h2", + "tendermint_rpc", + "tower_http", + "axum", + ]; + for crate_name in filter_crates { + filter = filter.add_directive(directive_checked(format!("{}=warn", crate_name))); + } + + filter = filter.add_directive(directive_checked("nym_bin_common=debug")); + filter = filter.add_directive(directive_checked("nym_explorer_client=debug")); + filter = filter.add_directive(directive_checked("nym_network_defaults=debug")); + filter = filter.add_directive(directive_checked("nym_validator_client=debug")); + + log_builder.with_env_filter(filter).init(); +} diff --git a/nym-node-status-agent/src/probe.rs b/nym-node-status-agent/src/probe.rs new file mode 100644 index 0000000000..c75900e936 --- /dev/null +++ b/nym-node-status-agent/src/probe.rs @@ -0,0 +1,54 @@ +use tracing::error; + +pub(crate) struct GwProbe { + path: String, +} + +impl GwProbe { + pub(crate) fn new(probe_path: String) -> Self { + Self { path: probe_path } + } + + pub(crate) async fn version(&self) -> String { + let mut command = tokio::process::Command::new(&self.path); + command.stdout(std::process::Stdio::piped()); + command.arg("--version"); + + match command.spawn() { + Ok(child) => { + if let Ok(output) = child.wait_with_output().await { + return String::from_utf8(output.stdout) + .unwrap_or("Unable to get log from test run".to_string()); + } + "Unable to get probe version".to_string() + } + Err(e) => { + error!("Failed to get probe version: {}", e); + "Failed to get probe version".to_string() + } + } + } + + pub(crate) fn run_and_get_log(&self, gateway_key: &Option) -> String { + let mut command = std::process::Command::new(&self.path); + command.stdout(std::process::Stdio::piped()); + + if let Some(gateway_id) = gateway_key { + command.arg("--gateway").arg(gateway_id); + } + + match command.spawn() { + Ok(child) => { + if let Ok(output) = child.wait_with_output() { + return String::from_utf8(output.stdout) + .unwrap_or("Unable to get log from test run".to_string()); + } + "Unable to get log from test run".to_string() + } + Err(e) => { + error!("Failed to spawn test: {}", e); + "Failed to spawn test run task".to_string() + } + } + } +} diff --git a/nym-node-status-api/.gitignore b/nym-node-status-api/.gitignore index b2a9b208f0..91459afabe 100644 --- a/nym-node-status-api/.gitignore +++ b/nym-node-status-api/.gitignore @@ -1,2 +1,6 @@ data/ enter_db.sh +nym-gateway-probe +nym-node-status-api +*.sqlite +*.sqlite-journal diff --git a/nym-node-status-api/Cargo.toml b/nym-node-status-api/Cargo.toml index 056271e41d..4cc9ff5d28 100644 --- a/nym-node-status-api/Cargo.toml +++ b/nym-node-status-api/Cargo.toml @@ -14,27 +14,31 @@ rust-version.workspace = true [dependencies] anyhow = { workspace = true } -axum = { workspace = true, features = ["tokio"] } +axum = { workspace = true, features = ["tokio", "macros"] } chrono = { workspace = true } clap = { workspace = true, features = ["cargo", "derive", "env", "string"] } cosmwasm-std = { workspace = true } envy = { workspace = true } futures-util = { workspace = true } moka = { workspace = true, features = ["future"] } -nym-bin-common = { path = "../common/bin-common" } +nym-bin-common = { path = "../common/bin-common", features = ["models"]} +nym-common-models = { path = "../common/models" } nym-explorer-client = { path = "../explorer-api/explorer-client" } -# TODO dz: ref before Nym API client changes. Update to latest develop once new Nym API is live -nym-network-defaults = { git = "https://github.com/nymtech/nym", rev = "f86e08866" } -nym-validator-client = { git = "https://github.com/nymtech/nym", rev = "f86e08866" } +# TODO dz: before Nym API client breaking changes. Update to latest develop once new Nym API is live +nym-network-defaults = { git = "https://github.com/nymtech/nym", branch = "pre-dir-v2-fork" } +nym-validator-client = { git = "https://github.com/nymtech/nym", branch = "pre-dir-v2-fork" } # nym-network-defaults = { path = "../common/network-defaults" } # nym-validator-client = { path = "../common/client-libs/validator-client" } nym-task = { path = "../common/task" } -nym-node-requests = { git = "https://github.com/nymtech/nym", rev = "f86e08866" } +nym-node-requests = { git = "https://github.com/nymtech/nym", branch = "pre-dir-v2-fork" } # nym-node-requests = { path = "../nym-node/nym-node-requests", features = ["openapi"] } +regex = { workspace = true } reqwest = { workspace = true } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } serde_json_path = { workspace = true } +strum = { workspace = true } +strum_macros = { workspace = true } sqlx = { workspace = true, features = ["runtime-tokio-rustls", "sqlite"] } thiserror = { workspace = true } tokio = { workspace = true, features = ["rt-multi-thread"] } diff --git a/nym-node-status-api/Dockerfile.dev b/nym-node-status-api/Dockerfile.dev new file mode 100644 index 0000000000..2967a6e605 --- /dev/null +++ b/nym-node-status-api/Dockerfile.dev @@ -0,0 +1,8 @@ +FROM ubuntu:22.04 + +RUN apt-get update && apt-get install -y ca-certificates + +WORKDIR /nym + +COPY nym-node-status-api/nym-node-status-api ./ +ENTRYPOINT [ "/nym/nym-node-status-api" ] diff --git a/nym-node-status-api/build.rs b/nym-node-status-api/build.rs index 394083f8be..7bc42cb03f 100644 --- a/nym-node-status-api/build.rs +++ b/nym-node-status-api/build.rs @@ -1,5 +1,7 @@ use anyhow::{anyhow, Result}; use sqlx::{Connection, SqliteConnection}; +use std::fs::Permissions; +use std::os::unix::fs::PermissionsExt; use tokio::{fs::File, io::AsyncWriteExt}; const SQLITE_DB_FILENAME: &str = "nym-node-status-api.sqlite"; @@ -41,6 +43,9 @@ async fn write_db_path_to_file(out_dir: &str, db_filename: &str) -> anyhow::Resu let mut file = File::create("enter_db.sh").await?; let _ = file.write(b"#!/bin/bash\n").await?; file.write_all(format!("sqlite3 {}/{}", out_dir, db_filename).as_bytes()) + .await?; + + file.set_permissions(Permissions::from_mode(0o755)) .await .map_err(From::from) } diff --git a/nym-node-status-api/launch_node_status_api.sh b/nym-node-status-api/launch_node_status_api.sh index 1db6bcc1b2..f9ebd364a9 100755 --- a/nym-node-status-api/launch_node_status_api.sh +++ b/nym-node-status-api/launch_node_status_api.sh @@ -6,13 +6,34 @@ export RUST_LOG=${RUST_LOG:-debug} export NYM_API_CLIENT_TIMEOUT=60 export EXPLORER_CLIENT_TIMEOUT=60 -#export NYXD=https://rpc.nymtech.net -#export NYM_API=https://validator.nymtech.net/api/ -#export EXPLORER_API=https://explorer.nymtech.net/api/ -#export NETWORK_NAME=mainnet -#cargo run --package nym-node-status-api --release -- --connection-url "sqlite://node-status-api.sqlite?mode=rwc" +export ENVIRONMENT="mainnet.env" -cd .. -docker build -t node-status-api -f nym-node-status-api/Dockerfile . -docker run --env-file envs/mainnet.env -e NYM_NODE_STATUS_API_CONNECTION_URL="sqlite://node-status-api.sqlite?mode=rwc" node-status-api +function run_bare() { + # export necessary env vars + set -a + source ../envs/$ENVIRONMENT + set +a + export RUST_LOG=debug + + # --conection-url is provided in build.rs + cargo run --package nym-node-status-api +} + +function run_docker() { + cargo build --package nym-node-status-api --release + cp ../target/release/nym-node-status-api . + + cd .. + docker build -t node-status-api -f nym-node-status-api/Dockerfile.dev . + docker run --env-file envs/${ENVIRONMENT} \ + -e EXPLORER_CLIENT_TIMEOUT=$EXPLORER_CLIENT_TIMEOUT \ + -e NYM_API_CLIENT_TIMEOUT=$NYM_API_CLIENT_TIMEOUT \ + -e DATABASE_URL="sqlite://node-status-api.sqlite?mode=rwc" \ + -e RUST_LOG=${RUST_LOG} node-status-api + +} + +run_bare + +# run_docker diff --git a/nym-node-status-api/migrations/000_init.sql b/nym-node-status-api/migrations/000_init.sql index 35aaa40654..1e5683e2c9 100644 --- a/nym-node-status-api/migrations/000_init.sql +++ b/nym-node-status-api/migrations/000_init.sql @@ -98,3 +98,15 @@ CREATE TABLE FOREIGN KEY (mix_id) REFERENCES mixnodes (mix_id), UNIQUE (mix_id, date_utc) -- This constraint automatically creates an index ); + + +CREATE TABLE testruns +( + id INTEGER PRIMARY KEY AUTOINCREMENT, + gateway_id INTEGER, + status INTEGER NOT NULL, -- 0=pending, 1=in-progress, 2=complete + timestamp_utc INTEGER NOT NULL, + ip_address VARCHAR NOT NULL, + log VARCHAR NOT NULL, + FOREIGN KEY (gateway_id) REFERENCES gateways (id) +); diff --git a/nym-node-status-api/src/cli/mod.rs b/nym-node-status-api/src/cli/mod.rs index ad214b6a23..84ee86577f 100644 --- a/nym-node-status-api/src/cli/mod.rs +++ b/nym-node-status-api/src/cli/mod.rs @@ -41,26 +41,34 @@ pub(crate) struct Cli { pub(crate) nyxd_addr: Url, /// Nym api client timeout. - #[clap( - long, - default_value = "15", - env = "NYM_NODE_STATUS_API_NYM_API_CLIENT_TIMEOUT" - )] + #[clap(long, default_value = "15", env = "NYM_API_CLIENT_TIMEOUT")] #[arg(value_parser = parse_duration)] pub(crate) nym_api_client_timeout: Duration, /// Explorer api client timeout. - #[clap( - long, - default_value = "15", - env = "NYM_NODE_STATUS_API_EXPLORER_CLIENT_TIMEOUT" - )] + #[clap(long, default_value = "15", env = "EXPLORER_CLIENT_TIMEOUT")] #[arg(value_parser = parse_duration)] pub(crate) explorer_client_timeout: Duration, /// Connection url for the database. - #[clap(long, env = "NYM_NODE_STATUS_API_CONNECTION_URL")] - pub(crate) connection_url: String, + #[clap(long, env = "DATABASE_URL")] + pub(crate) database_url: String, + + #[clap( + long, + default_value = "600", + env = "NODE_STATUS_API_MONITOR_REFRESH_INTERVAL" + )] + #[arg(value_parser = parse_duration)] + pub(crate) monitor_refresh_interval: Duration, + + #[clap( + long, + default_value = "600", + env = "NODE_STATUS_API_TESTRUN_REFRESH_INTERVAL" + )] + #[arg(value_parser = parse_duration)] + pub(crate) testruns_refresh_interval: Duration, } fn parse_duration(arg: &str) -> Result { diff --git a/nym-node-status-api/src/db/mod.rs b/nym-node-status-api/src/db/mod.rs index 8e840252f6..86e25e9227 100644 --- a/nym-node-status-api/src/db/mod.rs +++ b/nym-node-status-api/src/db/mod.rs @@ -1,7 +1,6 @@ -use std::str::FromStr; - use anyhow::{anyhow, Result}; use sqlx::{migrate::Migrator, sqlite::SqliteConnectOptions, ConnectOptions, SqlitePool}; +use std::str::FromStr; pub(crate) mod models; pub(crate) mod queries; @@ -30,7 +29,7 @@ impl Storage { } /// Cloning pool is cheap, it's the same underlying set of connections - pub async fn pool_owned(&self) -> DbPool { + pub fn pool_owned(&self) -> DbPool { self.pool.clone() } } diff --git a/nym-node-status-api/src/db/models.rs b/nym-node-status-api/src/db/models.rs index 54164b34e3..f704c1ed8a 100644 --- a/nym-node-status-api/src/db/models.rs +++ b/nym-node-status-api/src/db/models.rs @@ -4,6 +4,7 @@ use crate::{ }; use nym_node_requests::api::v1::node::models::NodeDescription; use serde::{Deserialize, Serialize}; +use strum_macros::{EnumString, FromRepr}; use utoipa::ToSchema; pub(crate) struct GatewayRecord { @@ -298,3 +299,36 @@ pub(crate) mod gateway { pub(crate) last_updated_utc: String, } } + +#[derive(Debug, Clone)] +pub struct TestRunDto { + pub id: i64, + pub gateway_id: i64, + pub status: i64, + pub timestamp_utc: i64, + pub ip_address: String, + pub log: String, +} + +#[derive(Debug, Clone, strum_macros::Display, EnumString, FromRepr, PartialEq)] +#[repr(u8)] +pub(crate) enum TestRunStatus { + Complete = 2, + InProgress = 1, + Pending = 0, +} + +#[derive(Debug, Clone)] +pub struct GatewayIdentityDto { + pub gateway_identity_key: String, + pub bonded: bool, +} + +#[allow(dead_code)] // it's not dead code but clippy doesn't detect usage in sqlx macros +#[derive(Debug, Clone)] +pub struct GatewayInfoDto { + pub id: i64, + pub gateway_identity_key: String, + pub self_described: Option, + pub explorer_pretty_bond: Option, +} diff --git a/nym-node-status-api/src/db/queries/mod.rs b/nym-node-status-api/src/db/queries/mod.rs index 279d31dc34..349e62e7c7 100644 --- a/nym-node-status-api/src/db/queries/mod.rs +++ b/nym-node-status-api/src/db/queries/mod.rs @@ -2,6 +2,7 @@ mod gateways; mod misc; mod mixnodes; mod summary; +pub(crate) mod testruns; pub(crate) use gateways::{ ensure_gateways_still_bonded, get_all_gateways, insert_gateways, diff --git a/nym-node-status-api/src/db/queries/testruns.rs b/nym-node-status-api/src/db/queries/testruns.rs new file mode 100644 index 0000000000..cf5b8a3bbf --- /dev/null +++ b/nym-node-status-api/src/db/queries/testruns.rs @@ -0,0 +1,126 @@ +use crate::http::models::TestrunAssignment; +use crate::{ + db::models::{TestRunDto, TestRunStatus}, + testruns::now_utc, +}; +use anyhow::Context; +use sqlx::{pool::PoolConnection, Sqlite}; + +pub(crate) async fn get_testrun_by_id( + conn: &mut PoolConnection, + testrun_id: i64, +) -> anyhow::Result { + sqlx::query_as!( + TestRunDto, + r#"SELECT + id as "id!", + gateway_id as "gateway_id!", + status as "status!", + timestamp_utc as "timestamp_utc!", + ip_address as "ip_address!", + log as "log!" + FROM testruns + WHERE id = ? + ORDER BY timestamp_utc"#, + testrun_id + ) + .fetch_one(conn.as_mut()) + .await + .context(format!("Couldn't retrieve testrun {testrun_id}")) +} + +pub(crate) async fn get_oldest_testrun_and_make_it_pending( + // TODO dz accept mut reference, repeat in all similar functions + conn: PoolConnection, +) -> anyhow::Result> { + let mut conn = conn; + let assignment = sqlx::query_as!( + TestrunAssignment, + r#"UPDATE testruns + SET status = ? + WHERE rowid = + ( + SELECT rowid + FROM testruns + WHERE status = ? + ORDER BY timestamp_utc asc + LIMIT 1 + ) + RETURNING + id as "testrun_id!", + gateway_id as "gateway_pk_id!" + "#, + TestRunStatus::InProgress as i64, + TestRunStatus::Pending as i64, + ) + .fetch_optional(&mut *conn) + .await?; + + Ok(assignment) +} + +pub(crate) async fn update_testrun_status( + conn: &mut PoolConnection, + testrun_id: i64, + status: TestRunStatus, +) -> anyhow::Result<()> { + let status = status as u32; + sqlx::query!( + "UPDATE testruns SET status = ? WHERE id = ?", + status, + testrun_id + ) + .execute(conn.as_mut()) + .await?; + + Ok(()) +} + +pub(crate) async fn update_gateway_last_probe_log( + conn: &mut PoolConnection, + gateway_pk: i64, + log: &str, +) -> anyhow::Result<()> { + sqlx::query!( + "UPDATE gateways SET last_probe_log = ? WHERE id = ?", + log, + gateway_pk + ) + .execute(conn.as_mut()) + .await + .map(drop) + .map_err(From::from) +} + +pub(crate) async fn update_gateway_last_probe_result( + conn: &mut PoolConnection, + gateway_pk: i64, + result: &str, +) -> anyhow::Result<()> { + sqlx::query!( + "UPDATE gateways SET last_probe_result = ? WHERE id = ?", + result, + gateway_pk + ) + .execute(conn.as_mut()) + .await + .map(drop) + .map_err(From::from) +} + +pub(crate) async fn update_gateway_score( + conn: &mut PoolConnection, + gateway_pk: i64, +) -> anyhow::Result<()> { + let now = now_utc().timestamp(); + sqlx::query!( + "UPDATE gateways SET last_testrun_utc = ?, last_updated_utc = ? WHERE id = ?", + now, + now, + gateway_pk + ) + .execute(conn.as_mut()) + .await + .map(drop) + .map_err(From::from) +} diff --git a/nym-node-status-api/src/http/api/gateways.rs b/nym-node-status-api/src/http/api/gateways.rs index 9dec3134e4..c1f4073767 100644 --- a/nym-node-status-api/src/http/api/gateways.rs +++ b/nym-node-status-api/src/http/api/gateways.rs @@ -16,7 +16,7 @@ pub(crate) fn routes() -> Router { Router::new() .route("/", axum::routing::get(gateways)) .route("/skinny", axum::routing::get(gateways_skinny)) - .route("/skinny/:identity_key", axum::routing::get(get_gateway)) + .route("/:identity_key", axum::routing::get(get_gateway)) } #[utoipa::path( diff --git a/nym-node-status-api/src/http/api/mod.rs b/nym-node-status-api/src/http/api/mod.rs index 4d385904b5..ed24fa80f5 100644 --- a/nym-node-status-api/src/http/api/mod.rs +++ b/nym-node-status-api/src/http/api/mod.rs @@ -35,7 +35,10 @@ impl RouterBuilder { .nest("/mixnodes", mixnodes::routes()) .nest("/services", services::routes()) .nest("/summary", summary::routes()), - // .nest("/testruns", testruns::_routes()), + ) + .nest( + "/internal", + Router::new().nest("/testruns", testruns::routes()), ); Self { diff --git a/nym-node-status-api/src/http/api/testruns.rs b/nym-node-status-api/src/http/api/testruns.rs index 875012b0fb..4bbc4d6295 100644 --- a/nym-node-status-api/src/http/api/testruns.rs +++ b/nym-node-status-api/src/http/api/testruns.rs @@ -1,7 +1,116 @@ -use axum::Router; +use axum::Json; +use axum::{ + extract::{Path, State}, + Router, +}; +use reqwest::StatusCode; -use crate::http::state::AppState; +use crate::db::models::TestRunStatus; +use crate::db::queries; +use crate::{ + db, + http::{ + error::{HttpError, HttpResult}, + models::TestrunAssignment, + state::AppState, + }, +}; -pub(crate) fn _routes() -> Router { - unimplemented!() +// TODO dz consider adding endpoint to trigger testrun scan for a given gateway_id +// like in H< src/http/testruns.rs + +pub(crate) fn routes() -> Router { + Router::new() + .route("/", axum::routing::get(request_testrun)) + .route("/:testrun_id", axum::routing::post(submit_testrun)) +} + +#[tracing::instrument(level = "debug", skip_all)] +async fn request_testrun(State(state): State) -> HttpResult> { + // TODO dz log agent's key + // TODO dz log agent's network probe version + tracing::debug!("Agent X requested testrun"); + + let db = state.db_pool(); + let conn = db + .acquire() + .await + .map_err(HttpError::internal_with_logging)?; + + return match db::queries::testruns::get_oldest_testrun_and_make_it_pending(conn).await { + Ok(res) => { + if let Some(testrun) = res { + // TODO dz consider adding a column to testruns table with agent's public key + tracing::debug!( + "🏃‍ Assigned testrun row_id {} to agent X", + &testrun.testrun_id + ); + Ok(Json(testrun)) + } else { + Err(HttpError::not_found("No testruns available")) + } + } + Err(err) => Err(HttpError::internal_with_logging(err)), + }; +} + +// TODO dz accept testrun_id as query parameter +#[tracing::instrument(level = "debug", skip_all)] +async fn submit_testrun( + Path(testrun_id): Path, + State(state): State, + body: String, +) -> HttpResult { + tracing::debug!( + "Agent submitted testrun {}. Total length: {}", + testrun_id, + body.len(), + ); + // TODO dz store testrun results + + let db = state.db_pool(); + let mut conn = db + .acquire() + .await + .map_err(HttpError::internal_with_logging)?; + + let testrun = queries::testruns::get_testrun_by_id(&mut conn, testrun_id) + .await + .map_err(|e| { + tracing::error!("{e}"); + HttpError::not_found(testrun_id) + })?; + // TODO dz this should be part of a single transaction: commit after everything is done + queries::testruns::update_testrun_status(&mut conn, testrun_id, TestRunStatus::Complete) + .await + .map_err(HttpError::internal_with_logging)?; + queries::testruns::update_gateway_last_probe_log(&mut conn, testrun.gateway_id, &body) + .await + .map_err(HttpError::internal_with_logging)?; + let result = get_result_from_log(&body); + queries::testruns::update_gateway_last_probe_result(&mut conn, testrun.gateway_id, &result) + .await + .map_err(HttpError::internal_with_logging)?; + queries::testruns::update_gateway_score(&mut conn, testrun.gateway_id) + .await + .map_err(HttpError::internal_with_logging)?; + // TODO dz log gw identity key + + tracing::info!( + "✅ Testrun row_id {} for gateway {} complete", + testrun.id, + testrun.gateway_id + ); + + Ok(StatusCode::CREATED) +} + +fn get_result_from_log(log: &str) -> String { + let re = regex::Regex::new(r"\n\{\s").unwrap(); + let result: Vec<_> = re.splitn(log, 2).collect(); + if result.len() == 2 { + let res = format!("{} {}", "{", result[1]).to_string(); + return res; + } + "".to_string() } diff --git a/nym-node-status-api/src/http/api_docs.rs b/nym-node-status-api/src/http/api_docs.rs index 172aa899a3..9ac5238fc4 100644 --- a/nym-node-status-api/src/http/api_docs.rs +++ b/nym-node-status-api/src/http/api_docs.rs @@ -8,7 +8,7 @@ use utoipauto::utoipauto; #[utoipauto(paths = "./nym-node-status-api/src")] #[derive(OpenApi)] #[openapi( - info(title = "Nym API"), + info(title = "Node Status API"), tags(), components(schemas(nym_node_requests::api::v1::node::models::NodeDescription,)) )] diff --git a/nym-node-status-api/src/http/error.rs b/nym-node-status-api/src/http/error.rs index 8bbd59e095..81a61e2d3e 100644 --- a/nym-node-status-api/src/http/error.rs +++ b/nym-node-status-api/src/http/error.rs @@ -1,3 +1,5 @@ +use std::fmt::Display; + pub(crate) type HttpResult = Result; pub(crate) struct HttpError { @@ -13,12 +15,24 @@ impl HttpError { } } + pub(crate) fn internal_with_logging(msg: impl Display) -> Self { + tracing::error!("{}", msg.to_string()); + Self::internal() + } + pub(crate) fn internal() -> Self { Self { message: serde_json::json!({"message": "Internal server error"}).to_string(), status: axum::http::StatusCode::INTERNAL_SERVER_ERROR, } } + + pub(crate) fn not_found(msg: impl Display) -> Self { + Self { + message: serde_json::json!({"message": msg.to_string()}).to_string(), + status: axum::http::StatusCode::NOT_FOUND, + } + } } impl axum::response::IntoResponse for HttpError { diff --git a/nym-node-status-api/src/http/models.rs b/nym-node-status-api/src/http/models.rs index 3a4c348b25..315bf085dc 100644 --- a/nym-node-status-api/src/http/models.rs +++ b/nym-node-status-api/src/http/models.rs @@ -1,7 +1,10 @@ +use crate::db::models::TestRunDto; use nym_node_requests::api::v1::node::models::NodeDescription; use serde::{Deserialize, Serialize}; use utoipa::ToSchema; +pub(crate) use nym_common_models::ns_api::TestrunAssignment; + #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] pub struct Gateway { pub gateway_identity_key: String, @@ -72,3 +75,12 @@ pub(crate) struct SummaryHistory { pub value_json: serde_json::Value, pub timestamp_utc: String, } + +impl From for TestrunAssignment { + fn from(value: TestRunDto) -> Self { + Self { + gateway_pk_id: value.gateway_id, + testrun_id: value.id, + } + } +} diff --git a/nym-node-status-api/src/http/server.rs b/nym-node-status-api/src/http/server.rs index 694f5fa79a..17d5d64ab0 100644 --- a/nym-node-status-api/src/http/server.rs +++ b/nym-node-status-api/src/http/server.rs @@ -22,6 +22,7 @@ pub(crate) async fn start_http_api( // TODO dz do we need this to be configurable? let bind_addr = format!("0.0.0.0:{}", http_port); + tracing::info!("Binding server to {bind_addr}"); let server = router.build_server(bind_addr).await?; Ok(start_server(server)) diff --git a/nym-node-status-api/src/logging.rs b/nym-node-status-api/src/logging.rs index 01dd31562e..6bdad3bae5 100644 --- a/nym-node-status-api/src/logging.rs +++ b/nym-node-status-api/src/logging.rs @@ -1,12 +1,10 @@ use tracing::level_filters::LevelFilter; use tracing_subscriber::{filter::Directive, EnvFilter}; -pub(crate) fn setup_tracing_logger() { - fn directive_checked(directive: impl Into) -> Directive { - directive - .into() - .parse() - .expect("Failed to parse log directive") +// TODO dz you can get the tracing-subscriber via basic-tracing feature on nym-bin-common +pub(crate) fn setup_tracing_logger() -> anyhow::Result<()> { + fn directive_checked(directive: impl Into) -> anyhow::Result { + directive.into().parse().map_err(From::from) } let log_builder = tracing_subscriber::fmt() @@ -22,10 +20,11 @@ pub(crate) fn setup_tracing_logger() { let mut filter = EnvFilter::builder() // if RUST_LOG isn't set, set default level - .with_default_directive(LevelFilter::INFO.into()) + .with_default_directive(LevelFilter::DEBUG.into()) .from_env_lossy(); + // these crates are more granularly filtered - let filter_crates = [ + let warn_crates = [ "reqwest", "rustls", "hyper", @@ -35,14 +34,36 @@ pub(crate) fn setup_tracing_logger() { "tower_http", "axum", ]; - for crate_name in filter_crates { - filter = filter.add_directive(directive_checked(format!("{}=warn", crate_name))); + for crate_name in warn_crates { + filter = filter.add_directive(directive_checked(format!("{}=warn", crate_name))?); } - filter = filter.add_directive(directive_checked("nym_bin_common=debug")); - filter = filter.add_directive(directive_checked("nym_explorer_client=debug")); - filter = filter.add_directive(directive_checked("nym_network_defaults=debug")); - filter = filter.add_directive(directive_checked("nym_validator_client=debug")); + let log_level_hint = filter.max_level_hint(); + + // debug or higher granularity (e.g. trace) + let debug_or_higher = std::cmp::max( + log_level_hint.unwrap_or(LevelFilter::DEBUG), + LevelFilter::DEBUG, + ); + filter = filter.add_directive(directive_checked(format!( + "nym_bin_common={}", + debug_or_higher + ))?); + filter = filter.add_directive(directive_checked(format!( + "nym_explorer_client={}", + debug_or_higher + ))?); + filter = filter.add_directive(directive_checked(format!( + "nym_network_defaults={}", + debug_or_higher + ))?); + filter = filter.add_directive(directive_checked(format!( + "nym_validator_client={}", + debug_or_higher + ))?); log_builder.with_env_filter(filter).init(); + tracing::info!("Log level: {:?}", log_level_hint); + + Ok(()) } diff --git a/nym-node-status-api/src/main.rs b/nym-node-status-api/src/main.rs index a15cfe68a4..b2c68b391b 100644 --- a/nym-node-status-api/src/main.rs +++ b/nym-node-status-api/src/main.rs @@ -6,26 +6,35 @@ mod db; mod http; mod logging; mod monitor; +mod testruns; #[tokio::main] async fn main() -> anyhow::Result<()> { - logging::setup_tracing_logger(); + logging::setup_tracing_logger()?; let args = cli::Cli::parse(); - let connection_url = args.connection_url.clone(); + let connection_url = args.database_url.clone(); tracing::debug!("Using config:\n{:#?}", args); let storage = db::Storage::init(connection_url).await?; - let db_pool = storage.pool_owned().await; + let db_pool = storage.pool_owned(); let args_clone = args.clone(); tokio::spawn(async move { - monitor::spawn_in_background(db_pool, args_clone).await; + monitor::spawn_in_background( + db_pool, + args_clone.explorer_client_timeout, + args_clone.nym_api_client_timeout, + &args_clone.nyxd_addr, + args_clone.monitor_refresh_interval, + ) + .await; + tracing::info!("Started monitor task"); }); - tracing::info!("Started monitor task"); + testruns::spawn(storage.pool_owned(), args.testruns_refresh_interval).await; let shutdown_handles = http::server::start_http_api( - storage.pool_owned().await, + storage.pool_owned(), args.http_port, args.nym_http_cache_ttl, ) diff --git a/nym-node-status-api/src/monitor/mod.rs b/nym-node-status-api/src/monitor/mod.rs index 3c5172c8a9..680a9ce312 100644 --- a/nym-node-status-api/src/monitor/mod.rs +++ b/nym-node-status-api/src/monitor/mod.rs @@ -1,4 +1,3 @@ -use crate::cli::Cli; use crate::db::models::{ gateway, mixnode, GatewayRecord, MixnodeRecord, NetworkSummary, GATEWAYS_BLACKLISTED_COUNT, GATEWAYS_BONDED_COUNT, GATEWAYS_EXPLORER_COUNT, GATEWAYS_HISTORICAL_COUNT, @@ -19,34 +18,50 @@ use nym_validator_client::NymApiClient; use reqwest::Url; use std::collections::HashSet; use std::str::FromStr; -use tokio::task::JoinHandle; use tokio::time::Duration; +use tracing::instrument; -const REFRESH_DELAY: Duration = Duration::from_secs(60 * 5); -const FAILURE_RETRY_DELAY: Duration = Duration::from_secs(15); +// TODO dz should be configurable +const FAILURE_RETRY_DELAY: Duration = Duration::from_secs(60); static DELEGATION_PROGRAM_WALLET: &str = "n1rnxpdpx3kldygsklfft0gech7fhfcux4zst5lw"; // TODO dz: query many NYM APIs: // multiple instances running directory cache, ask sachin -pub(crate) async fn spawn_in_background(db_pool: DbPool, config: Cli) -> JoinHandle<()> { +#[instrument(level = "debug", name = "data_monitor", skip_all)] +pub(crate) async fn spawn_in_background( + db_pool: DbPool, + explorer_client_timeout: Duration, + nym_api_client_timeout: Duration, + nyxd_addr: &Url, + refresh_interval: Duration, +) { let network_defaults = nym_network_defaults::NymNetworkDetails::new_from_env(); loop { tracing::info!("Refreshing node info..."); - if let Err(e) = run(&db_pool, &network_defaults, &config).await { + if let Err(e) = run( + &db_pool, + &network_defaults, + explorer_client_timeout, + nym_api_client_timeout, + nyxd_addr, + ) + .await + { tracing::error!( "Monitor run failed: {e}, retrying in {}s...", FAILURE_RETRY_DELAY.as_secs() ); + // TODO dz implement some sort of backoff tokio::time::sleep(FAILURE_RETRY_DELAY).await; } else { tracing::info!( "Info successfully collected, sleeping for {}s...", - REFRESH_DELAY.as_secs() + refresh_interval.as_secs() ); - tokio::time::sleep(REFRESH_DELAY).await; + tokio::time::sleep(refresh_interval).await; } } } @@ -54,7 +69,9 @@ pub(crate) async fn spawn_in_background(db_pool: DbPool, config: Cli) -> JoinHan async fn run( pool: &DbPool, network_details: &NymNetworkDetails, - config: &Cli, + explorer_client_timeout: Duration, + nym_api_client_timeout: Duration, + nyxd_addr: &Url, ) -> anyhow::Result<()> { let default_api_url = network_details .endpoints @@ -68,19 +85,17 @@ async fn run( .expect("rust sdk mainnet default explorer url not parseable") }); + // TODO dz replace explorer api with ipinfo.io let default_explorer_url = default_explorer_url.expect("explorer url missing in network config"); let explorer_client = - ExplorerClient::new_with_timeout(default_explorer_url, config.explorer_client_timeout)?; + ExplorerClient::new_with_timeout(default_explorer_url, explorer_client_timeout)?; let explorer_gateways = explorer_client .get_gateways() .await .log_error("get_gateways")?; - tracing::debug!("6"); - let api_client = - // TODO dz introduce timeout ? - NymApiClient::new(default_api_url); + let api_client = NymApiClient::new_with_timeout(default_api_url, nym_api_client_timeout); let gateways = api_client .get_cached_described_gateways() .await @@ -97,14 +112,16 @@ async fn run( .log_error("get_cached_mixnodes")?; tracing::debug!("Fetched {} mixnodes", mixnodes.len()); - // TODO dz can we calculate blacklisted GWs from their performance? - // where do we get their performance? - let gateways_blacklisted = api_client - .nym_api - .get_gateways_blacklisted() - .await - .map(|vec| vec.into_iter().collect::>()) - .log_error("get_gateways_blacklisted")?; + let gateways_blacklisted = skimmed_gateways + .iter() + .filter_map(|gw| { + if gw.performance.round_to_integer() <= 50 { + Some(gw.ed25519_identity_pubkey.to_owned()) + } else { + None + } + }) + .collect::>(); // Cached mixnodes don't include blacklisted nodes // We need that to calculate the total locked tokens later @@ -124,7 +141,7 @@ async fn run( .await .log_error("get_active_mixnodes")?; let delegation_program_members = - get_delegation_program_details(network_details, &config.nyxd_addr).await?; + get_delegation_program_details(network_details, nyxd_addr).await?; // keep stats for later let count_bonded_mixnodes = mixnodes.len(); diff --git a/nym-node-status-api/src/testruns/mod.rs b/nym-node-status-api/src/testruns/mod.rs new file mode 100644 index 0000000000..86b36cf48e --- /dev/null +++ b/nym-node-status-api/src/testruns/mod.rs @@ -0,0 +1,76 @@ +use crate::db::models::GatewayIdentityDto; +use crate::db::DbPool; +use futures_util::TryStreamExt; +use std::time::Duration; +use tracing::instrument; + +pub(crate) mod models; +mod queue; +pub(crate) use queue::now_utc; + +pub(crate) async fn spawn(pool: DbPool, refresh_interval: Duration) { + tokio::spawn(async move { + loop { + tracing::info!("Spawning testruns..."); + + if let Err(e) = run(&pool).await { + tracing::error!("Cron job failed: {}", e); + } + tracing::debug!("Sleeping for {}s...", refresh_interval.as_secs()); + tokio::time::sleep(refresh_interval).await; + } + }); +} + +// TODO dz make number of max agents configurable + +// TODO dz periodically clean up stale pending testruns +#[instrument(level = "debug", name = "testrun_queue", skip_all)] +async fn run(pool: &DbPool) -> anyhow::Result<()> { + if pool.is_closed() { + tracing::debug!("DB pool closed, returning early"); + return Ok(()); + } + + let mut conn = pool.acquire().await?; + + let gateways = sqlx::query_as!( + GatewayIdentityDto, + r#"SELECT + gateway_identity_key as "gateway_identity_key!", + bonded as "bonded: bool" + FROM gateways + ORDER BY last_testrun_utc"#, + ) + .fetch(&mut *conn) + .try_collect::>() + .await?; + + // TODO dz this filtering could be done in SQL + let gateways: Vec = gateways.into_iter().filter(|g| g.bonded).collect(); + + tracing::debug!("Trying to queue {} testruns", gateways.len()); + let mut testruns_created = 0; + for gateway in gateways { + if let Err(e) = queue::try_queue_testrun( + &mut conn, + gateway.gateway_identity_key.clone(), + // TODO dz read from config + "127.0.0.1".to_string(), + ) + .await + // TODO dz measure how many were actually inserted and how many were skipped + { + tracing::debug!( + "Skipping test for identity {} with error {}", + &gateway.gateway_identity_key, + e + ); + } else { + testruns_created += 1; + } + } + tracing::debug!("{} testruns queued in total", testruns_created); + + Ok(()) +} diff --git a/nym-node-status-api/src/testruns/models.rs b/nym-node-status-api/src/testruns/models.rs new file mode 100644 index 0000000000..fe4b33384c --- /dev/null +++ b/nym-node-status-api/src/testruns/models.rs @@ -0,0 +1,16 @@ +use serde::{Deserialize, Serialize}; + +#[allow(dead_code)] // it's not dead code but clippy doesn't detect usage in sqlx macros +#[derive(Debug, Clone)] +pub struct GatewayIdentityDto { + pub gateway_identity_key: String, + pub bonded: bool, +} + +#[derive(Debug, Clone, Deserialize, Serialize, utoipa::ToSchema)] +pub struct TestRun { + pub id: u32, + pub identity_key: String, + pub status: String, + pub log: String, +} diff --git a/nym-node-status-api/src/testruns/queue.rs b/nym-node-status-api/src/testruns/queue.rs new file mode 100644 index 0000000000..aa0ce32fa2 --- /dev/null +++ b/nym-node-status-api/src/testruns/queue.rs @@ -0,0 +1,118 @@ +use crate::db::models::{GatewayInfoDto, TestRunDto, TestRunStatus}; +use crate::testruns::models::TestRun; +use anyhow::anyhow; +use chrono::DateTime; +use futures_util::TryStreamExt; +use sqlx::pool::PoolConnection; +use sqlx::Sqlite; +use std::time::SystemTime; + +pub(crate) async fn try_queue_testrun( + conn: &mut PoolConnection, + identity_key: String, + ip_address: String, +) -> anyhow::Result { + let timestamp = now_utc().timestamp(); + let timestamp_pretty = now_utc_as_rfc3339(); + + let items = sqlx::query_as!( + GatewayInfoDto, + r#"SELECT + id as "id!", + gateway_identity_key as "gateway_identity_key!", + self_described as "self_described?", + explorer_pretty_bond as "explorer_pretty_bond?" + FROM gateways + WHERE gateway_identity_key = ? + ORDER BY gateway_identity_key + LIMIT 1"#, + identity_key, + ) + // TODO dz shoudl call .fetch_one + // TODO dz replace this in other queries as well + .fetch(conn.as_mut()) + .try_collect::>() + .await?; + + let gateway = items + .iter() + .find(|g| g.gateway_identity_key == identity_key); + + // TODO dz if let Some() = gateway.first() ... + if gateway.is_none() { + return Err(anyhow!("Unknown gateway {identity_key}")); + } + let gateway_id = gateway.unwrap().id; + + // + // check if there is already a test run for this gateway + // + let items = sqlx::query_as!( + TestRunDto, + r#"SELECT + id as "id!", + gateway_id as "gateway_id!", + status as "status!", + timestamp_utc as "timestamp_utc!", + ip_address as "ip_address!", + log as "log!" + FROM testruns + WHERE gateway_id = ? AND status != 2 + ORDER BY id DESC + LIMIT 1"#, + gateway_id, + ) + .fetch(conn.as_mut()) + .try_collect::>() + .await?; + + if !items.is_empty() { + let testrun = items.first().unwrap(); + return Ok(TestRun { + id: testrun.id as u32, + identity_key, + status: format!( + "{}", + TestRunStatus::from_repr(testrun.status as u8).unwrap() + ), + log: testrun.log.clone(), + }); + } + + // + // save test run + // + let status = TestRunStatus::Pending as u32; + let log = format!( + "Test for {identity_key} requested at {} UTC\n\n", + timestamp_pretty + ); + + let id = sqlx::query!( + "INSERT INTO testruns (gateway_id, status, ip_address, timestamp_utc, log) VALUES (?, ?, ?, ?, ?)", + gateway_id, + status, + ip_address, + timestamp, + log, + ) + .execute(conn.as_mut()) + .await? + .last_insert_rowid(); + + Ok(TestRun { + id: id as u32, + identity_key, + status: format!("{}", TestRunStatus::Pending), + log, + }) +} + +// TODO dz do we need these? +pub fn now_utc() -> DateTime { + SystemTime::now().into() +} + +pub fn now_utc_as_rfc3339() -> String { + now_utc().to_rfc3339() +} From 96b54c455e2246eea17dc3b2e1915778713aa0da Mon Sep 17 00:00:00 2001 From: Fran Arbanas Date: Mon, 28 Oct 2024 19:16:46 +0100 Subject: [PATCH 30/48] feat: add simple node-status-agent --- .github/workflows/push-node-status-agent.yaml | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .github/workflows/push-node-status-agent.yaml diff --git a/.github/workflows/push-node-status-agent.yaml b/.github/workflows/push-node-status-agent.yaml new file mode 100644 index 0000000000..750883efee --- /dev/null +++ b/.github/workflows/push-node-status-agent.yaml @@ -0,0 +1,11 @@ +name: Build and upload Node Status agent container to harbor.nymte.ch + +on: + workflow_dispatch: + +jobs: + my-job: + runs-on: arc-ubuntu-22.04 + steps: + - name: my-step + run: echo "Hello World!" From 9583a5c6c88bf74248b213c0285102d3f7af10e2 Mon Sep 17 00:00:00 2001 From: dynco-nym <173912580+dynco-nym@users.noreply.github.com> Date: Tue, 29 Oct 2024 00:24:18 +0100 Subject: [PATCH 31/48] Fix build script --- nym-node-status-api/build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nym-node-status-api/build.rs b/nym-node-status-api/build.rs index 7bc42cb03f..bfe6e54e74 100644 --- a/nym-node-status-api/build.rs +++ b/nym-node-status-api/build.rs @@ -8,7 +8,7 @@ const SQLITE_DB_FILENAME: &str = "nym-node-status-api.sqlite"; /// If you need to re-run migrations or reset the db, just run /// cargo clean -p nym-node-status-api -#[tokio::main] +#[tokio::main(flavor = "current_thread")] async fn main() -> Result<()> { let out_dir = read_env_var("OUT_DIR")?; let database_path = format!("sqlite://{}/{}?mode=rwc", out_dir, SQLITE_DB_FILENAME); From 317f7fffa986dff856e5b980b15872620bc052f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Stuczy=C5=84ski?= Date: Tue, 29 Oct 2024 08:35:07 +0000 Subject: [PATCH 32/48] added hacky routes to return nymnodes alongside legacy nodes (#5051) * added hacky routes to return nymnodes alongside legacy nodes * fixed mixing role * Update client (#5054) * removed hacky mixnodes endpoint for its not used * construct explorer-api client with timeout --------- Co-authored-by: Dinko Zdravac <173912580+dynco-nym@users.noreply.github.com> --- .../validator-client/src/client.rs | 69 +++++++- .../validator-client/src/nym_api/mod.rs | 21 ++- explorer-api/explorer-client/src/lib.rs | 15 ++ .../src/country_statistics/geolocate.rs | 79 +++++++++ explorer-api/src/http/mod.rs | 2 + explorer-api/src/main.rs | 1 + explorer-api/src/nym_nodes/http.rs | 26 +++ explorer-api/src/nym_nodes/location.rs | 8 + explorer-api/src/nym_nodes/mod.rs | 10 ++ explorer-api/src/nym_nodes/models.rs | 154 ++++++++++++++++++ explorer-api/src/state.rs | 9 + explorer-api/src/tasks.rs | 59 ++++++- 12 files changed, 446 insertions(+), 7 deletions(-) create mode 100644 explorer-api/src/nym_nodes/http.rs create mode 100644 explorer-api/src/nym_nodes/location.rs create mode 100644 explorer-api/src/nym_nodes/mod.rs create mode 100644 explorer-api/src/nym_nodes/models.rs diff --git a/common/client-libs/validator-client/src/client.rs b/common/client-libs/validator-client/src/client.rs index 6205e55998..8e1b756bd3 100644 --- a/common/client-libs/validator-client/src/client.rs +++ b/common/client-libs/validator-client/src/client.rs @@ -30,10 +30,10 @@ use time::Date; use url::Url; pub use crate::nym_api::NymApiClientExt; +use nym_mixnet_contract_common::NymNodeDetails; pub use nym_mixnet_contract_common::{ mixnode::MixNodeDetails, GatewayBond, IdentityKey, IdentityKeyRef, NodeId, }; - // re-export the type to not break existing imports pub use crate::coconut::EcashApiClient; @@ -106,7 +106,9 @@ impl Config { pub struct Client { // 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_api::Client, + // pub nym_api_client: NymApiClient, pub nyxd: NyxdClient, } @@ -243,6 +245,50 @@ impl Client { Ok(self.nym_api.get_gateways().await?) } + // TODO: combine with NymApiClient... + pub async fn get_all_cached_described_nodes( + &self, + ) -> Result, 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 descriptions = Vec::new(); + + loop { + let mut res = self.nym_api.get_nodes_described(Some(page), None).await?; + + descriptions.append(&mut res.data); + if descriptions.len() < res.pagination.total { + page += 1 + } else { + break; + } + } + + Ok(descriptions) + } + + // TODO: combine with NymApiClient... + pub async fn get_all_cached_bonded_nym_nodes( + &self, + ) -> Result, 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.nym_api.get_nym_nodes(Some(page), None).await?; + + bonds.append(&mut res.data); + if bonds.len() < res.pagination.total { + page += 1 + } else { + break; + } + } + + Ok(bonds) + } + pub async fn blind_sign( &self, request_body: &BlindSignRequestBody, @@ -418,6 +464,27 @@ impl NymApiClient { Ok(descriptions) } + pub async fn get_all_bonded_nym_nodes( + &self, + ) -> Result, 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.nym_api.get_nym_nodes(Some(page), None).await?; + + bonds.append(&mut res.data); + if bonds.len() < res.pagination.total { + page += 1 + } else { + break; + } + } + + Ok(bonds) + } + pub async fn get_gateway_core_status_count( &self, identity: IdentityKeyRef<'_>, diff --git a/common/client-libs/validator-client/src/nym_api/mod.rs b/common/client-libs/validator-client/src/nym_api/mod.rs index 8e13c84580..69185c4d82 100644 --- a/common/client-libs/validator-client/src/nym_api/mod.rs +++ b/common/client-libs/validator-client/src/nym_api/mod.rs @@ -39,7 +39,7 @@ use nym_contracts_common::IdentityKey; pub use nym_http_api_client::Client; use nym_http_api_client::{ApiClient, NO_PARAMS}; use nym_mixnet_contract_common::mixnode::MixNodeDetails; -use nym_mixnet_contract_common::{GatewayBond, IdentityKeyRef, NodeId}; +use nym_mixnet_contract_common::{GatewayBond, IdentityKeyRef, NodeId, NymNodeDetails}; use time::format_description::BorrowedFormatItem; use time::Date; @@ -139,6 +139,25 @@ pub trait NymApiClientExt: ApiClient { .await } + async fn get_nym_nodes( + &self, + page: Option, + per_page: Option, + ) -> Result, NymAPIError> { + let mut params = Vec::new(); + + if let Some(page) = page { + params.push(("page", page.to_string())) + } + + if let Some(per_page) = per_page { + params.push(("per_page", per_page.to_string())) + } + + self.get_json(&[routes::API_VERSION, "nym-nodes", "bonded"], ¶ms) + .await + } + async fn get_basic_mixnodes( &self, semver_compatibility: Option, diff --git a/explorer-api/explorer-client/src/lib.rs b/explorer-api/explorer-client/src/lib.rs index 3ff4807b2b..f23d10683d 100644 --- a/explorer-api/explorer-client/src/lib.rs +++ b/explorer-api/explorer-client/src/lib.rs @@ -12,6 +12,8 @@ pub use nym_explorer_api_requests::{ // Paths const API_VERSION: &str = "v1"; +const TMP: &str = "tmp"; +const UNSTABLE: &str = "unstable"; const MIXNODES: &str = "mix-nodes"; const GATEWAYS: &str = "gateways"; @@ -53,6 +55,12 @@ impl ExplorerClient { Ok(Self { client, url }) } + #[cfg(not(target_arch = "wasm32"))] + pub fn new_with_timeout(url: url::Url, timeout: Duration) -> Result { + let client = reqwest::Client::builder().timeout(timeout).build()?; + Ok(Self { client, url }) + } + async fn send_get_request( &self, paths: &[&str], @@ -86,6 +94,13 @@ impl ExplorerClient { pub async fn get_gateways(&self) -> Result, ExplorerApiError> { self.query_explorer_api(&[API_VERSION, GATEWAYS]).await } + + pub async fn unstable_get_gateways( + &self, + ) -> Result, ExplorerApiError> { + self.query_explorer_api(&[API_VERSION, TMP, UNSTABLE, GATEWAYS]) + .await + } } fn combine_url(mut base_url: Url, paths: &[&str]) -> Result { diff --git a/explorer-api/src/country_statistics/geolocate.rs b/explorer-api/src/country_statistics/geolocate.rs index 858f593986..18442fc6d7 100644 --- a/explorer-api/src/country_statistics/geolocate.rs +++ b/explorer-api/src/country_statistics/geolocate.rs @@ -4,6 +4,7 @@ use crate::state::ExplorerApiStateContext; use log::{info, warn}; use nym_explorer_api_requests::Location; +use nym_network_defaults::DEFAULT_NYM_NODE_HTTP_PORT; use nym_task::TaskClient; pub(crate) struct GeoLocateTask { @@ -25,6 +26,7 @@ impl GeoLocateTask { _ = interval_timer.tick() => { self.locate_mix_nodes().await; self.locate_gateways().await; + self.locate_nym_nodes().await; } _ = self.shutdown.recv() => { trace!("Listener: Received shutdown"); @@ -109,6 +111,83 @@ impl GeoLocateTask { trace!("All mix nodes located"); } + async fn locate_nym_nodes(&mut self) { + // I'm unwrapping to the default value to get rid of an extra indentation level from the `if let Some(...) = ...` + // If the value is None, we'll unwrap to an empty hashmap and the `values()` loop won't do any work anyway + let nym_nodes = self.state.inner.nymnodes.get_bonded_nymnodes().await; + + let geo_ip = self.state.inner.geo_ip.0.clone(); + + for (i, cache_item) in nym_nodes.values().enumerate() { + if self + .state + .inner + .nymnodes + .is_location_valid(cache_item.node_id()) + .await + { + // when the cached location is valid, don't locate and continue to next mix node + continue; + } + + let bonded_host = &cache_item.bond_information.node.host; + + match geo_ip.query( + bonded_host, + Some( + cache_item + .bond_information + .node + .custom_http_port + .unwrap_or(DEFAULT_NYM_NODE_HTTP_PORT), + ), + ) { + Ok(opt) => match opt { + Some(location) => { + let location: Location = location.into(); + + trace!( + "{} mix nodes already located. host {} is located in {:#?}", + i, + bonded_host, + location.three_letter_iso_country_code, + ); + + if i > 0 && (i % 100) == 0 { + info!("Located {} nym-nodes...", i + 1,); + } + + self.state + .inner + .nymnodes + .set_location(cache_item.node_id(), Some(location)) + .await; + + // one node has been located, so return out of the loop + return; + } + None => { + warn!("❌ Location for {bonded_host} not found."); + self.state + .inner + .nymnodes + .set_location(cache_item.node_id(), None) + .await; + } + }, + Err(_e) => { + // warn!( + // "❌ Oh no! Location for {} failed. Error: {:#?}", + // cache_item.mix_node().host, + // e + // ); + } + }; + } + + trace!("All nym-nodes nodes located"); + } + async fn locate_gateways(&mut self) { let gateways = self.state.inner.gateways.get_gateways().await; diff --git a/explorer-api/src/http/mod.rs b/explorer-api/src/http/mod.rs index 559093671e..2d4e2d9fab 100644 --- a/explorer-api/src/http/mod.rs +++ b/explorer-api/src/http/mod.rs @@ -10,6 +10,7 @@ use crate::gateways::http::gateways_make_default_routes; use crate::http::swagger::get_docs; use crate::mix_node::http::mix_node_make_default_routes; use crate::mix_nodes::http::mix_nodes_make_default_routes; +use crate::nym_nodes::http::unstable_temp_nymnodes_make_default_routes; use crate::overview::http::overview_make_default_routes; use crate::ping::http::ping_make_default_routes; use crate::service_providers::http::service_providers_make_default_routes; @@ -58,6 +59,7 @@ fn configure_rocket(state: ExplorerApiStateContext) -> Rocket { "/ping" => ping_make_default_routes(&openapi_settings), "/validators" => validators_make_default_routes(&openapi_settings), "/service-providers" => service_providers_make_default_routes(&openapi_settings), + "/tmp/unstable" => unstable_temp_nymnodes_make_default_routes(&openapi_settings), }; building_rocket diff --git a/explorer-api/src/main.rs b/explorer-api/src/main.rs index 8cb98280af..ecaae3f492 100644 --- a/explorer-api/src/main.rs +++ b/explorer-api/src/main.rs @@ -22,6 +22,7 @@ mod http; mod location; mod mix_node; pub(crate) mod mix_nodes; +mod nym_nodes; mod overview; mod ping; pub(crate) mod service_providers; diff --git a/explorer-api/src/nym_nodes/http.rs b/explorer-api/src/nym_nodes/http.rs new file mode 100644 index 0000000000..378fcc78d7 --- /dev/null +++ b/explorer-api/src/nym_nodes/http.rs @@ -0,0 +1,26 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::state::ExplorerApiStateContext; +use nym_explorer_api_requests::PrettyDetailedGatewayBond; +use okapi::openapi3::OpenApi; +use rocket::serde::json::Json; +use rocket::{Route, State}; +use rocket_okapi::settings::OpenApiSettings; + +pub fn unstable_temp_nymnodes_make_default_routes( + settings: &OpenApiSettings, +) -> (Vec, OpenApi) { + openapi_get_routes_spec![settings: all_gateways] +} + +#[openapi(tag = "UNSTABLE")] +#[get("/gateways")] +pub(crate) async fn all_gateways( + state: &State, +) -> Json> { + let mut gateways = state.inner.gateways.get_detailed_gateways().await; + gateways.append(&mut state.inner.nymnodes.pretty_gateways().await); + + Json(gateways) +} diff --git a/explorer-api/src/nym_nodes/location.rs b/explorer-api/src/nym_nodes/location.rs new file mode 100644 index 0000000000..134023f3ca --- /dev/null +++ b/explorer-api/src/nym_nodes/location.rs @@ -0,0 +1,8 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use nym_mixnet_contract_common::NodeId; + +use crate::location::LocationCache; + +pub(crate) type NymNodeLocationCache = LocationCache; diff --git a/explorer-api/src/nym_nodes/mod.rs b/explorer-api/src/nym_nodes/mod.rs new file mode 100644 index 0000000000..2ceb85885a --- /dev/null +++ b/explorer-api/src/nym_nodes/mod.rs @@ -0,0 +1,10 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use std::time::Duration; + +pub(crate) mod http; +pub(crate) mod location; +pub(crate) mod models; + +pub(crate) const CACHE_ENTRY_TTL: Duration = Duration::from_secs(1200); diff --git a/explorer-api/src/nym_nodes/models.rs b/explorer-api/src/nym_nodes/models.rs new file mode 100644 index 0000000000..7cdbbbdee3 --- /dev/null +++ b/explorer-api/src/nym_nodes/models.rs @@ -0,0 +1,154 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::location::{LocationCache, LocationCacheItem}; +use crate::nym_nodes::location::NymNodeLocationCache; +use crate::nym_nodes::CACHE_ENTRY_TTL; +use nym_explorer_api_requests::{Location, PrettyDetailedGatewayBond}; +use nym_mixnet_contract_common::{Gateway, NodeId, NymNodeDetails}; +use nym_validator_client::models::NymNodeDescription; +use std::collections::HashMap; +use std::sync::Arc; +use std::time::{Duration, SystemTime}; +use tokio::sync::{RwLock, RwLockReadGuard}; + +pub(crate) struct NymNodesCache { + pub(crate) valid_until: SystemTime, + pub(crate) bonded_nym_nodes: HashMap, + pub(crate) described_nodes: HashMap, +} + +impl NymNodesCache { + fn new() -> Self { + NymNodesCache { + valid_until: SystemTime::now() - Duration::from_secs(60), // in the past + bonded_nym_nodes: Default::default(), + described_nodes: Default::default(), + } + } + + // fn is_valid(&self) -> bool { + // self.valid_until >= SystemTime::now() + // } +} + +#[derive(Clone)] +pub(crate) struct ThreadSafeNymNodesCache { + nymnodes: Arc>, + locations: Arc>>, +} + +impl ThreadSafeNymNodesCache { + pub(crate) fn new() -> Self { + ThreadSafeNymNodesCache { + nymnodes: Arc::new(RwLock::new(NymNodesCache::new())), + locations: Arc::new(RwLock::new(NymNodeLocationCache::new())), + } + } + + pub(crate) fn new_with_location_cache(locations: NymNodeLocationCache) -> Self { + ThreadSafeNymNodesCache { + nymnodes: Arc::new(RwLock::new(NymNodesCache::new())), + locations: Arc::new(RwLock::new(locations)), + } + } + + pub(crate) async fn is_location_valid(&self, node_id: NodeId) -> bool { + self.locations + .read() + .await + .get(&node_id) + .map_or(false, |cache_item| { + cache_item.valid_until > SystemTime::now() + }) + } + + pub(crate) async fn get_bonded_nymnodes( + &self, + ) -> RwLockReadGuard> { + let guard = self.nymnodes.read().await; + RwLockReadGuard::map(guard, |n| &n.bonded_nym_nodes) + } + + pub(crate) async fn get_locations(&self) -> NymNodeLocationCache { + self.locations.read().await.clone() + } + + pub(crate) async fn set_location(&self, node_id: NodeId, location: Option) { + // cache the location for this mix node so that it can be used when the mix node list is refreshed + self.locations + .write() + .await + .insert(node_id, LocationCacheItem::new_from_location(location)); + } + + pub(crate) async fn update_cache( + &self, + all_bonds: Vec, + descriptions: Vec, + ) { + let mut guard = self.nymnodes.write().await; + guard.bonded_nym_nodes = all_bonds + .into_iter() + .map(|details| (details.node_id(), details)) + .collect(); + guard.described_nodes = descriptions + .into_iter() + .map(|description| (description.node_id, description)) + .collect(); + + guard.valid_until = SystemTime::now() + CACHE_ENTRY_TTL; + } + + pub(crate) async fn pretty_gateways(&self) -> Vec { + let nodes_guard = self.nymnodes.read().await; + let location_guard = self.locations.read().await; + + let mut pretty_gateways = vec![]; + + for (node_id, native_nymnode) in &nodes_guard.bonded_nym_nodes { + let Some(description) = nodes_guard.described_nodes.get(node_id) else { + continue; + }; + + if description.description.declared_role.entry { + let location = location_guard.get(node_id); + let bond = &native_nymnode.bond_information; + + pretty_gateways.push(PrettyDetailedGatewayBond { + pledge_amount: bond.original_pledge.clone(), + owner: bond.owner.clone(), + block_height: bond.bonding_height, + gateway: Gateway { + host: bond.node.host.clone(), + mix_port: description.description.mix_port(), + clients_port: description.description.mixnet_websockets.ws_port, + location: description + .description + .auxiliary_details + .location + .as_ref() + .map(|l| l.to_string()) + .unwrap_or_default(), + sphinx_key: description + .description + .host_information + .keys + .x25519 + .to_base58_string(), + identity_key: bond.node.identity_key.clone(), + version: description + .description + .build_information + .build_version + .clone(), + }, + proxy: None, + location: location.and_then(|l| l.location.clone()), + }) + } + } + + pretty_gateways + } +} diff --git a/explorer-api/src/state.rs b/explorer-api/src/state.rs index b007fae76d..28373aef80 100644 --- a/explorer-api/src/state.rs +++ b/explorer-api/src/state.rs @@ -18,6 +18,8 @@ use crate::gateways::models::ThreadsafeGatewayCache; use crate::mix_node::models::ThreadsafeMixNodeCache; use crate::mix_nodes::location::MixnodeLocationCache; use crate::mix_nodes::models::ThreadsafeMixNodesCache; +use crate::nym_nodes::location::NymNodeLocationCache; +use crate::nym_nodes::models::ThreadSafeNymNodesCache; use crate::ping::models::ThreadsafePingCache; use crate::validators::models::ThreadsafeValidatorCache; @@ -30,6 +32,7 @@ pub struct ExplorerApiState { pub(crate) gateways: ThreadsafeGatewayCache, pub(crate) mixnode: ThreadsafeMixNodeCache, pub(crate) mixnodes: ThreadsafeMixNodesCache, + pub(crate) nymnodes: ThreadSafeNymNodesCache, pub(crate) ping: ThreadsafePingCache, pub(crate) validators: ThreadsafeValidatorCache, pub(crate) geo_ip: ThreadsafeGeoIp, @@ -49,6 +52,7 @@ pub struct ExplorerApiStateOnDisk { pub(crate) country_node_distribution: CountryNodesDistribution, pub(crate) mixnode_location_cache: MixnodeLocationCache, pub(crate) gateway_location_cache: GatewayLocationCache, + pub(crate) nymnode_location_cache: NymNodeLocationCache, pub(crate) as_at: DateTime, } @@ -85,6 +89,9 @@ impl ExplorerApiStateContext { mixnodes: ThreadsafeMixNodesCache::new_with_location_cache( state.mixnode_location_cache, ), + nymnodes: ThreadSafeNymNodesCache::new_with_location_cache( + state.nymnode_location_cache, + ), ping: ThreadsafePingCache::new(), validators: ThreadsafeValidatorCache::new(), validator_client: ThreadsafeValidatorClient::new(), @@ -101,6 +108,7 @@ impl ExplorerApiStateContext { gateways: ThreadsafeGatewayCache::new(), mixnode: ThreadsafeMixNodeCache::new(), mixnodes: ThreadsafeMixNodesCache::new(), + nymnodes: ThreadSafeNymNodesCache::new(), ping: ThreadsafePingCache::new(), validators: ThreadsafeValidatorCache::new(), validator_client: ThreadsafeValidatorClient::new(), @@ -117,6 +125,7 @@ impl ExplorerApiStateContext { country_node_distribution: self.inner.country_node_distribution.get_all().await, mixnode_location_cache: self.inner.mixnodes.get_locations().await, gateway_location_cache: self.inner.gateways.get_locations().await, + nymnode_location_cache: self.inner.nymnodes.get_locations().await, as_at: Utc::now(), }; serde_json::to_writer(file, &state).expect("error writing state to disk"); diff --git a/explorer-api/src/tasks.rs b/explorer-api/src/tasks.rs index f14408f1c8..efb03f34a6 100644 --- a/explorer-api/src/tasks.rs +++ b/explorer-api/src/tasks.rs @@ -1,16 +1,16 @@ // Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use nym_mixnet_contract_common::GatewayBond; +use crate::mix_nodes::CACHE_REFRESH_RATE; +use crate::state::ExplorerApiStateContext; +use nym_mixnet_contract_common::{GatewayBond, NymNodeDetails}; use nym_task::TaskClient; -use nym_validator_client::models::MixNodeBondAnnotated; +use nym_validator_client::models::{MixNodeBondAnnotated, NymNodeDescription}; use nym_validator_client::nyxd::error::NyxdError; use nym_validator_client::nyxd::{Paging, TendermintRpcClient, ValidatorResponse}; use nym_validator_client::{QueryHttpRpcValidatorClient, ValidatorClientError}; use std::future::Future; - -use crate::mix_nodes::CACHE_REFRESH_RATE; -use crate::state::ExplorerApiStateContext; +use tokio::time::MissedTickBehavior; pub(crate) struct ExplorerApiTasks { state: ExplorerApiStateContext, @@ -39,6 +39,28 @@ impl ExplorerApiTasks { bonds } + async fn retrieve_bonded_nymnodes(&self) -> Result, ValidatorClientError> { + info!("About to retrieve all nymnode bonds..."); + self.state + .inner + .validator_client + .0 + .get_all_cached_bonded_nym_nodes() + .await + } + + async fn retrieve_node_descriptions( + &self, + ) -> Result, ValidatorClientError> { + info!("About to retrieve node descriptions..."); + self.state + .inner + .validator_client + .0 + .get_all_cached_described_nodes() + .await + } + async fn retrieve_all_mixnodes(&self) -> Vec { info!("About to retrieve all mixnode bonds..."); self.retrieve_mixnodes( @@ -130,10 +152,33 @@ impl ExplorerApiTasks { } } + async fn update_nymnodes_cache(&self) { + let nym_node_bonds = self.retrieve_bonded_nymnodes().await.unwrap_or_else(|err| { + error!("failed to retrieve nym node bonds: {err}"); + Vec::new() + }); + + let all_descriptions = self + .retrieve_node_descriptions() + .await + .unwrap_or_else(|err| { + error!("failed to retrieve node descriptions: {err}"); + Vec::new() + }); + + self.state + .inner + .nymnodes + .update_cache(nym_node_bonds, all_descriptions) + .await + } + pub(crate) fn start(mut self) { info!("Spawning mix nodes task runner..."); tokio::spawn(async move { let mut interval_timer = tokio::time::interval(CACHE_REFRESH_RATE); + interval_timer.set_missed_tick_behavior(MissedTickBehavior::Skip); + while !self.shutdown.is_shutdown() { tokio::select! { _ = interval_timer.tick() => { @@ -147,6 +192,10 @@ impl ExplorerApiTasks { info!("Updating mix node cache..."); self.update_mixnode_cache().await; + + info!("Updating nymnode cache..."); + self.update_nymnodes_cache().await; + info!("Done"); } _ = self.shutdown.recv() => { trace!("Listener: Received shutdown"); From afdd721cc3142d22299289ad2c253bc241915abe Mon Sep 17 00:00:00 2001 From: Dinko Zdravac <173912580+dynco-nym@users.noreply.github.com> Date: Tue, 29 Oct 2024 10:39:58 +0100 Subject: [PATCH 33/48] Ns agent workflow (#5055) * feat: add dockerfile * add github workflow for node status agent --------- Co-authored-by: Fran Arbanas --- .github/workflows/push-node-status-agent.yaml | 53 +++++++++++++++++-- nym-node-status-agent/Dockerfile | 28 ++++++++++ 2 files changed, 77 insertions(+), 4 deletions(-) create mode 100644 nym-node-status-agent/Dockerfile diff --git a/.github/workflows/push-node-status-agent.yaml b/.github/workflows/push-node-status-agent.yaml index 750883efee..a66d4dee41 100644 --- a/.github/workflows/push-node-status-agent.yaml +++ b/.github/workflows/push-node-status-agent.yaml @@ -3,9 +3,54 @@ name: Build and upload Node Status agent container to harbor.nymte.ch on: workflow_dispatch: +env: + WORKING_DIRECTORY: "nym-node-status-agent" + CONTAINER_NAME: "node-status-agent" + jobs: - my-job: - runs-on: arc-ubuntu-22.04 + build-container: + runs-on: arc-ubuntu-22.04-dind steps: - - name: my-step - run: echo "Hello World!" + - name: Login to Harbor + uses: docker/login-action@v3 + with: + registry: harbor.nymte.ch + username: ${{ secrets.HARBOR_ROBOT_USERNAME }} + password: ${{ secrets.HARBOR_ROBOT_SECRET }} + + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Configure git identity + run: | + git config --global user.email "lawrence@nymtech.net" + git config --global user.name "Lawrence Stalder" + + - name: Get version from cargo.toml + uses: mikefarah/yq@v4.44.3 + id: get_version + with: + cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml + + - name: Check if tag exists + run: | + if git rev-parse ${{ steps.get_version.outputs.value }} >/dev/null 2>&1; then + echo "Tag ${{ steps.get_version.outputs.value }} already exists" + fi + + - name: Remove existing tag if exists + run: | + if git rev-parse ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }} >/dev/null 2>&1; then + git push --delete origin ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }} + git tag -d ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }} + fi + + - name: Create tag + run: | + git tag -a ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }} -m "Version ${{ steps.get_version.outputs.result }}" + git push origin ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }} + + - name: BuildAndPushImageOnHarbor + run: | + docker build -f ${{ env.WORKING_DIRECTORY }}/Dockerfile . -t harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }}:${{ steps.get_version.outputs.result }} -t harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }}:latest + docker push harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }} --all-tags diff --git a/nym-node-status-agent/Dockerfile b/nym-node-status-agent/Dockerfile new file mode 100644 index 0000000000..0280fc5687 --- /dev/null +++ b/nym-node-status-agent/Dockerfile @@ -0,0 +1,28 @@ +FROM rust:latest AS builder + +RUN apt update && apt install -yy libdbus-1-dev pkg-config libclang-dev + +# Install go +RUN wget https://go.dev/dl/go1.22.5.linux-amd64.tar.gz -O go.tar.gz +RUN tar -xzvf go.tar.gz -C /usr/local + +RUN git clone https://github.com/nymtech/nym-vpn-client /usr/src/nym-vpn-client +ENV PATH=/go/bin:/usr/local/go/bin:$PATH +WORKDIR /usr/src/nym-vpn-client/nym-vpn-core +RUN cargo build --release --package nym-gateway-probe + +COPY ./ /usr/src/nym +WORKDIR /usr/src/nym/nym-node-status-agent +RUN cargo build --release + +FROM ubuntu:24.04 + +RUN apt-get update && apt-get install -y ca-certificates + +WORKDIR /nym + +COPY --from=builder /usr/src/nym/target/release/nym-node-status-agent ./ +COPY --from=builder /usr/src/nym-vpn-client/nym-vpn-core/target/release/nym-gateway-probe ./ + +ENV NODE_STATUS_AGENT_PROBE_PATH=/nym/nym-gateway-probe +ENTRYPOINT [ "/nym/nym-node-status-agent" ] From b747308f74777cd55ae1a2553868ca07ac0ccccc Mon Sep 17 00:00:00 2001 From: Dinko Zdravac <173912580+dynco-nym@users.noreply.github.com> Date: Tue, 29 Oct 2024 10:52:33 +0100 Subject: [PATCH 34/48] Add subcommand to image (#5056) --- nym-node-status-agent/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nym-node-status-agent/Dockerfile b/nym-node-status-agent/Dockerfile index 0280fc5687..1e85757a44 100644 --- a/nym-node-status-agent/Dockerfile +++ b/nym-node-status-agent/Dockerfile @@ -25,4 +25,4 @@ COPY --from=builder /usr/src/nym/target/release/nym-node-status-agent ./ COPY --from=builder /usr/src/nym-vpn-client/nym-vpn-core/target/release/nym-gateway-probe ./ ENV NODE_STATUS_AGENT_PROBE_PATH=/nym/nym-gateway-probe -ENTRYPOINT [ "/nym/nym-node-status-agent" ] +ENTRYPOINT [ "/nym/nym-node-status-agent", "run-probe" ] From ad84a6d85dbb2302ac1bae135748bc96a654f886 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20H=C3=A4ggblad?= Date: Thu, 10 Oct 2024 15:15:40 +0200 Subject: [PATCH 35/48] Add nym-vpn-api crates to main workspace --- Cargo.toml | 5 ++++- .../nym-credential-proxy-requests/Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 42eb03e8fb..7cc41c0cb6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -125,6 +125,9 @@ members = [ "nym-node-status-agent", "nym-outfox", "nym-validator-rewarder", + "nym-vpn-api/vpn-api-lib-wasm", + "nym-vpn-api/vpn-api-requests", + "nym-vpn-api/vpn-api-server", "tools/echo-server", "tools/internal/ssl-inject", # "tools/internal/sdk-version-bump", @@ -237,6 +240,7 @@ defguard_wireguard_rs = { git = "https://github.com/DefGuard/wireguard-rs.git", digest = "0.10.7" dirs = "5.0" doc-comment = "0.3" +dotenv = "0.15.0" dotenvy = "0.15.6" ecdsa = "0.16" ed25519-dalek = "2.1" @@ -408,7 +412,6 @@ wasm-bindgen-futures = "0.4.45" wasmtimer = "0.2.0" web-sys = "0.3.72" - # Profile settings for individual crates # Compile-time verified queries do quite a bit of work at compile time. Incremental diff --git a/nym-credential-proxy/nym-credential-proxy-requests/Cargo.toml b/nym-credential-proxy/nym-credential-proxy-requests/Cargo.toml index 5f1d6a49a0..f1735aa91e 100644 --- a/nym-credential-proxy/nym-credential-proxy-requests/Cargo.toml +++ b/nym-credential-proxy/nym-credential-proxy-requests/Cargo.toml @@ -27,7 +27,7 @@ utoipa = { workspace = true, optional = true } nym-credentials = { path = "../../common/credentials" } nym-credentials-interface = { path = "../../common/credentials-interface" } nym-http-api-common = { path = "../../common/http-api-common", optional = true } -nym-http-api-client = { path = "../../common/http-api-client" } +nym-http-api-client = { path = "../../common/http-api-client" } nym-serde-helpers = { path = "../../common/serde-helpers", features = ["bs58"] } [target."cfg(target_arch = \"wasm32\")".dependencies.wasmtimer] From 8e4d72a565e11609ac3d3be8e7f692ce044c5c69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20H=C3=A4ggblad?= Date: Thu, 24 Oct 2024 09:58:33 +0200 Subject: [PATCH 36/48] Update for rebase --- Cargo.lock | 101 +++++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 10 +++--- 2 files changed, 106 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9dc7a9c6b9..b0564a99e3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2186,6 +2186,12 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" +[[package]] +name = "dotenv" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" + [[package]] name = "dotenvy" version = "0.15.7" @@ -5194,6 +5200,75 @@ dependencies = [ "tokio", ] +[[package]] +name = "nym-credential-proxy" +version = "0.1.1" +dependencies = [ + "anyhow", + "async-trait", + "axum 0.7.7", + "bip39", + "bs58", + "cfg-if", + "clap 4.5.20", + "colored", + "dotenv", + "futures", + "humantime 2.1.0", + "nym-bin-common 0.6.0", + "nym-compact-ecash 0.1.0", + "nym-config 0.1.0", + "nym-credential-proxy-requests", + "nym-credentials", + "nym-credentials-interface 0.1.0", + "nym-crypto 0.4.0", + "nym-http-api-common", + "nym-network-defaults 0.1.0", + "nym-validator-client 0.1.0", + "rand", + "reqwest 0.12.4", + "serde", + "serde_json", + "sqlx", + "strum 0.26.3", + "strum_macros 0.26.4", + "tempfile", + "thiserror", + "time", + "tokio", + "tokio-util", + "tower 0.4.13", + "tower-http", + "tracing", + "url", + "utoipa", + "utoipa-swagger-ui", + "uuid", + "zeroize", +] + +[[package]] +name = "nym-credential-proxy-requests" +version = "0.1.0" +dependencies = [ + "async-trait", + "nym-credentials", + "nym-credentials-interface 0.1.0", + "nym-http-api-client 0.1.0", + "nym-http-api-common", + "nym-serde-helpers 0.1.0", + "reqwest 0.12.4", + "schemars", + "serde", + "serde_json", + "time", + "tsify", + "utoipa", + "uuid", + "wasm-bindgen", + "wasmtimer", +] + [[package]] name = "nym-credential-storage" version = "0.1.0" @@ -7096,6 +7171,31 @@ dependencies = [ "thiserror", ] +[[package]] +name = "nym-vpn-api-lib-wasm" +version = "0.1.0" +dependencies = [ + "bs58", + "getrandom", + "js-sys", + "nym-bin-common 0.6.0", + "nym-compact-ecash 0.1.0", + "nym-credential-proxy-requests", + "nym-credentials", + "nym-credentials-interface 0.1.0", + "nym-crypto 0.4.0", + "nym-ecash-time 0.1.0", + "serde", + "serde-wasm-bindgen 0.6.5", + "serde_json", + "thiserror", + "time", + "tsify", + "wasm-bindgen", + "wasm-utils", + "zeroize", +] + [[package]] name = "nym-wallet-types" version = "1.0.0" @@ -8724,6 +8824,7 @@ dependencies = [ "schemars_derive", "serde", "serde_json", + "uuid", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 7cc41c0cb6..e6fbec5579 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -113,11 +113,14 @@ members = [ "service-providers/common", "service-providers/ip-packet-router", "service-providers/network-requester", - "nym-network-monitor", "nym-api", - "nym-browser-extension/storage", "nym-api/nym-api-requests", + "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-data-observatory", + "nym-network-monitor", "nym-node", "nym-node/nym-node-http-api", "nym-node/nym-node-requests", @@ -125,9 +128,6 @@ members = [ "nym-node-status-agent", "nym-outfox", "nym-validator-rewarder", - "nym-vpn-api/vpn-api-lib-wasm", - "nym-vpn-api/vpn-api-requests", - "nym-vpn-api/vpn-api-server", "tools/echo-server", "tools/internal/ssl-inject", # "tools/internal/sdk-version-bump", From 25e3b4cd83ed7c3424fb13433f1db31d8005c2af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20H=C3=A4ggblad?= Date: Thu, 24 Oct 2024 10:06:05 +0200 Subject: [PATCH 37/48] Delete old Cargo files --- nym-credential-proxy/Cargo.lock | 5388 ------------------------------- nym-credential-proxy/Cargo.toml | 62 - 2 files changed, 5450 deletions(-) delete mode 100644 nym-credential-proxy/Cargo.lock delete mode 100644 nym-credential-proxy/Cargo.toml diff --git a/nym-credential-proxy/Cargo.lock b/nym-credential-proxy/Cargo.lock deleted file mode 100644 index c8c6b85b57..0000000000 --- a/nym-credential-proxy/Cargo.lock +++ /dev/null @@ -1,5388 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "addr2line" -version = "0.24.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler2" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" - -[[package]] -name = "aes" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" -dependencies = [ - "cfg-if", - "cipher", - "cpufeatures", -] - -[[package]] -name = "ahash" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" -dependencies = [ - "getrandom", - "once_cell", - "version_check", -] - -[[package]] -name = "ahash" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" -dependencies = [ - "cfg-if", - "getrandom", - "once_cell", - "version_check", - "zerocopy", -] - -[[package]] -name = "aho-corasick" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" -dependencies = [ - "memchr", -] - -[[package]] -name = "allocator-api2" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" - -[[package]] -name = "anstream" -version = "0.6.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "is_terminal_polyfill", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" - -[[package]] -name = "anstyle-parse" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" -dependencies = [ - "windows-sys 0.52.0", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" -dependencies = [ - "anstyle", - "windows-sys 0.52.0", -] - -[[package]] -name = "anyhow" -version = "1.0.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" - -[[package]] -name = "arbitrary" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" -dependencies = [ - "derive_arbitrary", -] - -[[package]] -name = "arrayref" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" - -[[package]] -name = "async-trait" -version = "0.1.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.79", -] - -[[package]] -name = "atoi" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" -dependencies = [ - "num-traits", -] - -[[package]] -name = "atomic-waker" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" - -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi", -] - -[[package]] -name = "autocfg" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" - -[[package]] -name = "axum" -version = "0.7.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "504e3947307ac8326a5437504c517c4b56716c9d98fac0028c2acc7ca47d70ae" -dependencies = [ - "async-trait", - "axum-core", - "bytes", - "futures-util", - "http 1.1.0", - "http-body 1.0.1", - "http-body-util", - "hyper 1.4.1", - "hyper-util", - "itoa", - "matchit", - "memchr", - "mime", - "percent-encoding", - "pin-project-lite", - "rustversion", - "serde", - "serde_json", - "serde_path_to_error", - "serde_urlencoded", - "sync_wrapper 1.0.1", - "tokio", - "tower", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "axum-core" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" -dependencies = [ - "async-trait", - "bytes", - "futures-util", - "http 1.1.0", - "http-body 1.0.1", - "http-body-util", - "mime", - "pin-project-lite", - "rustversion", - "sync_wrapper 1.0.1", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "backtrace" -version = "0.3.74" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" -dependencies = [ - "addr2line", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", - "windows-targets 0.52.6", -] - -[[package]] -name = "base16ct" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" - -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - -[[package]] -name = "base64" -version = "0.21.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" - -[[package]] -name = "base64" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" - -[[package]] -name = "base64ct" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" - -[[package]] -name = "bincode" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" -dependencies = [ - "serde", -] - -[[package]] -name = "bip32" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa13fae8b6255872fd86f7faf4b41168661d7d78609f7bfe6771b85c6739a15b" -dependencies = [ - "bs58", - "hmac", - "k256", - "rand_core 0.6.4", - "ripemd", - "sha2 0.10.8", - "subtle 2.6.1", - "zeroize", -] - -[[package]] -name = "bip39" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33415e24172c1b7d6066f6d999545375ab8e1d95421d6784bdfff9496f292387" -dependencies = [ - "bitcoin_hashes", - "rand", - "rand_core 0.6.4", - "serde", - "unicode-normalization", - "zeroize", -] - -[[package]] -name = "bitcoin-internals" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9425c3bf7089c983facbae04de54513cce73b41c7f9ff8c845b54e7bc64ebbfb" - -[[package]] -name = "bitcoin_hashes" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1930a4dabfebb8d7d9992db18ebe3ae2876f0a305fab206fd168df931ede293b" -dependencies = [ - "bitcoin-internals", - "hex-conservative", -] - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" -dependencies = [ - "serde", -] - -[[package]] -name = "blake2" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94cb07b0da6a73955f8fb85d24c466778e70cda767a568229b104f0264089330" -dependencies = [ - "byte-tools", - "crypto-mac", - "digest 0.8.1", - "opaque-debug 0.2.3", -] - -[[package]] -name = "block-buffer" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -dependencies = [ - "generic-array 0.14.7", -] - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array 0.14.7", -] - -[[package]] -name = "bls12_381" -version = "0.8.0" -source = "git+https://github.com/jstuczyn/bls12_381?branch=temp/experimental-serdect#22cd0a16b674af1629110a2dc8b6cf6c73ea4cd9" -dependencies = [ - "digest 0.9.0", - "ff", - "group", - "pairing", - "rand_core 0.6.4", - "serde", - "serdect 0.3.0-rc.0", - "subtle 2.6.1", - "zeroize", -] - -[[package]] -name = "bnum" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab9008b6bb9fc80b5277f2fe481c09e828743d9151203e804583eb4c9e15b31d" - -[[package]] -name = "bs58" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" -dependencies = [ - "sha2 0.10.8", - "tinyvec", -] - -[[package]] -name = "bumpalo" -version = "3.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" - -[[package]] -name = "byte-tools" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - -[[package]] -name = "bytes" -version = "1.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" -dependencies = [ - "serde", -] - -[[package]] -name = "camino" -version = "1.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" -dependencies = [ - "serde", -] - -[[package]] -name = "cargo-platform" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" -dependencies = [ - "serde", -] - -[[package]] -name = "cargo_metadata" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" -dependencies = [ - "camino", - "cargo-platform", - "semver", - "serde", - "serde_json", - "thiserror", -] - -[[package]] -name = "cc" -version = "1.1.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e80e3b6a3ab07840e1cae9b0666a63970dc28e8ed5ffbcdacbfc760c281bfc1" -dependencies = [ - "shlex", -] - -[[package]] -name = "celes" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39b9a21273925d7cc9e8a9a5f068122341336813c607014f5ef64f82b6acba58" -dependencies = [ - "serde", -] - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "chacha" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddf3c081b5fba1e5615640aae998e0fbd10c24cbd897ee39ed754a77601a4862" -dependencies = [ - "byteorder", - "keystream", -] - -[[package]] -name = "cipher" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" -dependencies = [ - "crypto-common", - "inout", -] - -[[package]] -name = "clap" -version = "4.5.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" -dependencies = [ - "clap_builder", - "clap_derive", -] - -[[package]] -name = "clap_builder" -version = "4.5.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" -dependencies = [ - "anstream", - "anstyle", - "clap_lex", - "strsim", -] - -[[package]] -name = "clap_derive" -version = "4.5.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" -dependencies = [ - "heck 0.5.0", - "proc-macro2", - "quote", - "syn 2.0.79", -] - -[[package]] -name = "clap_lex" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" - -[[package]] -name = "colorchoice" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" - -[[package]] -name = "colored" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" -dependencies = [ - "lazy_static", - "windows-sys 0.48.0", -] - -[[package]] -name = "const-oid" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" - -[[package]] -name = "const-str" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3618cccc083bb987a415d85c02ca6c9994ea5b44731ec28b9ecf09658655fba9" - -[[package]] -name = "core-foundation" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" - -[[package]] -name = "cosmos-sdk-proto" -version = "0.22.0-pre" -source = "git+https://github.com/cosmos/cosmos-rust?rev=4b1332e6d8258ac845cef71589c8d362a669675a#4b1332e6d8258ac845cef71589c8d362a669675a" -dependencies = [ - "prost", - "prost-types", - "tendermint-proto", -] - -[[package]] -name = "cosmrs" -version = "0.17.0-pre" -source = "git+https://github.com/cosmos/cosmos-rust?rev=4b1332e6d8258ac845cef71589c8d362a669675a#4b1332e6d8258ac845cef71589c8d362a669675a" -dependencies = [ - "bip32", - "cosmos-sdk-proto", - "ecdsa", - "eyre", - "k256", - "rand_core 0.6.4", - "serde", - "serde_json", - "signature", - "subtle-encoding", - "tendermint", - "tendermint-rpc", - "thiserror", -] - -[[package]] -name = "cosmwasm-crypto" -version = "1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ed6aa9f904de106fa16443ad14ec2abe75e94ba003bb61c681c0e43d4c58d2a" -dependencies = [ - "digest 0.10.7", - "ecdsa", - "ed25519-zebra", - "k256", - "rand_core 0.6.4", - "thiserror", -] - -[[package]] -name = "cosmwasm-derive" -version = "1.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8e07de16c800ac82fd188d055ecdb923ead0cf33960d3350089260bb982c09f" -dependencies = [ - "syn 1.0.109", -] - -[[package]] -name = "cosmwasm-schema" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11ae2e971fb831d0c4fa3c8c3d2291cdbdd73786a73d65196dbf983d9b2468af" -dependencies = [ - "cosmwasm-schema-derive", - "schemars", - "serde", - "serde_json", - "thiserror", -] - -[[package]] -name = "cosmwasm-schema-derive" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cadc57fd0825b85bc2f9b972c17da718b9efb4bc17e5935cc2d6036324f853d" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "cosmwasm-std" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e98e19fae6c3f468412f731274b0f9434602722009d6a77432d39c7c4bb09202" -dependencies = [ - "base64 0.21.7", - "bnum", - "cosmwasm-crypto", - "cosmwasm-derive", - "derivative", - "forward_ref", - "hex", - "schemars", - "serde", - "serde-json-wasm", - "sha2 0.10.8", - "thiserror", -] - -[[package]] -name = "cpufeatures" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" -dependencies = [ - "libc", -] - -[[package]] -name = "crc" -version = "3.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" -dependencies = [ - "crc-catalog", -] - -[[package]] -name = "crc-catalog" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" - -[[package]] -name = "crc32fast" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crossbeam-queue" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" - -[[package]] -name = "crypto-bigint" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" -dependencies = [ - "generic-array 0.14.7", - "rand_core 0.6.4", - "subtle 2.6.1", - "zeroize", -] - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array 0.14.7", - "typenum", -] - -[[package]] -name = "crypto-mac" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5" -dependencies = [ - "generic-array 0.12.4", - "subtle 1.0.0", -] - -[[package]] -name = "ctr" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" -dependencies = [ - "cipher", -] - -[[package]] -name = "curve25519-dalek" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" -dependencies = [ - "byteorder", - "digest 0.9.0", - "rand_core 0.5.1", - "subtle 2.6.1", - "zeroize", -] - -[[package]] -name = "curve25519-dalek" -version = "4.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" -dependencies = [ - "cfg-if", - "cpufeatures", - "curve25519-dalek-derive", - "digest 0.10.7", - "fiat-crypto", - "rustc_version", - "serde", - "subtle 2.6.1", - "zeroize", -] - -[[package]] -name = "curve25519-dalek-derive" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.79", -] - -[[package]] -name = "curve25519-dalek-ng" -version = "4.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c359b7249347e46fb28804470d071c921156ad62b3eef5d34e2ba867533dec8" -dependencies = [ - "byteorder", - "digest 0.9.0", - "rand_core 0.6.4", - "subtle-ng", - "zeroize", -] - -[[package]] -name = "cw-controllers" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5d8edce4b78785f36413f67387e4be7d0cb7d032b5d4164bcc024f9c3f3f2ea" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-storage-plus", - "cw-utils", - "schemars", - "serde", - "thiserror", -] - -[[package]] -name = "cw-storage-plus" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5ff29294ee99373e2cd5fd21786a3c0ced99a52fec2ca347d565489c61b723c" -dependencies = [ - "cosmwasm-std", - "schemars", - "serde", -] - -[[package]] -name = "cw-utils" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c80e93d1deccb8588db03945016a292c3c631e6325d349ebb35d2db6f4f946f7" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw2", - "schemars", - "semver", - "serde", - "thiserror", -] - -[[package]] -name = "cw2" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6c120b24fbbf5c3bedebb97f2cc85fbfa1c3287e09223428e7e597b5293c1fa" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-storage-plus", - "schemars", - "semver", - "serde", - "thiserror", -] - -[[package]] -name = "cw20" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "526e39bb20534e25a1cd0386727f0038f4da294e5e535729ba3ef54055246abd" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-utils", - "schemars", - "serde", -] - -[[package]] -name = "cw3" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2967fbd073d4b626dd9e7148e05a84a3bebd9794e71342e12351110ffbb12395" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-utils", - "cw20", - "schemars", - "serde", - "thiserror", -] - -[[package]] -name = "cw4" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24754ff6e45f2a1c60adc409d9b2eb87666012c44021329141ffaab3388fccd2" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-storage-plus", - "schemars", - "serde", -] - -[[package]] -name = "der" -version = "0.7.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" -dependencies = [ - "const-oid", - "pem-rfc7468", - "zeroize", -] - -[[package]] -name = "deranged" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" -dependencies = [ - "powerfmt", - "serde", -] - -[[package]] -name = "derivative" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "derive_arbitrary" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.79", -] - -[[package]] -name = "digest" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" -dependencies = [ - "generic-array 0.12.4", -] - -[[package]] -name = "digest" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" -dependencies = [ - "generic-array 0.14.7", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer 0.10.4", - "const-oid", - "crypto-common", - "subtle 2.6.1", -] - -[[package]] -name = "dirs" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" -dependencies = [ - "dirs-sys", -] - -[[package]] -name = "dirs-sys" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" -dependencies = [ - "libc", - "option-ext", - "redox_users", - "windows-sys 0.48.0", -] - -[[package]] -name = "displaydoc" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.79", -] - -[[package]] -name = "dotenv" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" - -[[package]] -name = "dotenvy" -version = "0.15.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" - -[[package]] -name = "dyn-clone" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" - -[[package]] -name = "ecdsa" -version = "0.16.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" -dependencies = [ - "der", - "digest 0.10.7", - "elliptic-curve", - "rfc6979", - "serdect 0.2.0", - "signature", - "spki", -] - -[[package]] -name = "ed25519" -version = "2.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" -dependencies = [ - "pkcs8", - "serde", - "signature", -] - -[[package]] -name = "ed25519-consensus" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c8465edc8ee7436ffea81d21a019b16676ee3db267aa8d5a8d729581ecf998b" -dependencies = [ - "curve25519-dalek-ng", - "hex", - "rand_core 0.6.4", - "sha2 0.9.9", - "zeroize", -] - -[[package]] -name = "ed25519-dalek" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" -dependencies = [ - "curve25519-dalek 4.1.3", - "ed25519", - "rand_core 0.6.4", - "serde", - "sha2 0.10.8", - "subtle 2.6.1", - "zeroize", -] - -[[package]] -name = "ed25519-zebra" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c24f403d068ad0b359e577a77f92392118be3f3c927538f2bb544a5ecd828c6" -dependencies = [ - "curve25519-dalek 3.2.0", - "hashbrown 0.12.3", - "hex", - "rand_core 0.6.4", - "serde", - "sha2 0.9.9", - "zeroize", -] - -[[package]] -name = "either" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" -dependencies = [ - "serde", -] - -[[package]] -name = "elliptic-curve" -version = "0.13.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9775b22bc152ad86a0cf23f0f348b884b26add12bf741e7ffc4d4ab2ab4d205" -dependencies = [ - "base16ct", - "crypto-bigint", - "digest 0.10.7", - "ff", - "generic-array 0.14.7", - "group", - "pkcs8", - "rand_core 0.6.4", - "sec1", - "serdect 0.2.0", - "subtle 2.6.1", - "zeroize", -] - -[[package]] -name = "encoding_rs" -version = "0.8.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "env_logger" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" -dependencies = [ - "atty", - "humantime 1.3.0", - "log", - "regex", - "termcolor", -] - -[[package]] -name = "equivalent" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" - -[[package]] -name = "errno" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "etcetera" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943" -dependencies = [ - "cfg-if", - "home", - "windows-sys 0.48.0", -] - -[[package]] -name = "event-listener" -version = "2.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" - -[[package]] -name = "eyre" -version = "0.6.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" -dependencies = [ - "indenter", - "once_cell", -] - -[[package]] -name = "fastrand" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" - -[[package]] -name = "ff" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" -dependencies = [ - "rand_core 0.6.4", - "subtle 2.6.1", -] - -[[package]] -name = "fiat-crypto" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" - -[[package]] -name = "flate2" -version = "1.0.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" -dependencies = [ - "crc32fast", - "miniz_oxide", -] - -[[package]] -name = "flex-error" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c606d892c9de11507fa0dcffc116434f94e105d0bbdc4e405b61519464c49d7b" -dependencies = [ - "eyre", - "paste", -] - -[[package]] -name = "flume" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" -dependencies = [ - "futures-core", - "futures-sink", - "spin", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - -[[package]] -name = "form_urlencoded" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "forward_ref" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8cbd1169bd7b4a0a20d92b9af7a7e0422888bd38a6f5ec29c1fd8c1558a272e" - -[[package]] -name = "futures" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" - -[[package]] -name = "futures-executor" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-intrusive" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" -dependencies = [ - "futures-core", - "lock_api", - "parking_lot", -] - -[[package]] -name = "futures-io" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" - -[[package]] -name = "futures-macro" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.79", -] - -[[package]] -name = "futures-sink" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" - -[[package]] -name = "futures-task" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" - -[[package]] -name = "futures-util" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - -[[package]] -name = "generic-array" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" -dependencies = [ - "typenum", -] - -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", - "zeroize", -] - -[[package]] -name = "getrandom" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "wasi", - "wasm-bindgen", -] - -[[package]] -name = "getset" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f636605b743120a8d32ed92fc27b6cde1a769f8f936c065151eb66f88ded513c" -dependencies = [ - "proc-macro-error2", - "proc-macro2", - "quote", - "syn 2.0.79", -] - -[[package]] -name = "gimli" -version = "0.31.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" - -[[package]] -name = "gloo-utils" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037fcb07216cb3a30f7292bd0176b050b7b9a052ba830ef7d5d65f6dc64ba58e" -dependencies = [ - "js-sys", - "serde", - "serde_json", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "gloo-utils" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa" -dependencies = [ - "js-sys", - "serde", - "serde_json", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "group" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" -dependencies = [ - "ff", - "rand_core 0.6.4", - "subtle 2.6.1", -] - -[[package]] -name = "h2" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http 0.2.12", - "indexmap 2.6.0", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "h2" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" -dependencies = [ - "atomic-waker", - "bytes", - "fnv", - "futures-core", - "futures-sink", - "http 1.1.0", - "indexmap 2.6.0", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "handlebars" -version = "3.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4498fc115fa7d34de968184e473529abb40eeb6be8bc5f7faba3d08c316cb3e3" -dependencies = [ - "log", - "pest", - "pest_derive", - "quick-error 2.0.1", - "serde", - "serde_json", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -dependencies = [ - "ahash 0.7.8", -] - -[[package]] -name = "hashbrown" -version = "0.14.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" -dependencies = [ - "ahash 0.8.11", - "allocator-api2", -] - -[[package]] -name = "hashbrown" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" - -[[package]] -name = "hashlink" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" -dependencies = [ - "hashbrown 0.14.5", -] - -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "hex-conservative" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "212ab92002354b4819390025006c897e8140934349e8635c9b077f47b4dcbd20" - -[[package]] -name = "hkdf" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" -dependencies = [ - "hmac", -] - -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest 0.10.7", -] - -[[package]] -name = "home" -version = "0.5.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" -dependencies = [ - "windows-sys 0.52.0", -] - -[[package]] -name = "http" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "http" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "http-body" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" -dependencies = [ - "bytes", - "http 0.2.12", - "pin-project-lite", -] - -[[package]] -name = "http-body" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" -dependencies = [ - "bytes", - "http 1.1.0", -] - -[[package]] -name = "http-body-util" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" -dependencies = [ - "bytes", - "futures-util", - "http 1.1.0", - "http-body 1.0.1", - "pin-project-lite", -] - -[[package]] -name = "httparse" -version = "1.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" - -[[package]] -name = "httpdate" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" - -[[package]] -name = "humantime" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" -dependencies = [ - "quick-error 1.2.3", -] - -[[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - -[[package]] -name = "humantime-serde" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57a3db5ea5923d99402c94e9feb261dc5ee9b4efa158b0315f788cf549cc200c" -dependencies = [ - "humantime 2.1.0", - "serde", -] - -[[package]] -name = "hyper" -version = "0.14.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2 0.3.26", - "http 0.2.12", - "http-body 0.4.6", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", - "want", -] - -[[package]] -name = "hyper" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" -dependencies = [ - "bytes", - "futures-channel", - "futures-util", - "h2 0.4.6", - "http 1.1.0", - "http-body 1.0.1", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "smallvec", - "tokio", - "want", -] - -[[package]] -name = "hyper-rustls" -version = "0.24.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" -dependencies = [ - "futures-util", - "http 0.2.12", - "hyper 0.14.30", - "rustls 0.21.12", - "tokio", - "tokio-rustls 0.24.1", -] - -[[package]] -name = "hyper-rustls" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0bea761b46ae2b24eb4aef630d8d1c398157b6fc29e6350ecf090a0b70c952c" -dependencies = [ - "futures-util", - "http 1.1.0", - "hyper 1.4.1", - "hyper-util", - "rustls 0.22.4", - "rustls-pki-types", - "tokio", - "tokio-rustls 0.25.0", - "tower-service", -] - -[[package]] -name = "hyper-tls" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" -dependencies = [ - "bytes", - "http-body-util", - "hyper 1.4.1", - "hyper-util", - "native-tls", - "tokio", - "tokio-native-tls", - "tower-service", -] - -[[package]] -name = "hyper-util" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41296eb09f183ac68eec06e03cdbea2e759633d4067b2f6552fc2e009bcad08b" -dependencies = [ - "bytes", - "futures-channel", - "futures-util", - "http 1.1.0", - "http-body 1.0.1", - "hyper 1.4.1", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", -] - -[[package]] -name = "idna" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "indenter" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" - -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", - "serde", -] - -[[package]] -name = "indexmap" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" -dependencies = [ - "equivalent", - "hashbrown 0.15.0", - "serde", -] - -[[package]] -name = "inout" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" -dependencies = [ - "generic-array 0.14.7", -] - -[[package]] -name = "ipnet" -version = "2.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" - -[[package]] -name = "is_terminal_polyfill" -version = "1.70.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" - -[[package]] -name = "itertools" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" -dependencies = [ - "either", -] - -[[package]] -name = "itertools" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" - -[[package]] -name = "js-sys" -version = "0.3.71" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cb94a0ffd3f3ee755c20f7d8752f45cac88605a4dcf808abcff72873296ec7b" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "k256" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f01b677d82ef7a676aa37e099defd83a28e15687112cafdd112d60236b6115b" -dependencies = [ - "cfg-if", - "ecdsa", - "elliptic-curve", - "once_cell", - "sha2 0.10.8", - "signature", -] - -[[package]] -name = "keystream" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c33070833c9ee02266356de0c43f723152bd38bd96ddf52c82b3af10c9138b28" - -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" -dependencies = [ - "spin", -] - -[[package]] -name = "libc" -version = "0.2.159" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" - -[[package]] -name = "libm" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" - -[[package]] -name = "libredox" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" -dependencies = [ - "bitflags 2.6.0", - "libc", -] - -[[package]] -name = "libsqlite3-sys" -version = "0.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4e226dcd58b4be396f7bd3c20da8fdee2911400705297ba7d2d7cc2c30f716" -dependencies = [ - "cc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "linux-raw-sys" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" - -[[package]] -name = "lioness" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae926706ba42c425c9457121178330d75e273df2e82e28b758faf3de3a9acb9" -dependencies = [ - "arrayref", - "blake2", - "chacha", - "keystream", -] - -[[package]] -name = "lock_api" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" -dependencies = [ - "autocfg", - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" - -[[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 = "matchit" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" - -[[package]] -name = "md-5" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" -dependencies = [ - "cfg-if", - "digest 0.10.7", -] - -[[package]] -name = "memchr" -version = "2.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" - -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - -[[package]] -name = "mime_guess" -version = "2.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" -dependencies = [ - "mime", - "unicase", -] - -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - -[[package]] -name = "miniz_oxide" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" -dependencies = [ - "adler2", -] - -[[package]] -name = "mio" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" -dependencies = [ - "hermit-abi 0.3.9", - "libc", - "wasi", - "windows-sys 0.52.0", -] - -[[package]] -name = "native-tls" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" -dependencies = [ - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - -[[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" -dependencies = [ - "memchr", - "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-dig" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" -dependencies = [ - "byteorder", - "lazy_static", - "libm", - "num-integer", - "num-iter", - "num-traits", - "rand", - "smallvec", - "zeroize", -] - -[[package]] -name = "num-conv" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" - -[[package]] -name = "num-integer" -version = "0.1.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-iter" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", - "libm", -] - -[[package]] -name = "num_enum" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" -dependencies = [ - "num_enum_derive", -] - -[[package]] -name = "num_enum_derive" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 2.0.79", -] - -[[package]] -name = "num_threads" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" -dependencies = [ - "libc", -] - -[[package]] -name = "nym-api-requests" -version = "0.1.0" -dependencies = [ - "bs58", - "cosmrs", - "cosmwasm-std", - "ecdsa", - "getset", - "nym-compact-ecash", - "nym-credentials-interface", - "nym-crypto", - "nym-ecash-time", - "nym-mixnet-contract-common", - "nym-network-defaults", - "nym-node-requests", - "nym-serde-helpers", - "schemars", - "serde", - "serde_json", - "sha2 0.10.8", - "tendermint", - "thiserror", - "time", - "utoipa", -] - -[[package]] -name = "nym-bin-common" -version = "0.6.0" -dependencies = [ - "const-str", - "log", - "pretty_env_logger", - "schemars", - "semver", - "serde", - "tracing-subscriber", - "utoipa", - "vergen", -] - -[[package]] -name = "nym-coconut-bandwidth-contract-common" -version = "0.1.0" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "nym-multisig-contract-common", -] - -[[package]] -name = "nym-coconut-dkg-common" -version = "0.1.0" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-utils", - "cw2", - "cw4", - "nym-contracts-common", - "nym-multisig-contract-common", -] - -[[package]] -name = "nym-compact-ecash" -version = "0.1.0" -dependencies = [ - "bincode", - "bls12_381", - "bs58", - "cfg-if", - "digest 0.9.0", - "ff", - "group", - "itertools 0.12.1", - "nym-network-defaults", - "nym-pemstore", - "rand", - "serde", - "sha2 0.9.9", - "subtle 2.6.1", - "thiserror", - "zeroize", -] - -[[package]] -name = "nym-config" -version = "0.1.0" -dependencies = [ - "dirs", - "handlebars", - "log", - "nym-network-defaults", - "serde", - "toml", - "url", -] - -[[package]] -name = "nym-contracts-common" -version = "0.5.0" -dependencies = [ - "bs58", - "cosmwasm-schema", - "cosmwasm-std", - "cw-storage-plus", - "schemars", - "serde", - "thiserror", - "vergen", -] - -[[package]] -name = "nym-credential-proxy" -version = "0.1.0" -dependencies = [ - "anyhow", - "async-trait", - "axum", - "bip39", - "bs58", - "cfg-if", - "clap", - "colored", - "dotenv", - "futures", - "humantime 2.1.0", - "nym-bin-common", - "nym-compact-ecash", - "nym-config", - "nym-credential-proxy-requests", - "nym-credentials", - "nym-credentials-interface", - "nym-crypto", - "nym-http-api-common", - "nym-network-defaults", - "nym-validator-client", - "rand", - "reqwest 0.12.4", - "serde", - "serde_json", - "sqlx", - "strum", - "strum_macros", - "tempfile", - "thiserror", - "time", - "tokio", - "tokio-util", - "tower", - "tower-http", - "tracing", - "url", - "utoipa", - "utoipa-swagger-ui", - "uuid", - "zeroize", -] - -[[package]] -name = "nym-credential-proxy-requests" -version = "0.1.0" -dependencies = [ - "async-trait", - "nym-credentials", - "nym-credentials-interface", - "nym-http-api-client", - "nym-http-api-common", - "nym-serde-helpers", - "reqwest 0.12.4", - "schemars", - "serde", - "serde_json", - "time", - "tsify", - "utoipa", - "uuid", - "wasm-bindgen", - "wasmtimer", -] - -[[package]] -name = "nym-credentials" -version = "0.1.0" -dependencies = [ - "bincode", - "bls12_381", - "cosmrs", - "log", - "nym-api-requests", - "nym-credentials-interface", - "nym-crypto", - "nym-ecash-contract-common", - "nym-ecash-time", - "nym-network-defaults", - "nym-serde-helpers", - "nym-validator-client", - "serde", - "thiserror", - "time", - "zeroize", -] - -[[package]] -name = "nym-credentials-interface" -version = "0.1.0" -dependencies = [ - "bls12_381", - "nym-compact-ecash", - "nym-ecash-time", - "nym-network-defaults", - "rand", - "serde", - "strum", - "thiserror", - "time", -] - -[[package]] -name = "nym-crypto" -version = "0.4.0" -dependencies = [ - "bs58", - "ed25519-dalek", - "nym-pemstore", - "nym-sphinx-types", - "rand", - "serde", - "serde_bytes", - "subtle-encoding", - "thiserror", - "x25519-dalek", - "zeroize", -] - -[[package]] -name = "nym-ecash-contract-common" -version = "0.1.0" -dependencies = [ - "bs58", - "cosmwasm-schema", - "cosmwasm-std", - "cw-controllers", - "cw-utils", - "nym-multisig-contract-common", - "thiserror", -] - -[[package]] -name = "nym-ecash-time" -version = "0.1.0" -dependencies = [ - "nym-compact-ecash", - "time", -] - -[[package]] -name = "nym-exit-policy" -version = "0.1.0" -dependencies = [ - "serde", - "serde_json", - "thiserror", - "tracing", - "utoipa", -] - -[[package]] -name = "nym-group-contract-common" -version = "0.1.0" -dependencies = [ - "cosmwasm-schema", - "cw-controllers", - "cw4", - "schemars", - "serde", -] - -[[package]] -name = "nym-http-api-client" -version = "0.1.0" -dependencies = [ - "async-trait", - "http 1.1.0", - "nym-bin-common", - "reqwest 0.12.4", - "serde", - "serde_json", - "thiserror", - "tracing", - "url", - "wasmtimer", -] - -[[package]] -name = "nym-http-api-common" -version = "0.1.0" -dependencies = [ - "axum", - "bytes", - "colored", - "mime", - "serde", - "serde_json", - "serde_yaml", - "tracing", - "utoipa", -] - -[[package]] -name = "nym-mixnet-contract-common" -version = "0.6.0" -dependencies = [ - "bs58", - "cosmwasm-schema", - "cosmwasm-std", - "cw-controllers", - "cw-storage-plus", - "humantime-serde", - "log", - "nym-contracts-common", - "schemars", - "serde", - "serde-json-wasm", - "serde_repr", - "thiserror", - "time", -] - -[[package]] -name = "nym-multisig-contract-common" -version = "0.1.0" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-storage-plus", - "cw-utils", - "cw3", - "cw4", - "schemars", - "serde", - "thiserror", -] - -[[package]] -name = "nym-network-defaults" -version = "0.1.0" -dependencies = [ - "dotenvy", - "log", - "schemars", - "serde", - "url", - "utoipa", -] - -[[package]] -name = "nym-node-requests" -version = "0.1.0" -dependencies = [ - "base64 0.22.1", - "celes", - "humantime 2.1.0", - "humantime-serde", - "nym-bin-common", - "nym-crypto", - "nym-exit-policy", - "nym-wireguard-types", - "schemars", - "serde", - "serde_json", - "thiserror", - "time", - "utoipa", -] - -[[package]] -name = "nym-pemstore" -version = "0.3.0" -dependencies = [ - "pem", -] - -[[package]] -name = "nym-serde-helpers" -version = "0.1.0" -dependencies = [ - "base64 0.22.1", - "bs58", - "hex", - "serde", - "time", -] - -[[package]] -name = "nym-sphinx-types" -version = "0.2.0" -dependencies = [ - "sphinx-packet", - "thiserror", -] - -[[package]] -name = "nym-validator-client" -version = "0.1.0" -dependencies = [ - "async-trait", - "base64 0.22.1", - "bip32", - "bip39", - "colored", - "cosmrs", - "cosmwasm-std", - "cw-controllers", - "cw-utils", - "cw2", - "cw3", - "cw4", - "eyre", - "flate2", - "futures", - "itertools 0.13.0", - "log", - "nym-api-requests", - "nym-coconut-bandwidth-contract-common", - "nym-coconut-dkg-common", - "nym-compact-ecash", - "nym-config", - "nym-contracts-common", - "nym-ecash-contract-common", - "nym-group-contract-common", - "nym-http-api-client", - "nym-mixnet-contract-common", - "nym-multisig-contract-common", - "nym-network-defaults", - "nym-serde-helpers", - "nym-vesting-contract-common", - "prost", - "reqwest 0.12.4", - "serde", - "serde_json", - "sha2 0.9.9", - "tendermint-rpc", - "thiserror", - "time", - "tokio", - "url", - "wasmtimer", - "zeroize", -] - -[[package]] -name = "nym-vesting-contract-common" -version = "0.7.0" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "nym-contracts-common", - "nym-mixnet-contract-common", - "serde", - "thiserror", -] - -[[package]] -name = "nym-vpn-api-lib-wasm" -version = "0.1.0" -dependencies = [ - "bs58", - "getrandom", - "js-sys", - "nym-bin-common", - "nym-compact-ecash", - "nym-credential-proxy-requests", - "nym-credentials", - "nym-credentials-interface", - "nym-crypto", - "nym-ecash-time", - "serde", - "serde-wasm-bindgen 0.6.5", - "serde_json", - "thiserror", - "time", - "tsify", - "wasm-bindgen", - "wasm-utils", - "zeroize", -] - -[[package]] -name = "nym-wireguard-types" -version = "0.1.0" -dependencies = [ - "base64 0.22.1", - "log", - "nym-config", - "nym-network-defaults", - "serde", - "thiserror", - "x25519-dalek", -] - -[[package]] -name = "object" -version = "0.36.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" -dependencies = [ - "memchr", -] - -[[package]] -name = "once_cell" -version = "1.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" - -[[package]] -name = "opaque-debug" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" - -[[package]] -name = "opaque-debug" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" - -[[package]] -name = "openssl" -version = "0.10.66" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" -dependencies = [ - "bitflags 2.6.0", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.79", -] - -[[package]] -name = "openssl-probe" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" - -[[package]] -name = "openssl-sys" -version = "0.9.103" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "option-ext" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" - -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - -[[package]] -name = "pairing" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fec4625e73cf41ef4bb6846cafa6d44736525f442ba45e407c4a000a13996f" -dependencies = [ - "group", -] - -[[package]] -name = "parking_lot" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-targets 0.52.6", -] - -[[package]] -name = "paste" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" - -[[package]] -name = "peg" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "295283b02df346d1ef66052a757869b2876ac29a6bb0ac3f5f7cd44aebe40e8f" -dependencies = [ - "peg-macros", - "peg-runtime", -] - -[[package]] -name = "peg-macros" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdad6a1d9cf116a059582ce415d5f5566aabcd4008646779dab7fdc2a9a9d426" -dependencies = [ - "peg-runtime", - "proc-macro2", - "quote", -] - -[[package]] -name = "peg-runtime" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3aeb8f54c078314c2065ee649a7241f46b9d8e418e1a9581ba0546657d7aa3a" - -[[package]] -name = "pem" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd56cbd21fea48d0c440b41cd69c589faacade08c992d9a54e471b79d0fd13eb" -dependencies = [ - "base64 0.13.1", - "once_cell", - "regex", -] - -[[package]] -name = "pem-rfc7468" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" -dependencies = [ - "base64ct", -] - -[[package]] -name = "percent-encoding" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" - -[[package]] -name = "pest" -version = "2.7.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdbef9d1d47087a895abd220ed25eb4ad973a5e26f6a4367b038c25e28dfc2d9" -dependencies = [ - "memchr", - "thiserror", - "ucd-trie", -] - -[[package]] -name = "pest_derive" -version = "2.7.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d3a6e3394ec80feb3b6393c725571754c6188490265c61aaf260810d6b95aa0" -dependencies = [ - "pest", - "pest_generator", -] - -[[package]] -name = "pest_generator" -version = "2.7.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94429506bde1ca69d1b5601962c73f4172ab4726571a59ea95931218cb0e930e" -dependencies = [ - "pest", - "pest_meta", - "proc-macro2", - "quote", - "syn 2.0.79", -] - -[[package]] -name = "pest_meta" -version = "2.7.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac8a071862e93690b6e34e9a5fb8e33ff3734473ac0245b27232222c4906a33f" -dependencies = [ - "once_cell", - "pest", - "sha2 0.10.8", -] - -[[package]] -name = "pin-project" -version = "1.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf123a161dde1e524adf36f90bc5d8d3462824a9c43553ad07a8183161189ec" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4502d8515ca9f32f1fb543d987f63d95a14934883db45bdb48060b6b69257f8" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.79", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "pkcs1" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" -dependencies = [ - "der", - "pkcs8", - "spki", -] - -[[package]] -name = "pkcs8" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" -dependencies = [ - "der", - "spki", -] - -[[package]] -name = "pkg-config" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" - -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - -[[package]] -name = "ppv-lite86" -version = "0.2.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" -dependencies = [ - "zerocopy", -] - -[[package]] -name = "pretty_env_logger" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d" -dependencies = [ - "env_logger", - "log", -] - -[[package]] -name = "proc-macro-crate" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" -dependencies = [ - "toml_edit", -] - -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.109", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr2" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" -dependencies = [ - "proc-macro2", - "quote", -] - -[[package]] -name = "proc-macro-error2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" -dependencies = [ - "proc-macro-error-attr2", - "proc-macro2", - "quote", - "syn 2.0.79", -] - -[[package]] -name = "proc-macro2" -version = "1.0.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "prost" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29" -dependencies = [ - "bytes", - "prost-derive", -] - -[[package]] -name = "prost-derive" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" -dependencies = [ - "anyhow", - "itertools 0.12.1", - "proc-macro2", - "quote", - "syn 2.0.79", -] - -[[package]] -name = "prost-types" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9091c90b0a32608e984ff2fa4091273cbdd755d54935c51d520887f4a1dbd5b0" -dependencies = [ - "prost", -] - -[[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" - -[[package]] -name = "quick-error" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" - -[[package]] -name = "quote" -version = "1.0.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", -] - -[[package]] -name = "rand_distr" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31" -dependencies = [ - "num-traits", - "rand", -] - -[[package]] -name = "redox_syscall" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" -dependencies = [ - "bitflags 2.6.0", -] - -[[package]] -name = "redox_users" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" -dependencies = [ - "getrandom", - "libredox", - "thiserror", -] - -[[package]] -name = "regex" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata 0.4.8", - "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]] -name = "regex-automata" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" -dependencies = [ - "aho-corasick", - "memchr", - "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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" - -[[package]] -name = "reqwest" -version = "0.11.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" -dependencies = [ - "base64 0.21.7", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "h2 0.3.26", - "http 0.2.12", - "http-body 0.4.6", - "hyper 0.14.30", - "hyper-rustls 0.24.2", - "ipnet", - "js-sys", - "log", - "mime", - "once_cell", - "percent-encoding", - "pin-project-lite", - "rustls 0.21.12", - "rustls-native-certs", - "rustls-pemfile 1.0.4", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper 0.1.2", - "system-configuration", - "tokio", - "tokio-rustls 0.24.1", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "winreg 0.50.0", -] - -[[package]] -name = "reqwest" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "566cafdd92868e0939d3fb961bd0dc25fcfaaed179291093b3d43e6b3150ea10" -dependencies = [ - "base64 0.22.1", - "bytes", - "encoding_rs", - "futures-channel", - "futures-core", - "futures-util", - "h2 0.4.6", - "http 1.1.0", - "http-body 1.0.1", - "http-body-util", - "hyper 1.4.1", - "hyper-rustls 0.26.0", - "hyper-tls", - "hyper-util", - "ipnet", - "js-sys", - "log", - "mime", - "native-tls", - "once_cell", - "percent-encoding", - "pin-project-lite", - "rustls 0.22.4", - "rustls-pemfile 2.2.0", - "rustls-pki-types", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper 0.1.2", - "system-configuration", - "tokio", - "tokio-native-tls", - "tokio-rustls 0.25.0", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "webpki-roots 0.26.6", - "winreg 0.52.0", -] - -[[package]] -name = "rfc6979" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" -dependencies = [ - "hmac", - "subtle 2.6.1", -] - -[[package]] -name = "ring" -version = "0.17.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" -dependencies = [ - "cc", - "cfg-if", - "getrandom", - "libc", - "spin", - "untrusted", - "windows-sys 0.52.0", -] - -[[package]] -name = "ripemd" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" -dependencies = [ - "digest 0.10.7", -] - -[[package]] -name = "rsa" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc" -dependencies = [ - "const-oid", - "digest 0.10.7", - "num-bigint-dig", - "num-integer", - "num-traits", - "pkcs1", - "pkcs8", - "rand_core 0.6.4", - "signature", - "spki", - "subtle 2.6.1", - "zeroize", -] - -[[package]] -name = "rust-embed" -version = "8.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa66af4a4fdd5e7ebc276f115e895611a34739a9c1c01028383d612d550953c0" -dependencies = [ - "rust-embed-impl", - "rust-embed-utils", - "walkdir", -] - -[[package]] -name = "rust-embed-impl" -version = "8.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6125dbc8867951125eec87294137f4e9c2c96566e61bf72c45095a7c77761478" -dependencies = [ - "proc-macro2", - "quote", - "rust-embed-utils", - "syn 2.0.79", - "walkdir", -] - -[[package]] -name = "rust-embed-utils" -version = "8.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e5347777e9aacb56039b0e1f28785929a8a3b709e87482e7442c72e7c12529d" -dependencies = [ - "sha2 0.10.8", - "walkdir", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" - -[[package]] -name = "rustc_version" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" -dependencies = [ - "semver", -] - -[[package]] -name = "rustix" -version = "0.38.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" -dependencies = [ - "bitflags 2.6.0", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.52.0", -] - -[[package]] -name = "rustls" -version = "0.21.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" -dependencies = [ - "log", - "ring", - "rustls-webpki 0.101.7", - "sct", -] - -[[package]] -name = "rustls" -version = "0.22.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" -dependencies = [ - "log", - "ring", - "rustls-pki-types", - "rustls-webpki 0.102.8", - "subtle 2.6.1", - "zeroize", -] - -[[package]] -name = "rustls-native-certs" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" -dependencies = [ - "openssl-probe", - "rustls-pemfile 1.0.4", - "schannel", - "security-framework", -] - -[[package]] -name = "rustls-pemfile" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" -dependencies = [ - "base64 0.21.7", -] - -[[package]] -name = "rustls-pemfile" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "rustls-pki-types" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e696e35370c65c9c541198af4543ccd580cf17fc25d8e05c5a242b202488c55" - -[[package]] -name = "rustls-webpki" -version = "0.101.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "rustls-webpki" -version = "0.102.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" -dependencies = [ - "ring", - "rustls-pki-types", - "untrusted", -] - -[[package]] -name = "rustversion" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" - -[[package]] -name = "ryu" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "schannel" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01227be5826fa0690321a2ba6c5cd57a19cf3f6a09e76973b58e61de6ab9d1c1" -dependencies = [ - "windows-sys 0.59.0", -] - -[[package]] -name = "schemars" -version = "0.8.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09c024468a378b7e36765cd36702b7a90cc3cba11654f6685c8f233408e89e92" -dependencies = [ - "dyn-clone", - "indexmap 1.9.3", - "schemars_derive", - "serde", - "serde_json", - "uuid", -] - -[[package]] -name = "schemars_derive" -version = "0.8.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1eee588578aff73f856ab961cd2f79e36bc45d7ded33a7562adba4667aecc0e" -dependencies = [ - "proc-macro2", - "quote", - "serde_derive_internals 0.29.1", - "syn 2.0.79", -] - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "sct" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "sec1" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" -dependencies = [ - "base16ct", - "der", - "generic-array 0.14.7", - "pkcs8", - "serdect 0.2.0", - "subtle 2.6.1", - "zeroize", -] - -[[package]] -name = "security-framework" -version = "2.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" -dependencies = [ - "bitflags 2.6.0", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "semver" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" -dependencies = [ - "serde", -] - -[[package]] -name = "serde" -version = "1.0.210" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde-json-wasm" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a15bee9b04dd165c3f4e142628982ddde884c2022a89e8ddf99c4829bf2c3a58" -dependencies = [ - "serde", -] - -[[package]] -name = "serde-wasm-bindgen" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3b143e2833c57ab9ad3ea280d21fd34e285a42837aeb0ee301f4f41890fa00e" -dependencies = [ - "js-sys", - "serde", - "wasm-bindgen", -] - -[[package]] -name = "serde-wasm-bindgen" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8302e169f0eddcc139c70f139d19d6467353af16f9fce27e8c30158036a1e16b" -dependencies = [ - "js-sys", - "serde", - "wasm-bindgen", -] - -[[package]] -name = "serde_bytes" -version = "0.11.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "387cc504cb06bb40a96c8e04e951fe01854cf6bc921053c954e4a606d9675c6a" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_derive" -version = "1.0.210" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.79", -] - -[[package]] -name = "serde_derive_internals" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e578a843d40b4189a4d66bba51d7684f57da5bd7c304c64e14bd63efbef49509" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.79", -] - -[[package]] -name = "serde_derive_internals" -version = "0.29.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.79", -] - -[[package]] -name = "serde_json" -version = "1.0.128" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" -dependencies = [ - "itoa", - "memchr", - "ryu", - "serde", -] - -[[package]] -name = "serde_path_to_error" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" -dependencies = [ - "itoa", - "serde", -] - -[[package]] -name = "serde_repr" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.79", -] - -[[package]] -name = "serde_spanned" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "serde_yaml" -version = "0.9.34+deprecated" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" -dependencies = [ - "indexmap 2.6.0", - "itoa", - "ryu", - "serde", - "unsafe-libyaml", -] - -[[package]] -name = "serdect" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a84f14a19e9a014bb9f4512488d9829a68e04ecabffb0f9904cd1ace94598177" -dependencies = [ - "base16ct", - "serde", -] - -[[package]] -name = "serdect" -version = "0.3.0-rc.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a504c8ee181e3e594d84052f983d60afe023f4d94d050900be18062bbbf7b58" -dependencies = [ - "base16ct", - "serde", -] - -[[package]] -name = "sha1" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest 0.10.7", -] - -[[package]] -name = "sha2" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" -dependencies = [ - "block-buffer 0.9.0", - "cfg-if", - "cpufeatures", - "digest 0.9.0", - "opaque-debug 0.3.1", -] - -[[package]] -name = "sha2" -version = "0.10.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" -dependencies = [ - "cfg-if", - "cpufeatures", - "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 = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - -[[package]] -name = "signal-hook-registry" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" -dependencies = [ - "libc", -] - -[[package]] -name = "signature" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" -dependencies = [ - "digest 0.10.7", - "rand_core 0.6.4", -] - -[[package]] -name = "slab" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] - -[[package]] -name = "smallvec" -version = "1.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" - -[[package]] -name = "socket2" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "sphinx-packet" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dabeca95bf5fd0563d6be7ebcb1c6a9fcb135746a0ba9050c47dc68c8607e595" -dependencies = [ - "aes", - "arrayref", - "blake2", - "bs58", - "byteorder", - "chacha", - "ctr", - "curve25519-dalek 4.1.3", - "digest 0.10.7", - "hkdf", - "hmac", - "lioness", - "log", - "rand", - "rand_distr", - "sha2 0.10.8", - "subtle 2.6.1", -] - -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" -dependencies = [ - "lock_api", -] - -[[package]] -name = "spki" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" -dependencies = [ - "base64ct", - "der", -] - -[[package]] -name = "sqlformat" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bba3a93db0cc4f7bdece8bb09e77e2e785c20bfebf79eb8340ed80708048790" -dependencies = [ - "nom", - "unicode_categories", -] - -[[package]] -name = "sqlx" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9a2ccff1a000a5a59cd33da541d9f2fdcd9e6e8229cc200565942bff36d0aaa" -dependencies = [ - "sqlx-core", - "sqlx-macros", - "sqlx-mysql", - "sqlx-postgres", - "sqlx-sqlite", -] - -[[package]] -name = "sqlx-core" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24ba59a9342a3d9bab6c56c118be528b27c9b60e490080e9711a04dccac83ef6" -dependencies = [ - "ahash 0.8.11", - "atoi", - "byteorder", - "bytes", - "crc", - "crossbeam-queue", - "either", - "event-listener", - "futures-channel", - "futures-core", - "futures-intrusive", - "futures-io", - "futures-util", - "hashlink", - "hex", - "indexmap 2.6.0", - "log", - "memchr", - "once_cell", - "paste", - "percent-encoding", - "rustls 0.21.12", - "rustls-pemfile 1.0.4", - "serde", - "serde_json", - "sha2 0.10.8", - "smallvec", - "sqlformat", - "thiserror", - "time", - "tokio", - "tokio-stream", - "tracing", - "url", - "webpki-roots 0.25.4", -] - -[[package]] -name = "sqlx-macros" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ea40e2345eb2faa9e1e5e326db8c34711317d2b5e08d0d5741619048a803127" -dependencies = [ - "proc-macro2", - "quote", - "sqlx-core", - "sqlx-macros-core", - "syn 1.0.109", -] - -[[package]] -name = "sqlx-macros-core" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5833ef53aaa16d860e92123292f1f6a3d53c34ba8b1969f152ef1a7bb803f3c8" -dependencies = [ - "dotenvy", - "either", - "heck 0.4.1", - "hex", - "once_cell", - "proc-macro2", - "quote", - "serde", - "serde_json", - "sha2 0.10.8", - "sqlx-core", - "sqlx-mysql", - "sqlx-postgres", - "sqlx-sqlite", - "syn 1.0.109", - "tempfile", - "tokio", - "url", -] - -[[package]] -name = "sqlx-mysql" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ed31390216d20e538e447a7a9b959e06ed9fc51c37b514b46eb758016ecd418" -dependencies = [ - "atoi", - "base64 0.21.7", - "bitflags 2.6.0", - "byteorder", - "bytes", - "crc", - "digest 0.10.7", - "dotenvy", - "either", - "futures-channel", - "futures-core", - "futures-io", - "futures-util", - "generic-array 0.14.7", - "hex", - "hkdf", - "hmac", - "itoa", - "log", - "md-5", - "memchr", - "once_cell", - "percent-encoding", - "rand", - "rsa", - "serde", - "sha1", - "sha2 0.10.8", - "smallvec", - "sqlx-core", - "stringprep", - "thiserror", - "time", - "tracing", - "whoami", -] - -[[package]] -name = "sqlx-postgres" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c824eb80b894f926f89a0b9da0c7f435d27cdd35b8c655b114e58223918577e" -dependencies = [ - "atoi", - "base64 0.21.7", - "bitflags 2.6.0", - "byteorder", - "crc", - "dotenvy", - "etcetera", - "futures-channel", - "futures-core", - "futures-io", - "futures-util", - "hex", - "hkdf", - "hmac", - "home", - "itoa", - "log", - "md-5", - "memchr", - "once_cell", - "rand", - "serde", - "serde_json", - "sha2 0.10.8", - "smallvec", - "sqlx-core", - "stringprep", - "thiserror", - "time", - "tracing", - "whoami", -] - -[[package]] -name = "sqlx-sqlite" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b244ef0a8414da0bed4bb1910426e890b19e5e9bccc27ada6b797d05c55ae0aa" -dependencies = [ - "atoi", - "flume", - "futures-channel", - "futures-core", - "futures-executor", - "futures-intrusive", - "futures-util", - "libsqlite3-sys", - "log", - "percent-encoding", - "serde", - "sqlx-core", - "time", - "tracing", - "url", - "urlencoding", -] - -[[package]] -name = "stringprep" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" -dependencies = [ - "unicode-bidi", - "unicode-normalization", - "unicode-properties", -] - -[[package]] -name = "strsim" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" - -[[package]] -name = "strum" -version = "0.26.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" -dependencies = [ - "strum_macros", -] - -[[package]] -name = "strum_macros" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" -dependencies = [ - "heck 0.5.0", - "proc-macro2", - "quote", - "rustversion", - "syn 2.0.79", -] - -[[package]] -name = "subtle" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" - -[[package]] -name = "subtle" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" - -[[package]] -name = "subtle-encoding" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dcb1ed7b8330c5eed5441052651dd7a12c75e2ed88f2ec024ae1fa3a5e59945" -dependencies = [ - "zeroize", -] - -[[package]] -name = "subtle-ng" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "734676eb262c623cec13c3155096e08d1f8f29adce39ba17948b18dad1e54142" - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.79" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "sync_wrapper" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" - -[[package]] -name = "sync_wrapper" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" - -[[package]] -name = "system-configuration" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "system-configuration-sys", -] - -[[package]] -name = "system-configuration-sys" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "tempfile" -version = "3.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" -dependencies = [ - "cfg-if", - "fastrand", - "once_cell", - "rustix", - "windows-sys 0.59.0", -] - -[[package]] -name = "tendermint" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "954496fbc9716eb4446cdd6d00c071a3e2f22578d62aa03b40c7e5b4fda3ed42" -dependencies = [ - "bytes", - "digest 0.10.7", - "ed25519", - "ed25519-consensus", - "flex-error", - "futures", - "k256", - "num-traits", - "once_cell", - "prost", - "prost-types", - "ripemd", - "serde", - "serde_bytes", - "serde_json", - "serde_repr", - "sha2 0.10.8", - "signature", - "subtle 2.6.1", - "subtle-encoding", - "tendermint-proto", - "time", - "zeroize", -] - -[[package]] -name = "tendermint-config" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f84b11b57d20ee4492a1452faff85f5c520adc36ca9fe5e701066935255bb89f" -dependencies = [ - "flex-error", - "serde", - "serde_json", - "tendermint", - "toml", - "url", -] - -[[package]] -name = "tendermint-proto" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc87024548c7f3da479885201e3da20ef29e85a3b13d04606b380ac4c7120d87" -dependencies = [ - "bytes", - "flex-error", - "prost", - "prost-types", - "serde", - "serde_bytes", - "subtle-encoding", - "time", -] - -[[package]] -name = "tendermint-rpc" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfdc2281e271277fda184d96d874a6fe59f569b130b634289257baacfc95aa85" -dependencies = [ - "async-trait", - "bytes", - "flex-error", - "futures", - "getrandom", - "peg", - "pin-project", - "rand", - "reqwest 0.11.27", - "semver", - "serde", - "serde_bytes", - "serde_json", - "subtle 2.6.1", - "subtle-encoding", - "tendermint", - "tendermint-config", - "tendermint-proto", - "thiserror", - "time", - "tokio", - "tracing", - "url", - "uuid", - "walkdir", -] - -[[package]] -name = "termcolor" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "thiserror" -version = "1.0.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.79", -] - -[[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 = "time" -version = "0.3.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" -dependencies = [ - "deranged", - "itoa", - "js-sys", - "libc", - "num-conv", - "num_threads", - "powerfmt", - "serde", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" - -[[package]] -name = "time-macros" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" -dependencies = [ - "num-conv", - "time-core", -] - -[[package]] -name = "tinyvec" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "tokio" -version = "1.40.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" -dependencies = [ - "backtrace", - "bytes", - "libc", - "mio", - "pin-project-lite", - "signal-hook-registry", - "socket2", - "tokio-macros", - "windows-sys 0.52.0", -] - -[[package]] -name = "tokio-macros" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.79", -] - -[[package]] -name = "tokio-native-tls" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" -dependencies = [ - "native-tls", - "tokio", -] - -[[package]] -name = "tokio-rustls" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" -dependencies = [ - "rustls 0.21.12", - "tokio", -] - -[[package]] -name = "tokio-rustls" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" -dependencies = [ - "rustls 0.22.4", - "rustls-pki-types", - "tokio", -] - -[[package]] -name = "tokio-stream" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "tokio-util" -version = "0.7.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "futures-util", - "hashbrown 0.14.5", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "toml" -version = "0.8.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit", -] - -[[package]] -name = "toml_datetime" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" -dependencies = [ - "serde", -] - -[[package]] -name = "toml_edit" -version = "0.22.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" -dependencies = [ - "indexmap 2.6.0", - "serde", - "serde_spanned", - "toml_datetime", - "winnow", -] - -[[package]] -name = "tower" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2873938d487c3cfb9aed7546dc9f2711d867c9f90c46b889989a2cb84eba6b4f" -dependencies = [ - "futures-core", - "futures-util", - "pin-project-lite", - "sync_wrapper 0.1.2", - "tokio", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tower-http" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" -dependencies = [ - "bitflags 2.6.0", - "bytes", - "http 1.1.0", - "http-body 1.0.1", - "http-body-util", - "pin-project-lite", - "tower-layer", - "tower-service", -] - -[[package]] -name = "tower-layer" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" - -[[package]] -name = "tower-service" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" - -[[package]] -name = "tracing" -version = "0.1.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" -dependencies = [ - "log", - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.79", -] - -[[package]] -name = "tracing-core" -version = "0.1.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" -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.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" -dependencies = [ - "matchers", - "nu-ansi-term", - "once_cell", - "regex", - "sharded-slab", - "smallvec", - "thread_local", - "tracing", - "tracing-core", - "tracing-log", -] - -[[package]] -name = "try-lock" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" - -[[package]] -name = "tsify" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6b26cf145f2f3b9ff84e182c448eaf05468e247f148cf3d2a7d67d78ff023a0" -dependencies = [ - "gloo-utils 0.1.7", - "serde", - "serde-wasm-bindgen 0.5.0", - "serde_json", - "tsify-macros", - "wasm-bindgen", -] - -[[package]] -name = "tsify-macros" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a94b0f0954b3e59bfc2c246b4c8574390d94a4ad4ad246aaf2fb07d7dfd3b47" -dependencies = [ - "proc-macro2", - "quote", - "serde_derive_internals 0.28.0", - "syn 2.0.79", -] - -[[package]] -name = "typenum" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" - -[[package]] -name = "ucd-trie" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" - -[[package]] -name = "unicase" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" -dependencies = [ - "version_check", -] - -[[package]] -name = "unicode-bidi" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" - -[[package]] -name = "unicode-ident" -version = "1.0.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" - -[[package]] -name = "unicode-normalization" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "unicode-properties" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" - -[[package]] -name = "unicode-segmentation" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" - -[[package]] -name = "unicode_categories" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" - -[[package]] -name = "unsafe-libyaml" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" - -[[package]] -name = "untrusted" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" - -[[package]] -name = "url" -version = "2.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", - "serde", -] - -[[package]] -name = "urlencoding" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" - -[[package]] -name = "utf8parse" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" - -[[package]] -name = "utoipa" -version = "4.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5afb1a60e207dca502682537fefcfd9921e71d0b83e9576060f09abc6efab23" -dependencies = [ - "indexmap 2.6.0", - "serde", - "serde_json", - "utoipa-gen", -] - -[[package]] -name = "utoipa-gen" -version = "4.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20c24e8ab68ff9ee746aad22d39b5535601e6416d1b0feeabf78be986a5c4392" -dependencies = [ - "proc-macro-error", - "proc-macro2", - "quote", - "regex", - "syn 2.0.79", -] - -[[package]] -name = "utoipa-swagger-ui" -version = "7.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "943e0ff606c6d57d410fd5663a4d7c074ab2c5f14ab903b9514565e59fa1189e" -dependencies = [ - "axum", - "mime_guess", - "regex", - "reqwest 0.12.4", - "rust-embed", - "serde", - "serde_json", - "url", - "utoipa", - "zip", -] - -[[package]] -name = "uuid" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" -dependencies = [ - "serde", -] - -[[package]] -name = "valuable" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" - -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - -[[package]] -name = "vergen" -version = "8.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e27d6bdd219887a9eadd19e1c34f32e47fa332301184935c6d9bca26f3cca525" -dependencies = [ - "anyhow", - "cargo_metadata", - "cfg-if", - "regex", - "rustc_version", - "rustversion", - "time", -] - -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - -[[package]] -name = "walkdir" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" -dependencies = [ - "same-file", - "winapi-util", -] - -[[package]] -name = "want" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" -dependencies = [ - "try-lock", -] - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "wasite" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" - -[[package]] -name = "wasm-bindgen" -version = "0.2.94" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef073ced962d62984fb38a36e5fdc1a2b23c9e0e1fa0689bb97afa4202ef6887" -dependencies = [ - "cfg-if", - "once_cell", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.94" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4bfab14ef75323f4eb75fa52ee0a3fb59611977fd3240da19b2cf36ff85030e" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn 2.0.79", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65471f79c1022ffa5291d33520cbbb53b7687b01c2f8e83b57d102eed7ed479d" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.94" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7bec9830f60924d9ceb3ef99d55c155be8afa76954edffbb5936ff4509474e7" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.94" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c74f6e152a76a2ad448e223b0fc0b6b5747649c3d769cc6bf45737bf97d0ed6" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.79", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.94" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a42f6c679374623f295a8623adfe63d9284091245c3504bde47c17a3ce2777d9" - -[[package]] -name = "wasm-utils" -version = "0.1.0" -dependencies = [ - "futures", - "gloo-utils 0.2.0", - "js-sys", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "wasmtimer" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f656cd8858a5164932d8a90f936700860976ec21eb00e0fe2aa8cab13f6b4cf" -dependencies = [ - "futures", - "js-sys", - "parking_lot", - "pin-utils", - "slab", - "wasm-bindgen", -] - -[[package]] -name = "web-sys" -version = "0.3.71" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44188d185b5bdcae1052d08bcbcf9091a5524038d4572cc4f4f2bb9d5554ddd9" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[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.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841c67bff177718f1d4dfefde8d8f0e78f9b6589319ba88312f567fc5841a958" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "whoami" -version = "1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "372d5b87f58ec45c384ba03563b03544dc5fadc3983e434b286913f5b4a9bb6d" -dependencies = [ - "redox_syscall", - "wasite", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" -dependencies = [ - "windows-sys 0.59.0", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] - -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", -] - -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - -[[package]] -name = "winnow" -version = "0.6.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" -dependencies = [ - "memchr", -] - -[[package]] -name = "winreg" -version = "0.50.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] - -[[package]] -name = "winreg" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] - -[[package]] -name = "x25519-dalek" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" -dependencies = [ - "curve25519-dalek 4.1.3", - "rand_core 0.6.4", - "serde", - "zeroize", -] - -[[package]] -name = "zerocopy" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" -dependencies = [ - "byteorder", - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.79", -] - -[[package]] -name = "zeroize" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" -dependencies = [ - "zeroize_derive", -] - -[[package]] -name = "zeroize_derive" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.79", -] - -[[package]] -name = "zip" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cc23c04387f4da0374be4533ad1208cbb091d5c11d070dfef13676ad6497164" -dependencies = [ - "arbitrary", - "crc32fast", - "crossbeam-utils", - "displaydoc", - "flate2", - "indexmap 2.6.0", - "num_enum", - "thiserror", -] diff --git a/nym-credential-proxy/Cargo.toml b/nym-credential-proxy/Cargo.toml deleted file mode 100644 index b3ebb7f459..0000000000 --- a/nym-credential-proxy/Cargo.toml +++ /dev/null @@ -1,62 +0,0 @@ -[profile.release] -panic = "abort" -opt-level = "s" -overflow-checks = true - -[profile.dev] -panic = "abort" - -[workspace] - -resolver = "2" -members = [ - "nym-credential-proxy", - "nym-credential-proxy-requests", - "vpn-api-lib-wasm" -] - -[workspace.package] -authors = ["Nym Technologies SA"] -repository = "https://github.com/nymtech/nym" -homepage = "https://nymtech.net" -documentation = "https://nymtech.net" -edition = "2021" -license = "GPL-3.0" - -[workspace.dependencies] -async-trait = "0.1.80" -axum = "0.7.5" -anyhow = "1.0.71" -bip39 = "2.0.0" -bs58 = "0.5.1" -colored = "2.1.0" -cfg-if = "1.0.0" -clap = "4.5.4" -dotenv = "0.15.0" -futures = "0.3.30" -humantime = "2.1.0" -thiserror = "1.0.59" -rand = "0.8.5" -reqwest = "0.12.4" -schemars = "0.8.17" -strum = "0.26.3" -strum_macros = "0.26.4" -serde = "1.0.200" -serde_json = "1.0.117" -sqlx = "0.7.4" -tempfile = "3.12.0" -time = "0.3.36" -tracing = "0.1.40" -tsify = "0.4.5" -tokio = "1.37.0" -tokio-util = "0.7.10" -tower = "0.5.0" -tower-http = "0.5.2" -uuid = "1.8.0" -url = "2.5.2" -utoipa = "4.2.0" -utoipa-swagger-ui = "7.0.1" -zeroize = "1.6.0" - -wasm-bindgen = "0.2.93" -wasmtimer = "0.2.0" From 23da0f4d8ec4ca4635df46e7f3d7d5383911952c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20H=C3=A4ggblad?= Date: Thu, 24 Oct 2024 10:06:44 +0200 Subject: [PATCH 38/48] Workspace updates --- Cargo.lock | 8 +------- Cargo.toml | 1 - nym-credential-proxy/nym-credential-proxy/Cargo.toml | 2 +- 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b0564a99e3..26739d2a72 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2186,12 +2186,6 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" -[[package]] -name = "dotenv" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" - [[package]] name = "dotenvy" version = "0.15.7" @@ -5212,7 +5206,7 @@ dependencies = [ "cfg-if", "clap 4.5.20", "colored", - "dotenv", + "dotenvy", "futures", "humantime 2.1.0", "nym-bin-common 0.6.0", diff --git a/Cargo.toml b/Cargo.toml index e6fbec5579..9e4b6716c3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -240,7 +240,6 @@ defguard_wireguard_rs = { git = "https://github.com/DefGuard/wireguard-rs.git", digest = "0.10.7" dirs = "5.0" doc-comment = "0.3" -dotenv = "0.15.0" dotenvy = "0.15.6" ecdsa = "0.16" ed25519-dalek = "2.1" diff --git a/nym-credential-proxy/nym-credential-proxy/Cargo.toml b/nym-credential-proxy/nym-credential-proxy/Cargo.toml index eb852c891e..55a57a0800 100644 --- a/nym-credential-proxy/nym-credential-proxy/Cargo.toml +++ b/nym-credential-proxy/nym-credential-proxy/Cargo.toml @@ -19,7 +19,7 @@ bs58.workspace = true cfg-if = { workspace = true } colored.workspace = true clap = { workspace = true, features = ["derive", "env"] } -dotenv.workspace = true +dotenvy.workspace = true futures.workspace = true humantime.workspace = true rand.workspace = true From ac5baab693a6816d8e5d48280bf65366e4bd6635 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20H=C3=A4ggblad?= Date: Thu, 24 Oct 2024 10:12:49 +0200 Subject: [PATCH 39/48] Add to default workspace --- Cargo.toml | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9e4b6716c3..f48074b745 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,33 +19,33 @@ members = [ "clients/native", "clients/native/websocket-requests", "clients/socks5", - "common/authenticator-requests", "common/async-file-watcher", + "common/authenticator-requests", "common/bandwidth-controller", "common/bin-common", "common/client-core", "common/client-core/config-types", - "common/client-core/surb-storage", "common/client-core/gateways-storage", + "common/client-core/surb-storage", "common/client-libs/gateway-client", "common/client-libs/mixnet-client", "common/client-libs/validator-client", "common/commands", "common/config", "common/cosmwasm-smart-contracts/coconut-bandwidth-contract", - "common/cosmwasm-smart-contracts/ecash-contract", "common/cosmwasm-smart-contracts/coconut-dkg", "common/cosmwasm-smart-contracts/contracts-common", + "common/cosmwasm-smart-contracts/ecash-contract", "common/cosmwasm-smart-contracts/group-contract", "common/cosmwasm-smart-contracts/mixnet-contract", "common/cosmwasm-smart-contracts/multisig-contract", "common/cosmwasm-smart-contracts/vesting-contract", "common/country-group", "common/credential-storage", - "common/credentials", "common/credential-utils", - "common/credentials-interface", "common/credential-verification", + "common/credentials", + "common/credentials-interface", "common/crypto", "common/dkg", "common/ecash-double-spending", @@ -65,10 +65,10 @@ members = [ "common/network-defaults", "common/node-tester-utils", "common/nonexhaustive-delayqueue", - "common/nymcoconut", - "common/nym_offline_compact_ecash", "common/nym-id", "common/nym-metrics", + "common/nym_offline_compact_ecash", + "common/nymcoconut", "common/nymsphinx", "common/nymsphinx/acknowledgements", "common/nymsphinx/addressing", @@ -104,11 +104,11 @@ members = [ "gateway", "integrations/bity", "mixnode", + "sdk/ffi/cpp", + "sdk/ffi/go", + "sdk/ffi/shared", "sdk/lib/socks5-listener", "sdk/rust/nym-sdk", - "sdk/ffi/shared", - "sdk/ffi/go", - "sdk/ffi/cpp", "service-providers/authenticator", "service-providers/common", "service-providers/ip-packet-router", @@ -143,11 +143,11 @@ members = [ "wasm/mix-fetch", "wasm/node-tester", "wasm/zknym-lib", - "tools/internal/testnet-manager", - "tools/internal/testnet-manager/dkg-bypass-contract", "tools/echo-server", "tools/internal/contract-state-importer/importer-cli", "tools/internal/contract-state-importer/importer-contract", + "tools/internal/testnet-manager", + "tools/internal/testnet-manager/dkg-bypass-contract", ] default-members = [ @@ -158,6 +158,7 @@ default-members = [ "gateway", "mixnode", "nym-api", + "nym-credential-proxy/nym-credential-proxy", "nym-data-observatory", "nym-node", "nym-node-status-api", @@ -202,11 +203,8 @@ axum-extra = "0.9.4" base64 = "0.22.1" bincode = "1.3.3" bip39 = { version = "2.0.0", features = ["zeroize"] } - -# can we unify those? -bit-vec = "0.7.0" +bit-vec = "0.7.0" # can we unify those? bitvec = "1.0.0" - blake3 = "1.5.4" bloomfilter = "1.0.14" bs58 = "0.5.1" From fce322c789f5282995eb945e1f11ddbe5dc47965 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20H=C3=A4ggblad?= Date: Thu, 24 Oct 2024 14:38:13 +0200 Subject: [PATCH 40/48] Remove unused workflow --- .github/workflows/ci-nym-credential-proxy.yml | 45 ------------------- 1 file changed, 45 deletions(-) delete mode 100644 .github/workflows/ci-nym-credential-proxy.yml diff --git a/.github/workflows/ci-nym-credential-proxy.yml b/.github/workflows/ci-nym-credential-proxy.yml deleted file mode 100644 index 8b70a1a73c..0000000000 --- a/.github/workflows/ci-nym-credential-proxy.yml +++ /dev/null @@ -1,45 +0,0 @@ -name: ci-nym-credential-proxy - -on: - pull_request: - paths: - - 'common/**' - - 'nym-credential-proxy/**' - - '.github/workspace/ci-nym-credential-proxy.yml' - workflow_dispatch: - -jobs: - build: - runs-on: arc-ubuntu-22.04 - env: - CARGO_TERM_COLOR: always - MANIFEST_PATH: "--manifest-path nym-credential-proxy/Cargo.toml" - steps: - - name: Check out repository code - uses: actions/checkout@v4 - - - name: Install rust toolchain - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: stable - override: true - components: rustfmt, clippy - - - name: Check formatting - uses: actions-rs/cargo@v1 - with: - command: fmt - args: ${{ env.MANIFEST_PATH }} --all -- --check - - - name: Build - uses: actions-rs/cargo@v1 - with: - command: build - args: ${{ env.MANIFEST_PATH }} --workspace --all-targets - - - name: Clippy - uses: actions-rs/cargo@v1 - with: - command: clippy - args: ${{ env.MANIFEST_PATH }} --workspace --all-targets -- -D warnings From 94c6cdc7b2ccdf1ec9065698ed8033949b75a136 Mon Sep 17 00:00:00 2001 From: dynco-nym <173912580+dynco-nym@users.noreply.github.com> Date: Fri, 25 Oct 2024 13:23:29 +0200 Subject: [PATCH 41/48] Type coercion into time::Date --- .../nym-credential-proxy/src/storage/manager.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/nym-credential-proxy/nym-credential-proxy/src/storage/manager.rs b/nym-credential-proxy/nym-credential-proxy/src/storage/manager.rs index c1dd782e00..f391defb98 100644 --- a/nym-credential-proxy/nym-credential-proxy/src/storage/manager.rs +++ b/nym-credential-proxy/nym-credential-proxy/src/storage/manager.rs @@ -40,7 +40,7 @@ impl SqliteStorageManager { sqlx::query_as!( MinimalWalletShare, r#" - SELECT t1.node_id, t1.blinded_signature, t1.epoch_id, t1.expiration_date + SELECT t1.node_id, t1.blinded_signature, t1.epoch_id, t1.expiration_date as "expiration_date!: Date" FROM partial_blinded_wallet as t1 JOIN ticketbook_deposit as t2 on t1.corresponding_deposit = t2.deposit_id @@ -79,10 +79,11 @@ impl SqliteStorageManager { device_id: &str, credential_id: &str, ) -> Result, sqlx::Error> { + // https://docs.rs/sqlx/latest/sqlx/macro.query.html#force-a-differentcustom-type sqlx::query_as!( MinimalWalletShare, r#" - SELECT t1.node_id, t1.blinded_signature, t1.epoch_id, t1.expiration_date + SELECT t1.node_id, t1.blinded_signature, t1.epoch_id, t1.expiration_date as "expiration_date!: Date" FROM partial_blinded_wallet as t1 JOIN ticketbook_deposit as t2 on t1.corresponding_deposit = t2.deposit_id From e680e8dc49601c1c09942d3999d2b34cd98c88ae Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Oct 2024 23:27:10 +0100 Subject: [PATCH 42/48] build(deps): bump once_cell from 1.19.0 to 1.20.2 (#4952) Bumps [once_cell](https://github.com/matklad/once_cell) from 1.19.0 to 1.20.2. - [Changelog](https://github.com/matklad/once_cell/blob/master/CHANGELOG.md) - [Commits](https://github.com/matklad/once_cell/compare/v1.19.0...v1.20.2) --- updated-dependencies: - dependency-name: once_cell dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 26739d2a72..dae1bbaa09 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7340,9 +7340,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "oneshot-uniffi" diff --git a/Cargo.toml b/Cargo.toml index f48074b745..e055fd405c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -281,7 +281,7 @@ moka = { version = "0.12", features = ["future"] } nix = "0.27.1" notify = "5.1.0" okapi = "0.7.0" -once_cell = "1.7.2" +once_cell = "1.20.2" opentelemetry = "0.19.0" opentelemetry-jaeger = "0.18.0" parking_lot = "0.12.3" From 2ca7c7a252a852a9712ff567a301913f9ea47b7d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 30 Oct 2024 07:07:39 +0100 Subject: [PATCH 43/48] build(deps): bump lazy_static from 1.4.0 to 1.5.0 (#4913) --- Cargo.lock | 18 ++++++------------ Cargo.toml | 2 +- sdk/ffi/cpp/Cargo.toml | 2 +- sdk/ffi/go/Cargo.toml | 2 +- sdk/ffi/shared/Cargo.toml | 2 +- 5 files changed, 10 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dae1bbaa09..8afe0c0421 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2625,7 +2625,7 @@ checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" dependencies = [ "futures-core", "futures-sink", - "spin 0.9.8", + "spin", ] [[package]] @@ -3776,11 +3776,11 @@ dependencies = [ [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" dependencies = [ - "spin 0.5.2", + "spin", ] [[package]] @@ -4166,7 +4166,7 @@ dependencies = [ "log", "memchr", "mime", - "spin 0.9.8", + "spin", "tokio", "tokio-util", "version_check", @@ -8400,7 +8400,7 @@ dependencies = [ "cfg-if", "getrandom", "libc", - "spin 0.9.8", + "spin", "untrusted", "windows-sys 0.52.0", ] @@ -9386,12 +9386,6 @@ dependencies = [ "subtle 2.5.0", ] -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - [[package]] name = "spin" version = "0.9.8" diff --git a/Cargo.toml b/Cargo.toml index e055fd405c..7c323a3d31 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -271,7 +271,7 @@ ipnetwork = "0.20" isocountry = "0.3.2" itertools = "0.13.0" k256 = "0.13" -lazy_static = "1.4.0" +lazy_static = "1.5.0" ledger-transport = "0.10.0" ledger-transport-hid = "0.10.0" log = "0.4" diff --git a/sdk/ffi/cpp/Cargo.toml b/sdk/ffi/cpp/Cargo.toml index 8e20af6287..7c3a10f431 100644 --- a/sdk/ffi/cpp/Cargo.toml +++ b/sdk/ffi/cpp/Cargo.toml @@ -16,7 +16,7 @@ nym-sdk = { path = "../../rust/nym-sdk/" } nym-bin-common = { path = "../../../common/bin-common" } nym-sphinx-anonymous-replies = { path = "../../../common/nymsphinx/anonymous-replies" } nym-ffi-shared = { path = "../shared" } -lazy_static = "1.4.0" +lazy_static = "1.5.0" # error handling anyhow = "1.0.90" # base58 en/decoding diff --git a/sdk/ffi/go/Cargo.toml b/sdk/ffi/go/Cargo.toml index 6b9416e173..e1aff9faca 100644 --- a/sdk/ffi/go/Cargo.toml +++ b/sdk/ffi/go/Cargo.toml @@ -18,7 +18,7 @@ nym-sphinx-anonymous-replies = { path = "../../../common/nymsphinx/anonymous-rep nym-ffi-shared = { path = "../shared" } # Async runtime tokio = { version = "1", features = ["full"] } -lazy_static = "1.4.0" +lazy_static = "1.5.0" # error handling anyhow = "1.0.90" thiserror = "1.0.64" diff --git a/sdk/ffi/shared/Cargo.toml b/sdk/ffi/shared/Cargo.toml index 25a9b171b8..9f66e095ef 100644 --- a/sdk/ffi/shared/Cargo.toml +++ b/sdk/ffi/shared/Cargo.toml @@ -12,7 +12,7 @@ nym-sdk = { path = "../../rust/nym-sdk/" } nym-bin-common = { path = "../../../common/bin-common" } nym-sphinx-anonymous-replies = { path = "../../../common/nymsphinx/anonymous-replies" } # static var macro -lazy_static = "1.4.0" +lazy_static = "1.5.0" # error handling anyhow = "1.0.90" # base58 en/decoding From 76da4ab53231644c59f4434d5513861a2bdafa9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Stuczy=C5=84ski?= Date: Wed, 30 Oct 2024 09:11:13 +0000 Subject: [PATCH 44/48] bugfix: mark migrated gateways as rewarded in the previous epoch in case theyre in the rewarded set (#5049) --- contracts/mixnet/src/gateways/transactions.rs | 6 ++++++ contracts/mixnet/src/nodes/helpers.rs | 9 ++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/contracts/mixnet/src/gateways/transactions.rs b/contracts/mixnet/src/gateways/transactions.rs index 541e7e86c8..ef8f282f3e 100644 --- a/contracts/mixnet/src/gateways/transactions.rs +++ b/contracts/mixnet/src/gateways/transactions.rs @@ -4,6 +4,7 @@ use super::helpers::must_get_gateway_bond_by_owner; use super::storage; use crate::constants::default_node_costs; +use crate::interval::storage as interval_storage; use crate::mixnet_contract_settings::storage as mixnet_params_storage; use crate::nodes::helpers::save_new_nymnode_with_id; use crate::nodes::transactions::add_nym_node_inner; @@ -115,6 +116,10 @@ pub fn try_migrate_to_nymnode( comment: "legacy gateway did not have a pre-assigned node id".to_string(), })?; + let current_epoch = + interval_storage::current_interval(deps.storage)?.current_epoch_absolute_id(); + let previous_epoch = current_epoch.saturating_sub(1); + // create nym-node entry // for gateways it's quite straightforward as there are no delegations or rewards to worry about save_new_nymnode_with_id( @@ -125,6 +130,7 @@ pub fn try_migrate_to_nymnode( cost_params, info.sender.clone(), gateway_bond.pledge_amount, + previous_epoch, )?; storage::PREASSIGNED_LEGACY_IDS.remove(deps.storage, gateway_identity.clone()); diff --git a/contracts/mixnet/src/nodes/helpers.rs b/contracts/mixnet/src/nodes/helpers.rs index b9b2706380..12c01ab1f6 100644 --- a/contracts/mixnet/src/nodes/helpers.rs +++ b/contracts/mixnet/src/nodes/helpers.rs @@ -22,6 +22,8 @@ pub(crate) fn save_new_nymnode( pledge: Coin, ) -> Result { let node_id = next_nymnode_id_counter(storage)?; + let current_epoch = interval_storage::current_interval(storage)?.current_epoch_absolute_id(); + save_new_nymnode_with_id( storage, node_id, @@ -30,11 +32,13 @@ pub(crate) fn save_new_nymnode( cost_params, owner, pledge, + current_epoch, )?; Ok(node_id) } +#[allow(clippy::too_many_arguments)] pub(crate) fn save_new_nymnode_with_id( storage: &mut dyn Storage, node_id: NodeId, @@ -43,10 +47,9 @@ pub(crate) fn save_new_nymnode_with_id( cost_params: NodeCostParams, owner: Addr, pledge: Coin, + last_rewarding_epoch: u32, ) -> Result<(), MixnetContractError> { - let current_epoch = interval_storage::current_interval(storage)?.current_epoch_absolute_id(); - - let node_rewarding = NodeRewarding::initialise_new(cost_params, &pledge, current_epoch)?; + let node_rewarding = NodeRewarding::initialise_new(cost_params, &pledge, last_rewarding_epoch)?; let node_bond = NymNodeBond::new(node_id, owner, pledge, node, bonding_height); // save node bond data From 753a21f8ca855617d4dc637800b860487c68adab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Stuczy=C5=84ski?= Date: Wed, 30 Oct 2024 12:21:27 +0000 Subject: [PATCH 45/48] bugfix/feature: added NymApiClient method to get all skimmed nodes (#5062) * bugfix/feature: added NymApiClient method to get all skimmed nodes * wasm * helper: utility method for getting ed25519 identity directly from node description --- .../topology_control/geo_aware_provider.rs | 2 +- .../topology_control/nym_api_provider.rs | 2 +- common/client-core/src/init/helpers.rs | 4 ++- .../validator-client/src/client.rs | 30 +++++++++++++++-- .../validator-client/src/nym_api/mod.rs | 32 +++++++++++++++++++ common/wasm/client-core/src/helpers.rs | 2 +- nym-api/nym-api-requests/src/models.rs | 4 +++ .../examples/custom_topology_provider.rs | 2 +- 8 files changed, 71 insertions(+), 7 deletions(-) diff --git a/common/client-core/src/client/topology_control/geo_aware_provider.rs b/common/client-core/src/client/topology_control/geo_aware_provider.rs index 660acdf749..7e961bb8d2 100644 --- a/common/client-core/src/client/topology_control/geo_aware_provider.rs +++ b/common/client-core/src/client/topology_control/geo_aware_provider.rs @@ -112,7 +112,7 @@ impl GeoAwareTopologyProvider { async fn get_topology(&self) -> Option { let mixnodes = match self .validator_client - .get_basic_active_mixing_assigned_nodes(Some(self.client_version.clone())) + .get_all_basic_active_mixing_assigned_nodes(Some(self.client_version.clone())) .await { Err(err) => { diff --git a/common/client-core/src/client/topology_control/nym_api_provider.rs b/common/client-core/src/client/topology_control/nym_api_provider.rs index d3b4713c44..7734ea7461 100644 --- a/common/client-core/src/client/topology_control/nym_api_provider.rs +++ b/common/client-core/src/client/topology_control/nym_api_provider.rs @@ -99,7 +99,7 @@ impl NymApiTopologyProvider { async fn get_current_compatible_topology(&mut self) -> Option { let mixnodes = match self .validator_client - .get_basic_active_mixing_assigned_nodes(Some(self.client_version.clone())) + .get_all_basic_active_mixing_assigned_nodes(Some(self.client_version.clone())) .await { Err(err) => { diff --git a/common/client-core/src/init/helpers.rs b/common/client-core/src/init/helpers.rs index 8e90f30240..3f6a390bd0 100644 --- a/common/client-core/src/init/helpers.rs +++ b/common/client-core/src/init/helpers.rs @@ -121,7 +121,9 @@ pub async fn current_mixnodes( log::trace!("Fetching list of mixnodes from: {nym_api}"); - let mixnodes = client.get_basic_active_mixing_assigned_nodes(None).await?; + let mixnodes = client + .get_all_basic_active_mixing_assigned_nodes(None) + .await?; let valid_mixnodes = mixnodes .iter() .filter_map(|mixnode| mixnode.try_into().ok()) diff --git a/common/client-libs/validator-client/src/client.rs b/common/client-libs/validator-client/src/client.rs index 8e1b756bd3..8667678b05 100644 --- a/common/client-libs/validator-client/src/client.rs +++ b/common/client-libs/validator-client/src/client.rs @@ -329,7 +329,7 @@ impl NymApiClient { self.nym_api.change_base_url(new_endpoint); } - #[deprecated(note = "use get_basic_active_mixing_assigned_nodes instead")] + #[deprecated(note = "use get_all_basic_active_mixing_assigned_nodes instead")] pub async fn get_basic_mixnodes( &self, semver_compatibility: Option, @@ -387,7 +387,7 @@ impl NymApiClient { /// retrieve basic information for nodes that got assigned 'mixing' node in this epoch /// this includes legacy mixnodes and nym-nodes - pub async fn get_basic_active_mixing_assigned_nodes( + pub async fn get_all_basic_active_mixing_assigned_nodes( &self, semver_compatibility: Option, ) -> Result, ValidatorClientError> { @@ -417,6 +417,32 @@ impl NymApiClient { Ok(nodes) } + /// retrieve basic information for all bonded nodes on the network + pub async fn get_all_basic_nodes( + &self, + semver_compatibility: Option, + ) -> Result, 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 nodes = Vec::new(); + + loop { + let mut res = self + .nym_api + .get_basic_nodes(semver_compatibility.clone(), false, Some(page), None) + .await?; + + nodes.append(&mut res.nodes.data); + if nodes.len() < res.nodes.pagination.total { + page += 1 + } else { + break; + } + } + + Ok(nodes) + } + pub async fn get_cached_active_mixnodes( &self, ) -> Result, ValidatorClientError> { diff --git a/common/client-libs/validator-client/src/nym_api/mod.rs b/common/client-libs/validator-client/src/nym_api/mod.rs index 69185c4d82..5de7c73aec 100644 --- a/common/client-libs/validator-client/src/nym_api/mod.rs +++ b/common/client-libs/validator-client/src/nym_api/mod.rs @@ -286,6 +286,38 @@ pub trait NymApiClientExt: ApiClient { .await } + async fn get_basic_nodes( + &self, + semver_compatibility: Option, + no_legacy: bool, + page: Option, + per_page: Option, + ) -> Result, NymAPIError> { + let mut params = Vec::new(); + + if let Some(arg) = &semver_compatibility { + params.push(("semver_compatibility", arg.clone())) + } + + if no_legacy { + params.push(("no_legacy", "true".to_string())) + } + + if let Some(page) = page { + params.push(("page", page.to_string())) + } + + if let Some(per_page) = per_page { + params.push(("per_page", per_page.to_string())) + } + + self.get_json( + &[routes::API_VERSION, "unstable", "nym-nodes", "skimmed"], + ¶ms, + ) + .await + } + async fn get_active_mixnodes(&self) -> Result, NymAPIError> { self.get_json( &[routes::API_VERSION, routes::MIXNODES, routes::ACTIVE], diff --git a/common/wasm/client-core/src/helpers.rs b/common/wasm/client-core/src/helpers.rs index 41b203d1b5..c403e9a8de 100644 --- a/common/wasm/client-core/src/helpers.rs +++ b/common/wasm/client-core/src/helpers.rs @@ -68,7 +68,7 @@ pub async fn current_network_topology_async( let api_client = NymApiClient::new(url); let mixnodes = api_client - .get_basic_active_mixing_assigned_nodes(None) + .get_all_basic_active_mixing_assigned_nodes(None) .await?; let gateways = api_client.get_all_basic_entry_assigned_nodes(None).await?; diff --git a/nym-api/nym-api-requests/src/models.rs b/nym-api/nym-api-requests/src/models.rs index 93e9be195d..a1bf8182e2 100644 --- a/nym-api/nym-api-requests/src/models.rs +++ b/nym-api/nym-api-requests/src/models.rs @@ -852,6 +852,10 @@ impl NymNodeDescription { } } + pub fn ed25519_identity_key(&self) -> ed25519::PublicKey { + self.description.host_information.keys.ed25519 + } + pub fn to_skimmed_node(&self, role: NodeRole, performance: Performance) -> SkimmedNode { let keys = &self.description.host_information.keys; let entry = if self.description.declared_role.entry { diff --git a/sdk/rust/nym-sdk/examples/custom_topology_provider.rs b/sdk/rust/nym-sdk/examples/custom_topology_provider.rs index 7b22a4f584..3a617b6b78 100644 --- a/sdk/rust/nym-sdk/examples/custom_topology_provider.rs +++ b/sdk/rust/nym-sdk/examples/custom_topology_provider.rs @@ -21,7 +21,7 @@ impl MyTopologyProvider { async fn get_topology(&self) -> NymTopology { let mixnodes = self .validator_client - .get_basic_active_mixing_assigned_nodes(None) + .get_all_basic_active_mixing_assigned_nodes(None) .await .unwrap(); From c740f8433675d3e9343b22b4989e326c94f18d38 Mon Sep 17 00:00:00 2001 From: Dinko Zdravac <173912580+dynco-nym@users.noreply.github.com> Date: Thu, 31 Oct 2024 04:32:41 +0100 Subject: [PATCH 46/48] NS API with directory v2 (#5058) * Use unstable explorer client * Clean up stale testruns & logging - log gw identity key - better agent testrun logging - log responses - change response code for agents * Better logging on agent * Testrun stores gw identity key instead of gw pk * Agent 0.1.3 * Agent 0.1.4 * Sqlx offline query data + clippy * Compatible with directory v2 * Point to internal deps + rebase + v0.1.5 * self described field not null * Fix build.rs typo --- Cargo.lock | 1064 ++++++----------- common/models/src/ns_api.rs | 3 +- nym-node-status-agent/Cargo.toml | 2 +- nym-node-status-agent/run.sh | 14 +- nym-node-status-agent/src/cli.rs | 12 +- nym-node-status-agent/src/probe.rs | 6 + ...55c69cd5c1a7e7d87073c94600c783a0a3984.json | 20 + ...7dc549cf503409fd6dfedcdc02dbcd61d5454.json | 32 + ...f2c63cdf90f2f3b6d81afb9000bb0968dcaea.json | 12 + ...5ceb89b9925cba46efcf4ed79ef0759a01129.json | 26 + ...965acf3bcfb3f84ba8d24ed645d79976cf784.json | 12 + ...e692afc04b51e8782bcbf59f1eb4116112536.json | 12 + ...8632c29c81bfafb043bb8744129cf9e0266ad.json | 38 + ...203c8569f03fdd39ce09d7b74177896e52a8c.json | 12 + ...289dd776749dd7a20cda61b0480f2fba60889.json | 50 + ...570ee787fc7daf00fcb916f18d1cb7d6c8f08.json | 12 + ...17b40dc01845f554b5479f37855d89b309e6f.json | 32 + ...a667e8cacdf677ccb906415b3fe92be0c436b.json | 20 + ...56f816956b0824c66a25da611dce688105d36.json | 50 + ...df795147ac3724e89df5b2f24f7215d87dce1.json | 12 + ...f3de5949a5ff2083453cb879af8a1689efe2f.json | 12 + ...29d1320a5eb10797cdb5a435455d22c6aeac1.json | 98 ++ ...03a2752e28df775d1af8fd943a5fc8d7dc00d.json | 38 + ...6c5e41f3c0bb56a27648b5e25466b8634a578.json | 12 + ...d17ca6eb090df7f6d52ef89c5d51564f8b45c.json | 12 + ...86b86d1b9be62336c9eac0eef44987a5451b5.json | 20 + ...90f6c66c14d4267a2cc2ca73354becc2c8bb8.json | 26 + ...c586c4c29c03efb4cf0c40b73a5c76159cf5c.json | 12 + ...ea1f6dc2aed00cedde25f2be3567e47064351.json | 44 + ...dadfcf19095935867a51cbd5b8362d1505fcc.json | 32 + ...ee2aa37f6163525bbdce1a4cebef4a285f8d9.json | 12 + ...74a5bf4912ee73468e62e7d0d96b1d9074cbe.json | 12 + ...3d31cb4bcc4071d31d8d2f7755e2d2c2e3e35.json | 86 ++ ...b925e033ceec4f8112258feb4ac9e96fc5924.json | 12 + ...b5d2f2d08b2e53203aab9f07ea9b52acbd407.json | 26 + nym-node-status-api/Cargo.toml | 12 +- nym-node-status-api/build.rs | 8 +- nym-node-status-api/launch_node_status_api.sh | 3 +- nym-node-status-api/migrations/000_init.sql | 4 +- nym-node-status-api/src/db/models.rs | 5 +- .../src/db/queries/gateways.rs | 26 +- nym-node-status-api/src/db/queries/mod.rs | 2 +- .../src/db/queries/testruns.rs | 84 +- nym-node-status-api/src/http/api/mod.rs | 10 +- nym-node-status-api/src/http/api/testruns.rs | 41 +- nym-node-status-api/src/http/error.rs | 10 +- nym-node-status-api/src/http/models.rs | 10 - nym-node-status-api/src/monitor/mod.rs | 101 +- nym-node-status-api/src/testruns/mod.rs | 16 +- nym-node-status-api/src/testruns/queue.rs | 4 +- 50 files changed, 1390 insertions(+), 841 deletions(-) create mode 100644 nym-node-status-api/.sqlx/query-06b17d1e5f61201a1b7542896ba55c69cd5c1a7e7d87073c94600c783a0a3984.json create mode 100644 nym-node-status-api/.sqlx/query-1327b5118f9144dddbcf8edb11f7dc549cf503409fd6dfedcdc02dbcd61d5454.json create mode 100644 nym-node-status-api/.sqlx/query-18abc8fde56cf86baed7b4afa38f2c63cdf90f2f3b6d81afb9000bb0968dcaea.json create mode 100644 nym-node-status-api/.sqlx/query-2236299f9f691376db54cbd58ec5ceb89b9925cba46efcf4ed79ef0759a01129.json create mode 100644 nym-node-status-api/.sqlx/query-3c584e211d07c511644c8079187965acf3bcfb3f84ba8d24ed645d79976cf784.json create mode 100644 nym-node-status-api/.sqlx/query-3d3a1fa429e3090741c6b6a8e82e692afc04b51e8782bcbf59f1eb4116112536.json create mode 100644 nym-node-status-api/.sqlx/query-3d5fc502f976f5081f01352856b8632c29c81bfafb043bb8744129cf9e0266ad.json create mode 100644 nym-node-status-api/.sqlx/query-418944f2eccb838cb3882f34469203c8569f03fdd39ce09d7b74177896e52a8c.json create mode 100644 nym-node-status-api/.sqlx/query-46d76bc6d3fba2dae3b21511a36289dd776749dd7a20cda61b0480f2fba60889.json create mode 100644 nym-node-status-api/.sqlx/query-4afcc6673890f795c2793f1e2f8570ee787fc7daf00fcb916f18d1cb7d6c8f08.json create mode 100644 nym-node-status-api/.sqlx/query-4b61a4bc32333c92a8f5ad4ad0017b40dc01845f554b5479f37855d89b309e6f.json create mode 100644 nym-node-status-api/.sqlx/query-670b7ed7d57a6986181b24be24ca667e8cacdf677ccb906415b3fe92be0c436b.json create mode 100644 nym-node-status-api/.sqlx/query-6d7967b831b355d5f2c77950abc56f816956b0824c66a25da611dce688105d36.json create mode 100644 nym-node-status-api/.sqlx/query-6eb1a682cf13205cf701590021cdf795147ac3724e89df5b2f24f7215d87dce1.json create mode 100644 nym-node-status-api/.sqlx/query-6ef3efde571d46961244cd90420f3de5949a5ff2083453cb879af8a1689efe2f.json create mode 100644 nym-node-status-api/.sqlx/query-71a455c705f9c25d3843ff2fb8629d1320a5eb10797cdb5a435455d22c6aeac1.json create mode 100644 nym-node-status-api/.sqlx/query-7600823da7ce80b8ffda933608603a2752e28df775d1af8fd943a5fc8d7dc00d.json create mode 100644 nym-node-status-api/.sqlx/query-788515c34588aec352773df4b6e6c5e41f3c0bb56a27648b5e25466b8634a578.json create mode 100644 nym-node-status-api/.sqlx/query-8571faad2f66e08f24acfbfe036d17ca6eb090df7f6d52ef89c5d51564f8b45c.json create mode 100644 nym-node-status-api/.sqlx/query-86ff64db477a1d6235179b0b88d86b86d1b9be62336c9eac0eef44987a5451b5.json create mode 100644 nym-node-status-api/.sqlx/query-930a41e612b4e964ae214843da190f6c66c14d4267a2cc2ca73354becc2c8bb8.json create mode 100644 nym-node-status-api/.sqlx/query-c214c001acbbf79fa499816f36ec586c4c29c03efb4cf0c40b73a5c76159cf5c.json create mode 100644 nym-node-status-api/.sqlx/query-c5e3cd7284b334df5aa979b1627ea1f6dc2aed00cedde25f2be3567e47064351.json create mode 100644 nym-node-status-api/.sqlx/query-c7ba2621becb9ac4b5dee0ce303dadfcf19095935867a51cbd5b8362d1505fcc.json create mode 100644 nym-node-status-api/.sqlx/query-d8ea93e781666e6267902170709ee2aa37f6163525bbdce1a4cebef4a285f8d9.json create mode 100644 nym-node-status-api/.sqlx/query-e0c76a959276e3b0f44c720af9c74a5bf4912ee73468e62e7d0d96b1d9074cbe.json create mode 100644 nym-node-status-api/.sqlx/query-f0a4316081d1be9444a87b95d933d31cb4bcc4071d31d8d2f7755e2d2c2e3e35.json create mode 100644 nym-node-status-api/.sqlx/query-f5048d9926a5f5329f7f3b96d43b925e033ceec4f8112258feb4ac9e96fc5924.json create mode 100644 nym-node-status-api/.sqlx/query-ff9334ba7b670b218b2f9100e9ab5d2f2d08b2e53203aab9f07ea9b52acbd407.json diff --git a/Cargo.lock b/Cargo.lock index 8afe0c0421..4c65c456de 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2176,8 +2176,8 @@ dependencies = [ "cosmwasm-std", "cosmwasm-storage", "cw-storage-plus", - "nym-coconut-dkg-common 0.1.0", - "nym-contracts-common 0.5.0", + "nym-coconut-dkg-common", + "nym-contracts-common", ] [[package]] @@ -2438,13 +2438,13 @@ dependencies = [ "itertools 0.13.0", "log", "maxminddb", - "nym-bin-common 0.6.0", - "nym-contracts-common 0.5.0", + "nym-bin-common", + "nym-contracts-common", "nym-explorer-api-requests", - "nym-mixnet-contract-common 0.6.0", - "nym-network-defaults 0.1.0", + "nym-mixnet-contract-common", + "nym-network-defaults", "nym-task", - "nym-validator-client 0.1.0", + "nym-validator-client", "okapi", "pretty_env_logger", "rand", @@ -3437,11 +3437,11 @@ dependencies = [ "clap 4.5.20", "dirs", "importer-contract", - "nym-bin-common 0.6.0", - "nym-mixnet-contract-common 0.6.0", - "nym-network-defaults 0.1.0", - "nym-validator-client 0.1.0", - "nym-vesting-contract-common 0.7.0", + "nym-bin-common", + "nym-mixnet-contract-common", + "nym-network-defaults", + "nym-validator-client", + "nym-vesting-contract-common", "serde", "serde_json", "tokio", @@ -4110,8 +4110,8 @@ dependencies = [ "async-trait", "futures", "js-sys", - "nym-bin-common 0.6.0", - "nym-http-api-client 0.1.0", + "nym-bin-common", + "nym-http-api-client", "nym-ordered-buffer", "nym-service-providers-common", "nym-socks5-requests", @@ -4476,37 +4476,37 @@ dependencies = [ "humantime-serde", "itertools 0.13.0", "k256", - "nym-api-requests 0.1.0", + "nym-api-requests", "nym-bandwidth-controller", - "nym-bin-common 0.6.0", + "nym-bin-common", "nym-coconut", - "nym-coconut-dkg-common 0.1.0", - "nym-compact-ecash 0.1.0", - "nym-config 0.1.0", - "nym-contracts-common 0.5.0", + "nym-coconut-dkg-common", + "nym-compact-ecash", + "nym-config", + "nym-contracts-common", "nym-credential-storage", "nym-credentials", - "nym-credentials-interface 0.1.0", - "nym-crypto 0.4.0", + "nym-credentials-interface", + "nym-crypto", "nym-dkg", - "nym-ecash-contract-common 0.1.0", + "nym-ecash-contract-common", "nym-ecash-double-spending", - "nym-ecash-time 0.1.0", + "nym-ecash-time", "nym-gateway-client", "nym-http-api-common", "nym-inclusion-probability", - "nym-mixnet-contract-common 0.6.0", - "nym-multisig-contract-common 0.1.0", - "nym-node-requests 0.1.0", + "nym-mixnet-contract-common", + "nym-multisig-contract-common", + "nym-node-requests", "nym-node-tester-utils", - "nym-pemstore 0.3.0", - "nym-serde-helpers 0.1.0", + "nym-pemstore", + "nym-serde-helpers", "nym-sphinx", "nym-task", "nym-topology", "nym-types", - "nym-validator-client 0.1.0", - "nym-vesting-contract-common 0.7.0", + "nym-validator-client", + "nym-vesting-contract-common", "pin-project", "rand", "rand_chacha", @@ -4541,14 +4541,14 @@ dependencies = [ "cosmwasm-std", "ecdsa", "getset", - "nym-compact-ecash 0.1.0", - "nym-credentials-interface 0.1.0", - "nym-crypto 0.4.0", - "nym-ecash-time 0.1.0", - "nym-mixnet-contract-common 0.6.0", - "nym-network-defaults 0.1.0", - "nym-node-requests 0.1.0", - "nym-serde-helpers 0.1.0", + "nym-compact-ecash", + "nym-credentials-interface", + "nym-crypto", + "nym-ecash-time", + "nym-mixnet-contract-common", + "nym-network-defaults", + "nym-node-requests", + "nym-serde-helpers", "schemars", "serde", "serde_json", @@ -4560,32 +4560,6 @@ dependencies = [ "utoipa", ] -[[package]] -name = "nym-api-requests" -version = "0.1.0" -source = "git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork#8d68cf88dac1adf1f68726a9b6e0740f8cddcf32" -dependencies = [ - "bs58", - "cosmrs 0.17.0-pre", - "cosmwasm-std", - "ecdsa", - "getset", - "nym-compact-ecash 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", - "nym-credentials-interface 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", - "nym-crypto 0.4.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", - "nym-ecash-time 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", - "nym-mixnet-contract-common 0.6.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", - "nym-node-requests 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", - "nym-serde-helpers 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", - "schemars", - "serde", - "sha2 0.10.8", - "tendermint 0.37.0", - "thiserror", - "time", - "utoipa", -] - [[package]] name = "nym-async-file-watcher" version = "0.1.0" @@ -4611,16 +4585,16 @@ dependencies = [ "ipnetwork 0.20.0", "log", "nym-authenticator-requests", - "nym-bin-common 0.6.0", + "nym-bin-common", "nym-client-core", - "nym-config 0.1.0", + "nym-config", "nym-credential-verification", - "nym-credentials-interface 0.1.0", - "nym-crypto 0.4.0", + "nym-credentials-interface", + "nym-crypto", "nym-gateway-requests", "nym-gateway-storage", "nym-id", - "nym-network-defaults 0.1.0", + "nym-network-defaults", "nym-sdk", "nym-service-provider-requests-common", "nym-service-providers-common", @@ -4628,7 +4602,7 @@ dependencies = [ "nym-task", "nym-types", "nym-wireguard", - "nym-wireguard-types 0.1.0", + "nym-wireguard-types", "rand", "serde", "serde_json", @@ -4646,11 +4620,11 @@ dependencies = [ "base64 0.22.1", "bincode", "hmac", - "nym-credentials-interface 0.1.0", - "nym-crypto 0.4.0", + "nym-credentials-interface", + "nym-crypto", "nym-service-provider-requests-common", "nym-sphinx", - "nym-wireguard-types 0.1.0", + "nym-wireguard-types", "rand", "serde", "sha2 0.10.8", @@ -4666,12 +4640,12 @@ dependencies = [ "log", "nym-credential-storage", "nym-credentials", - "nym-credentials-interface 0.1.0", - "nym-crypto 0.4.0", - "nym-ecash-contract-common 0.1.0", - "nym-ecash-time 0.1.0", - "nym-network-defaults 0.1.0", - "nym-validator-client 0.1.0", + "nym-credentials-interface", + "nym-crypto", + "nym-ecash-contract-common", + "nym-ecash-time", + "nym-network-defaults", + "nym-validator-client", "rand", "thiserror", "url", @@ -4701,21 +4675,6 @@ dependencies = [ "vergen", ] -[[package]] -name = "nym-bin-common" -version = "0.6.0" -source = "git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork#8d68cf88dac1adf1f68726a9b6e0740f8cddcf32" -dependencies = [ - "const-str", - "log", - "pretty_env_logger", - "schemars", - "semver 1.0.23", - "serde", - "utoipa", - "vergen", -] - [[package]] name = "nym-bity-integration" version = "0.1.0" @@ -4725,7 +4684,7 @@ dependencies = [ "eyre", "k256", "nym-cli-commands", - "nym-validator-client 0.1.0", + "nym-validator-client", "serde", "serde_json", "thiserror", @@ -4745,10 +4704,10 @@ dependencies = [ "dotenvy", "inquire", "log", - "nym-bin-common 0.6.0", + "nym-bin-common", "nym-cli-commands", - "nym-network-defaults 0.1.0", - "nym-validator-client 0.1.0", + "nym-network-defaults", + "nym-validator-client", "pretty_env_logger", "serde", "serde_json", @@ -4779,26 +4738,26 @@ dependencies = [ "k256", "log", "nym-bandwidth-controller", - "nym-bin-common 0.6.0", + "nym-bin-common", "nym-client-core", - "nym-coconut-dkg-common 0.1.0", - "nym-config 0.1.0", - "nym-contracts-common 0.5.0", + "nym-coconut-dkg-common", + "nym-config", + "nym-contracts-common", "nym-credential-storage", "nym-credential-utils", "nym-credentials", - "nym-credentials-interface 0.1.0", - "nym-crypto 0.4.0", - "nym-ecash-contract-common 0.1.0", + "nym-credentials-interface", + "nym-crypto", + "nym-ecash-contract-common", "nym-id", - "nym-mixnet-contract-common 0.6.0", - "nym-multisig-contract-common 0.1.0", - "nym-network-defaults 0.1.0", - "nym-pemstore 0.3.0", + "nym-mixnet-contract-common", + "nym-multisig-contract-common", + "nym-network-defaults", + "nym-pemstore", "nym-sphinx", "nym-types", - "nym-validator-client 0.1.0", - "nym-vesting-contract-common 0.7.0", + "nym-validator-client", + "nym-vesting-contract-common", "rand", "serde", "serde_json", @@ -4822,21 +4781,21 @@ dependencies = [ "futures", "log", "nym-bandwidth-controller", - "nym-bin-common 0.6.0", + "nym-bin-common", "nym-client-core", "nym-client-websocket-requests", - "nym-config 0.1.0", + "nym-config", "nym-credential-storage", "nym-credentials", - "nym-crypto 0.4.0", + "nym-crypto", "nym-gateway-requests", "nym-id", - "nym-network-defaults 0.1.0", - "nym-pemstore 0.3.0", + "nym-network-defaults", + "nym-pemstore", "nym-sphinx", "nym-task", "nym-topology", - "nym-validator-client 0.1.0", + "nym-validator-client", "rand", "serde", "serde_json", @@ -4870,24 +4829,24 @@ dependencies = [ "nym-client-core-config-types", "nym-client-core-gateways-storage", "nym-client-core-surb-storage", - "nym-config 0.1.0", + "nym-config", "nym-country-group", "nym-credential-storage", - "nym-credentials-interface 0.1.0", - "nym-crypto 0.4.0", - "nym-ecash-time 0.1.0", + "nym-credentials-interface", + "nym-crypto", + "nym-ecash-time", "nym-explorer-client", "nym-gateway-client", "nym-gateway-requests", "nym-id", "nym-metrics", - "nym-network-defaults 0.1.0", + "nym-network-defaults", "nym-nonexhaustive-delayqueue", - "nym-pemstore 0.3.0", + "nym-pemstore", "nym-sphinx", "nym-task", "nym-topology", - "nym-validator-client 0.1.0", + "nym-validator-client", "rand", "rand_chacha", "serde", @@ -4915,9 +4874,9 @@ name = "nym-client-core-config-types" version = "0.1.0" dependencies = [ "humantime-serde", - "nym-config 0.1.0", + "nym-config", "nym-country-group", - "nym-pemstore 0.3.0", + "nym-pemstore", "nym-sphinx-addressing", "nym-sphinx-params", "serde", @@ -4932,7 +4891,7 @@ dependencies = [ "async-trait", "cosmrs 0.17.0-pre", "log", - "nym-crypto 0.4.0", + "nym-crypto", "nym-gateway-requests", "serde", "sqlx", @@ -4950,7 +4909,7 @@ dependencies = [ "async-trait", "dashmap", "log", - "nym-crypto 0.4.0", + "nym-crypto", "nym-sphinx", "nym-task", "sqlx", @@ -4966,7 +4925,7 @@ dependencies = [ "anyhow", "futures", "js-sys", - "nym-bin-common 0.6.0", + "nym-bin-common", "nym-node-tester-utils", "nym-node-tester-wasm", "rand", @@ -5005,7 +4964,7 @@ dependencies = [ "group", "itertools 0.13.0", "nym-dkg", - "nym-pemstore 0.3.0", + "nym-pemstore", "rand", "rand_chacha", "serde", @@ -5022,17 +4981,7 @@ dependencies = [ "cosmwasm-schema", "cosmwasm-std", "cw2", - "nym-multisig-contract-common 0.1.0", -] - -[[package]] -name = "nym-coconut-bandwidth-contract-common" -version = "0.1.0" -source = "git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork#8d68cf88dac1adf1f68726a9b6e0740f8cddcf32" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "nym-multisig-contract-common 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", + "nym-multisig-contract-common", ] [[package]] @@ -5044,22 +4993,8 @@ dependencies = [ "cw-utils", "cw2", "cw4", - "nym-contracts-common 0.5.0", - "nym-multisig-contract-common 0.1.0", -] - -[[package]] -name = "nym-coconut-dkg-common" -version = "0.1.0" -source = "git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork#8d68cf88dac1adf1f68726a9b6e0740f8cddcf32" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-utils", - "cw2", - "cw4", - "nym-contracts-common 0.5.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", - "nym-multisig-contract-common 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", + "nym-contracts-common", + "nym-multisig-contract-common", ] [[package]] @@ -5082,8 +5017,8 @@ dependencies = [ "ff", "group", "itertools 0.13.0", - "nym-network-defaults 0.1.0", - "nym-pemstore 0.3.0", + "nym-network-defaults", + "nym-pemstore", "rand", "rayon", "serde", @@ -5093,28 +5028,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "nym-compact-ecash" -version = "0.1.0" -source = "git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork#8d68cf88dac1adf1f68726a9b6e0740f8cddcf32" -dependencies = [ - "bincode", - "bls12_381", - "bs58", - "cfg-if", - "digest 0.9.0", - "ff", - "group", - "itertools 0.12.1", - "nym-network-defaults 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", - "nym-pemstore 0.3.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", - "rand", - "serde", - "sha2 0.9.9", - "thiserror", - "zeroize", -] - [[package]] name = "nym-config" version = "0.1.0" @@ -5122,21 +5035,7 @@ dependencies = [ "dirs", "handlebars", "log", - "nym-network-defaults 0.1.0", - "serde", - "toml 0.8.14", - "url", -] - -[[package]] -name = "nym-config" -version = "0.1.0" -source = "git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork#8d68cf88dac1adf1f68726a9b6e0740f8cddcf32" -dependencies = [ - "dirs", - "handlebars", - "log", - "nym-network-defaults 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", + "nym-network-defaults", "serde", "toml 0.8.14", "url", @@ -5157,21 +5056,6 @@ dependencies = [ "vergen", ] -[[package]] -name = "nym-contracts-common" -version = "0.5.0" -source = "git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork#8d68cf88dac1adf1f68726a9b6e0740f8cddcf32" -dependencies = [ - "bs58", - "cosmwasm-schema", - "cosmwasm-std", - "cw-storage-plus", - "schemars", - "serde", - "thiserror", - "vergen", -] - [[package]] name = "nym-country-group" version = "0.1.0" @@ -5187,7 +5071,7 @@ dependencies = [ "anyhow", "bs58", "lazy_static", - "nym-bin-common 0.6.0", + "nym-bin-common", "nym-ffi-shared", "nym-sdk", "nym-sphinx-anonymous-replies", @@ -5209,16 +5093,16 @@ dependencies = [ "dotenvy", "futures", "humantime 2.1.0", - "nym-bin-common 0.6.0", - "nym-compact-ecash 0.1.0", - "nym-config 0.1.0", + "nym-bin-common", + "nym-compact-ecash", + "nym-config", "nym-credential-proxy-requests", "nym-credentials", - "nym-credentials-interface 0.1.0", - "nym-crypto 0.4.0", + "nym-credentials-interface", + "nym-crypto", "nym-http-api-common", - "nym-network-defaults 0.1.0", - "nym-validator-client 0.1.0", + "nym-network-defaults", + "nym-validator-client", "rand", "reqwest 0.12.4", "serde", @@ -5247,10 +5131,10 @@ version = "0.1.0" dependencies = [ "async-trait", "nym-credentials", - "nym-credentials-interface 0.1.0", - "nym-http-api-client 0.1.0", + "nym-credentials-interface", + "nym-http-api-client", "nym-http-api-common", - "nym-serde-helpers 0.1.0", + "nym-serde-helpers", "reqwest 0.12.4", "schemars", "serde", @@ -5270,9 +5154,9 @@ dependencies = [ "async-trait", "bincode", "log", - "nym-compact-ecash 0.1.0", + "nym-compact-ecash", "nym-credentials", - "nym-ecash-time 0.1.0", + "nym-ecash-time", "serde", "sqlx", "thiserror", @@ -5287,12 +5171,12 @@ dependencies = [ "log", "nym-bandwidth-controller", "nym-client-core", - "nym-config 0.1.0", + "nym-config", "nym-credential-storage", "nym-credentials", - "nym-credentials-interface 0.1.0", - "nym-ecash-time 0.1.0", - "nym-validator-client 0.1.0", + "nym-credentials-interface", + "nym-ecash-time", + "nym-validator-client", "thiserror", "time", "tokio", @@ -5306,15 +5190,15 @@ dependencies = [ "cosmwasm-std", "cw-utils", "futures", - "nym-api-requests 0.1.0", + "nym-api-requests", "nym-credentials", - "nym-credentials-interface 0.1.0", - "nym-ecash-contract-common 0.1.0", + "nym-credentials-interface", + "nym-ecash-contract-common", "nym-ecash-double-spending", "nym-gateway-requests", "nym-gateway-storage", "nym-task", - "nym-validator-client 0.1.0", + "nym-validator-client", "rand", "si-scale", "thiserror", @@ -5331,14 +5215,14 @@ dependencies = [ "bls12_381", "cosmrs 0.17.0-pre", "log", - "nym-api-requests 0.1.0", - "nym-credentials-interface 0.1.0", - "nym-crypto 0.4.0", - "nym-ecash-contract-common 0.1.0", - "nym-ecash-time 0.1.0", - "nym-network-defaults 0.1.0", - "nym-serde-helpers 0.1.0", - "nym-validator-client 0.1.0", + "nym-api-requests", + "nym-credentials-interface", + "nym-crypto", + "nym-ecash-contract-common", + "nym-ecash-time", + "nym-network-defaults", + "nym-serde-helpers", + "nym-validator-client", "rand", "serde", "thiserror", @@ -5351,25 +5235,9 @@ name = "nym-credentials-interface" version = "0.1.0" dependencies = [ "bls12_381", - "nym-compact-ecash 0.1.0", - "nym-ecash-time 0.1.0", - "nym-network-defaults 0.1.0", - "rand", - "serde", - "strum 0.26.3", - "thiserror", - "time", -] - -[[package]] -name = "nym-credentials-interface" -version = "0.1.0" -source = "git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork#8d68cf88dac1adf1f68726a9b6e0740f8cddcf32" -dependencies = [ - "bls12_381", - "nym-compact-ecash 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", - "nym-ecash-time 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", - "nym-network-defaults 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", + "nym-compact-ecash", + "nym-ecash-time", + "nym-network-defaults", "rand", "serde", "strum 0.26.3", @@ -5393,8 +5261,8 @@ dependencies = [ "generic-array 0.14.7", "hkdf", "hmac", - "nym-pemstore 0.3.0", - "nym-sphinx-types 0.2.0", + "nym-pemstore", + "nym-sphinx-types", "rand", "rand_chacha", "serde", @@ -5405,23 +5273,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "nym-crypto" -version = "0.4.0" -source = "git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork#8d68cf88dac1adf1f68726a9b6e0740f8cddcf32" -dependencies = [ - "bs58", - "ed25519-dalek", - "nym-pemstore 0.3.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", - "nym-sphinx-types 0.2.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", - "serde", - "serde_bytes", - "subtle-encoding", - "thiserror", - "x25519-dalek", - "zeroize", -] - [[package]] name = "nym-data-observatory" version = "0.1.0" @@ -5430,9 +5281,9 @@ dependencies = [ "axum 0.7.7", "chrono", "clap 4.5.20", - "nym-bin-common 0.6.0", - "nym-network-defaults 0.1.0", - "nym-node-requests 0.1.0", + "nym-bin-common", + "nym-network-defaults", + "nym-node-requests", "nym-task", "serde", "serde_json", @@ -5458,8 +5309,8 @@ dependencies = [ "ff", "group", "lazy_static", - "nym-contracts-common 0.5.0", - "nym-pemstore 0.3.0", + "nym-contracts-common", + "nym-pemstore", "rand", "rand_chacha", "rand_core 0.6.4", @@ -5480,21 +5331,7 @@ dependencies = [ "cw-controllers", "cw-utils", "cw2", - "nym-multisig-contract-common 0.1.0", - "thiserror", -] - -[[package]] -name = "nym-ecash-contract-common" -version = "0.1.0" -source = "git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork#8d68cf88dac1adf1f68726a9b6e0740f8cddcf32" -dependencies = [ - "bs58", - "cosmwasm-schema", - "cosmwasm-std", - "cw-controllers", - "cw-utils", - "nym-multisig-contract-common 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", + "nym-multisig-contract-common", "thiserror", ] @@ -5504,22 +5341,14 @@ version = "0.1.0" dependencies = [ "bit-vec", "bloomfilter", - "nym-network-defaults 0.1.0", + "nym-network-defaults", ] [[package]] name = "nym-ecash-time" version = "0.1.0" dependencies = [ - "nym-compact-ecash 0.1.0", - "time", -] - -[[package]] -name = "nym-ecash-time" -version = "0.1.0" -source = "git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork#8d68cf88dac1adf1f68726a9b6e0740f8cddcf32" -dependencies = [ + "nym-compact-ecash", "time", ] @@ -5543,25 +5372,13 @@ dependencies = [ "utoipa", ] -[[package]] -name = "nym-exit-policy" -version = "0.1.0" -source = "git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork#8d68cf88dac1adf1f68726a9b6e0740f8cddcf32" -dependencies = [ - "serde", - "serde_json", - "thiserror", - "tracing", - "utoipa", -] - [[package]] name = "nym-explorer-api-requests" version = "0.1.0" dependencies = [ - "nym-api-requests 0.1.0", - "nym-contracts-common 0.5.0", - "nym-mixnet-contract-common 0.6.0", + "nym-api-requests", + "nym-contracts-common", + "nym-mixnet-contract-common", "schemars", "serde", "ts-rs", @@ -5587,7 +5404,7 @@ dependencies = [ "anyhow", "bs58", "lazy_static", - "nym-bin-common 0.6.0", + "nym-bin-common", "nym-sdk", "nym-sphinx-anonymous-replies", "tokio", @@ -5612,33 +5429,33 @@ dependencies = [ "futures", "humantime-serde", "ipnetwork 0.20.0", - "nym-api-requests 0.1.0", + "nym-api-requests", "nym-authenticator", - "nym-bin-common 0.6.0", - "nym-config 0.1.0", + "nym-bin-common", + "nym-config", "nym-credential-verification", "nym-credentials", - "nym-credentials-interface 0.1.0", - "nym-crypto 0.4.0", + "nym-credentials-interface", + "nym-crypto", "nym-gateway-requests", "nym-gateway-stats-storage", "nym-gateway-storage", "nym-ip-packet-router", "nym-mixnet-client", "nym-mixnode-common", - "nym-network-defaults 0.1.0", + "nym-network-defaults", "nym-network-requester", "nym-node-http-api", - "nym-pemstore 0.3.0", + "nym-pemstore", "nym-sdk", "nym-sphinx", "nym-statistics-common", "nym-task", "nym-topology", "nym-types", - "nym-validator-client 0.1.0", + "nym-validator-client", "nym-wireguard", - "nym-wireguard-types 0.1.0", + "nym-wireguard-types", "once_cell", "rand", "serde", @@ -5667,14 +5484,14 @@ dependencies = [ "nym-bandwidth-controller", "nym-credential-storage", "nym-credentials", - "nym-credentials-interface 0.1.0", - "nym-crypto 0.4.0", + "nym-credentials-interface", + "nym-crypto", "nym-gateway-requests", - "nym-network-defaults 0.1.0", - "nym-pemstore 0.3.0", + "nym-network-defaults", + "nym-pemstore", "nym-sphinx", "nym-task", - "nym-validator-client 0.1.0", + "nym-validator-client", "rand", "serde", "si-scale", @@ -5700,11 +5517,11 @@ dependencies = [ "bs58", "futures", "generic-array 0.14.7", - "nym-compact-ecash 0.1.0", + "nym-compact-ecash", "nym-credentials", - "nym-credentials-interface 0.1.0", - "nym-crypto 0.4.0", - "nym-pemstore 0.3.0", + "nym-credentials-interface", + "nym-crypto", + "nym-pemstore", "nym-sphinx", "nym-task", "rand", @@ -5723,7 +5540,7 @@ dependencies = [ name = "nym-gateway-stats-storage" version = "0.1.0" dependencies = [ - "nym-credentials-interface 0.1.0", + "nym-credentials-interface", "nym-sphinx", "sqlx", "thiserror", @@ -5740,7 +5557,7 @@ dependencies = [ "bincode", "defguard_wireguard_rs", "log", - "nym-credentials-interface 0.1.0", + "nym-credentials-interface", "nym-gateway-requests", "nym-sphinx", "sqlx", @@ -5756,7 +5573,7 @@ version = "0.2.0" dependencies = [ "anyhow", "lazy_static", - "nym-bin-common 0.6.0", + "nym-bin-common", "nym-ffi-shared", "nym-sdk", "nym-sphinx-anonymous-replies", @@ -5777,42 +5594,13 @@ dependencies = [ "serde", ] -[[package]] -name = "nym-group-contract-common" -version = "0.1.0" -source = "git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork#8d68cf88dac1adf1f68726a9b6e0740f8cddcf32" -dependencies = [ - "cosmwasm-schema", - "cw-controllers", - "cw4", - "schemars", - "serde", -] - [[package]] name = "nym-http-api-client" version = "0.1.0" dependencies = [ "async-trait", "http 1.1.0", - "nym-bin-common 0.6.0", - "reqwest 0.12.4", - "serde", - "serde_json", - "thiserror", - "tracing", - "url", - "wasmtimer", -] - -[[package]] -name = "nym-http-api-client" -version = "0.1.0" -source = "git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork#8d68cf88dac1adf1f68726a9b6e0740f8cddcf32" -dependencies = [ - "async-trait", - "http 1.1.0", - "nym-bin-common 0.6.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", + "nym-bin-common", "reqwest 0.12.4", "serde", "serde_json", @@ -5857,7 +5645,7 @@ dependencies = [ "anyhow", "bs58", "clap 4.5.20", - "nym-bin-common 0.6.0", + "nym-bin-common", "nym-credential-storage", "nym-id", "tokio", @@ -5879,8 +5667,8 @@ version = "0.1.0" dependencies = [ "bincode", "bytes", - "nym-bin-common 0.6.0", - "nym-crypto 0.4.0", + "nym-bin-common", + "nym-crypto", "nym-sphinx", "rand", "serde", @@ -5902,14 +5690,14 @@ dependencies = [ "etherparse", "futures", "log", - "nym-bin-common 0.6.0", + "nym-bin-common", "nym-client-core", - "nym-config 0.1.0", - "nym-crypto 0.4.0", - "nym-exit-policy 0.1.0", + "nym-config", + "nym-crypto", + "nym-exit-policy", "nym-id", "nym-ip-packet-requests", - "nym-network-defaults 0.1.0", + "nym-network-defaults", "nym-network-requester", "nym-sdk", "nym-service-providers-common", @@ -5918,7 +5706,7 @@ dependencies = [ "nym-tun", "nym-types", "nym-wireguard", - "nym-wireguard-types 0.1.0", + "nym-wireguard-types", "rand", "reqwest 0.12.4", "serde", @@ -5977,7 +5765,7 @@ dependencies = [ "cw2", "humantime-serde", "log", - "nym-contracts-common 0.5.0", + "nym-contracts-common", "rand_chacha", "schemars", "serde", @@ -5989,26 +5777,6 @@ dependencies = [ "utoipa", ] -[[package]] -name = "nym-mixnet-contract-common" -version = "0.6.0" -source = "git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork#8d68cf88dac1adf1f68726a9b6e0740f8cddcf32" -dependencies = [ - "bs58", - "cosmwasm-schema", - "cosmwasm-std", - "cw-controllers", - "humantime-serde", - "log", - "nym-contracts-common 0.5.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", - "schemars", - "serde", - "serde-json-wasm", - "serde_repr", - "thiserror", - "time", -] - [[package]] name = "nym-mixnode" version = "1.1.37" @@ -6024,24 +5792,24 @@ dependencies = [ "humantime-serde", "lazy_static", "log", - "nym-bin-common 0.6.0", - "nym-config 0.1.0", - "nym-contracts-common 0.5.0", - "nym-crypto 0.4.0", + "nym-bin-common", + "nym-config", + "nym-contracts-common", + "nym-crypto", "nym-http-api-common", "nym-metrics", "nym-mixnet-client", "nym-mixnode-common", "nym-node-http-api", "nym-nonexhaustive-delayqueue", - "nym-pemstore 0.3.0", + "nym-pemstore", "nym-sphinx", "nym-sphinx-params", - "nym-sphinx-types 0.2.0", + "nym-sphinx-types", "nym-task", "nym-topology", "nym-types", - "nym-validator-client 0.1.0", + "nym-validator-client", "rand", "serde", "serde_json", @@ -6062,19 +5830,19 @@ dependencies = [ "futures", "humantime-serde", "log", - "nym-bin-common 0.6.0", - "nym-crypto 0.4.0", + "nym-bin-common", + "nym-crypto", "nym-metrics", - "nym-network-defaults 0.1.0", + "nym-network-defaults", "nym-node-http-api", "nym-sphinx-acknowledgements", "nym-sphinx-addressing", "nym-sphinx-forwarding", "nym-sphinx-framing", "nym-sphinx-params", - "nym-sphinx-types 0.2.0", + "nym-sphinx-types", "nym-task", - "nym-validator-client 0.1.0", + "nym-validator-client", "rand", "serde", "thiserror", @@ -6099,22 +5867,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "nym-multisig-contract-common" -version = "0.1.0" -source = "git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork#8d68cf88dac1adf1f68726a9b6e0740f8cddcf32" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-storage-plus", - "cw-utils", - "cw3", - "cw4", - "schemars", - "serde", - "thiserror", -] - [[package]] name = "nym-network-defaults" version = "0.1.0" @@ -6127,19 +5879,6 @@ dependencies = [ "utoipa", ] -[[package]] -name = "nym-network-defaults" -version = "0.1.0" -source = "git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork#8d68cf88dac1adf1f68726a9b6e0740f8cddcf32" -dependencies = [ - "dotenvy", - "log", - "schemars", - "serde", - "url", - "utoipa", -] - [[package]] name = "nym-network-monitor" version = "0.1.0" @@ -6150,14 +5889,14 @@ dependencies = [ "dashmap", "futures", "log", - "nym-bin-common 0.6.0", - "nym-crypto 0.4.0", - "nym-network-defaults 0.1.0", + "nym-bin-common", + "nym-crypto", + "nym-network-defaults", "nym-sdk", "nym-sphinx", "nym-topology", "nym-types", - "nym-validator-client 0.1.0", + "nym-validator-client", "petgraph", "rand", "rand_chacha", @@ -6185,16 +5924,16 @@ dependencies = [ "ipnetwork 0.20.0", "log", "nym-async-file-watcher", - "nym-bin-common 0.6.0", + "nym-bin-common", "nym-client-core", "nym-client-websocket-requests", - "nym-config 0.1.0", + "nym-config", "nym-credential-storage", "nym-credentials", - "nym-crypto 0.4.0", - "nym-exit-policy 0.1.0", + "nym-crypto", + "nym-exit-policy", "nym-id", - "nym-network-defaults 0.1.0", + "nym-network-defaults", "nym-ordered-buffer", "nym-sdk", "nym-service-providers-common", @@ -6236,22 +5975,22 @@ dependencies = [ "humantime-serde", "ipnetwork 0.20.0", "nym-authenticator", - "nym-bin-common 0.6.0", + "nym-bin-common", "nym-client-core-config-types", - "nym-config 0.1.0", - "nym-crypto 0.4.0", + "nym-config", + "nym-crypto", "nym-gateway", "nym-ip-packet-router", "nym-mixnode", "nym-network-requester", "nym-node-http-api", - "nym-pemstore 0.3.0", + "nym-pemstore", "nym-sphinx-acknowledgements", "nym-sphinx-addressing", "nym-task", "nym-types", "nym-wireguard", - "nym-wireguard-types 0.1.0", + "nym-wireguard-types", "rand", "semver 1.0.23", "serde", @@ -6279,10 +6018,10 @@ dependencies = [ "hmac", "hyper 1.4.1", "ipnetwork 0.20.0", - "nym-crypto 0.4.0", + "nym-crypto", "nym-http-api-common", "nym-metrics", - "nym-node-requests 0.1.0", + "nym-node-requests", "nym-task", "nym-wireguard", "rand", @@ -6307,11 +6046,11 @@ dependencies = [ "celes", "humantime 2.1.0", "humantime-serde", - "nym-bin-common 0.6.0", - "nym-crypto 0.4.0", - "nym-exit-policy 0.1.0", - "nym-http-api-client 0.1.0", - "nym-wireguard-types 0.1.0", + "nym-bin-common", + "nym-crypto", + "nym-exit-policy", + "nym-http-api-client", + "nym-wireguard-types", "rand_chacha", "schemars", "serde", @@ -6322,36 +6061,13 @@ dependencies = [ "utoipa", ] -[[package]] -name = "nym-node-requests" -version = "0.1.0" -source = "git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork#8d68cf88dac1adf1f68726a9b6e0740f8cddcf32" -dependencies = [ - "async-trait", - "base64 0.22.1", - "celes", - "humantime 2.1.0", - "humantime-serde", - "nym-bin-common 0.6.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", - "nym-crypto 0.4.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", - "nym-exit-policy 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", - "nym-http-api-client 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", - "nym-wireguard-types 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", - "schemars", - "serde", - "serde_json", - "thiserror", - "time", - "utoipa", -] - [[package]] name = "nym-node-status-agent" -version = "0.1.0" +version = "0.1.4" dependencies = [ "anyhow", "clap 4.5.20", - "nym-bin-common 0.6.0", + "nym-bin-common", "nym-common-models", "reqwest 0.12.4", "serde_json", @@ -6363,7 +6079,7 @@ dependencies = [ [[package]] name = "nym-node-status-api" -version = "0.1.0" +version = "0.1.5" dependencies = [ "anyhow", "axum 0.7.7", @@ -6373,13 +6089,13 @@ dependencies = [ "envy", "futures-util", "moka", - "nym-bin-common 0.6.0", + "nym-bin-common", "nym-common-models", "nym-explorer-client", - "nym-network-defaults 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", - "nym-node-requests 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", + "nym-network-defaults", + "nym-node-requests", "nym-task", - "nym-validator-client 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", + "nym-validator-client", "regex", "reqwest 0.12.4", "serde", @@ -6406,7 +6122,7 @@ version = "0.1.0" dependencies = [ "futures", "log", - "nym-crypto 0.4.0", + "nym-crypto", "nym-sphinx", "nym-sphinx-params", "nym-task", @@ -6457,8 +6173,8 @@ dependencies = [ "anyhow", "clap 4.5.20", "log", - "nym-bin-common 0.6.0", - "nym-network-defaults 0.1.0", + "nym-bin-common", + "nym-network-defaults", "nym-sdk", "nym-service-providers-common", "nym-socks5-requests", @@ -6500,14 +6216,6 @@ dependencies = [ "pem", ] -[[package]] -name = "nym-pemstore" -version = "0.3.0" -source = "git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork#8d68cf88dac1adf1f68726a9b6e0740f8cddcf32" -dependencies = [ - "pem", -] - [[package]] name = "nym-sdk" version = "0.1.0" @@ -6527,15 +6235,15 @@ dependencies = [ "httpcodec", "log", "nym-bandwidth-controller", - "nym-bin-common 0.6.0", + "nym-bin-common", "nym-client-core", "nym-credential-storage", "nym-credential-utils", "nym-credentials", - "nym-credentials-interface 0.1.0", - "nym-crypto 0.4.0", + "nym-credentials-interface", + "nym-crypto", "nym-gateway-requests", - "nym-network-defaults 0.1.0", + "nym-network-defaults", "nym-ordered-buffer", "nym-service-providers-common", "nym-socks5-client-core", @@ -6543,7 +6251,7 @@ dependencies = [ "nym-sphinx", "nym-task", "nym-topology", - "nym-validator-client 0.1.0", + "nym-validator-client", "parking_lot", "pretty_env_logger", "rand", @@ -6573,17 +6281,6 @@ dependencies = [ "time", ] -[[package]] -name = "nym-serde-helpers" -version = "0.1.0" -source = "git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork#8d68cf88dac1adf1f68726a9b6e0740f8cddcf32" -dependencies = [ - "base64 0.22.1", - "bs58", - "serde", - "time", -] - [[package]] name = "nym-service-provider-requests-common" version = "0.1.0" @@ -6598,7 +6295,7 @@ dependencies = [ "anyhow", "async-trait", "log", - "nym-bin-common 0.6.0", + "nym-bin-common", "nym-sdk", "nym-socks5-requests", "nym-sphinx-anonymous-replies", @@ -6615,21 +6312,21 @@ dependencies = [ "bs58", "clap 4.5.20", "log", - "nym-bin-common 0.6.0", + "nym-bin-common", "nym-client-core", - "nym-config 0.1.0", + "nym-config", "nym-credential-storage", "nym-credentials", - "nym-crypto 0.4.0", + "nym-crypto", "nym-gateway-requests", "nym-id", - "nym-network-defaults 0.1.0", + "nym-network-defaults", "nym-ordered-buffer", - "nym-pemstore 0.3.0", + "nym-pemstore", "nym-socks5-client-core", "nym-sphinx", "nym-topology", - "nym-validator-client 0.1.0", + "nym-validator-client", "rand", "serde", "serde_json", @@ -6651,17 +6348,17 @@ dependencies = [ "log", "nym-bandwidth-controller", "nym-client-core", - "nym-config 0.1.0", - "nym-contracts-common 0.5.0", + "nym-config", + "nym-contracts-common", "nym-credential-storage", - "nym-mixnet-contract-common 0.6.0", - "nym-network-defaults 0.1.0", + "nym-mixnet-contract-common", + "nym-network-defaults", "nym-service-providers-common", "nym-socks5-proxy-helpers", "nym-socks5-requests", "nym-sphinx", "nym-task", - "nym-validator-client 0.1.0", + "nym-validator-client", "pin-project", "rand", "reqwest 0.12.4", @@ -6683,11 +6380,11 @@ dependencies = [ "jni", "lazy_static", "log", - "nym-bin-common 0.6.0", + "nym-bin-common", "nym-client-core", - "nym-config 0.1.0", + "nym-config", "nym-credential-storage", - "nym-crypto 0.4.0", + "nym-crypto", "nym-socks5-client-core", "rand", "safer-ffi", @@ -6716,7 +6413,7 @@ version = "0.1.0" dependencies = [ "bincode", "log", - "nym-exit-policy 0.1.0", + "nym-exit-policy", "nym-service-providers-common", "nym-sphinx-addressing", "serde", @@ -6730,9 +6427,9 @@ name = "nym-sphinx" version = "0.1.0" dependencies = [ "log", - "nym-crypto 0.4.0", + "nym-crypto", "nym-metrics", - "nym-mixnet-contract-common 0.6.0", + "nym-mixnet-contract-common", "nym-sphinx-acknowledgements", "nym-sphinx-addressing", "nym-sphinx-anonymous-replies", @@ -6742,7 +6439,7 @@ dependencies = [ "nym-sphinx-framing", "nym-sphinx-params", "nym-sphinx-routing", - "nym-sphinx-types 0.2.0", + "nym-sphinx-types", "nym-topology", "rand", "rand_chacha", @@ -6756,12 +6453,12 @@ name = "nym-sphinx-acknowledgements" version = "0.1.0" dependencies = [ "generic-array 0.14.7", - "nym-crypto 0.4.0", - "nym-pemstore 0.3.0", + "nym-crypto", + "nym-pemstore", "nym-sphinx-addressing", "nym-sphinx-params", "nym-sphinx-routing", - "nym-sphinx-types 0.2.0", + "nym-sphinx-types", "nym-topology", "rand", "serde", @@ -6773,8 +6470,8 @@ dependencies = [ name = "nym-sphinx-addressing" version = "0.1.0" dependencies = [ - "nym-crypto 0.4.0", - "nym-sphinx-types 0.2.0", + "nym-crypto", + "nym-sphinx-types", "rand", "serde", "thiserror", @@ -6785,11 +6482,11 @@ name = "nym-sphinx-anonymous-replies" version = "0.1.0" dependencies = [ "bs58", - "nym-crypto 0.4.0", + "nym-crypto", "nym-sphinx-addressing", "nym-sphinx-params", "nym-sphinx-routing", - "nym-sphinx-types 0.2.0", + "nym-sphinx-types", "nym-topology", "rand", "rand_chacha", @@ -6804,11 +6501,11 @@ version = "0.1.0" dependencies = [ "dashmap", "log", - "nym-crypto 0.4.0", + "nym-crypto", "nym-metrics", "nym-sphinx-addressing", "nym-sphinx-params", - "nym-sphinx-types 0.2.0", + "nym-sphinx-types", "rand", "serde", "thiserror", @@ -6819,14 +6516,14 @@ dependencies = [ name = "nym-sphinx-cover" version = "0.1.0" dependencies = [ - "nym-crypto 0.4.0", + "nym-crypto", "nym-sphinx-acknowledgements", "nym-sphinx-addressing", "nym-sphinx-chunking", "nym-sphinx-forwarding", "nym-sphinx-params", "nym-sphinx-routing", - "nym-sphinx-types 0.2.0", + "nym-sphinx-types", "nym-topology", "rand", "thiserror", @@ -6839,7 +6536,7 @@ dependencies = [ "nym-outfox", "nym-sphinx-addressing", "nym-sphinx-params", - "nym-sphinx-types 0.2.0", + "nym-sphinx-types", "thiserror", ] @@ -6854,7 +6551,7 @@ dependencies = [ "nym-sphinx-addressing", "nym-sphinx-forwarding", "nym-sphinx-params", - "nym-sphinx-types 0.2.0", + "nym-sphinx-types", "thiserror", "tokio", "tokio-util", @@ -6864,8 +6561,8 @@ dependencies = [ name = "nym-sphinx-params" version = "0.1.0" dependencies = [ - "nym-crypto 0.4.0", - "nym-sphinx-types 0.2.0", + "nym-crypto", + "nym-sphinx-types", "serde", "thiserror", ] @@ -6875,7 +6572,7 @@ name = "nym-sphinx-routing" version = "0.1.0" dependencies = [ "nym-sphinx-addressing", - "nym-sphinx-types 0.2.0", + "nym-sphinx-types", "thiserror", ] @@ -6888,21 +6585,12 @@ dependencies = [ "thiserror", ] -[[package]] -name = "nym-sphinx-types" -version = "0.2.0" -source = "git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork#8d68cf88dac1adf1f68726a9b6e0740f8cddcf32" -dependencies = [ - "sphinx-packet", - "thiserror", -] - [[package]] name = "nym-statistics-common" version = "0.1.0" dependencies = [ "futures", - "nym-credentials-interface 0.1.0", + "nym-credentials-interface", "nym-sphinx", "time", ] @@ -6942,14 +6630,14 @@ dependencies = [ "async-trait", "bs58", "log", - "nym-api-requests 0.1.0", - "nym-bin-common 0.6.0", - "nym-config 0.1.0", - "nym-crypto 0.4.0", - "nym-mixnet-contract-common 0.6.0", + "nym-api-requests", + "nym-bin-common", + "nym-config", + "nym-crypto", + "nym-mixnet-contract-common", "nym-sphinx-addressing", "nym-sphinx-routing", - "nym-sphinx-types 0.2.0", + "nym-sphinx-types", "rand", "reqwest 0.12.4", "semver 1.0.23", @@ -6967,7 +6655,7 @@ version = "0.1.0" dependencies = [ "etherparse", "log", - "nym-wireguard-types 0.1.0", + "nym-wireguard-types", "thiserror", "tokio", "tokio-tun", @@ -6984,11 +6672,11 @@ dependencies = [ "hmac", "itertools 0.13.0", "log", - "nym-config 0.1.0", - "nym-crypto 0.4.0", - "nym-mixnet-contract-common 0.6.0", - "nym-validator-client 0.1.0", - "nym-vesting-contract-common 0.7.0", + "nym-config", + "nym-crypto", + "nym-mixnet-contract-common", + "nym-validator-client", + "nym-vesting-contract-common", "reqwest 0.12.4", "schemars", "serde", @@ -7022,20 +6710,20 @@ dependencies = [ "flate2", "futures", "itertools 0.13.0", - "nym-api-requests 0.1.0", - "nym-coconut-bandwidth-contract-common 0.1.0", - "nym-coconut-dkg-common 0.1.0", - "nym-compact-ecash 0.1.0", - "nym-config 0.1.0", - "nym-contracts-common 0.5.0", - "nym-ecash-contract-common 0.1.0", - "nym-group-contract-common 0.1.0", - "nym-http-api-client 0.1.0", - "nym-mixnet-contract-common 0.6.0", - "nym-multisig-contract-common 0.1.0", - "nym-network-defaults 0.1.0", - "nym-serde-helpers 0.1.0", - "nym-vesting-contract-common 0.7.0", + "nym-api-requests", + "nym-coconut-bandwidth-contract-common", + "nym-coconut-dkg-common", + "nym-compact-ecash", + "nym-config", + "nym-contracts-common", + "nym-ecash-contract-common", + "nym-group-contract-common", + "nym-http-api-client", + "nym-mixnet-contract-common", + "nym-multisig-contract-common", + "nym-network-defaults", + "nym-serde-helpers", + "nym-vesting-contract-common", "prost 0.12.6", "reqwest 0.12.4", "serde", @@ -7052,55 +6740,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "nym-validator-client" -version = "0.1.0" -source = "git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork#8d68cf88dac1adf1f68726a9b6e0740f8cddcf32" -dependencies = [ - "async-trait", - "base64 0.22.1", - "bip32", - "bip39", - "colored", - "cosmrs 0.17.0-pre", - "cosmwasm-std", - "cw-controllers", - "cw-utils", - "cw2", - "cw3", - "cw4", - "eyre", - "flate2", - "futures", - "itertools 0.13.0", - "nym-api-requests 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", - "nym-coconut-bandwidth-contract-common 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", - "nym-coconut-dkg-common 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", - "nym-compact-ecash 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", - "nym-config 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", - "nym-contracts-common 0.5.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", - "nym-ecash-contract-common 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", - "nym-group-contract-common 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", - "nym-http-api-client 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", - "nym-mixnet-contract-common 0.6.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", - "nym-multisig-contract-common 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", - "nym-network-defaults 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", - "nym-vesting-contract-common 0.7.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", - "prost 0.12.6", - "reqwest 0.12.4", - "serde", - "serde_json", - "sha2 0.9.9", - "tendermint-rpc", - "thiserror", - "time", - "tokio", - "tracing", - "url", - "wasmtimer", - "zeroize", -] - [[package]] name = "nym-validator-rewarder" version = "0.1.0" @@ -7112,18 +6751,18 @@ dependencies = [ "futures", "humantime 2.1.0", "humantime-serde", - "nym-bin-common 0.6.0", - "nym-coconut-bandwidth-contract-common 0.1.0", - "nym-coconut-dkg-common 0.1.0", - "nym-compact-ecash 0.1.0", - "nym-config 0.1.0", + "nym-bin-common", + "nym-coconut-bandwidth-contract-common", + "nym-coconut-dkg-common", + "nym-compact-ecash", + "nym-config", "nym-credentials", - "nym-credentials-interface 0.1.0", - "nym-crypto 0.4.0", - "nym-ecash-time 0.1.0", - "nym-network-defaults 0.1.0", + "nym-credentials-interface", + "nym-crypto", + "nym-ecash-time", + "nym-network-defaults", "nym-task", - "nym-validator-client 0.1.0", + "nym-validator-client", "nyxd-scraper", "rand_chacha", "serde", @@ -7145,26 +6784,13 @@ dependencies = [ "cosmwasm-schema", "cosmwasm-std", "cw2", - "nym-contracts-common 0.5.0", - "nym-mixnet-contract-common 0.6.0", + "nym-contracts-common", + "nym-mixnet-contract-common", "serde", "thiserror", "ts-rs", ] -[[package]] -name = "nym-vesting-contract-common" -version = "0.7.0" -source = "git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork#8d68cf88dac1adf1f68726a9b6e0740f8cddcf32" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "nym-contracts-common 0.5.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", - "nym-mixnet-contract-common 0.6.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", - "serde", - "thiserror", -] - [[package]] name = "nym-vpn-api-lib-wasm" version = "0.1.0" @@ -7172,13 +6798,13 @@ dependencies = [ "bs58", "getrandom", "js-sys", - "nym-bin-common 0.6.0", - "nym-compact-ecash 0.1.0", + "nym-bin-common", + "nym-compact-ecash", "nym-credential-proxy-requests", "nym-credentials", - "nym-credentials-interface 0.1.0", - "nym-crypto 0.4.0", - "nym-ecash-time 0.1.0", + "nym-credentials-interface", + "nym-crypto", + "nym-ecash-time", "serde", "serde-wasm-bindgen 0.6.5", "serde_json", @@ -7197,12 +6823,12 @@ dependencies = [ "cosmrs 0.15.0", "cosmwasm-std", "hex-literal", - "nym-config 0.1.0", - "nym-mixnet-contract-common 0.6.0", - "nym-network-defaults 0.1.0", + "nym-config", + "nym-mixnet-contract-common", + "nym-network-defaults", "nym-types", - "nym-validator-client 0.1.0", - "nym-vesting-contract-common 0.7.0", + "nym-validator-client", + "nym-vesting-contract-common", "serde", "serde_json", "strum 0.23.0", @@ -7223,11 +6849,11 @@ dependencies = [ "log", "nym-authenticator-requests", "nym-credential-verification", - "nym-crypto 0.4.0", + "nym-crypto", "nym-gateway-storage", - "nym-network-defaults 0.1.0", + "nym-network-defaults", "nym-task", - "nym-wireguard-types 0.1.0", + "nym-wireguard-types", "thiserror", "tokio", "tokio-stream", @@ -7240,29 +6866,15 @@ version = "0.1.0" dependencies = [ "base64 0.22.1", "log", - "nym-config 0.1.0", - "nym-crypto 0.4.0", - "nym-network-defaults 0.1.0", + "nym-config", + "nym-crypto", + "nym-network-defaults", "rand", "serde", "thiserror", "x25519-dalek", ] -[[package]] -name = "nym-wireguard-types" -version = "0.1.0" -source = "git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork#8d68cf88dac1adf1f68726a9b6e0740f8cddcf32" -dependencies = [ - "base64 0.22.1", - "log", - "nym-config 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", - "nym-network-defaults 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)", - "serde", - "thiserror", - "x25519-dalek", -] - [[package]] name = "nymvisor" version = "0.1.8" @@ -7278,8 +6890,8 @@ dependencies = [ "humantime-serde", "nix 0.27.1", "nym-async-file-watcher", - "nym-bin-common 0.6.0", - "nym-config 0.1.0", + "nym-bin-common", + "nym-config", "nym-task", "reqwest 0.12.4", "serde", @@ -10052,19 +9664,19 @@ dependencies = [ "cw-utils", "dkg-bypass-contract", "indicatif", - "nym-bin-common 0.6.0", - "nym-coconut-dkg-common 0.1.0", - "nym-compact-ecash 0.1.0", - "nym-config 0.1.0", - "nym-contracts-common 0.5.0", - "nym-crypto 0.4.0", - "nym-ecash-contract-common 0.1.0", - "nym-group-contract-common 0.1.0", - "nym-mixnet-contract-common 0.6.0", - "nym-multisig-contract-common 0.1.0", - "nym-pemstore 0.3.0", - "nym-validator-client 0.1.0", - "nym-vesting-contract-common 0.7.0", + "nym-bin-common", + "nym-coconut-dkg-common", + "nym-compact-ecash", + "nym-config", + "nym-contracts-common", + "nym-crypto", + "nym-ecash-contract-common", + "nym-group-contract-common", + "nym-mixnet-contract-common", + "nym-multisig-contract-common", + "nym-pemstore", + "nym-validator-client", + "nym-vesting-contract-common", "rand", "serde", "serde_json", @@ -10658,11 +10270,11 @@ name = "ts-rs-cli" version = "0.1.0" dependencies = [ "anyhow", - "nym-api-requests 0.1.0", - "nym-mixnet-contract-common 0.6.0", + "nym-api-requests", + "nym-mixnet-contract-common", "nym-types", - "nym-validator-client 0.1.0", - "nym-vesting-contract-common 0.7.0", + "nym-validator-client", + "nym-vesting-contract-common", "nym-wallet-types", "ts-rs", "walkdir", @@ -11306,15 +10918,15 @@ dependencies = [ "js-sys", "nym-bandwidth-controller", "nym-client-core", - "nym-config 0.1.0", + "nym-config", "nym-credential-storage", - "nym-crypto 0.4.0", + "nym-crypto", "nym-gateway-client", "nym-sphinx", "nym-sphinx-acknowledgements", "nym-task", "nym-topology", - "nym-validator-client 0.1.0", + "nym-validator-client", "rand", "serde", "serde-wasm-bindgen 0.6.5", @@ -11869,12 +11481,12 @@ dependencies = [ "bs58", "getrandom", "js-sys", - "nym-bin-common 0.6.0", + "nym-bin-common", "nym-coconut", - "nym-compact-ecash 0.1.0", + "nym-compact-ecash", "nym-credentials", - "nym-crypto 0.4.0", - "nym-http-api-client 0.1.0", + "nym-crypto", + "nym-http-api-client", "rand", "reqwest 0.12.4", "serde", diff --git a/common/models/src/ns_api.rs b/common/models/src/ns_api.rs index 9c3373802a..5d875420e2 100644 --- a/common/models/src/ns_api.rs +++ b/common/models/src/ns_api.rs @@ -2,7 +2,6 @@ use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Deserialize, Serialize)] pub struct TestrunAssignment { - /// has nothing to do with GW identity key. This is PK from `gateways` table pub testrun_id: i64, - pub gateway_pk_id: i64, + pub gateway_identity_key: String, } diff --git a/nym-node-status-agent/Cargo.toml b/nym-node-status-agent/Cargo.toml index a2ce8cdf97..f89065b5b3 100644 --- a/nym-node-status-agent/Cargo.toml +++ b/nym-node-status-agent/Cargo.toml @@ -4,7 +4,7 @@ [package] name = "nym-node-status-agent" -version = "0.1.0" +version = "0.1.4" authors.workspace = true repository.workspace = true homepage.workspace = true diff --git a/nym-node-status-agent/run.sh b/nym-node-status-agent/run.sh index 5054a90825..675e39c109 100755 --- a/nym-node-status-agent/run.sh +++ b/nym-node-status-agent/run.sh @@ -2,7 +2,11 @@ set -eu -export RUST_LOG=${RUST_LOG:-debug} +environment="qa" + +source ../envs/${environment}.env + +export RUST_LOG="debug" crate_root=$(dirname $(realpath "$0")) gateway_probe_src=$(dirname $(dirname "$crate_root"))/nym-vpn-client/nym-vpn-core @@ -14,6 +18,8 @@ export NODE_STATUS_AGENT_PROBE_PATH="$crate_root/nym-gateway-probe" # build & copy over GW probe function copy_gw_probe() { pushd $gateway_probe_src + git switch main + git pull cargo build --release --package nym-gateway-probe cp target/release/nym-gateway-probe "$crate_root" $crate_root/nym-gateway-probe --version @@ -30,8 +36,8 @@ function swarm() { build_agent - for ((i=1; i<=$workers; i++)); do - ../target/release/nym-node-status-agent run-probe & + for ((i = 1; i <= $workers; i++)); do + ../target/release/nym-node-status-agent run-probe & done wait @@ -44,6 +50,6 @@ export NODE_STATUS_AGENT_SERVER_PORT="8000" copy_gw_probe -swarm 30 +swarm 8 # cargo run -- run-probe diff --git a/nym-node-status-agent/src/cli.rs b/nym-node-status-agent/src/cli.rs index c4465797d0..81e70e2da9 100644 --- a/nym-node-status-agent/src/cli.rs +++ b/nym-node-status-agent/src/cli.rs @@ -31,24 +31,19 @@ pub(crate) enum Command { /// path of binary to run #[arg(long, env = "NODE_STATUS_AGENT_PROBE_PATH")] probe_path: String, - #[arg(short, long, env = "NODE_STATUS_AGENT_GATEWAY_ID")] - gateway_id: Option, }, } impl Args { pub(crate) async fn execute(&self) -> anyhow::Result<()> { match &self.command { - Command::RunProbe { - probe_path, - gateway_id, - } => self.run_probe(probe_path, gateway_id).await?, + Command::RunProbe { probe_path } => self.run_probe(probe_path).await?, } Ok(()) } - async fn run_probe(&self, probe_path: &str, gateway_id: &Option) -> anyhow::Result<()> { + async fn run_probe(&self, probe_path: &str) -> anyhow::Result<()> { let server_address = format!("{}:{}", &self.server_address, self.server_port); let probe = GwProbe::new(probe_path.to_string()); @@ -58,7 +53,7 @@ impl Args { let testrun = request_testrun(&server_address).await?; - let log = probe.run_and_get_log(gateway_id); + let log = probe.run_and_get_log(&Some(testrun.gateway_identity_key)); submit_results(&server_address, testrun.testrun_id, log).await?; @@ -97,6 +92,7 @@ async fn submit_results( ) -> anyhow::Result<()> { let target_url = format!("{}/{}/{}", server_addr, URL_BASE, testrun_id); let client = reqwest::Client::new(); + let res = client .post(target_url) .body(probe_outcome) diff --git a/nym-node-status-agent/src/probe.rs b/nym-node-status-agent/src/probe.rs index c75900e936..f779f3af53 100644 --- a/nym-node-status-agent/src/probe.rs +++ b/nym-node-status-agent/src/probe.rs @@ -40,6 +40,12 @@ impl GwProbe { match command.spawn() { Ok(child) => { if let Ok(output) = child.wait_with_output() { + if !output.status.success() { + let out = String::from_utf8_lossy(&output.stdout); + let err = String::from_utf8_lossy(&output.stderr); + tracing::error!("Probe exited with {:?}:\n{}\n{}", output.status, out, err); + } + return String::from_utf8(output.stdout) .unwrap_or("Unable to get log from test run".to_string()); } diff --git a/nym-node-status-api/.sqlx/query-06b17d1e5f61201a1b7542896ba55c69cd5c1a7e7d87073c94600c783a0a3984.json b/nym-node-status-api/.sqlx/query-06b17d1e5f61201a1b7542896ba55c69cd5c1a7e7d87073c94600c783a0a3984.json new file mode 100644 index 0000000000..ad57224826 --- /dev/null +++ b/nym-node-status-api/.sqlx/query-06b17d1e5f61201a1b7542896ba55c69cd5c1a7e7d87073c94600c783a0a3984.json @@ -0,0 +1,20 @@ +{ + "db_name": "SQLite", + "query": "SELECT\n gateway_identity_key\n FROM\n gateways\n WHERE\n id = ?", + "describe": { + "columns": [ + { + "name": "gateway_identity_key", + "ordinal": 0, + "type_info": "Text" + } + ], + "parameters": { + "Right": 1 + }, + "nullable": [ + false + ] + }, + "hash": "06b17d1e5f61201a1b7542896ba55c69cd5c1a7e7d87073c94600c783a0a3984" +} diff --git a/nym-node-status-api/.sqlx/query-1327b5118f9144dddbcf8edb11f7dc549cf503409fd6dfedcdc02dbcd61d5454.json b/nym-node-status-api/.sqlx/query-1327b5118f9144dddbcf8edb11f7dc549cf503409fd6dfedcdc02dbcd61d5454.json new file mode 100644 index 0000000000..8b69daa6a3 --- /dev/null +++ b/nym-node-status-api/.sqlx/query-1327b5118f9144dddbcf8edb11f7dc549cf503409fd6dfedcdc02dbcd61d5454.json @@ -0,0 +1,32 @@ +{ + "db_name": "SQLite", + "query": "SELECT\n key as \"key!\",\n value_json as \"value_json!\",\n last_updated_utc as \"last_updated_utc!\"\n FROM summary", + "describe": { + "columns": [ + { + "name": "key!", + "ordinal": 0, + "type_info": "Text" + }, + { + "name": "value_json!", + "ordinal": 1, + "type_info": "Text" + }, + { + "name": "last_updated_utc!", + "ordinal": 2, + "type_info": "Int64" + } + ], + "parameters": { + "Right": 0 + }, + "nullable": [ + true, + true, + false + ] + }, + "hash": "1327b5118f9144dddbcf8edb11f7dc549cf503409fd6dfedcdc02dbcd61d5454" +} diff --git a/nym-node-status-api/.sqlx/query-18abc8fde56cf86baed7b4afa38f2c63cdf90f2f3b6d81afb9000bb0968dcaea.json b/nym-node-status-api/.sqlx/query-18abc8fde56cf86baed7b4afa38f2c63cdf90f2f3b6d81afb9000bb0968dcaea.json new file mode 100644 index 0000000000..9bc2966149 --- /dev/null +++ b/nym-node-status-api/.sqlx/query-18abc8fde56cf86baed7b4afa38f2c63cdf90f2f3b6d81afb9000bb0968dcaea.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "UPDATE mixnodes\n SET bonded = ?, last_updated_utc = ?\n WHERE id = ?;", + "describe": { + "columns": [], + "parameters": { + "Right": 3 + }, + "nullable": [] + }, + "hash": "18abc8fde56cf86baed7b4afa38f2c63cdf90f2f3b6d81afb9000bb0968dcaea" +} diff --git a/nym-node-status-api/.sqlx/query-2236299f9f691376db54cbd58ec5ceb89b9925cba46efcf4ed79ef0759a01129.json b/nym-node-status-api/.sqlx/query-2236299f9f691376db54cbd58ec5ceb89b9925cba46efcf4ed79ef0759a01129.json new file mode 100644 index 0000000000..d9bf0dd6aa --- /dev/null +++ b/nym-node-status-api/.sqlx/query-2236299f9f691376db54cbd58ec5ceb89b9925cba46efcf4ed79ef0759a01129.json @@ -0,0 +1,26 @@ +{ + "db_name": "SQLite", + "query": "\n SELECT\n id,\n gateway_identity_key\n FROM gateways\n WHERE id = ?\n LIMIT 1", + "describe": { + "columns": [ + { + "name": "id", + "ordinal": 0, + "type_info": "Int64" + }, + { + "name": "gateway_identity_key", + "ordinal": 1, + "type_info": "Text" + } + ], + "parameters": { + "Right": 1 + }, + "nullable": [ + false, + false + ] + }, + "hash": "2236299f9f691376db54cbd58ec5ceb89b9925cba46efcf4ed79ef0759a01129" +} diff --git a/nym-node-status-api/.sqlx/query-3c584e211d07c511644c8079187965acf3bcfb3f84ba8d24ed645d79976cf784.json b/nym-node-status-api/.sqlx/query-3c584e211d07c511644c8079187965acf3bcfb3f84ba8d24ed645d79976cf784.json new file mode 100644 index 0000000000..56898ca5ad --- /dev/null +++ b/nym-node-status-api/.sqlx/query-3c584e211d07c511644c8079187965acf3bcfb3f84ba8d24ed645d79976cf784.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "INSERT INTO testruns (gateway_id, status, ip_address, timestamp_utc, log) VALUES (?, ?, ?, ?, ?)", + "describe": { + "columns": [], + "parameters": { + "Right": 5 + }, + "nullable": [] + }, + "hash": "3c584e211d07c511644c8079187965acf3bcfb3f84ba8d24ed645d79976cf784" +} diff --git a/nym-node-status-api/.sqlx/query-3d3a1fa429e3090741c6b6a8e82e692afc04b51e8782bcbf59f1eb4116112536.json b/nym-node-status-api/.sqlx/query-3d3a1fa429e3090741c6b6a8e82e692afc04b51e8782bcbf59f1eb4116112536.json new file mode 100644 index 0000000000..10158436b6 --- /dev/null +++ b/nym-node-status-api/.sqlx/query-3d3a1fa429e3090741c6b6a8e82e692afc04b51e8782bcbf59f1eb4116112536.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "UPDATE gateways\n SET bonded = ?, last_updated_utc = ?\n WHERE id = ?;", + "describe": { + "columns": [], + "parameters": { + "Right": 3 + }, + "nullable": [] + }, + "hash": "3d3a1fa429e3090741c6b6a8e82e692afc04b51e8782bcbf59f1eb4116112536" +} diff --git a/nym-node-status-api/.sqlx/query-3d5fc502f976f5081f01352856b8632c29c81bfafb043bb8744129cf9e0266ad.json b/nym-node-status-api/.sqlx/query-3d5fc502f976f5081f01352856b8632c29c81bfafb043bb8744129cf9e0266ad.json new file mode 100644 index 0000000000..2a834fe87e --- /dev/null +++ b/nym-node-status-api/.sqlx/query-3d5fc502f976f5081f01352856b8632c29c81bfafb043bb8744129cf9e0266ad.json @@ -0,0 +1,38 @@ +{ + "db_name": "SQLite", + "query": "SELECT\n id as \"id!\",\n gateway_identity_key as \"gateway_identity_key!\",\n self_described as \"self_described?\",\n explorer_pretty_bond as \"explorer_pretty_bond?\"\n FROM gateways\n WHERE gateway_identity_key = ?\n ORDER BY gateway_identity_key\n LIMIT 1", + "describe": { + "columns": [ + { + "name": "id!", + "ordinal": 0, + "type_info": "Int64" + }, + { + "name": "gateway_identity_key!", + "ordinal": 1, + "type_info": "Text" + }, + { + "name": "self_described?", + "ordinal": 2, + "type_info": "Text" + }, + { + "name": "explorer_pretty_bond?", + "ordinal": 3, + "type_info": "Text" + } + ], + "parameters": { + "Right": 1 + }, + "nullable": [ + true, + false, + true, + true + ] + }, + "hash": "3d5fc502f976f5081f01352856b8632c29c81bfafb043bb8744129cf9e0266ad" +} diff --git a/nym-node-status-api/.sqlx/query-418944f2eccb838cb3882f34469203c8569f03fdd39ce09d7b74177896e52a8c.json b/nym-node-status-api/.sqlx/query-418944f2eccb838cb3882f34469203c8569f03fdd39ce09d7b74177896e52a8c.json new file mode 100644 index 0000000000..f9eb3657e7 --- /dev/null +++ b/nym-node-status-api/.sqlx/query-418944f2eccb838cb3882f34469203c8569f03fdd39ce09d7b74177896e52a8c.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "UPDATE testruns SET status = ? WHERE id = ?", + "describe": { + "columns": [], + "parameters": { + "Right": 2 + }, + "nullable": [] + }, + "hash": "418944f2eccb838cb3882f34469203c8569f03fdd39ce09d7b74177896e52a8c" +} diff --git a/nym-node-status-api/.sqlx/query-46d76bc6d3fba2dae3b21511a36289dd776749dd7a20cda61b0480f2fba60889.json b/nym-node-status-api/.sqlx/query-46d76bc6d3fba2dae3b21511a36289dd776749dd7a20cda61b0480f2fba60889.json new file mode 100644 index 0000000000..2cb8760009 --- /dev/null +++ b/nym-node-status-api/.sqlx/query-46d76bc6d3fba2dae3b21511a36289dd776749dd7a20cda61b0480f2fba60889.json @@ -0,0 +1,50 @@ +{ + "db_name": "SQLite", + "query": "SELECT\n id as \"id!\",\n gateway_id as \"gateway_id!\",\n status as \"status!\",\n timestamp_utc as \"timestamp_utc!\",\n ip_address as \"ip_address!\",\n log as \"log!\"\n FROM testruns\n WHERE gateway_id = ? AND status != 2\n ORDER BY id DESC\n LIMIT 1", + "describe": { + "columns": [ + { + "name": "id!", + "ordinal": 0, + "type_info": "Int64" + }, + { + "name": "gateway_id!", + "ordinal": 1, + "type_info": "Int64" + }, + { + "name": "status!", + "ordinal": 2, + "type_info": "Int64" + }, + { + "name": "timestamp_utc!", + "ordinal": 3, + "type_info": "Int64" + }, + { + "name": "ip_address!", + "ordinal": 4, + "type_info": "Text" + }, + { + "name": "log!", + "ordinal": 5, + "type_info": "Text" + } + ], + "parameters": { + "Right": 1 + }, + "nullable": [ + false, + false, + false, + false, + false, + false + ] + }, + "hash": "46d76bc6d3fba2dae3b21511a36289dd776749dd7a20cda61b0480f2fba60889" +} diff --git a/nym-node-status-api/.sqlx/query-4afcc6673890f795c2793f1e2f8570ee787fc7daf00fcb916f18d1cb7d6c8f08.json b/nym-node-status-api/.sqlx/query-4afcc6673890f795c2793f1e2f8570ee787fc7daf00fcb916f18d1cb7d6c8f08.json new file mode 100644 index 0000000000..6a9fd9d4eb --- /dev/null +++ b/nym-node-status-api/.sqlx/query-4afcc6673890f795c2793f1e2f8570ee787fc7daf00fcb916f18d1cb7d6c8f08.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "UPDATE gateways SET last_probe_log = ? WHERE id = ?", + "describe": { + "columns": [], + "parameters": { + "Right": 2 + }, + "nullable": [] + }, + "hash": "4afcc6673890f795c2793f1e2f8570ee787fc7daf00fcb916f18d1cb7d6c8f08" +} diff --git a/nym-node-status-api/.sqlx/query-4b61a4bc32333c92a8f5ad4ad0017b40dc01845f554b5479f37855d89b309e6f.json b/nym-node-status-api/.sqlx/query-4b61a4bc32333c92a8f5ad4ad0017b40dc01845f554b5479f37855d89b309e6f.json new file mode 100644 index 0000000000..8b9c9699f8 --- /dev/null +++ b/nym-node-status-api/.sqlx/query-4b61a4bc32333c92a8f5ad4ad0017b40dc01845f554b5479f37855d89b309e6f.json @@ -0,0 +1,32 @@ +{ + "db_name": "SQLite", + "query": "SELECT\n id as \"id!\",\n gateway_identity_key as \"identity_key!\",\n bonded as \"bonded: bool\"\n FROM gateways\n WHERE bonded = ?", + "describe": { + "columns": [ + { + "name": "id!", + "ordinal": 0, + "type_info": "Int64" + }, + { + "name": "identity_key!", + "ordinal": 1, + "type_info": "Text" + }, + { + "name": "bonded: bool", + "ordinal": 2, + "type_info": "Int64" + } + ], + "parameters": { + "Right": 1 + }, + "nullable": [ + false, + false, + false + ] + }, + "hash": "4b61a4bc32333c92a8f5ad4ad0017b40dc01845f554b5479f37855d89b309e6f" +} diff --git a/nym-node-status-api/.sqlx/query-670b7ed7d57a6986181b24be24ca667e8cacdf677ccb906415b3fe92be0c436b.json b/nym-node-status-api/.sqlx/query-670b7ed7d57a6986181b24be24ca667e8cacdf677ccb906415b3fe92be0c436b.json new file mode 100644 index 0000000000..38fe641a3a --- /dev/null +++ b/nym-node-status-api/.sqlx/query-670b7ed7d57a6986181b24be24ca667e8cacdf677ccb906415b3fe92be0c436b.json @@ -0,0 +1,20 @@ +{ + "db_name": "SQLite", + "query": "SELECT count(id) FROM mixnodes", + "describe": { + "columns": [ + { + "name": "count(id)", + "ordinal": 0, + "type_info": "Int" + } + ], + "parameters": { + "Right": 0 + }, + "nullable": [ + false + ] + }, + "hash": "670b7ed7d57a6986181b24be24ca667e8cacdf677ccb906415b3fe92be0c436b" +} diff --git a/nym-node-status-api/.sqlx/query-6d7967b831b355d5f2c77950abc56f816956b0824c66a25da611dce688105d36.json b/nym-node-status-api/.sqlx/query-6d7967b831b355d5f2c77950abc56f816956b0824c66a25da611dce688105d36.json new file mode 100644 index 0000000000..e12434a85a --- /dev/null +++ b/nym-node-status-api/.sqlx/query-6d7967b831b355d5f2c77950abc56f816956b0824c66a25da611dce688105d36.json @@ -0,0 +1,50 @@ +{ + "db_name": "SQLite", + "query": "SELECT\n id as \"id!\",\n gateway_id as \"gateway_id!\",\n status as \"status!\",\n timestamp_utc as \"timestamp_utc!\",\n ip_address as \"ip_address!\",\n log as \"log!\"\n FROM testruns\n WHERE\n id = ?\n AND\n status = ?\n ORDER BY timestamp_utc", + "describe": { + "columns": [ + { + "name": "id!", + "ordinal": 0, + "type_info": "Int64" + }, + { + "name": "gateway_id!", + "ordinal": 1, + "type_info": "Int64" + }, + { + "name": "status!", + "ordinal": 2, + "type_info": "Int64" + }, + { + "name": "timestamp_utc!", + "ordinal": 3, + "type_info": "Int64" + }, + { + "name": "ip_address!", + "ordinal": 4, + "type_info": "Text" + }, + { + "name": "log!", + "ordinal": 5, + "type_info": "Text" + } + ], + "parameters": { + "Right": 2 + }, + "nullable": [ + false, + false, + false, + false, + false, + false + ] + }, + "hash": "6d7967b831b355d5f2c77950abc56f816956b0824c66a25da611dce688105d36" +} diff --git a/nym-node-status-api/.sqlx/query-6eb1a682cf13205cf701590021cdf795147ac3724e89df5b2f24f7215d87dce1.json b/nym-node-status-api/.sqlx/query-6eb1a682cf13205cf701590021cdf795147ac3724e89df5b2f24f7215d87dce1.json new file mode 100644 index 0000000000..86b61631a6 --- /dev/null +++ b/nym-node-status-api/.sqlx/query-6eb1a682cf13205cf701590021cdf795147ac3724e89df5b2f24f7215d87dce1.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "INSERT INTO mixnodes\n (mix_id, identity_key, bonded, total_stake,\n host, http_api_port, blacklisted, full_details,\n self_described, last_updated_utc, is_dp_delegatee)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT(mix_id) DO UPDATE SET\n bonded=excluded.bonded,\n total_stake=excluded.total_stake, host=excluded.host,\n http_api_port=excluded.http_api_port,blacklisted=excluded.blacklisted,\n full_details=excluded.full_details,self_described=excluded.self_described,\n last_updated_utc=excluded.last_updated_utc,\n is_dp_delegatee = excluded.is_dp_delegatee;", + "describe": { + "columns": [], + "parameters": { + "Right": 11 + }, + "nullable": [] + }, + "hash": "6eb1a682cf13205cf701590021cdf795147ac3724e89df5b2f24f7215d87dce1" +} diff --git a/nym-node-status-api/.sqlx/query-6ef3efde571d46961244cd90420f3de5949a5ff2083453cb879af8a1689efe2f.json b/nym-node-status-api/.sqlx/query-6ef3efde571d46961244cd90420f3de5949a5ff2083453cb879af8a1689efe2f.json new file mode 100644 index 0000000000..9d93b219f9 --- /dev/null +++ b/nym-node-status-api/.sqlx/query-6ef3efde571d46961244cd90420f3de5949a5ff2083453cb879af8a1689efe2f.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "UPDATE gateways SET last_probe_result = ? WHERE id = ?", + "describe": { + "columns": [], + "parameters": { + "Right": 2 + }, + "nullable": [] + }, + "hash": "6ef3efde571d46961244cd90420f3de5949a5ff2083453cb879af8a1689efe2f" +} diff --git a/nym-node-status-api/.sqlx/query-71a455c705f9c25d3843ff2fb8629d1320a5eb10797cdb5a435455d22c6aeac1.json b/nym-node-status-api/.sqlx/query-71a455c705f9c25d3843ff2fb8629d1320a5eb10797cdb5a435455d22c6aeac1.json new file mode 100644 index 0000000000..f061747404 --- /dev/null +++ b/nym-node-status-api/.sqlx/query-71a455c705f9c25d3843ff2fb8629d1320a5eb10797cdb5a435455d22c6aeac1.json @@ -0,0 +1,98 @@ +{ + "db_name": "SQLite", + "query": "SELECT\n gw.gateway_identity_key as \"gateway_identity_key!\",\n gw.bonded as \"bonded: bool\",\n gw.blacklisted as \"blacklisted: bool\",\n gw.performance as \"performance!\",\n gw.self_described as \"self_described?\",\n gw.explorer_pretty_bond as \"explorer_pretty_bond?\",\n gw.last_probe_result as \"last_probe_result?\",\n gw.last_probe_log as \"last_probe_log?\",\n gw.last_testrun_utc as \"last_testrun_utc?\",\n gw.last_updated_utc as \"last_updated_utc!\",\n COALESCE(gd.moniker, \"NA\") as \"moniker!\",\n COALESCE(gd.website, \"NA\") as \"website!\",\n COALESCE(gd.security_contact, \"NA\") as \"security_contact!\",\n COALESCE(gd.details, \"NA\") as \"details!\"\n FROM gateways gw\n LEFT JOIN gateway_description gd\n ON gw.gateway_identity_key = gd.gateway_identity_key\n ORDER BY gw.gateway_identity_key", + "describe": { + "columns": [ + { + "name": "gateway_identity_key!", + "ordinal": 0, + "type_info": "Text" + }, + { + "name": "bonded: bool", + "ordinal": 1, + "type_info": "Int64" + }, + { + "name": "blacklisted: bool", + "ordinal": 2, + "type_info": "Int64" + }, + { + "name": "performance!", + "ordinal": 3, + "type_info": "Int64" + }, + { + "name": "self_described?", + "ordinal": 4, + "type_info": "Text" + }, + { + "name": "explorer_pretty_bond?", + "ordinal": 5, + "type_info": "Text" + }, + { + "name": "last_probe_result?", + "ordinal": 6, + "type_info": "Text" + }, + { + "name": "last_probe_log?", + "ordinal": 7, + "type_info": "Text" + }, + { + "name": "last_testrun_utc?", + "ordinal": 8, + "type_info": "Int64" + }, + { + "name": "last_updated_utc!", + "ordinal": 9, + "type_info": "Int64" + }, + { + "name": "moniker!", + "ordinal": 10, + "type_info": "Text" + }, + { + "name": "website!", + "ordinal": 11, + "type_info": "Text" + }, + { + "name": "security_contact!", + "ordinal": 12, + "type_info": "Text" + }, + { + "name": "details!", + "ordinal": 13, + "type_info": "Text" + } + ], + "parameters": { + "Right": 0 + }, + "nullable": [ + false, + false, + false, + false, + true, + true, + true, + true, + true, + false, + false, + false, + false, + false + ] + }, + "hash": "71a455c705f9c25d3843ff2fb8629d1320a5eb10797cdb5a435455d22c6aeac1" +} diff --git a/nym-node-status-api/.sqlx/query-7600823da7ce80b8ffda933608603a2752e28df775d1af8fd943a5fc8d7dc00d.json b/nym-node-status-api/.sqlx/query-7600823da7ce80b8ffda933608603a2752e28df775d1af8fd943a5fc8d7dc00d.json new file mode 100644 index 0000000000..9cba203bdb --- /dev/null +++ b/nym-node-status-api/.sqlx/query-7600823da7ce80b8ffda933608603a2752e28df775d1af8fd943a5fc8d7dc00d.json @@ -0,0 +1,38 @@ +{ + "db_name": "SQLite", + "query": "SELECT\n id as \"id!\",\n date as \"date!\",\n timestamp_utc as \"timestamp_utc!\",\n value_json as \"value_json!\"\n FROM summary_history\n ORDER BY date DESC\n LIMIT 30", + "describe": { + "columns": [ + { + "name": "id!", + "ordinal": 0, + "type_info": "Int64" + }, + { + "name": "date!", + "ordinal": 1, + "type_info": "Text" + }, + { + "name": "timestamp_utc!", + "ordinal": 2, + "type_info": "Int64" + }, + { + "name": "value_json!", + "ordinal": 3, + "type_info": "Text" + } + ], + "parameters": { + "Right": 0 + }, + "nullable": [ + true, + false, + false, + true + ] + }, + "hash": "7600823da7ce80b8ffda933608603a2752e28df775d1af8fd943a5fc8d7dc00d" +} diff --git a/nym-node-status-api/.sqlx/query-788515c34588aec352773df4b6e6c5e41f3c0bb56a27648b5e25466b8634a578.json b/nym-node-status-api/.sqlx/query-788515c34588aec352773df4b6e6c5e41f3c0bb56a27648b5e25466b8634a578.json new file mode 100644 index 0000000000..5252ccf2e7 --- /dev/null +++ b/nym-node-status-api/.sqlx/query-788515c34588aec352773df4b6e6c5e41f3c0bb56a27648b5e25466b8634a578.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "INSERT INTO summary_history\n (date, timestamp_utc, value_json)\n VALUES (?, ?, ?)\n ON CONFLICT(date) DO UPDATE SET\n timestamp_utc=excluded.timestamp_utc,\n value_json=excluded.value_json;", + "describe": { + "columns": [], + "parameters": { + "Right": 3 + }, + "nullable": [] + }, + "hash": "788515c34588aec352773df4b6e6c5e41f3c0bb56a27648b5e25466b8634a578" +} diff --git a/nym-node-status-api/.sqlx/query-8571faad2f66e08f24acfbfe036d17ca6eb090df7f6d52ef89c5d51564f8b45c.json b/nym-node-status-api/.sqlx/query-8571faad2f66e08f24acfbfe036d17ca6eb090df7f6d52ef89c5d51564f8b45c.json new file mode 100644 index 0000000000..3f171170db --- /dev/null +++ b/nym-node-status-api/.sqlx/query-8571faad2f66e08f24acfbfe036d17ca6eb090df7f6d52ef89c5d51564f8b45c.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "UPDATE gateways\n SET blacklisted = true\n WHERE gateway_identity_key = ?;", + "describe": { + "columns": [], + "parameters": { + "Right": 1 + }, + "nullable": [] + }, + "hash": "8571faad2f66e08f24acfbfe036d17ca6eb090df7f6d52ef89c5d51564f8b45c" +} diff --git a/nym-node-status-api/.sqlx/query-86ff64db477a1d6235179b0b88d86b86d1b9be62336c9eac0eef44987a5451b5.json b/nym-node-status-api/.sqlx/query-86ff64db477a1d6235179b0b88d86b86d1b9be62336c9eac0eef44987a5451b5.json new file mode 100644 index 0000000000..60583ed8b2 --- /dev/null +++ b/nym-node-status-api/.sqlx/query-86ff64db477a1d6235179b0b88d86b86d1b9be62336c9eac0eef44987a5451b5.json @@ -0,0 +1,20 @@ +{ + "db_name": "SQLite", + "query": "SELECT count(id) FROM gateways", + "describe": { + "columns": [ + { + "name": "count(id)", + "ordinal": 0, + "type_info": "Int" + } + ], + "parameters": { + "Right": 0 + }, + "nullable": [ + false + ] + }, + "hash": "86ff64db477a1d6235179b0b88d86b86d1b9be62336c9eac0eef44987a5451b5" +} diff --git a/nym-node-status-api/.sqlx/query-930a41e612b4e964ae214843da190f6c66c14d4267a2cc2ca73354becc2c8bb8.json b/nym-node-status-api/.sqlx/query-930a41e612b4e964ae214843da190f6c66c14d4267a2cc2ca73354becc2c8bb8.json new file mode 100644 index 0000000000..6c2a194e80 --- /dev/null +++ b/nym-node-status-api/.sqlx/query-930a41e612b4e964ae214843da190f6c66c14d4267a2cc2ca73354becc2c8bb8.json @@ -0,0 +1,26 @@ +{ + "db_name": "SQLite", + "query": "SELECT\n gateway_identity_key as \"gateway_identity_key!\",\n bonded as \"bonded: bool\"\n FROM gateways\n ORDER BY last_testrun_utc", + "describe": { + "columns": [ + { + "name": "gateway_identity_key!", + "ordinal": 0, + "type_info": "Text" + }, + { + "name": "bonded: bool", + "ordinal": 1, + "type_info": "Int64" + } + ], + "parameters": { + "Right": 0 + }, + "nullable": [ + false, + false + ] + }, + "hash": "930a41e612b4e964ae214843da190f6c66c14d4267a2cc2ca73354becc2c8bb8" +} diff --git a/nym-node-status-api/.sqlx/query-c214c001acbbf79fa499816f36ec586c4c29c03efb4cf0c40b73a5c76159cf5c.json b/nym-node-status-api/.sqlx/query-c214c001acbbf79fa499816f36ec586c4c29c03efb4cf0c40b73a5c76159cf5c.json new file mode 100644 index 0000000000..8cc95e72de --- /dev/null +++ b/nym-node-status-api/.sqlx/query-c214c001acbbf79fa499816f36ec586c4c29c03efb4cf0c40b73a5c76159cf5c.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "UPDATE gateways SET last_testrun_utc = ?, last_updated_utc = ? WHERE id = ?", + "describe": { + "columns": [], + "parameters": { + "Right": 3 + }, + "nullable": [] + }, + "hash": "c214c001acbbf79fa499816f36ec586c4c29c03efb4cf0c40b73a5c76159cf5c" +} diff --git a/nym-node-status-api/.sqlx/query-c5e3cd7284b334df5aa979b1627ea1f6dc2aed00cedde25f2be3567e47064351.json b/nym-node-status-api/.sqlx/query-c5e3cd7284b334df5aa979b1627ea1f6dc2aed00cedde25f2be3567e47064351.json new file mode 100644 index 0000000000..2e4f34fd85 --- /dev/null +++ b/nym-node-status-api/.sqlx/query-c5e3cd7284b334df5aa979b1627ea1f6dc2aed00cedde25f2be3567e47064351.json @@ -0,0 +1,44 @@ +{ + "db_name": "SQLite", + "query": "\n SELECT\n date_utc as \"date_utc!\",\n packets_received as \"total_packets_received!: i64\",\n packets_sent as \"total_packets_sent!: i64\",\n packets_dropped as \"total_packets_dropped!: i64\",\n total_stake as \"total_stake!: i64\"\n FROM (\n SELECT\n date_utc,\n SUM(packets_received) as packets_received,\n SUM(packets_sent) as packets_sent,\n SUM(packets_dropped) as packets_dropped,\n SUM(total_stake) as total_stake\n FROM mixnode_daily_stats\n GROUP BY date_utc\n ORDER BY date_utc DESC\n LIMIT 30\n )\n GROUP BY date_utc\n ORDER BY date_utc\n ", + "describe": { + "columns": [ + { + "name": "date_utc!", + "ordinal": 0, + "type_info": "Text" + }, + { + "name": "total_packets_received!: i64", + "ordinal": 1, + "type_info": "Int64" + }, + { + "name": "total_packets_sent!: i64", + "ordinal": 2, + "type_info": "Int64" + }, + { + "name": "total_packets_dropped!: i64", + "ordinal": 3, + "type_info": "Int64" + }, + { + "name": "total_stake!: i64", + "ordinal": 4, + "type_info": "Int64" + } + ], + "parameters": { + "Right": 0 + }, + "nullable": [ + false, + true, + true, + true, + false + ] + }, + "hash": "c5e3cd7284b334df5aa979b1627ea1f6dc2aed00cedde25f2be3567e47064351" +} diff --git a/nym-node-status-api/.sqlx/query-c7ba2621becb9ac4b5dee0ce303dadfcf19095935867a51cbd5b8362d1505fcc.json b/nym-node-status-api/.sqlx/query-c7ba2621becb9ac4b5dee0ce303dadfcf19095935867a51cbd5b8362d1505fcc.json new file mode 100644 index 0000000000..4d0edd1e88 --- /dev/null +++ b/nym-node-status-api/.sqlx/query-c7ba2621becb9ac4b5dee0ce303dadfcf19095935867a51cbd5b8362d1505fcc.json @@ -0,0 +1,32 @@ +{ + "db_name": "SQLite", + "query": "SELECT\n id as \"id!\",\n identity_key as \"identity_key!\",\n bonded as \"bonded: bool\"\n FROM mixnodes\n WHERE bonded = ?", + "describe": { + "columns": [ + { + "name": "id!", + "ordinal": 0, + "type_info": "Int64" + }, + { + "name": "identity_key!", + "ordinal": 1, + "type_info": "Text" + }, + { + "name": "bonded: bool", + "ordinal": 2, + "type_info": "Int64" + } + ], + "parameters": { + "Right": 1 + }, + "nullable": [ + false, + false, + false + ] + }, + "hash": "c7ba2621becb9ac4b5dee0ce303dadfcf19095935867a51cbd5b8362d1505fcc" +} diff --git a/nym-node-status-api/.sqlx/query-d8ea93e781666e6267902170709ee2aa37f6163525bbdce1a4cebef4a285f8d9.json b/nym-node-status-api/.sqlx/query-d8ea93e781666e6267902170709ee2aa37f6163525bbdce1a4cebef4a285f8d9.json new file mode 100644 index 0000000000..94e5513279 --- /dev/null +++ b/nym-node-status-api/.sqlx/query-d8ea93e781666e6267902170709ee2aa37f6163525bbdce1a4cebef4a285f8d9.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "INSERT INTO gateways\n (gateway_identity_key, bonded, blacklisted,\n self_described, explorer_pretty_bond,\n last_updated_utc, performance)\n VALUES (?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT(gateway_identity_key) DO UPDATE SET\n bonded=excluded.bonded,\n blacklisted=excluded.blacklisted,\n self_described=excluded.self_described,\n explorer_pretty_bond=excluded.explorer_pretty_bond,\n last_updated_utc=excluded.last_updated_utc,\n performance = excluded.performance;", + "describe": { + "columns": [], + "parameters": { + "Right": 7 + }, + "nullable": [] + }, + "hash": "d8ea93e781666e6267902170709ee2aa37f6163525bbdce1a4cebef4a285f8d9" +} diff --git a/nym-node-status-api/.sqlx/query-e0c76a959276e3b0f44c720af9c74a5bf4912ee73468e62e7d0d96b1d9074cbe.json b/nym-node-status-api/.sqlx/query-e0c76a959276e3b0f44c720af9c74a5bf4912ee73468e62e7d0d96b1d9074cbe.json new file mode 100644 index 0000000000..5f7443bd50 --- /dev/null +++ b/nym-node-status-api/.sqlx/query-e0c76a959276e3b0f44c720af9c74a5bf4912ee73468e62e7d0d96b1d9074cbe.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "INSERT INTO summary\n (key, value_json, last_updated_utc)\n VALUES (?, ?, ?)\n ON CONFLICT(key) DO UPDATE SET\n value_json=excluded.value_json,\n last_updated_utc=excluded.last_updated_utc;", + "describe": { + "columns": [], + "parameters": { + "Right": 3 + }, + "nullable": [] + }, + "hash": "e0c76a959276e3b0f44c720af9c74a5bf4912ee73468e62e7d0d96b1d9074cbe" +} diff --git a/nym-node-status-api/.sqlx/query-f0a4316081d1be9444a87b95d933d31cb4bcc4071d31d8d2f7755e2d2c2e3e35.json b/nym-node-status-api/.sqlx/query-f0a4316081d1be9444a87b95d933d31cb4bcc4071d31d8d2f7755e2d2c2e3e35.json new file mode 100644 index 0000000000..d3f7611ba2 --- /dev/null +++ b/nym-node-status-api/.sqlx/query-f0a4316081d1be9444a87b95d933d31cb4bcc4071d31d8d2f7755e2d2c2e3e35.json @@ -0,0 +1,86 @@ +{ + "db_name": "SQLite", + "query": "SELECT\n mn.mix_id as \"mix_id!\",\n mn.bonded as \"bonded: bool\",\n mn.blacklisted as \"blacklisted: bool\",\n mn.is_dp_delegatee as \"is_dp_delegatee: bool\",\n mn.total_stake as \"total_stake!\",\n mn.full_details as \"full_details!\",\n mn.self_described as \"self_described\",\n mn.last_updated_utc as \"last_updated_utc!\",\n COALESCE(md.moniker, \"NA\") as \"moniker!\",\n COALESCE(md.website, \"NA\") as \"website!\",\n COALESCE(md.security_contact, \"NA\") as \"security_contact!\",\n COALESCE(md.details, \"NA\") as \"details!\"\n FROM mixnodes mn\n LEFT JOIN mixnode_description md ON mn.mix_id = md.mix_id\n ORDER BY mn.mix_id", + "describe": { + "columns": [ + { + "name": "mix_id!", + "ordinal": 0, + "type_info": "Int64" + }, + { + "name": "bonded: bool", + "ordinal": 1, + "type_info": "Int64" + }, + { + "name": "blacklisted: bool", + "ordinal": 2, + "type_info": "Int64" + }, + { + "name": "is_dp_delegatee: bool", + "ordinal": 3, + "type_info": "Int64" + }, + { + "name": "total_stake!", + "ordinal": 4, + "type_info": "Int64" + }, + { + "name": "full_details!", + "ordinal": 5, + "type_info": "Text" + }, + { + "name": "self_described", + "ordinal": 6, + "type_info": "Text" + }, + { + "name": "last_updated_utc!", + "ordinal": 7, + "type_info": "Int64" + }, + { + "name": "moniker!", + "ordinal": 8, + "type_info": "Text" + }, + { + "name": "website!", + "ordinal": 9, + "type_info": "Text" + }, + { + "name": "security_contact!", + "ordinal": 10, + "type_info": "Text" + }, + { + "name": "details!", + "ordinal": 11, + "type_info": "Text" + } + ], + "parameters": { + "Right": 0 + }, + "nullable": [ + false, + false, + false, + false, + false, + true, + true, + false, + false, + false, + false, + false + ] + }, + "hash": "f0a4316081d1be9444a87b95d933d31cb4bcc4071d31d8d2f7755e2d2c2e3e35" +} diff --git a/nym-node-status-api/.sqlx/query-f5048d9926a5f5329f7f3b96d43b925e033ceec4f8112258feb4ac9e96fc5924.json b/nym-node-status-api/.sqlx/query-f5048d9926a5f5329f7f3b96d43b925e033ceec4f8112258feb4ac9e96fc5924.json new file mode 100644 index 0000000000..06d098bebc --- /dev/null +++ b/nym-node-status-api/.sqlx/query-f5048d9926a5f5329f7f3b96d43b925e033ceec4f8112258feb4ac9e96fc5924.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "UPDATE\n testruns\n SET\n status = ?\n WHERE\n status = ?\n AND\n timestamp_utc < ?\n ", + "describe": { + "columns": [], + "parameters": { + "Right": 3 + }, + "nullable": [] + }, + "hash": "f5048d9926a5f5329f7f3b96d43b925e033ceec4f8112258feb4ac9e96fc5924" +} diff --git a/nym-node-status-api/.sqlx/query-ff9334ba7b670b218b2f9100e9ab5d2f2d08b2e53203aab9f07ea9b52acbd407.json b/nym-node-status-api/.sqlx/query-ff9334ba7b670b218b2f9100e9ab5d2f2d08b2e53203aab9f07ea9b52acbd407.json new file mode 100644 index 0000000000..24e735e96d --- /dev/null +++ b/nym-node-status-api/.sqlx/query-ff9334ba7b670b218b2f9100e9ab5d2f2d08b2e53203aab9f07ea9b52acbd407.json @@ -0,0 +1,26 @@ +{ + "db_name": "SQLite", + "query": "UPDATE testruns\n SET status = ?\n WHERE rowid =\n (\n SELECT rowid\n FROM testruns\n WHERE status = ?\n ORDER BY timestamp_utc asc\n LIMIT 1\n )\n RETURNING\n id as \"id!\",\n gateway_id\n ", + "describe": { + "columns": [ + { + "name": "id!", + "ordinal": 0, + "type_info": "Int64" + }, + { + "name": "gateway_id", + "ordinal": 1, + "type_info": "Int64" + } + ], + "parameters": { + "Right": 2 + }, + "nullable": [ + true, + false + ] + }, + "hash": "ff9334ba7b670b218b2f9100e9ab5d2f2d08b2e53203aab9f07ea9b52acbd407" +} diff --git a/nym-node-status-api/Cargo.toml b/nym-node-status-api/Cargo.toml index 4cc9ff5d28..0969d31045 100644 --- a/nym-node-status-api/Cargo.toml +++ b/nym-node-status-api/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "nym-node-status-api" -version = "0.1.0" +version = "0.1.5" authors.workspace = true repository.workspace = true homepage.workspace = true @@ -24,14 +24,10 @@ moka = { workspace = true, features = ["future"] } nym-bin-common = { path = "../common/bin-common", features = ["models"]} nym-common-models = { path = "../common/models" } nym-explorer-client = { path = "../explorer-api/explorer-client" } -# TODO dz: before Nym API client breaking changes. Update to latest develop once new Nym API is live -nym-network-defaults = { git = "https://github.com/nymtech/nym", branch = "pre-dir-v2-fork" } -nym-validator-client = { git = "https://github.com/nymtech/nym", branch = "pre-dir-v2-fork" } -# nym-network-defaults = { path = "../common/network-defaults" } -# nym-validator-client = { path = "../common/client-libs/validator-client" } +nym-network-defaults = { path = "../common/network-defaults" } +nym-validator-client = { path = "../common/client-libs/validator-client" } nym-task = { path = "../common/task" } -nym-node-requests = { git = "https://github.com/nymtech/nym", branch = "pre-dir-v2-fork" } -# nym-node-requests = { path = "../nym-node/nym-node-requests", features = ["openapi"] } +nym-node-requests = { path = "../nym-node/nym-node-requests", features = ["openapi"] } regex = { workspace = true } reqwest = { workspace = true } serde = { workspace = true, features = ["derive"] } diff --git a/nym-node-status-api/build.rs b/nym-node-status-api/build.rs index bfe6e54e74..025e755088 100644 --- a/nym-node-status-api/build.rs +++ b/nym-node-status-api/build.rs @@ -11,7 +11,7 @@ const SQLITE_DB_FILENAME: &str = "nym-node-status-api.sqlite"; #[tokio::main(flavor = "current_thread")] async fn main() -> Result<()> { let out_dir = read_env_var("OUT_DIR")?; - let database_path = format!("sqlite://{}/{}?mode=rwc", out_dir, SQLITE_DB_FILENAME); + let database_path = format!("{}/{}?mode=rwc", out_dir, SQLITE_DB_FILENAME); write_db_path_to_file(&out_dir, SQLITE_DB_FILENAME).await?; let mut conn = SqliteConnection::connect(&database_path).await?; @@ -25,7 +25,6 @@ async fn main() -> Result<()> { // not a valid windows path... but hey, it works... println!("cargo::rustc-env=DATABASE_URL=sqlite:///{}", &database_path); - rerun_if_changed(); Ok(()) } @@ -33,11 +32,6 @@ fn read_env_var(var: &str) -> Result { std::env::var(var).map_err(|_| anyhow!("You need to set {} env var", var)) } -fn rerun_if_changed() { - println!("cargo::rerun-if-changed=migrations"); - println!("cargo::rerun-if-changed=src/db/queries"); -} - /// use `./enter_db.sh` to inspect DB async fn write_db_path_to_file(out_dir: &str, db_filename: &str) -> anyhow::Result<()> { let mut file = File::create("enter_db.sh").await?; diff --git a/nym-node-status-api/launch_node_status_api.sh b/nym-node-status-api/launch_node_status_api.sh index f9ebd364a9..5d92675412 100755 --- a/nym-node-status-api/launch_node_status_api.sh +++ b/nym-node-status-api/launch_node_status_api.sh @@ -6,8 +6,9 @@ export RUST_LOG=${RUST_LOG:-debug} export NYM_API_CLIENT_TIMEOUT=60 export EXPLORER_CLIENT_TIMEOUT=60 +export NODE_STATUS_API_TESTRUN_REFRESH_INTERVAL=60 -export ENVIRONMENT="mainnet.env" +export ENVIRONMENT="qa.env" function run_bare() { # export necessary env vars diff --git a/nym-node-status-api/migrations/000_init.sql b/nym-node-status-api/migrations/000_init.sql index 1e5683e2c9..4f9fd7da60 100644 --- a/nym-node-status-api/migrations/000_init.sql +++ b/nym-node-status-api/migrations/000_init.sql @@ -2,7 +2,7 @@ CREATE TABLE gateways ( id INTEGER PRIMARY KEY AUTOINCREMENT, gateway_identity_key VARCHAR NOT NULL UNIQUE, - self_described VARCHAR, + self_described VARCHAR NOT NULL, explorer_pretty_bond VARCHAR, last_probe_result VARCHAR, last_probe_log VARCHAR, @@ -103,7 +103,7 @@ CREATE TABLE CREATE TABLE testruns ( id INTEGER PRIMARY KEY AUTOINCREMENT, - gateway_id INTEGER, + gateway_id INTEGER NOT NULL, status INTEGER NOT NULL, -- 0=pending, 1=in-progress, 2=complete timestamp_utc INTEGER NOT NULL, ip_address VARCHAR NOT NULL, diff --git a/nym-node-status-api/src/db/models.rs b/nym-node-status-api/src/db/models.rs index f704c1ed8a..a5511787f9 100644 --- a/nym-node-status-api/src/db/models.rs +++ b/nym-node-status-api/src/db/models.rs @@ -11,7 +11,7 @@ pub(crate) struct GatewayRecord { pub(crate) identity_key: String, pub(crate) bonded: bool, pub(crate) blacklisted: bool, - pub(crate) self_described: Option, + pub(crate) self_described: String, pub(crate) explorer_pretty_bond: Option, pub(crate) last_updated_utc: i64, pub(crate) performance: u8, @@ -300,6 +300,7 @@ pub(crate) mod gateway { } } +#[allow(dead_code)] // not dead code, this is SQL data model #[derive(Debug, Clone)] pub struct TestRunDto { pub id: i64, @@ -315,7 +316,7 @@ pub struct TestRunDto { pub(crate) enum TestRunStatus { Complete = 2, InProgress = 1, - Pending = 0, + Queued = 0, } #[derive(Debug, Clone)] diff --git a/nym-node-status-api/src/db/queries/gateways.rs b/nym-node-status-api/src/db/queries/gateways.rs index 02b5d05dc3..bcf9c2d6ca 100644 --- a/nym-node-status-api/src/db/queries/gateways.rs +++ b/nym-node-status-api/src/db/queries/gateways.rs @@ -6,9 +6,29 @@ use crate::{ http::models::Gateway, }; use futures_util::TryStreamExt; -use nym_validator_client::models::DescribedGateway; +use nym_validator_client::models::NymNodeDescription; +use sqlx::{pool::PoolConnection, Sqlite}; use tracing::error; +pub(crate) async fn select_gateway_identity( + conn: &mut PoolConnection, + gateway_pk: i64, +) -> anyhow::Result { + let record = sqlx::query!( + r#"SELECT + gateway_identity_key + FROM + gateways + WHERE + id = ?"#, + gateway_pk + ) + .fetch_one(conn.as_mut()) + .await?; + + Ok(record.gateway_identity_key) +} + pub(crate) async fn insert_gateways( pool: &DbPool, gateways: Vec, @@ -68,13 +88,13 @@ where /// Ensure all gateways that are set as bonded, are still bonded pub(crate) async fn ensure_gateways_still_bonded( pool: &DbPool, - gateways: &[DescribedGateway], + gateways: &[&NymNodeDescription], ) -> anyhow::Result { let bonded_gateways_rows = get_all_bonded_gateways_row_ids_by_status(pool, true).await?; let unbonded_gateways_rows = bonded_gateways_rows.iter().filter(|v| { !gateways .iter() - .any(|bonded| *bonded.bond.identity() == v.identity_key) + .any(|bonded| *bonded.ed25519_identity_key().to_base58_string() == v.identity_key) }); let recently_unbonded_gateways = unbonded_gateways_rows.to_owned().count(); diff --git a/nym-node-status-api/src/db/queries/mod.rs b/nym-node-status-api/src/db/queries/mod.rs index 349e62e7c7..fe22ec27aa 100644 --- a/nym-node-status-api/src/db/queries/mod.rs +++ b/nym-node-status-api/src/db/queries/mod.rs @@ -5,7 +5,7 @@ mod summary; pub(crate) mod testruns; pub(crate) use gateways::{ - ensure_gateways_still_bonded, get_all_gateways, insert_gateways, + ensure_gateways_still_bonded, get_all_gateways, insert_gateways, select_gateway_identity, write_blacklisted_gateways_to_db, }; pub(crate) use misc::insert_summaries; diff --git a/nym-node-status-api/src/db/queries/testruns.rs b/nym-node-status-api/src/db/queries/testruns.rs index cf5b8a3bbf..91a3a86b13 100644 --- a/nym-node-status-api/src/db/queries/testruns.rs +++ b/nym-node-status-api/src/db/queries/testruns.rs @@ -1,12 +1,14 @@ +use crate::db::DbPool; use crate::http::models::TestrunAssignment; use crate::{ db::models::{TestRunDto, TestRunStatus}, testruns::now_utc, }; use anyhow::Context; +use chrono::Duration; use sqlx::{pool::PoolConnection, Sqlite}; -pub(crate) async fn get_testrun_by_id( +pub(crate) async fn get_in_progress_testrun_by_id( conn: &mut PoolConnection, testrun_id: i64, ) -> anyhow::Result { @@ -20,22 +22,58 @@ pub(crate) async fn get_testrun_by_id( ip_address as "ip_address!", log as "log!" FROM testruns - WHERE id = ? + WHERE + id = ? + AND + status = ? ORDER BY timestamp_utc"#, - testrun_id + testrun_id, + TestRunStatus::InProgress as i64, ) .fetch_one(conn.as_mut()) .await .context(format!("Couldn't retrieve testrun {testrun_id}")) } +pub(crate) async fn update_testruns_older_than(db: &DbPool, age: Duration) -> anyhow::Result { + let mut conn = db.acquire().await?; + let previous_run = now_utc() - age; + let cutoff_timestamp = previous_run.timestamp(); + + let res = sqlx::query!( + r#"UPDATE + testruns + SET + status = ? + WHERE + status = ? + AND + timestamp_utc < ? + "#, + TestRunStatus::Queued as i64, + TestRunStatus::InProgress as i64, + cutoff_timestamp + ) + .execute(conn.as_mut()) + .await?; + + let stale_testruns = res.rows_affected(); + if stale_testruns > 0 { + tracing::debug!( + "Refreshed {} stale testruns, scheduled before {} but not yet finished", + stale_testruns, + previous_run + ); + } + + Ok(stale_testruns) +} + pub(crate) async fn get_oldest_testrun_and_make_it_pending( - // TODO dz accept mut reference, repeat in all similar functions - conn: PoolConnection, + conn: &mut PoolConnection, ) -> anyhow::Result> { - let mut conn = conn; - let assignment = sqlx::query_as!( - TestrunAssignment, + // find & mark as "In progress" in the same transaction to avoid race conditions + let returning = sqlx::query!( r#"UPDATE testruns SET status = ? WHERE rowid = @@ -47,16 +85,36 @@ pub(crate) async fn get_oldest_testrun_and_make_it_pending( LIMIT 1 ) RETURNING - id as "testrun_id!", - gateway_id as "gateway_pk_id!" + id as "id!", + gateway_id "#, TestRunStatus::InProgress as i64, - TestRunStatus::Pending as i64, + TestRunStatus::Queued as i64, ) - .fetch_optional(&mut *conn) + .fetch_optional(conn.as_mut()) .await?; - Ok(assignment) + if let Some(testrun) = returning { + let gw_identity = sqlx::query!( + r#" + SELECT + id, + gateway_identity_key + FROM gateways + WHERE id = ? + LIMIT 1"#, + testrun.gateway_id + ) + .fetch_one(conn.as_mut()) + .await?; + + Ok(Some(TestrunAssignment { + testrun_id: testrun.id, + gateway_identity_key: gw_identity.gateway_identity_key, + })) + } else { + Ok(None) + } } pub(crate) async fn update_testrun_status( diff --git a/nym-node-status-api/src/http/api/mod.rs b/nym-node-status-api/src/http/api/mod.rs index ed24fa80f5..4e4693ac1f 100644 --- a/nym-node-status-api/src/http/api/mod.rs +++ b/nym-node-status-api/src/http/api/mod.rs @@ -1,7 +1,10 @@ use anyhow::anyhow; use axum::{response::Redirect, Router}; use tokio::net::ToSocketAddrs; -use tower_http::{cors::CorsLayer, trace::TraceLayer}; +use tower_http::{ + cors::CorsLayer, + trace::{DefaultOnResponse, TraceLayer}, +}; use utoipa::OpenApi; use utoipa_swagger_ui::SwaggerUi; @@ -58,7 +61,10 @@ impl RouterBuilder { // CORS layer needs to wrap other API layers .layer(setup_cors()) // logger should be outermost layer - .layer(TraceLayer::new_for_http()) + .layer( + TraceLayer::new_for_http() + .on_response(DefaultOnResponse::new().level(tracing::Level::DEBUG)), + ) } } diff --git a/nym-node-status-api/src/http/api/testruns.rs b/nym-node-status-api/src/http/api/testruns.rs index 4bbc4d6295..e55e462110 100644 --- a/nym-node-status-api/src/http/api/testruns.rs +++ b/nym-node-status-api/src/http/api/testruns.rs @@ -1,3 +1,4 @@ +use axum::extract::DefaultBodyLimit; use axum::Json; use axum::{ extract::{Path, State}, @@ -23,31 +24,32 @@ pub(crate) fn routes() -> Router { Router::new() .route("/", axum::routing::get(request_testrun)) .route("/:testrun_id", axum::routing::post(submit_testrun)) + .layer(DefaultBodyLimit::max(1024 * 1024 * 5)) } #[tracing::instrument(level = "debug", skip_all)] async fn request_testrun(State(state): State) -> HttpResult> { // TODO dz log agent's key // TODO dz log agent's network probe version - tracing::debug!("Agent X requested testrun"); + tracing::debug!("Agent requested testrun"); let db = state.db_pool(); - let conn = db + let mut conn = db .acquire() .await .map_err(HttpError::internal_with_logging)?; - return match db::queries::testruns::get_oldest_testrun_and_make_it_pending(conn).await { + return match db::queries::testruns::get_oldest_testrun_and_make_it_pending(&mut conn).await { Ok(res) => { if let Some(testrun) = res { - // TODO dz consider adding a column to testruns table with agent's public key tracing::debug!( - "🏃‍ Assigned testrun row_id {} to agent X", - &testrun.testrun_id + "🏃‍ Assigned testrun row_id {} gateway {} to agent", + &testrun.testrun_id, + testrun.gateway_identity_key ); Ok(Json(testrun)) } else { - Err(HttpError::not_found("No testruns available")) + Err(HttpError::no_available_testruns()) } } Err(err) => Err(HttpError::internal_with_logging(err)), @@ -61,25 +63,32 @@ async fn submit_testrun( State(state): State, body: String, ) -> HttpResult { - tracing::debug!( - "Agent submitted testrun {}. Total length: {}", - testrun_id, - body.len(), - ); - // TODO dz store testrun results - let db = state.db_pool(); let mut conn = db .acquire() .await .map_err(HttpError::internal_with_logging)?; - let testrun = queries::testruns::get_testrun_by_id(&mut conn, testrun_id) + let testrun = queries::testruns::get_in_progress_testrun_by_id(&mut conn, testrun_id) .await .map_err(|e| { tracing::error!("{e}"); HttpError::not_found(testrun_id) })?; + + let gw_identity = db::queries::select_gateway_identity(&mut conn, testrun.gateway_id) + .await + .map_err(|_| { + // should never happen: + HttpError::internal_with_logging("No gateway found for testrun") + })?; + tracing::debug!( + "Agent submitted testrun {} for gateway {} ({} bytes)", + testrun_id, + gw_identity, + body.len(), + ); + // TODO dz this should be part of a single transaction: commit after everything is done queries::testruns::update_testrun_status(&mut conn, testrun_id, TestRunStatus::Complete) .await @@ -99,7 +108,7 @@ async fn submit_testrun( tracing::info!( "✅ Testrun row_id {} for gateway {} complete", testrun.id, - testrun.gateway_id + gw_identity ); Ok(StatusCode::CREATED) diff --git a/nym-node-status-api/src/http/error.rs b/nym-node-status-api/src/http/error.rs index 81a61e2d3e..808ace9cec 100644 --- a/nym-node-status-api/src/http/error.rs +++ b/nym-node-status-api/src/http/error.rs @@ -8,9 +8,9 @@ pub(crate) struct HttpError { } impl HttpError { - pub(crate) fn invalid_input(message: String) -> Self { + pub(crate) fn invalid_input(msg: impl Display) -> Self { Self { - message, + message: serde_json::json!({"message": msg.to_string()}).to_string(), status: axum::http::StatusCode::BAD_REQUEST, } } @@ -27,6 +27,12 @@ impl HttpError { } } + pub(crate) fn no_available_testruns() -> Self { + Self { + message: serde_json::json!({"message": "No available testruns"}).to_string(), + status: axum::http::StatusCode::SERVICE_UNAVAILABLE, + } + } pub(crate) fn not_found(msg: impl Display) -> Self { Self { message: serde_json::json!({"message": msg.to_string()}).to_string(), diff --git a/nym-node-status-api/src/http/models.rs b/nym-node-status-api/src/http/models.rs index 315bf085dc..82011fc286 100644 --- a/nym-node-status-api/src/http/models.rs +++ b/nym-node-status-api/src/http/models.rs @@ -1,4 +1,3 @@ -use crate::db::models::TestRunDto; use nym_node_requests::api::v1::node::models::NodeDescription; use serde::{Deserialize, Serialize}; use utoipa::ToSchema; @@ -75,12 +74,3 @@ pub(crate) struct SummaryHistory { pub value_json: serde_json::Value, pub timestamp_utc: String, } - -impl From for TestrunAssignment { - fn from(value: TestRunDto) -> Self { - Self { - gateway_pk_id: value.gateway_id, - testrun_id: value.id, - } - } -} diff --git a/nym-node-status-api/src/monitor/mod.rs b/nym-node-status-api/src/monitor/mod.rs index 680a9ce312..a9f3761f35 100644 --- a/nym-node-status-api/src/monitor/mod.rs +++ b/nym-node-status-api/src/monitor/mod.rs @@ -10,7 +10,9 @@ use cosmwasm_std::Decimal; use nym_explorer_client::{ExplorerClient, PrettyDetailedGatewayBond}; use nym_network_defaults::NymNetworkDetails; use nym_validator_client::client::NymApiClientExt; -use nym_validator_client::models::{DescribedGateway, DescribedMixNode, MixNodeBondAnnotated}; +use nym_validator_client::models::{ + LegacyDescribedMixNode, MixNodeBondAnnotated, NymNodeDescription, +}; use nym_validator_client::nym_nodes::SkimmedNode; use nym_validator_client::nyxd::contract_traits::PagedMixnetQueryClient; use nym_validator_client::nyxd::{AccountId, NyxdClient}; @@ -91,20 +93,39 @@ async fn run( let explorer_client = ExplorerClient::new_with_timeout(default_explorer_url, explorer_client_timeout)?; let explorer_gateways = explorer_client - .get_gateways() + .unstable_get_gateways() .await - .log_error("get_gateways")?; + .log_error("unstable_get_gateways")?; let api_client = NymApiClient::new_with_timeout(default_api_url, nym_api_client_timeout); - let gateways = api_client - .get_cached_described_gateways() + + let all_nodes = api_client + .get_all_described_nodes() .await - .log_error("get_described_gateways")?; - tracing::debug!("Fetched {} gateways", gateways.len()); - let skimmed_gateways = api_client - .get_basic_gateways(None) + .log_error("get_all_described_nodes")?; + tracing::debug!("Fetched {} total nodes", all_nodes.len()); + + let gateways = all_nodes + .iter() + .filter(|node| node.description.declared_role.entry) + .collect::>(); + tracing::debug!("Of those, {} gateways", gateways.len()); + for gw in gateways.iter() { + tracing::debug!("{}", gw.ed25519_identity_key().to_base58_string()); + } + + let mixnodes = all_nodes + .iter() + .filter(|node| node.description.declared_role.mixnode) + .collect::>(); + tracing::debug!("Of those, {} mixnodes", mixnodes.len()); + + log_gw_in_explorer_not_api(explorer_gateways.as_slice(), gateways.as_slice()); + + let all_skimmed_nodes = api_client + .get_all_basic_nodes(None) .await - .log_error("get_basic_gateways")?; + .log_error("get_all_basic_nodes")?; let mixnodes = api_client .get_cached_mixnodes() @@ -112,11 +133,12 @@ async fn run( .log_error("get_cached_mixnodes")?; tracing::debug!("Fetched {} mixnodes", mixnodes.len()); - let gateways_blacklisted = skimmed_gateways + // let gateways_blacklisted = gateways.iter().filter(|gw|gw.) + let gateways_blacklisted = all_skimmed_nodes .iter() - .filter_map(|gw| { - if gw.performance.round_to_integer() <= 50 { - Some(gw.ed25519_identity_pubkey.to_owned()) + .filter_map(|node| { + if node.performance.round_to_integer() <= 50 && node.supported_roles.entry { + Some(node.ed25519_identity_pubkey.to_base58_string()) } else { None } @@ -153,7 +175,7 @@ async fn run( &gateways, &gateways_blacklisted, explorer_gateways, - skimmed_gateways, + all_skimmed_nodes, )?; queries::insert_gateways(pool, gateway_records) .await @@ -165,8 +187,8 @@ async fn run( let count_gateways_blacklisted = gateways .iter() .filter(|gw| { - let gw_identity = gw.bond.identity(); - gateways_blacklisted.contains(gw_identity) + let gw_identity = gw.ed25519_identity_key().to_base58_string(); + gateways_blacklisted.contains(&gw_identity) }) .count(); @@ -277,7 +299,7 @@ async fn run( } fn prepare_gateway_data( - gateways: &[DescribedGateway], + gateways: &[&NymNodeDescription], gateways_blacklisted: &HashSet, explorer_gateways: Vec, skimmed_gateways: Vec, @@ -285,24 +307,25 @@ fn prepare_gateway_data( let mut gateway_records = Vec::new(); for gateway in gateways { - let identity_key = gateway.bond.identity(); + let identity_key = gateway.ed25519_identity_key().to_base58_string(); let bonded = true; let last_updated_utc = chrono::offset::Utc::now().timestamp(); - let blacklisted = gateways_blacklisted.contains(identity_key); + let blacklisted = gateways_blacklisted.contains(&identity_key); - let self_described = gateway - .self_described - .as_ref() - .and_then(|v| serde_json::to_string(&v).ok()); + let self_described = serde_json::to_string(&gateway.description)?; let explorer_pretty_bond = explorer_gateways .iter() - .find(|g| g.gateway.identity_key.eq(identity_key)); + .find(|g| g.gateway.identity_key.eq(&identity_key)); let explorer_pretty_bond = explorer_pretty_bond.and_then(|g| serde_json::to_string(g).ok()); let performance = skimmed_gateways .iter() - .find(|g| g.ed25519_identity_pubkey.eq(identity_key)) + .find(|g| { + g.ed25519_identity_pubkey + .to_base58_string() + .eq(&identity_key) + }) .map(|g| g.performance) .unwrap_or_default() .round_to_integer(); @@ -323,7 +346,7 @@ fn prepare_gateway_data( fn prepare_mixnode_data( mixnodes: &[MixNodeBondAnnotated], - mixnodes_described: Vec, + mixnodes_described: Vec, delegation_program_members: Vec, ) -> anyhow::Result> { let mut mixnode_records = Vec::new(); @@ -364,6 +387,28 @@ fn prepare_mixnode_data( Ok(mixnode_records) } +fn log_gw_in_explorer_not_api( + explorer: &[PrettyDetailedGatewayBond], + api_gateways: &[&NymNodeDescription], +) { + let api_gateways = api_gateways + .iter() + .map(|gw| gw.ed25519_identity_key().to_base58_string()) + .collect::>(); + let explorer_only = explorer + .iter() + .filter(|gw| !api_gateways.contains(&gw.gateway.identity_key.to_string())) + .collect::>(); + + tracing::debug!( + "Gateways listed by explorer but not by Nym API: {}", + explorer_only.len() + ); + for gw in explorer_only.iter() { + tracing::debug!("{}", gw.gateway.identity_key.to_string()); + } +} + // TODO dz is there a common monorepo place this can be put? pub trait NumericalCheckedCast where @@ -423,7 +468,7 @@ async fn get_delegation_program_details( let mix_ids: Vec = delegations .iter() - .map(|delegation| delegation.mix_id) + .map(|delegation| delegation.node_id) .collect(); Ok(mix_ids) diff --git a/nym-node-status-api/src/testruns/mod.rs b/nym-node-status-api/src/testruns/mod.rs index 86b36cf48e..f487523f36 100644 --- a/nym-node-status-api/src/testruns/mod.rs +++ b/nym-node-status-api/src/testruns/mod.rs @@ -11,10 +11,12 @@ pub(crate) use queue::now_utc; pub(crate) async fn spawn(pool: DbPool, refresh_interval: Duration) { tokio::spawn(async move { loop { - tracing::info!("Spawning testruns..."); + if let Err(e) = refresh_stale_testruns(&pool, refresh_interval).await { + tracing::error!("{e}"); + } if let Err(e) = run(&pool).await { - tracing::error!("Cron job failed: {}", e); + tracing::error!("Assigning testruns failed: {}", e); } tracing::debug!("Sleeping for {}s...", refresh_interval.as_secs()); tokio::time::sleep(refresh_interval).await; @@ -24,9 +26,9 @@ pub(crate) async fn spawn(pool: DbPool, refresh_interval: Duration) { // TODO dz make number of max agents configurable -// TODO dz periodically clean up stale pending testruns #[instrument(level = "debug", name = "testrun_queue", skip_all)] async fn run(pool: &DbPool) -> anyhow::Result<()> { + tracing::info!("Spawning testruns..."); if pool.is_closed() { tracing::debug!("DB pool closed, returning early"); return Ok(()); @@ -74,3 +76,11 @@ async fn run(pool: &DbPool) -> anyhow::Result<()> { Ok(()) } + +#[instrument(level = "debug", skip_all)] +async fn refresh_stale_testruns(pool: &DbPool, refresh_interval: Duration) -> anyhow::Result<()> { + let chrono_duration = chrono::Duration::from_std(refresh_interval)?; + crate::db::queries::testruns::update_testruns_older_than(pool, chrono_duration).await?; + + Ok(()) +} diff --git a/nym-node-status-api/src/testruns/queue.rs b/nym-node-status-api/src/testruns/queue.rs index aa0ce32fa2..88804fff1b 100644 --- a/nym-node-status-api/src/testruns/queue.rs +++ b/nym-node-status-api/src/testruns/queue.rs @@ -82,7 +82,7 @@ pub(crate) async fn try_queue_testrun( // // save test run // - let status = TestRunStatus::Pending as u32; + let status = TestRunStatus::Queued as u32; let log = format!( "Test for {identity_key} requested at {} UTC\n\n", timestamp_pretty @@ -103,7 +103,7 @@ pub(crate) async fn try_queue_testrun( Ok(TestRun { id: id as u32, identity_key, - status: format!("{}", TestRunStatus::Pending), + status: format!("{}", TestRunStatus::Queued), log, }) } From de4239a5ddf2e368fab41b955dc14b783758f509 Mon Sep 17 00:00:00 2001 From: Fran Arbanas Date: Mon, 4 Nov 2024 10:35:49 +0100 Subject: [PATCH 47/48] fix: update dockerfile env vars description (#5079) --- nym-node-status-agent/Dockerfile | 9 +++++ nym-node-status-api/Dockerfile | 21 +++++++++++ nym-node/Dockerfile | 60 ++++++++++++++++++++++++++++++++ 3 files changed, 90 insertions(+) diff --git a/nym-node-status-agent/Dockerfile b/nym-node-status-agent/Dockerfile index 1e85757a44..a6653e6f79 100644 --- a/nym-node-status-agent/Dockerfile +++ b/nym-node-status-agent/Dockerfile @@ -15,6 +15,15 @@ COPY ./ /usr/src/nym WORKDIR /usr/src/nym/nym-node-status-agent RUN cargo build --release +#------------------------------------------------------------------- +# The following environment variables are required at runtime: +# +# NODE_STATUS_AGENT_SERVER_ADDRESS +# NODE_STATUS_AGENT_SERVER_PORT +# +# see https://github.com/nymtech/nym/blob/develop/nym-node-status-agent/src/cli.rs for details +#------------------------------------------------------------------- + FROM ubuntu:24.04 RUN apt-get update && apt-get install -y ca-certificates diff --git a/nym-node-status-api/Dockerfile b/nym-node-status-api/Dockerfile index ceab7c9392..035f186c65 100644 --- a/nym-node-status-api/Dockerfile +++ b/nym-node-status-api/Dockerfile @@ -5,6 +5,27 @@ WORKDIR /usr/src/nym/nym-node-status-api RUN cargo build --release + +#------------------------------------------------------------------- +# The following environment variables are required at runtime: +# +# EXPLORER_API +# NYXD +# NYM_API +# DATABASE_URL +# +# And optionally: +# +# NYM_NODE_STATUS_API_NYM_HTTP_CACHE_TTL +# NYM_NODE_STATUS_API_HTTP_PORT +# NYM_API_CLIENT_TIMEOUT +# EXPLORER_CLIENT_TIMEOUT +# NODE_STATUS_API_MONITOR_REFRESH_INTERVAL +# NODE_STATUS_API_TESTRUN_REFRESH_INTERVAL +# +# see https://github.com/nymtech/nym/blob/develop/nym-node-status-api/src/cli.rs for details +#------------------------------------------------------------------- + FROM ubuntu:24.04 RUN apt-get update && apt-get install -y ca-certificates diff --git a/nym-node/Dockerfile b/nym-node/Dockerfile index d7a47a28e9..87b6783669 100644 --- a/nym-node/Dockerfile +++ b/nym-node/Dockerfile @@ -5,6 +5,66 @@ WORKDIR /usr/src/nym/nym-node RUN cargo build --release +#------------------------------------------------------------------- +# The following environment variables are required at runtime: +# +# NYMNODE_ACCEPT_OPERATOR_TERMS +# NYMNODE_MODE +# +# And optionally: +# +# NYMNODE_BONDING_INFORMATION_OUTPUT +# NYMNODE_DENY_INIT +# NYMNODE_INIT_ONLY +# NYMNODE_LOCAL +# NYMMONDE_WRITE_CONFIG_CHANGES +# NYMNODE_OUTPUT +# +# Host args +# NYMNODE_PUBLIC_IPS +# NYMNODE_HOSTNAME +# NYMNODE_LOCATION +# +# Http args +# NYMNODE_HTTP_BIND_ADDRESS +# NYMNODE_HTTP_LANDING_ASSETS +# NYMNODE_HTTP_ACCESS_TOKEN +# NYMNODE_HTTP_EXPOSE_SYSTEM_INFO +# NYMNODE_HTTP_EXPOSE_SYSTEM_HARDWARE +# NYMNODE_HTTP_EXPOSE_CRYPTO_HARDWARE +# +# Mixnet args +# NYMNODE_MIXNET_BIND_ADDRESS +# NYMNODE_MIXNET_ANNOUNCE_PORT +# NYMNODE_NYM_APIS +# NYMNODE_NYXD +# UNSAFE_DISABLE_NOISE +# +# Wireguard args +# NYMNODE_WG_ENABLED +# NYMNODE_WG_BIND_ADDRESS +# NYMNODE_WG_IP +# NYMNODE_WG_ANNOUNCED_PORT +# NYMNODE_WG_PRIVATE_NETWORK_PREFIX +# +# Mixnode args +# NYMNODE_VERLOC_BIND_ADDRESS +# NYMNODE_VERLOC_ANNOUNCE_PORT +# +# Entry gateway args +# NYMNODE_ENTRY_BIND_ADDRESS +# NYMNODE_ENTRY_ANNOUNCE_WS_PORT +# NYMNODE_ENTRY_ANNOUNCE_WSS_PORT +# NYMNODE_ENFORCE_ZK_NYMS +# NYMNODE_MNEMONIC +# +# Exit gateway args +# NYMNODE_UPSTREAM_EXIT_POLICY +# NYMNODE_OPEN_PROXY +# +# see https://github.com/nymtech/nym/blob/develop/nym-node/src/env.rs for details +#------------------------------------------------------------------- + FROM ubuntu:24.04 WORKDIR /nym From 5e0417ebe7ac04252702f1256421ccc7785cdef8 Mon Sep 17 00:00:00 2001 From: Fran Arbanas Date: Mon, 4 Nov 2024 10:41:40 +0100 Subject: [PATCH 48/48] feat: add nym node GH workflow (#5080) --- .github/workflows/push-nym-node.yaml | 55 ++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 .github/workflows/push-nym-node.yaml diff --git a/.github/workflows/push-nym-node.yaml b/.github/workflows/push-nym-node.yaml new file mode 100644 index 0000000000..c80199dc1b --- /dev/null +++ b/.github/workflows/push-nym-node.yaml @@ -0,0 +1,55 @@ +name: Build and upload nym node container to harbor.nymte.ch +on: + workflow_dispatch: + +env: + WORKING_DIRECTORY: "nym-node" + CONTAINER_NAME: "nym-node" + +jobs: + build-container: + runs-on: arc-ubuntu-22.04-dind + steps: + - name: Login to Harbor + uses: docker/login-action@v3 + with: + registry: harbor.nymte.ch + username: ${{ secrets.HARBOR_ROBOT_USERNAME }} + password: ${{ secrets.HARBOR_ROBOT_SECRET }} + + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Configure git identity + run: | + git config --global user.email "lawrence@nymtech.net" + git config --global user.name "Lawrence Stalder" + + - name: Get version from cargo.toml + uses: mikefarah/yq@v4.44.3 + id: get_version + with: + cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml + + - name: Check if tag exists + run: | + if git rev-parse ${{ steps.get_version.outputs.value }} >/dev/null 2>&1; then + echo "Tag ${{ steps.get_version.outputs.value }} already exists" + fi + + - name: Remove existing tag if exists + run: | + if git rev-parse ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }} >/dev/null 2>&1; then + git push --delete origin ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }} + git tag -d ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }} + fi + + - name: Create tag + run: | + git tag -a ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }} -m "Version ${{ steps.get_version.outputs.result }}" + git push origin ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }} + + - name: BuildAndPushImageOnHarbor + run: | + docker build -f ${{ env.WORKING_DIRECTORY }}/Dockerfile . -t harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }}:${{ steps.get_version.outputs.result }} -t harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }}:latest + docker push harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }} --all-tags