Compare commits

..

6 Commits

Author SHA1 Message Date
mfahampshire 25b45a911c push to remote for cleanbuild 2025-01-24 11:37:55 +01:00
mfahampshire ffc72feb12 add temp warning 2025-01-17 15:24:25 +01:00
mfahampshire 0793133c95 Merge branch 'develop' of github.com:nymtech/nym into develop 2025-01-16 14:13:32 +01:00
mfahampshire b1d10b41a4 Merge branch 'develop' of github.com:nymtech/nym into develop 2025-01-15 13:12:31 +01:00
mfahampshire b33708bbe6 Merge branch 'develop' of github.com:nymtech/nym into develop 2025-01-14 23:52:00 +01:00
mfahampshire eea8ee589f removed old todos 2025-01-14 15:28:05 +01:00
99 changed files with 5565 additions and 37116 deletions
@@ -79,6 +79,7 @@ jobs:
target/release/nym-socks5-client
target/release/nym-api
target/release/nym-network-requester
target/release/nym-data-observatory
target/release/nym-cli
target/release/nymvisor
target/release/nym-node
@@ -96,6 +97,7 @@ jobs:
cp target/release/nym-socks5-client $OUTPUT_DIR
cp target/release/nym-api $OUTPUT_DIR
cp target/release/nym-network-requester $OUTPUT_DIR
cp target/release/nym-data-observatory $OUTPUT_DIR
cp target/release/nymvisor $OUTPUT_DIR
cp target/release/nym-node $OUTPUT_DIR
cp target/release/nym-cli $OUTPUT_DIR
+3 -4
View File
@@ -8,18 +8,17 @@ on:
- 'explorer-api/**'
- 'gateway/**'
- 'integrations/**'
- 'mixnode/**'
- 'nym-api/**'
- 'nym-credential-proxy/**'
- 'nym-data-observatory/**'
- 'nym-network-monitor/**'
- 'nym-node/**'
- 'nym-node-status-api/**'
- 'nym-outfox/**'
- 'nym-validator-rewarder/**'
- 'nyx-chain-watcher/**'
- 'sdk/ffi/**'
- 'sdk/lib/**'
- 'sdk/rust/**'
- 'service-providers/**'
- 'nym-browser-extension/storage/**'
- 'tools/**'
- 'wasm/**'
- 'Cargo.toml'
-1
View File
@@ -1,7 +1,6 @@
name: ci-sdk-wasm
on:
workflow_dispatch:
pull_request:
paths:
- 'wasm/**'
Generated
+101 -364
View File
File diff suppressed because it is too large Load Diff
+36 -34
View File
@@ -53,8 +53,8 @@ members = [
"common/execute",
"common/exit-policy",
"common/gateway-requests",
"common/gateway-stats-storage",
"common/gateway-storage",
"common/gateway-stats-storage",
"common/http-api-client",
"common/http-api-common",
"common/inclusion-probability",
@@ -93,7 +93,6 @@ members = [
"common/topology",
"common/tun",
"common/types",
"common/verloc",
"common/wasm/client-core",
"common/wasm/storage",
"common/wasm/utils",
@@ -105,22 +104,6 @@ members = [
"explorer-api/explorer-client",
"gateway",
"integrations/bity",
"nym-api",
"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-network-monitor",
"nym-node",
"nym-node-status-api/nym-node-status-agent",
"nym-node-status-api/nym-node-status-api",
"nym-node-status-api/nym-node-status-client",
"nym-node/nym-node-metrics",
"nym-node/nym-node-requests",
"nym-outfox",
"nym-validator-rewarder",
"nyx-chain-watcher",
"sdk/ffi/cpp",
"sdk/ffi/go",
"sdk/ffi/shared",
@@ -129,16 +112,26 @@ members = [
"service-providers/common",
"service-providers/ip-packet-router",
"service-providers/network-requester",
"nym-api",
"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-network-monitor",
"nyx-chain-watcher",
"nym-node",
"nym-node/nym-node-requests",
"nym-node/nym-node-metrics",
"nym-node-status-api/nym-node-status-agent",
"nym-node-status-api/nym-node-status-api",
"nym-node-status-api/nym-node-status-client",
"nym-outfox",
"nym-validator-rewarder",
"tools/echo-server",
"tools/echo-server",
"tools/internal/contract-state-importer/importer-cli",
"tools/internal/contract-state-importer/importer-contract",
"tools/internal/mixnet-connectivity-check",
# "tools/internal/sdk-version-bump",
"tools/internal/ssl-inject",
# "tools/internal/sdk-version-bump",
"tools/internal/testnet-manager",
"tools/internal/testnet-manager",
"tools/internal/testnet-manager/dkg-bypass-contract",
"tools/internal/testnet-manager/dkg-bypass-contract",
"tools/nym-cli",
"tools/nym-id-cli",
@@ -150,6 +143,13 @@ members = [
"wasm/mix-fetch",
"wasm/node-tester",
"wasm/zknym-lib",
"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",
"common/verloc",
"tools/internal/mixnet-connectivity-check",
]
default-members = [
@@ -173,6 +173,7 @@ exclude = [
"explorer",
"contracts",
"nym-wallet",
"nym-vpn/ui/src-tauri",
"cpu-cycles",
]
@@ -193,7 +194,6 @@ aes-gcm = "0.10.1"
aes-gcm-siv = "0.11.1"
aead = "0.5.2"
anyhow = "1.0.95"
arc-swap = "1.7.1"
argon2 = "0.5.0"
async-trait = "0.1.85"
axum-client-ip = "0.6.1"
@@ -254,7 +254,6 @@ handlebars = "3.5.5"
headers = "0.4.0"
hex = "0.4.3"
hex-literal = "0.3.3"
hickory-resolver = "0.24.2"
hkdf = "0.12.3"
hmac = "0.12.1"
http = "1"
@@ -325,7 +324,7 @@ strum = "0.26"
strum_macros = "0.26"
subtle-encoding = "0.5"
syn = "1"
sysinfo = "0.33.0"
sysinfo = "0.30.13"
tap = "1.0.1"
tar = "0.4.43"
tempfile = "3.15"
@@ -387,17 +386,20 @@ cw-controllers = { version = "=1.1.0" }
# cosmrs-related
bip32 = { version = "0.5.2", default-features = false }
cosmrs = { version = "0.21.0" }
tendermint = "0.40.0"
tendermint-rpc = "0.40.0"
prost = { version = "0.13", default-features = false }
# temporarily using a fork again (yay.) because we need staking and slashing support (which are already on main but not released)
# plus response message parsing (which is, as of the time of writing this message, waiting to get merged)
#cosmrs = { path = "../cosmos-rust-fork/cosmos-rust/cosmrs" }
cosmrs = { git = "https://github.com/cosmos/cosmos-rust", rev = "4b1332e6d8258ac845cef71589c8d362a669675a" } # unfortuntely we need a fork by yours truly to get the staking support
tendermint = "0.37.0" # same version as used by cosmrs
tendermint-rpc = "0.37.0" # same version as used by cosmrs
prost = { version = "0.12", default-features = false }
# wasm-related dependencies
gloo-utils = "0.2.0"
gloo-net = "0.6.0"
indexed_db_futures = "0.6.0"
# TODO: migrate to 0.6+
indexed_db_futures = "0.4.2"
js-sys = "0.3.76"
serde-wasm-bindgen = "0.6.5"
tsify = "0.4.5"
+2 -2
View File
@@ -13,7 +13,7 @@ The platform is composed of multiple Rust crates. Top-level executable binary cr
* `nym-client` - an executable which you can build into your own applications. Use it for interacting with Nym nodes.
* `nym-socks5-client` - a Socks5 proxy you can run on your machine and use with existing applications.
* `nym-explorer` - a (projected) block explorer and (existing) mixnet viewer.
* `nym-wallet` - a desktop wallet implemented using the [Tauri](https://tauri.app)) framework.
* `nym-wallet` - a desktop wallet implemented using the [Tauri](https://tauri.studio/en/docs/about/intro) framework.
* `nym-cli` - a tool for interacting with the network from the CLI.
<!-- coming soon
* `nym-network-monitor` - sends packets through the full system to check that they are working as expected, and stores node uptime histories as the basis of a rewards system ("mixmining" or "proof-of-mixing").
@@ -66,4 +66,4 @@ As a general approach, licensing is as follows this pattern:
- libraries and components are Apache 2.0 or MIT
- documentation is Apache 2.0 or CC0-1.0
Nym Node Operators and Validators Terms and Conditions can be found [here](https://nym.com/operators-validators-terms).
Nym Node Operators and Validators Temrs and Conditions can be found [here](https://nym.com/terms-and-conditions/operators/v1.0.0).
@@ -553,6 +553,7 @@ pub struct Topology {
/// Specifies whether this client should attempt to retrieve all available network nodes
/// as opposed to just active mixnodes/gateways.
/// Useless without `ignore_epoch_roles = true`
pub use_extended_topology: bool,
/// Specifies whether this client should ignore the current epoch role of the target egress node
@@ -15,7 +15,6 @@ pub mod error;
mod manager;
mod models;
#[derive(Clone)]
pub struct OnDiskGatewaysDetails {
manager: StorageManager,
}
@@ -20,12 +20,12 @@ pub enum InMemStorageError {
MalformedGateway(#[from] BadGateway),
}
#[derive(Clone, Debug, Default)]
#[derive(Debug, Default)]
pub struct InMemGatewaysDetails {
inner: Arc<RwLock<InMemStorageInner>>,
}
#[derive(Clone, Debug, Default)]
#[derive(Debug, Default)]
struct InMemStorageInner {
active_gateway: Option<String>,
gateways: HashMap<String, GatewayRegistration>,
@@ -115,7 +115,7 @@ where
hardcoded_topology.entry_capable_nodes().cloned().collect()
} else {
let mut rng = rand::thread_rng();
crate::init::helpers::gateways_for_init(
crate::init::helpers::current_gateways(
&mut rng,
&core.client.nym_api_urls,
user_agent,
@@ -170,7 +170,7 @@ where
hardcoded_topology.entry_capable_nodes().cloned().collect()
} else {
let mut rng = rand::thread_rng();
crate::init::helpers::gateways_for_init(
crate::init::helpers::current_gateways(
&mut rng,
&core.client.nym_api_urls,
user_agent,
@@ -63,7 +63,7 @@ pub trait MixnetClientStorage {
fn gateway_details_store(&self) -> &Self::GatewaysDetailsStore;
}
#[derive(Clone, Default)]
#[derive(Default)]
pub struct Ephemeral {
key_store: InMemEphemeralKeys,
reply_store: reply_storage::Empty,
@@ -114,7 +114,6 @@ impl MixnetClientStorage for Ephemeral {
}
}
#[derive(Clone)]
#[cfg(all(
not(target_arch = "wasm32"),
feature = "fs-surb-storage",
@@ -4,7 +4,6 @@
use crate::client::key_manager::ClientKeys;
use async_trait::async_trait;
use std::error::Error;
use std::sync::Arc;
use tokio::sync::Mutex;
#[cfg(not(target_arch = "wasm32"))]
@@ -65,7 +64,6 @@ pub enum OnDiskKeysError {
},
}
#[derive(Clone)]
#[cfg(not(target_arch = "wasm32"))]
pub struct OnDiskKeys {
paths: ClientKeysPaths,
@@ -195,9 +193,9 @@ impl KeyStore for OnDiskKeys {
}
}
#[derive(Clone, Default)]
#[derive(Default)]
pub struct InMemEphemeralKeys {
keys: Arc<Mutex<Option<ClientKeys>>>,
keys: Mutex<Option<ClientKeys>>,
}
#[derive(Debug, thiserror::Error)]
+1 -4
View File
@@ -86,7 +86,7 @@ impl<'a, G: ConnectableGateway> GatewayWithLatency<'a, G> {
}
}
pub async fn gateways_for_init<R: Rng>(
pub async fn current_gateways<R: Rng>(
rng: &mut R,
nym_apis: &[Url],
user_agent: Option<UserAgent>,
@@ -108,11 +108,8 @@ pub async fn gateways_for_init<R: Rng>(
log::trace!("Gateways: {:#?}", gateways);
// filter out gateways below minimum performance and ones that could operate as a mixnode
// (we don't want instability)
let valid_gateways = gateways
.iter()
.filter(|g| !g.supported_roles.mixnode)
.filter(|g| g.performance.round_to_integer() >= minimum_performance)
.filter_map(|gateway| gateway.try_into().ok())
.collect::<Vec<_>>();
@@ -22,7 +22,7 @@ mod error;
mod manager;
mod models;
#[derive(Clone, Debug)]
#[derive(Debug)]
pub struct Backend {
temporary_old_path: Option<PathBuf>,
database_path: PathBuf,
@@ -19,7 +19,7 @@ pub mod fs_backend;
#[error("no information provided")]
pub struct UndefinedError;
#[derive(Clone, Debug)]
#[derive(Debug)]
pub struct Empty {
// we need to keep 'basic' metadata here to "load" the CombinedReplyStorage
pub min_surb_threshold: usize,
@@ -19,9 +19,8 @@ use nym_api_requests::ecash::{
PartialExpirationDateSignatureResponse, VerificationKeyResponse,
};
use nym_api_requests::models::{
ApiHealthResponse, GatewayBondAnnotated, GatewayCoreStatusResponse,
HistoricalPerformanceResponse, MixnodeCoreStatusResponse, MixnodeStatusResponse,
NymNodeDescription, RewardEstimationResponse, StakeSaturationResponse,
ApiHealthResponse, GatewayBondAnnotated, GatewayCoreStatusResponse, MixnodeCoreStatusResponse,
MixnodeStatusResponse, NymNodeDescription, RewardEstimationResponse, StakeSaturationResponse,
};
use nym_api_requests::models::{LegacyDescribedGateway, MixNodeBondAnnotated};
use nym_api_requests::nym_nodes::SkimmedNode;
@@ -265,31 +264,6 @@ impl<C, S> Client<C, S> {
Ok(self.nym_api.get_gateways_detailed_unfiltered().await?)
}
pub async fn get_full_node_performance_history(
&self,
node_id: NodeId,
) -> Result<Vec<HistoricalPerformanceResponse>, 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 history = Vec::new();
loop {
let mut res = self
.nym_api
.get_node_performance_history(node_id, Some(page), None)
.await?;
history.append(&mut res.history.data);
if history.len() < res.history.pagination.total {
page += 1
} else {
break;
}
}
Ok(history)
}
// TODO: combine with NymApiClient...
pub async fn get_all_cached_described_nodes(
&self,
@@ -13,7 +13,7 @@ use nym_api_requests::ecash::models::{
use nym_api_requests::ecash::VerificationKeyResponse;
use nym_api_requests::models::{
AnnotationResponse, ApiHealthResponse, LegacyDescribedMixNode, NodePerformanceResponse,
NodeRefreshBody, NymNodeDescription, PerformanceHistoryResponse, RewardedSetResponse,
NodeRefreshBody, NymNodeDescription, RewardedSetResponse,
};
use nym_api_requests::nym_nodes::PaginatedCachedNodesResponse;
use nym_api_requests::pagination::PaginatedResponse;
@@ -163,35 +163,6 @@ pub trait NymApiClientExt: ApiClient {
.await
}
#[tracing::instrument(level = "debug", skip_all)]
async fn get_node_performance_history(
&self,
node_id: NodeId,
page: Option<u32>,
per_page: Option<u32>,
) -> Result<PerformanceHistoryResponse, 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,
routes::NYM_NODES_ROUTES,
routes::NYM_NODES_PERFORMANCE_HISTORY,
&*node_id.to_string(),
],
&params,
)
.await
}
#[tracing::instrument(level = "debug", skip_all)]
async fn get_nodes_described(
&self,
@@ -208,15 +179,8 @@ pub trait NymApiClientExt: ApiClient {
params.push(("per_page", per_page.to_string()))
}
self.get_json(
&[
routes::API_VERSION,
routes::NYM_NODES_ROUTES,
routes::NYM_NODES_DESCRIBED,
],
&params,
)
.await
self.get_json(&[routes::API_VERSION, "nym-nodes", "described"], &params)
.await
}
#[tracing::instrument(level = "debug", skip_all)]
@@ -235,15 +199,8 @@ pub trait NymApiClientExt: ApiClient {
params.push(("per_page", per_page.to_string()))
}
self.get_json(
&[
routes::API_VERSION,
routes::NYM_NODES_ROUTES,
routes::NYM_NODES_BONDED,
],
&params,
)
.await
self.get_json(&[routes::API_VERSION, "nym-nodes", "bonded"], &params)
.await
}
#[deprecated]
@@ -253,7 +210,7 @@ pub trait NymApiClientExt: ApiClient {
&[
routes::API_VERSION,
"unstable",
routes::NYM_NODES_ROUTES,
"nym-nodes",
"mixnodes",
"skimmed",
],
@@ -269,7 +226,7 @@ pub trait NymApiClientExt: ApiClient {
&[
routes::API_VERSION,
"unstable",
routes::NYM_NODES_ROUTES,
"nym-nodes",
"gateways",
"skimmed",
],
@@ -281,11 +238,7 @@ pub trait NymApiClientExt: ApiClient {
#[instrument(level = "debug", skip(self))]
async fn get_rewarded_set(&self) -> Result<RewardedSetResponse, NymAPIError> {
self.get_json(
&[
routes::API_VERSION,
routes::NYM_NODES_ROUTES,
routes::NYM_NODES_REWARDED_SET,
],
&[routes::API_VERSION, "nym-nodes", "rewarded-set"],
NO_PARAMS,
)
.await
@@ -318,7 +271,7 @@ pub trait NymApiClientExt: ApiClient {
&[
routes::API_VERSION,
"unstable",
routes::NYM_NODES_ROUTES,
"nym-nodes",
"skimmed",
"entry-gateways",
"all",
@@ -355,7 +308,7 @@ pub trait NymApiClientExt: ApiClient {
&[
routes::API_VERSION,
"unstable",
routes::NYM_NODES_ROUTES,
"nym-nodes",
"skimmed",
"mixnodes",
"active",
@@ -392,7 +345,7 @@ pub trait NymApiClientExt: ApiClient {
&[
routes::API_VERSION,
"unstable",
routes::NYM_NODES_ROUTES,
"nym-nodes",
"skimmed",
"mixnodes",
"all",
@@ -424,12 +377,7 @@ pub trait NymApiClientExt: ApiClient {
}
self.get_json(
&[
routes::API_VERSION,
"unstable",
routes::NYM_NODES_ROUTES,
"skimmed",
],
&[routes::API_VERSION, "unstable", "nym-nodes", "skimmed"],
&params,
)
.await
@@ -738,8 +686,8 @@ pub trait NymApiClientExt: ApiClient {
self.get_json(
&[
routes::API_VERSION,
routes::NYM_NODES_ROUTES,
routes::NYM_NODES_PERFORMANCE,
"nym-nodes",
"performance",
&node_id.to_string(),
],
NO_PARAMS,
@@ -754,8 +702,8 @@ pub trait NymApiClientExt: ApiClient {
self.get_json(
&[
routes::API_VERSION,
routes::NYM_NODES_ROUTES,
routes::NYM_NODES_ANNOTATION,
"nym-nodes",
"annotation",
&node_id.to_string(),
],
NO_PARAMS,
@@ -979,11 +927,7 @@ pub trait NymApiClientExt: ApiClient {
request: &NodeRefreshBody,
) -> Result<(), NymAPIError> {
self.post_json(
&[
routes::API_VERSION,
routes::NYM_NODES_ROUTES,
routes::NYM_NODES_REFRESH_DESCRIBED,
],
&[routes::API_VERSION, "nym-nodes", "refresh-described"],
NO_PARAMS,
request,
)
@@ -34,19 +34,6 @@ pub mod ecash {
pub const EPOCH_ID_PARAM: &str = "epoch_id";
}
pub const NYM_NODES_ROUTES: &str = "nym-nodes";
pub use nym_nodes::*;
pub mod nym_nodes {
pub const NYM_NODES_PERFORMANCE_HISTORY: &str = "performance-history";
pub const NYM_NODES_PERFORMANCE: &str = "performance";
pub const NYM_NODES_ANNOTATION: &str = "annotation";
pub const NYM_NODES_DESCRIBED: &str = "described";
pub const NYM_NODES_BONDED: &str = "bonded";
pub const NYM_NODES_REWARDED_SET: &str = "rewarded-set";
pub const NYM_NODES_REFRESH_DESCRIBED: &str = "refresh-described";
}
pub const STATUS_ROUTES: &str = "status";
pub const API_STATUS_ROUTES: &str = "api-status";
pub const HEALTH: &str = "health";
@@ -153,20 +153,13 @@ pub trait CosmWasmClient: TendermintRpcClient {
let req = QueryAllBalancesRequest {
address: address.to_string(),
pagination,
resolve_denom: false,
};
let mut res = self
.make_abci_query::<_, QueryAllBalancesResponse>(path.clone(), req)
.await?;
let early_break = res.balances.is_empty();
raw_balances.append(&mut res.balances);
if early_break {
break;
}
if let Some(next_key) = next_page_key(res.pagination) {
pagination = Some(create_pagination(next_key))
} else {
@@ -194,13 +187,7 @@ pub trait CosmWasmClient: TendermintRpcClient {
.make_abci_query::<_, QueryTotalSupplyResponse>(path.clone(), req)
.await?;
let early_break = res.supply.is_empty();
supply.append(&mut res.supply);
if early_break {
break;
}
if let Some(next_key) = next_page_key(res.pagination) {
pagination = Some(create_pagination(next_key))
} else {
@@ -341,13 +328,7 @@ pub trait CosmWasmClient: TendermintRpcClient {
.make_abci_query::<_, QueryCodesResponse>(path.clone(), req)
.await?;
let early_break = res.code_infos.is_empty();
raw_codes.append(&mut res.code_infos);
if early_break {
break;
}
if let Some(next_key) = next_page_key(res.pagination) {
pagination = Some(create_pagination(next_key))
} else {
@@ -392,13 +373,7 @@ pub trait CosmWasmClient: TendermintRpcClient {
.make_abci_query::<_, QueryContractsByCodeResponse>(path.clone(), req)
.await?;
let early_break = res.contracts.is_empty();
raw_contracts.append(&mut res.contracts);
if early_break {
break;
}
if let Some(next_key) = next_page_key(res.pagination) {
pagination = Some(create_pagination(next_key))
} else {
@@ -454,13 +429,7 @@ pub trait CosmWasmClient: TendermintRpcClient {
.make_abci_query::<_, QueryContractHistoryResponse>(path.clone(), req)
.await?;
let early_break = res.entries.is_empty();
raw_entries.append(&mut res.entries);
if early_break {
break;
}
if let Some(next_key) = next_page_key(res.pagination) {
pagination = Some(create_pagination(next_key))
} else {
@@ -4,11 +4,9 @@
use crate::rpc::TendermintRpcClient;
use async_trait::async_trait;
use base64::Engine;
use cosmrs::tendermint;
use cosmrs::tendermint::{block::Height, evidence::Evidence, Hash};
use reqwest::header::HeaderMap;
use reqwest::{header, RequestBuilder};
use tendermint_rpc::dialect::{v0_34, v0_37, v0_38, LatestDialect};
use tendermint_rpc::{
client::CompatMode,
dialect::{self, Dialect},
@@ -23,21 +21,8 @@ macro_rules! perform_with_compat {
($self:expr, $request:expr) => {{
let request = $request;
match $self.compat {
CompatMode::V0_38 => {
$self
.perform_request_with_dialect(request, dialect::v0_38::Dialect)
.await
}
CompatMode::V0_37 => {
$self
.perform_request_with_dialect(request, dialect::v0_37::Dialect)
.await
}
CompatMode::V0_34 => {
$self
.perform_request_with_dialect(request, dialect::v0_34::Dialect)
.await
}
CompatMode::V0_37 => $self.perform_v0_37(request).await,
CompatMode::V0_34 => $self.perform_v0_34(request).await,
}
}};
}
@@ -85,11 +70,7 @@ impl ReqwestRpcClient {
.headers(headers)
}
async fn perform_request_with_dialect<R, S>(
&self,
request: R,
_dialect: S,
) -> Result<R::Output, Error>
async fn perform_request<R, S>(&self, request: R) -> Result<R::Output, Error>
where
R: SimpleRequest<S>,
S: Dialect,
@@ -100,25 +81,26 @@ impl ReqwestRpcClient {
.send()
.await
.map_err(TendermintRpcErrorMap::into_rpc_err)?;
let response_status = response.status();
let bytes = response
.bytes()
.await
.map_err(TendermintRpcErrorMap::into_rpc_err)?;
// Successful JSON-RPC requests are expected to return a 200 OK HTTP status.
// Otherwise, this means that the HTTP request failed as a whole,
// as opposed to the JSON-RPC request returning an error,
// and we cannot expect the response body to be a valid JSON-RPC response.
if response_status != reqwest::StatusCode::OK {
// hehe, that's so nasty but we have to somehow convert between different versions of the same lib
return Err(Error::http_request_failed(
response_status.as_u16().try_into().unwrap(),
));
}
R::Response::from_string(bytes).map(Into::into)
}
async fn perform_v0_34<R>(&self, request: R) -> Result<R::Output, Error>
where
R: SimpleRequest<dialect::v0_34::Dialect>,
{
self.perform_request(request).await
}
async fn perform_v0_37<R>(&self, request: R) -> Result<R::Output, Error>
where
R: SimpleRequest<dialect::v0_37::Dialect>,
{
self.perform_request(request).await
}
}
trait TendermintRpcErrorMap {
@@ -138,50 +120,18 @@ impl TendermintRpcClient for ReqwestRpcClient {
where
R: SimpleRequest,
{
self.perform_request_with_dialect(request, LatestDialect)
.await
self.perform_request(request).await
}
async fn block<H>(&self, height: H) -> Result<endpoint::block::Response, Error>
async fn block_results<H>(&self, height: H) -> Result<block_results::Response, Error>
where
H: Into<Height> + Send,
{
perform_with_compat!(self, endpoint::block::Request::new(height.into()))
perform_with_compat!(self, block_results::Request::new(height.into()))
}
async fn block_by_hash(
&self,
hash: tendermint::Hash,
) -> Result<endpoint::block_by_hash::Response, Error> {
perform_with_compat!(self, endpoint::block_by_hash::Request::new(hash))
}
async fn latest_block(&self) -> Result<endpoint::block::Response, Error> {
perform_with_compat!(self, endpoint::block::Request::default())
}
async fn block_results<H>(&self, height: H) -> Result<endpoint::block_results::Response, Error>
where
H: Into<Height> + Send,
{
perform_with_compat!(self, endpoint::block_results::Request::new(height.into()))
}
async fn latest_block_results(&self) -> Result<endpoint::block_results::Response, Error> {
perform_with_compat!(self, endpoint::block_results::Request::default())
}
async fn block_search(
&self,
query: Query,
page: u32,
per_page: u8,
order: Order,
) -> Result<endpoint::block_search::Response, Error> {
perform_with_compat!(
self,
endpoint::block_search::Request::new(query, page, per_page, order)
)
async fn latest_block_results(&self) -> Result<block_results::Response, Error> {
perform_with_compat!(self, block_results::Request::default())
}
async fn header<H>(&self, height: H) -> Result<endpoint::header::Response, Error>
@@ -190,26 +140,11 @@ impl TendermintRpcClient for ReqwestRpcClient {
{
let height = height.into();
match self.compat {
CompatMode::V0_38 => {
self.perform_request_with_dialect(
endpoint::header::Request::new(height),
v0_38::Dialect,
)
.await
}
CompatMode::V0_37 => {
self.perform_request_with_dialect(
endpoint::header::Request::new(height),
v0_37::Dialect,
)
.await
}
CompatMode::V0_37 => self.perform(endpoint::header::Request::new(height)).await,
CompatMode::V0_34 => {
// Back-fill with a request to /block endpoint and
// taking just the header from the response.
let resp = self
.perform_request_with_dialect(block::Request::new(height), v0_34::Dialect)
.await?;
let resp = self.perform_v0_34(block::Request::new(height)).await?;
Ok(resp.into())
}
}
@@ -217,25 +152,12 @@ impl TendermintRpcClient for ReqwestRpcClient {
async fn header_by_hash(&self, hash: Hash) -> Result<header_by_hash::Response, Error> {
match self.compat {
CompatMode::V0_38 => {
self.perform_request_with_dialect(
header_by_hash::Request::new(hash),
v0_38::Dialect,
)
.await
}
CompatMode::V0_37 => {
self.perform_request_with_dialect(
header_by_hash::Request::new(hash),
v0_37::Dialect,
)
.await
}
CompatMode::V0_37 => self.perform(header_by_hash::Request::new(hash)).await,
CompatMode::V0_34 => {
// Back-fill with a request to /block_by_hash endpoint and
// taking just the header from the response.
let resp = self
.perform_request_with_dialect(block_by_hash::Request::new(hash), v0_34::Dialect)
.perform_v0_34(block_by_hash::Request::new(hash))
.await?;
Ok(resp.into())
}
@@ -245,18 +167,8 @@ impl TendermintRpcClient for ReqwestRpcClient {
/// `/broadcast_evidence`: broadcast an evidence.
async fn broadcast_evidence(&self, e: Evidence) -> Result<evidence::Response, Error> {
match self.compat {
CompatMode::V0_38 => {
self.perform_request_with_dialect(evidence::Request::new(e), v0_38::Dialect)
.await
}
CompatMode::V0_37 => {
self.perform_request_with_dialect(evidence::Request::new(e), v0_37::Dialect)
.await
}
CompatMode::V0_34 => {
self.perform_request_with_dialect(evidence::Request::new(e), v0_34::Dialect)
.await
}
CompatMode::V0_37 => self.perform(evidence::Request::new(e)).await,
CompatMode::V0_34 => self.perform_v0_34(evidence::Request::new(e)).await,
}
}
@@ -113,10 +113,6 @@ impl Role {
pub fn is_standby(&self) -> bool {
matches!(self, Role::Standby)
}
pub fn is_mixnode(&self) -> bool {
matches!(self, Role::Layer1 | Role::Layer2 | Role::Layer3)
}
}
impl Display for Role {
+1 -1
View File
@@ -19,7 +19,7 @@ use std::error::Error;
// `SELECT total_tickets, used_tickets FROM ecash_ticketbook WHERE expiration_date >= ?`, today_date
// then for each calculate the diff total_tickets - used_tickets and multiply the result by the size of the ticket
#[async_trait]
pub trait Storage: Clone + Send + Sync {
pub trait Storage: Send + Sync {
type StorageError: Error;
async fn close(&self);
-7
View File
@@ -15,7 +15,6 @@ async-trait = { workspace = true }
reqwest = { workspace = true, features = ["json"] }
http.workspace = true
url = { workspace = true }
once_cell = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
thiserror = { workspace = true }
@@ -23,13 +22,7 @@ tracing = { workspace = true }
nym-bin-common = { path = "../bin-common" }
[target."cfg(not(target_arch = \"wasm32\"))".dependencies]
hickory-resolver = { workspace = true, features = ["dns-over-https-rustls", "webpki-roots"] }
# for request timeout until https://github.com/seanmonstar/reqwest/issues/1135 is fixed
[target."cfg(target_arch = \"wasm32\")".dependencies.wasmtimer]
workspace = true
features = ["tokio"]
[dev-dependencies]
tokio = { workspace = true, features=["rt", "macros"] }
-177
View File
@@ -1,177 +0,0 @@
//! DNS resolver configuration for internal lookups.
//!
//! The resolver itself is the set combination of the google, cloudflare, and quad9 endpoints
//! supporting DoH and DoT.
//!
//! This resolver implements a fallback mechanism where, should the DNS-over-TLS resolution fail, a
//! followup resolution will be done using the hosts configured default (e.g. `/etc/resolve.conf` on
//! linux).
//!
//! Requires the `dns-over-https-rustls`, `webpki-roots` feature for the
//! `hickory-resolver` crate
#![deny(missing_docs)]
use crate::ClientBuilder;
use std::{net::SocketAddr, sync::Arc};
use hickory_resolver::lookup_ip::LookupIp;
use hickory_resolver::{
config::{LookupIpStrategy, NameServerConfigGroup, ResolverConfig, ResolverOpts},
error::ResolveError,
lookup_ip::LookupIpIntoIter,
TokioAsyncResolver,
};
use once_cell::sync::OnceCell;
use reqwest::dns::{Addrs, Name, Resolve, Resolving};
use tracing::warn;
impl ClientBuilder {
/// Override the DNS resolver implementation used by the underlying http client.
pub fn dns_resolver<R: Resolve + 'static>(mut self, resolver: Arc<R>) -> Self {
self.reqwest_client_builder = self.reqwest_client_builder.dns_resolver(resolver);
self
}
}
struct SocketAddrs {
iter: LookupIpIntoIter,
}
#[derive(Debug, thiserror::Error)]
#[error("hickory-dns resolver error: {hickory_error}")]
pub struct HickoryDnsError {
#[from]
hickory_error: ResolveError,
}
/// Wrapper around an `AsyncResolver`, which implements the `Resolve` trait.
#[derive(Debug, Default, Clone)]
pub struct HickoryDnsResolver {
/// Since we might not have been called in the context of a
/// Tokio Runtime in initialization, so we must delay the actual
/// construction of the resolver.
state: Arc<OnceCell<TokioAsyncResolver>>,
fallback: Arc<OnceCell<TokioAsyncResolver>>,
}
impl Resolve for HickoryDnsResolver {
fn resolve(&self, name: Name) -> Resolving {
let resolver = self.state.clone();
let fallback = self.fallback.clone();
Box::pin(async move {
let resolver = resolver.get_or_try_init(new_resolver)?;
// try the primary DNS resolver that we set up (DoH or DoT or whatever)
let lookup = match resolver.lookup_ip(name.as_str()).await {
Ok(res) => res,
Err(e) => {
// on failure use the fall back system configured DNS resolver
warn!("primary DNS failed w/ error {e}: using system fallback");
let resolver = fallback.get_or_try_init(new_resolver_system)?;
resolver.lookup_ip(name.as_str()).await?
}
};
let addrs: Addrs = Box::new(SocketAddrs {
iter: lookup.into_iter(),
});
Ok(addrs)
})
}
}
impl Iterator for SocketAddrs {
type Item = SocketAddr;
fn next(&mut self) -> Option<Self::Item> {
self.iter.next().map(|ip_addr| SocketAddr::new(ip_addr, 0))
}
}
impl HickoryDnsResolver {
/// Attempt to resolve a domain name to a set of ['IpAddr']s
pub async fn resolve_str(&self, name: &str) -> Result<LookupIp, HickoryDnsError> {
let resolver = self.state.get_or_try_init(new_resolver)?;
// try the primary DNS resolver that we set up (DoH or DoT or whatever)
let lookup = match resolver.lookup_ip(name).await {
Ok(res) => res,
Err(e) => {
// on failure use the fall back system configured DNS resolver
warn!("primary DNS failed w/ error {e}: using system fallback");
let resolver = self.fallback.get_or_try_init(new_resolver_system)?;
resolver.lookup_ip(name).await?
}
};
Ok(lookup)
}
}
/// Create a new resolver with a custom DoT based configuration. The options are overridden to look
/// up for both IPv4 and IPv6 addresses to work with "happy eyeballs" algorithm.
fn new_resolver() -> Result<TokioAsyncResolver, HickoryDnsError> {
let mut name_servers = NameServerConfigGroup::google_tls();
name_servers.merge(NameServerConfigGroup::google_https());
// name_servers.merge(NameServerConfigGroup::google_h3());
name_servers.merge(NameServerConfigGroup::quad9_tls());
name_servers.merge(NameServerConfigGroup::quad9_https());
name_servers.merge(NameServerConfigGroup::cloudflare_tls());
name_servers.merge(NameServerConfigGroup::cloudflare_https());
let config = ResolverConfig::from_parts(None, Vec::new(), name_servers);
let mut opts = ResolverOpts::default();
opts.ip_strategy = LookupIpStrategy::Ipv4AndIpv6;
// Would like to enable this when 0.25 stabilizes
// opts.server_ordering_strategy = ServerOrderingStrategy::RoundRobin;
Ok(TokioAsyncResolver::tokio(config, opts))
}
/// Create a new resolver with the default configuration, which reads from the system DNS config
/// (i.e. `/etc/resolve.conf` in unix). The options are overridden to look up for both IPv4 and IPv6
/// addresses to work with "happy eyeballs" algorithm.
fn new_resolver_system() -> Result<TokioAsyncResolver, HickoryDnsError> {
let (config, mut opts) = hickory_resolver::system_conf::read_system_conf()?;
opts.ip_strategy = LookupIpStrategy::Ipv4AndIpv6;
Ok(TokioAsyncResolver::tokio(config, opts))
}
#[cfg(test)]
mod test {
use super::*;
#[tokio::test]
async fn reqwest_hickory_doh() {
let resolver = HickoryDnsResolver::default();
let client = reqwest::ClientBuilder::new()
.dns_resolver(resolver.into())
.build()
.unwrap();
let resp = client
.get("http://ifconfig.me:80")
.send()
.await
.unwrap()
.bytes()
.await
.unwrap();
assert!(!resp.is_empty());
}
#[tokio::test]
async fn dns_lookup() -> Result<(), HickoryDnsError> {
let resolver = HickoryDnsResolver::default();
let domain = "ifconfig.me";
let addrs = resolver.resolve_str(domain).await?;
assert!(addrs.into_iter().next().is_some());
Ok(())
}
}
+4 -17
View File
@@ -6,23 +6,17 @@ use reqwest::header::HeaderValue;
use reqwest::{RequestBuilder, Response, StatusCode};
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use std::fmt::Display;
use std::time::Duration;
use thiserror::Error;
use tracing::{instrument, warn};
use url::Url;
#[cfg(not(target_arch = "wasm32"))]
use std::sync::Arc;
use std::{fmt::Display, time::Duration};
pub use reqwest::IntoUrl;
mod user_agent;
pub use user_agent::UserAgent;
#[cfg(not(target_arch = "wasm32"))]
mod dns;
#[cfg(not(target_arch = "wasm32"))]
pub use dns::HickoryDnsResolver;
mod user_agent;
// The timeout is relatively high as we are often making requests over the mixnet, where latency is
// high and chatty protocols take a while to complete.
@@ -92,18 +86,11 @@ impl ClientBuilder {
// TODO: or should we maybe default to https?
Self::new(alt)
} else {
#[cfg(target_arch = "wasm32")]
let reqwest_client_builder = reqwest::ClientBuilder::new();
#[cfg(not(target_arch = "wasm32"))]
let reqwest_client_builder =
reqwest::ClientBuilder::new().dns_resolver(Arc::new(HickoryDnsResolver::default()));
Ok(ClientBuilder {
url: url.into_url()?,
timeout: None,
custom_user_agent: false,
reqwest_client_builder,
reqwest_client_builder: reqwest::ClientBuilder::new(),
})
}
}
+1 -1
View File
@@ -67,7 +67,7 @@ impl Default for ClientStatsReport {
pub struct OsInformation {
pub(crate) os_type: String,
pub(crate) os_version: Option<String>,
pub(crate) os_arch: String,
pub(crate) os_arch: Option<String>,
}
impl OsInformation {
+11 -8
View File
@@ -401,17 +401,20 @@ impl NymTopology {
});
};
// a 'valid' egress is one that is currently **not** acting as a mixnode
// a 'valid' egress is one assigned to either entry role (i.e. entry for another client)
// or exit role (as a service provider)
if !ignore_epoch_roles {
if let Some(role) = self.rewarded_set.role(node.node_id) {
if role.is_mixnode() {
return Err(NymTopologyError::InvalidEgressRole {
node_identity: Box::new(node_identity),
});
}
let Some(role) = self.rewarded_set.role(node.node_id) else {
return Err(NymTopologyError::InvalidEgressRole {
node_identity: Box::new(node_identity),
});
};
if !matches!(role, Role::EntryGateway | Role::ExitGateway) {
return Err(NymTopologyError::InvalidEgressRole {
node_identity: Box::new(node_identity),
});
}
}
Ok(node)
}
@@ -390,6 +390,7 @@ pub struct TopologyWasm {
/// Specifies whether this client should attempt to retrieve all available network nodes
/// as opposed to just active mixnodes/gateways.
/// Useless without `ignore_epoch_roles = true`
pub use_extended_topology: bool,
/// Specifies whether this client should ignore the current epoch role of the target egress node
@@ -274,6 +274,7 @@ pub struct TopologyWasmOverride {
/// Specifies whether this client should attempt to retrieve all available network nodes
/// as opposed to just active mixnodes/gateways.
/// Useless without `ignore_epoch_roles = true`
#[tsify(optional)]
pub use_extended_topology: Option<bool>,
+3 -3
View File
@@ -10,7 +10,7 @@ use nym_client_core::client::base_client::storage::GatewaysDetailsStore;
use nym_client_core::client::replies::reply_storage::browser_backend;
use nym_client_core::config;
use nym_client_core::error::ClientCoreError;
use nym_client_core::init::helpers::gateways_for_init;
use nym_client_core::init::helpers::current_gateways;
use nym_client_core::init::types::GatewaySelectionSpecification;
use nym_client_core::init::{
self, setup_gateway,
@@ -134,7 +134,7 @@ pub async fn setup_gateway_from_api(
minimum_performance: u8,
) -> Result<InitialisationResult, WasmCoreError> {
let mut rng = thread_rng();
let gateways = gateways_for_init(&mut rng, nym_apis, None, minimum_performance).await?;
let gateways = current_gateways(&mut rng, nym_apis, None, minimum_performance).await?;
setup_gateway_wasm(client_store, force_tls, chosen_gateway, gateways).await
}
@@ -144,7 +144,7 @@ pub async fn current_gateways_wasm(
minimum_performance: u8,
) -> Result<Vec<RoutingNode>, ClientCoreError> {
let mut rng = thread_rng();
gateways_for_init(&mut rng, nym_apis, user_agent, minimum_performance).await
current_gateways(&mut rng, nym_apis, user_agent, minimum_performance).await
}
pub async fn setup_from_topology(
+15 -18
View File
@@ -4,15 +4,13 @@
use crate::error::WasmCoreError;
use crate::storage::wasm_client_traits::{v1, v2, WasmClientStorage};
use async_trait::async_trait;
use js_sys::Promise;
use js_sys::{Array, Promise};
use serde::de::DeserializeOwned;
use serde::Serialize;
use wasm_bindgen::prelude::*;
use wasm_bindgen_futures::future_to_promise;
use wasm_storage::traits::BaseWasmStorage;
use wasm_storage::{
Build, Database, RawDbResult, TryFromJs, TryToJs, VersionChangeEvent, WasmStorage,
};
use wasm_storage::{IdbVersionChangeEvent, WasmStorage};
use wasm_utils::error::{simple_js_error, PromisableResult};
use zeroize::Zeroizing;
@@ -46,29 +44,28 @@ impl ClientStorage {
// special care must be taken on JS side to ensure it's correctly used there.
let passphrase = Zeroizing::new(passphrase);
let migrate_fn = Some(|evt: VersionChangeEvent, db: Database| -> RawDbResult<()> {
let migrate_fn = Some(|evt: &IdbVersionChangeEvent| -> Result<(), JsValue> {
// Even if the web-sys bindings expose the version as a f64, the IndexedDB API
// works with an unsigned integer.
// See <https://github.com/rustwasm/wasm-bindgen/issues/1149>
let old_version = evt.old_version() as u32;
let db = evt.db();
if old_version < 1 {
// migrating to version 2
db.create_object_store(v1::KEYS_STORE).build()?;
db.create_object_store(v1::CORE_STORE).build()?;
db.create_object_store(v1::KEYS_STORE)?;
db.create_object_store(v1::CORE_STORE)?;
db.create_object_store(v2::GATEWAY_REGISTRATIONS_ACTIVE_GATEWAY_STORE)
.build()?;
db.create_object_store(v2::GATEWAY_REGISTRATIONS_REGISTERED_GATEWAYS_STORE)
.build()?;
db.create_object_store(v2::GATEWAY_REGISTRATIONS_ACTIVE_GATEWAY_STORE)?;
db.create_object_store(v2::GATEWAY_REGISTRATIONS_REGISTERED_GATEWAYS_STORE)?;
return Ok(());
}
// version 1 -> unimplemented migration
if old_version < 2 {
return Err(simple_js_error("this client is incompatible with existing storage. please initialise it again.").into());
return Err(simple_js_error("this client is incompatible with existing storage. please initialise it again."));
}
Ok(())
@@ -115,7 +112,7 @@ impl BaseWasmStorage for ClientStorage {
async fn read_value<T, K>(&self, store: &str, key: K) -> Result<Option<T>, Self::StorageError>
where
T: DeserializeOwned,
K: TryToJs,
K: JsCast,
{
Ok(self.inner.read_value(store, key).await?)
}
@@ -128,33 +125,33 @@ impl BaseWasmStorage for ClientStorage {
) -> Result<(), Self::StorageError>
where
T: Serialize,
K: TryToJs + TryFromJs,
K: JsCast,
{
Ok(self.inner.store_value(store, key, value).await?)
}
async fn remove_value<K>(&self, store: &str, key: K) -> Result<(), Self::StorageError>
where
K: TryToJs,
K: JsCast,
{
Ok(self.inner.remove_value(store, key).await?)
}
async fn has_value<K>(&self, store: &str, key: K) -> Result<bool, Self::StorageError>
where
K: TryToJs,
K: JsCast,
{
Ok(self.inner.has_value(store, key).await?)
}
async fn key_count<K>(&self, store: &str, key: K) -> Result<u32, Self::StorageError>
where
K: TryToJs,
K: JsCast,
{
Ok(self.inner.key_count(store, key).await?)
}
async fn get_all_keys(&self, store: &str) -> Result<Vec<JsValue>, Self::StorageError> {
async fn get_all_keys(&self, store: &str) -> Result<Array, Self::StorageError> {
Ok(self.inner.get_all_keys(store).await?)
}
}
@@ -286,8 +286,8 @@ pub trait WasmClientStorage: BaseWasmStorage {
.await
.map_err(Into::into)
.map(|arr| {
arr.iter()
.cloned()
arr.to_vec()
.into_iter()
.filter_map(|key| key.as_string())
.collect()
})
+1 -1
View File
@@ -9,7 +9,7 @@ license.workspace = true
[dependencies]
async-trait = { workspace = true }
getrandom = { workspace = true, features = ["js"] }
futures = { workspace = true }
js-sys = { workspace = true }
wasm-bindgen = { workspace = true }
serde = { workspace = true, features = ["derive"] }
+16 -17
View File
@@ -1,6 +1,7 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use indexed_db_futures::web_sys::DomException;
use serde_wasm_bindgen::Error;
use thiserror::Error;
use wasm_bindgen::JsValue;
@@ -11,11 +12,15 @@ pub enum StorageError {
#[error("{0}")]
Json(String),
#[error("storage failure: {message}")]
InternalStorageFailure { message: String },
#[error("failed to open the db file: {message}")]
DbOpenFailure { message: String },
#[error("DomException {name} ({code}): {message}")]
DomException {
/// DomException code
code: u16,
/// Specific name of the DomException
name: String,
/// Message given to the DomException
message: String,
},
#[error("FATAL ERROR: storage key is somehow present {count} times in the table!")]
DuplicateKey { count: u32 },
@@ -41,18 +46,12 @@ impl From<StorageError> for JsValue {
}
}
impl From<indexed_db_futures::error::Error> for StorageError {
fn from(value: indexed_db_futures::error::Error) -> Self {
StorageError::InternalStorageFailure {
message: value.to_string(),
}
}
}
impl From<indexed_db_futures::error::OpenDbError> for StorageError {
fn from(value: indexed_db_futures::error::OpenDbError) -> Self {
StorageError::DbOpenFailure {
message: value.to_string(),
impl From<DomException> for StorageError {
fn from(value: DomException) -> StorageError {
StorageError::DomException {
name: value.name(),
message: value.message(),
code: value.code(),
}
}
}
+57 -74
View File
@@ -1,9 +1,8 @@
// Copyright 2023-2025 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::cipher_export::StoredExportedStoreCipher;
use crate::error::StorageError;
use indexed_db_futures::transaction::TransactionMode;
use nym_store_cipher::{
Aes256Gcm, Algorithm, EncryptedData, KdfInfo, KeySizeUser, Params, StoreCipher, Unsigned,
Version,
@@ -14,10 +13,7 @@ use std::future::IntoFuture;
use wasm_bindgen::JsValue;
use wasm_utils::console_log;
pub use indexed_db_futures::database::{Database, VersionChangeEvent};
pub use indexed_db_futures::prelude::*;
pub use indexed_db_futures::primitive::{TryFromJs, TryToJs};
pub use indexed_db_futures::Result as RawDbResult;
mod cipher_export;
pub mod error;
@@ -58,29 +54,31 @@ impl WasmStorage {
passphrase: Option<&[u8]>,
) -> Result<Self, StorageError>
where
F: Fn(VersionChangeEvent, Database) -> RawDbResult<()> + 'static,
F: Fn(&IdbVersionChangeEvent) -> Result<(), JsValue> + 'static,
{
let mut db_req: OpenDbRequest = IdbDatabase::open_u32(db_name, version)?;
// we must always ensure the cipher table is present
let db = Database::open(db_name)
.with_version(version)
.with_on_upgrade_needed(move |event, db| {
db_req.set_on_upgrade_needed(Some(
move |evt: &IdbVersionChangeEvent| -> Result<(), JsValue> {
// Even if the web-sys bindings expose the version as a f64, the IndexedDB API
// works with an unsigned integer.
// See <https://github.com/rustwasm/wasm-bindgen/issues/1149>
let old_version = event.old_version() as u32;
let old_version = evt.old_version() as u32;
if old_version < 1 {
db.create_object_store(CIPHER_INFO_STORE).build()?;
evt.db().create_object_store(CIPHER_INFO_STORE)?;
}
if let Some(migrate) = migrate_fn.as_ref() {
migrate(event, db)
migrate(evt)
} else {
Ok(())
}
})
.await?;
},
));
let db: IdbDatabase = db_req.into_future().await?;
let inner = IdbWrapper(db);
let store_cipher = inner.setup_store_cipher(passphrase).await?;
@@ -96,12 +94,13 @@ impl WasmStorage {
}
pub async fn remove(db_name: &str) -> Result<(), StorageError> {
Database::delete_by_name(db_name)?.into_future().await?;
IdbDatabase::delete_by_name(db_name)?.into_future().await?;
Ok(())
}
pub async fn exists(db_name: &str) -> Result<bool, StorageError> {
let db = Database::open(db_name).await?;
let db_req: OpenDbRequest = IdbDatabase::open(db_name)?;
let db: IdbDatabase = db_req.into_future().await?;
// if the db was already created before, at the very least cipher info store should exist,
// thus the iterator should return at least one value
@@ -140,7 +139,7 @@ impl WasmStorage {
pub async fn read_value<T, K>(&self, store: &str, key: K) -> Result<Option<T>, StorageError>
where
T: DeserializeOwned,
K: TryToJs,
K: wasm_bindgen::JsCast,
{
self.inner
.read_value_raw(store, key)
@@ -157,7 +156,7 @@ impl WasmStorage {
) -> Result<(), StorageError>
where
T: Serialize,
K: TryToJs + TryFromJs,
K: wasm_bindgen::JsCast,
{
self.inner
.store_value_raw(store, key, &self.serialize_value(&value)?)
@@ -166,14 +165,14 @@ impl WasmStorage {
pub async fn remove_value<K>(&self, store: &str, key: K) -> Result<(), StorageError>
where
K: TryToJs,
K: wasm_bindgen::JsCast,
{
self.inner.remove_value_raw(store, key).await
}
pub async fn has_value<K>(&self, store: &str, key: K) -> Result<bool, StorageError>
where
K: TryToJs,
K: wasm_bindgen::JsCast,
{
match self.key_count(store, key).await? {
0 => Ok(false),
@@ -184,98 +183,82 @@ impl WasmStorage {
pub async fn key_count<K>(&self, store: &str, key: K) -> Result<u32, StorageError>
where
K: TryToJs,
K: wasm_bindgen::JsCast,
{
self.inner.get_key_count(store, key).await
}
pub async fn get_all_keys(&self, store: &str) -> Result<Vec<JsValue>, StorageError> {
pub async fn get_all_keys(&self, store: &str) -> Result<js_sys::Array, StorageError> {
self.inner.get_all_keys(store).await
}
}
struct IdbWrapper(Database);
struct IdbWrapper(IdbDatabase);
impl IdbWrapper {
async fn read_value_raw<K>(&self, store: &str, key: K) -> Result<Option<JsValue>, StorageError>
where
K: TryToJs,
K: wasm_bindgen::JsCast,
{
self.0
.transaction(store)
.with_mode(TransactionMode::Readonly)
.build()?
.transaction_on_one_with_mode(store, IdbTransactionMode::Readonly)?
.object_store(store)?
.get(&key)
.primitive()?
.get(&key)?
.await
.map_err(Into::into)
}
async fn store_value_raw<K, T>(
async fn store_value_raw<K>(
&self,
store: &str,
key: K,
value: &T,
value: &JsValue,
) -> Result<(), StorageError>
where
K: TryToJs + TryFromJs,
T: TryToJs,
K: wasm_bindgen::JsCast,
{
let tx = self
.0
.transaction(store)
.with_mode(TransactionMode::Readwrite)
.build()?;
let store = tx.object_store(store)?;
store.put(value).with_key(key).primitive()?.await?;
tx.commit().await.map_err(Into::into)
self.0
.transaction_on_one_with_mode(store, IdbTransactionMode::Readwrite)?
.object_store(store)?
.put_key_val_owned(key, value)?
.into_future()
.await
.map_err(Into::into)
}
async fn remove_value_raw<K>(&self, store: &str, key: K) -> Result<(), StorageError>
where
K: TryToJs,
{
let tx = self
.0
.transaction(store)
.with_mode(TransactionMode::Readwrite)
.build()?;
let store = tx.object_store(store)?;
store.delete(key).primitive()?.await?;
tx.commit().await.map_err(Into::into)
}
async fn get_key_count<K>(&self, store: &str, key: K) -> Result<u32, StorageError>
where
K: TryToJs,
K: wasm_bindgen::JsCast,
{
self.0
.transaction(store)
.with_mode(TransactionMode::Readonly)
.build()?
.transaction_on_one_with_mode(store, IdbTransactionMode::Readwrite)?
.object_store(store)?
.count()
.with_query(key)
.primitive()?
.delete_owned(key)?
.into_future()
.await
.map_err(Into::into)
}
async fn get_all_keys(&self, store: &str) -> Result<Vec<JsValue>, StorageError> {
async fn get_key_count<K>(&self, store: &str, key: K) -> Result<u32, StorageError>
where
K: wasm_bindgen::JsCast,
{
self.0
.transaction(store)
.with_mode(TransactionMode::Readonly)
.build()?
.transaction_on_one_with_mode(store, IdbTransactionMode::Readwrite)?
.object_store(store)?
.get_all_keys()
.primitive()?
.await?
.collect::<Result<Vec<_>, _>>()
.count_with_key_owned(key)?
.into_future()
.await
.map_err(Into::into)
}
async fn get_all_keys(&self, store: &str) -> Result<js_sys::Array, StorageError> {
self.0
.transaction_on_one_with_mode(store, IdbTransactionMode::Readonly)?
.object_store(store)?
.get_all_keys()?
.into_future()
.await
.map_err(Into::into)
}
+13 -14
View File
@@ -3,11 +3,10 @@
use crate::WasmStorage;
use async_trait::async_trait;
use indexed_db_futures::primitive::{TryFromJs, TryToJs};
use js_sys::Array;
use serde::de::DeserializeOwned;
use serde::Serialize;
use std::error::Error;
use wasm_bindgen::JsValue;
#[async_trait(?Send)]
pub trait BaseWasmStorage {
@@ -18,7 +17,7 @@ pub trait BaseWasmStorage {
async fn read_value<T, K>(&self, store: &str, key: K) -> Result<Option<T>, Self::StorageError>
where
T: DeserializeOwned,
K: TryToJs;
K: wasm_bindgen::JsCast;
async fn store_value<T, K>(
&self,
@@ -28,21 +27,21 @@ pub trait BaseWasmStorage {
) -> Result<(), Self::StorageError>
where
T: Serialize,
K: TryToJs + TryFromJs;
K: wasm_bindgen::JsCast;
async fn remove_value<K>(&self, store: &str, key: K) -> Result<(), Self::StorageError>
where
K: TryToJs;
K: wasm_bindgen::JsCast;
async fn has_value<K>(&self, store: &str, key: K) -> Result<bool, Self::StorageError>
where
K: TryToJs;
K: wasm_bindgen::JsCast;
async fn key_count<K>(&self, store: &str, key: K) -> Result<u32, Self::StorageError>
where
K: TryToJs;
K: wasm_bindgen::JsCast;
async fn get_all_keys(&self, store: &str) -> Result<Vec<JsValue>, Self::StorageError>;
async fn get_all_keys(&self, store: &str) -> Result<js_sys::Array, Self::StorageError>;
}
#[async_trait(?Send)]
@@ -56,7 +55,7 @@ impl BaseWasmStorage for WasmStorage {
async fn read_value<T, K>(&self, store: &str, key: K) -> Result<Option<T>, Self::StorageError>
where
T: DeserializeOwned,
K: TryToJs,
K: wasm_bindgen::JsCast,
{
self.read_value(store, key).await
}
@@ -69,33 +68,33 @@ impl BaseWasmStorage for WasmStorage {
) -> Result<(), Self::StorageError>
where
T: Serialize,
K: TryToJs + TryFromJs,
K: wasm_bindgen::JsCast,
{
self.store_value(store, key, value).await
}
async fn remove_value<K>(&self, store: &str, key: K) -> Result<(), Self::StorageError>
where
K: TryToJs,
K: wasm_bindgen::JsCast,
{
self.remove_value(store, key).await
}
async fn has_value<K>(&self, store: &str, key: K) -> Result<bool, Self::StorageError>
where
K: TryToJs,
K: wasm_bindgen::JsCast,
{
self.has_value(store, key).await
}
async fn key_count<K>(&self, store: &str, key: K) -> Result<u32, Self::StorageError>
where
K: TryToJs,
K: wasm_bindgen::JsCast,
{
self.key_count(store, key).await
}
async fn get_all_keys(&self, store: &str) -> Result<Vec<JsValue>, Self::StorageError> {
async fn get_all_keys(&self, store: &str) -> Result<Array, Self::StorageError> {
self.get_all_keys(store).await
}
}
@@ -10,7 +10,7 @@ import Stack from '@mui/material/Stack';
import Paper from '@mui/material/Paper';
import type { SetupMixFetchOps } from '@nymproject/mix-fetch';
const defaultUrl = 'https://nym.com/favicon.svg';
const defaultUrl = 'https://nymtech.net/favicon.svg';
const args = { mode: 'unsafe-ignore-cors' };
const mixFetchOptions: SetupMixFetchOps = {
@@ -1,9 +0,0 @@
import { Callout } from 'nextra/components';
### Terms & Conditions
<Callout type="info" emoji="️">
From `nym-node` version `1.1.3` onward is required to accept [**Operators Terms & Conditions**](https://nymtech.net/terms-and-conditions/operators/v1.0.0) in order to be part of the active set. Make sure to read them before you add the flag.
</Callout>
There has been a long ongoing discussion whether and how to apply Terms and Conditions for Nym network operators, with an aim to stay aligned with the philosophy of Free Software and provide legal defense for both node operators and Nym developers. To understand better the reasoning behind this decision, you can listen to the first [Nym Operator Town Hall](https://www.youtube.com/live/7hwb8bAZIuc?si=3mQ2ed7AyUA1SsCp&t=915) introducing the T&Cs or to [Operator AMA with CEO Harry Halpin](https://www.youtube.com/watch?v=yIN-zYQw0I0) from June 4th, 2024, explaining pros and cons of T&Cs implementation.
@@ -1 +1 @@
Thursday, January 23rd 2025, 10:41:32 UTC
Wednesday, January 8th 2025, 15:01:28 UTC
+25 -20
View File
@@ -1,21 +1,26 @@
**ISP**,**Locations**,**Public IPv6**,**Crypto Payments**,**Comments**,**Last Updated**
[Flokinet](https://flokinet.is),"Netherlands, Iceland, Romania,France","Yes, needs a ticket and custom setup","yes, including XMR",Very slow customer support,05/2024
[BitLaunch](https://bitlaunch.io),"Canada, USA, UK",No,Yes,Expensive. Digial Ocean through BitLanch has IPv6,05/2024
[Hostinger](https://hostinger.com),"France, Lithuania, India, USA, Brazil","Yes, out of the box",Yes,Crypto payments must be done per each server monthly or annually.,05/2024
[Linode](https://linode.com),"USA, Canada, Japan, India, Indonesia, Sweden, Netherlands, Germany, Brazil, France, UK, Australia, Italy",Yes out of the box,"No, only through [BitLAunch](https://bitlaunch.io)","IPv6 sometimes need to be re-added in Networking tab, no reboot needed",05/2024
[Cherry Servers](https://www.cherryservers.com),"Lithuania, Netherlands, USA, Singapore",No,Yes,Issued IP doesnt match the location offered by the provider.,05/2024
[Njalla](https://nja.la),Sweden,Yes,Yes,"Privacy vandguards! The biggest VPS 45 is 3 cores only, but it works better than many “larger” servers on the market.",05/2024
[HostSailor](https://hostsailor.com),USA,"Yes, based on ticket",Yes,The IPv6 setup needs custom research and is not documented,05/2024
[Misaka](https://www.misaka.io/),South Africa,"Yes, native support",No,Very Expensive,05/2024
[IsHosting](https://ishosting.com/en),"Brazil, Netherlands","Yes, based on ticket",Yes,Expensive,05/2024
[AlexHost](https://alexhost.com),"Moldova, Bulgaria, Sweden, Netherlands","Yes, on by default",Yes,"They allow TOR Bridges, Relays. Exit nodes are only allowed on dedicated servers (prices start from 26 EUR)",07/2024
[iHostArt](https://ihostart.com),Romania,"Yes, on by default",Yes,"Super permissive provider. They do allow Tor Exit/Relay/Bridge. Pro-free speech etc. Recently, IPv6 geolocation was set to North Korea, so be aware.",07/2024
[Incognet](https://incognet.io),Netherlands and USA,"Yes, on by default",Yes,They allow Tor exit nodes but you must adhere to their rules https://incognet.io/tor-exits,07/2024
[vSys Host](https://vsys.host),"Ukraine, Netherlands, USA","Yes, on by default",Yes,"Pretty permissive provider registered in Ukraine. Should allow Relay/Exit nodes but nothing in T&C, so better double check.",07/2024
[LiteServer](https://liteserver.nl),Netherlands,"Yes, on by default",Yes,Very reliable Dutch provider. They do allow Relay nodes but for Exit nodes you need to contact them. Always check T&C https://liteserver.nl/legal,07/2024
[TerraHost](https://terrahost.no),Norway,"Yes, on by default",Yes,Very reliable Norwegian provider. Only allow exit nodes on Dedicated servers subject to certain caveats (you must open a ticket). Always check T&C https://terrahost.no/avtalebetingelser,07/2024
[Mevspace](https://mevspace.com),Poland,"Yes, on by default",Yes,"Flexible Polish providers with 3 DCs in Poland. They do allow Tor Exit nodes but you may need a dedicated server for this. Make sure you open a ticket to check. As of today's date, they have 48h for 1 EUR tariff",07/2024
[Hostiko](https://hostiko.com.ua),"Ukraine, Germany","Yes, on by default",Yes,"Ukrainian provider. They allow Exit nodes on Germany boxes but limit the bandwidth, you also have to restrict certain ports like 25 and 587. Make sure you open a ticket.",07/2024
[Hostslick](https://hostslick.com),"Netherlands, Germany","Yes, on by default",Yes,Good amount of bandwidth for the price. Make sure you open the ticket if you want to run Exit node,07/2024
[RDP](https://rdp.sh),"Netherlands, USA, Poland","Yes, on by default",Yes,"German provider. Exit nodes are allowed, policy is here https://rdp.sh/docs/faq/tor ports 25,465,587 must be closed. Make sure you open a ticket before running an exit node.",07/2024
[PQ.Hosting](https://bill.pq.hosting),"Netherlands, USA, UK, Moldova, France","Yes, for a small fee",Yes,Support claims that they allow everything. So far Nym nodes never had any problems,01/2025
[Flokinet](https://flokinet.is),"Netherlands, Iceland, Romania,France","Yes, needs a ticket and custom setup","yes, including XMR","Very slow customer support","05/2024"
[BitLaunch](https://bitlaunch.io),"Canada, USA, UK","No","Yes","Expensive. Digial Ocean through BitLanch has IPv6","05/2024"
[Hostinger](https://hostinger.com),"France, Lithuania, India, USA, Brazil","Yes, out of the box","Yes","Crypto payments must be done per each server monthly or annually.","05/2024"
[Linode](https://linode.com),"USA, Canada, Japan, India, Indonesia, Sweden, Netherlands, Germany, Brazil, France, UK, Australia, Italy","Yes out of the box","No, only through [BitLAunch](https://bitlaunch.io)","IPv6 sometimes need to be re-added in Networking tab, no reboot needed","05/2024"
[Cherry Servers](https://www.cherryservers.com),"Lithuania, Netherlands, USA, Singapore","No","Yes","Issued IP doesnt match the location offered by the provider.","05/2024"
[Njalla](https://nja.la),"Sweden","Yes","Yes","Privacy vandguards! The biggest VPS 45 is 3 cores only, but it works better than many “larger” servers on the market.","05/2024"
[HostSailor](https://hostsailor.com),"USA","Yes, based on ticket","Yes","The IPv6 setup needs custom research and is not documented","05/2024"
[Misaka](https://www.misaka.io/),"South Africa","Yes, native support","No","Very Expensive","05/2024"
[IsHosting](https://ishosting.com/en),"Brazil, Netherlands","Yes, based on ticket","Yes","Expensive","05/2024"
[AlexHost](https://alexhost.com),"Moldova, Bulgaria, Sweden, Netherlands","Yes, on by default","Yes","They allow TOR Bridges, Relays. Exit nodes are only allowed on dedicated servers (prices start from 26 EUR)","07/2024"
[iHostArt](https://ihostart.com),"Romania","Yes, on by default","Yes","Super permissive provider. They do allow Tor Exit/Relay/Bridge. Pro-free speech etc. Recently, IPv6 geolocation was set to North Korea, so be aware.","07/2024"
[Incognet](https://incognet.io),"Netherlands and USA","Yes, on by default","Yes","They allow Tor exit nodes but you must adhere to their rules https://incognet.io/tor-exits","07/2024"
[vSys Host](https://vsys.host),"Ukraine, Netherlands, USA","Yes, on by default","Yes","Pretty permissive provider registered in Ukraine. Should allow Relay/Exit nodes but nothing in T&C, so better double check.","07/2024"
[LiteServer](https://liteserver.nl),"Netherlands","Yes, on by default","Yes","Very reliable Dutch provider. They do allow Relay nodes but for Exit nodes you need to contact them. Always check T&C https://liteserver.nl/legal","07/2024"
[TerraHost](https://terrahost.no),"Norway","Yes, on by default","Yes","Very reliable Norwegian provider. Only allow exit nodes on Dedicated servers subject to certain caveats (you must open a ticket). Always check T&C https://terrahost.no/avtalebetingelser","07/2024"
[Mevspace](https://mevspace.com),"Poland","Yes, on by default","Yes","Flexible Polish providers with 3 DCs in Poland. They do allow Tor Exit nodes but you may need a dedicated server for this. Make sure you open a ticket to check. As of today's date, they have 48h for 1 EUR tariff","07/2024"
[Hostiko](https://hostiko.com.ua),"Ukraine, Germany","Yes, on by default","Yes","Ukrainian provider. They allow Exit nodes on Germany boxes but limit the bandwidth, you also have to restrict certain ports like 25 and 587. Make sure you open a ticket.","07/2024"
[Hostslick](https://hostslick.com),"Netherlands, Germany","Yes, on by default","Yes","Good amount of bandwidth for the price. Make sure you open the ticket if you want to run Exit node","07/2024"
[RDP](https://rdp.sh),"Netherlands, USA, Poland","Yes, on by default","Yes","German provider. Exit nodes are allowed, policy is here https://rdp.sh/docs/faq/tor ports 25,465,587 must be closed. Make sure you open a ticket before running an exit node.","07/2024"
1 **ISP** **Locations** **Public IPv6** **Crypto Payments** **Comments** **Last Updated**
2 [Flokinet](https://flokinet.is) Netherlands, Iceland, Romania,France Yes, needs a ticket and custom setup yes, including XMR Very slow customer support 05/2024
3 [BitLaunch](https://bitlaunch.io) Canada, USA, UK No Yes Expensive. Digial Ocean through BitLanch has IPv6 05/2024
4 [Hostinger](https://hostinger.com) France, Lithuania, India, USA, Brazil Yes, out of the box Yes Crypto payments must be done per each server monthly or annually. 05/2024
5 [Linode](https://linode.com) USA, Canada, Japan, India, Indonesia, Sweden, Netherlands, Germany, Brazil, France, UK, Australia, Italy Yes out of the box No, only through [BitLAunch](https://bitlaunch.io) IPv6 sometimes need to be re-added in Networking tab, no reboot needed 05/2024
6 [Cherry Servers](https://www.cherryservers.com) Lithuania, Netherlands, USA, Singapore No Yes Issued IP doesn’t match the location offered by the provider. 05/2024
7 [Njalla](https://nja.la) Sweden Yes Yes Privacy vandguards! The biggest VPS 45 is 3 cores only, but it works better than many “larger” servers on the market. 05/2024
8 [HostSailor](https://hostsailor.com) USA Yes, based on ticket Yes The IPv6 setup needs custom research and is not documented 05/2024
9 [Misaka](https://www.misaka.io/) South Africa Yes, native support No Very Expensive 05/2024
10 [IsHosting](https://ishosting.com/en) Brazil, Netherlands Yes, based on ticket Yes Expensive 05/2024
11 [AlexHost](https://alexhost.com) Moldova, Bulgaria, Sweden, Netherlands Yes, on by default Yes They allow TOR Bridges, Relays. Exit nodes are only allowed on dedicated servers (prices start from 26 EUR) 07/2024
12 [iHostArt](https://ihostart.com) Romania Yes, on by default Yes Super permissive provider. They do allow Tor Exit/Relay/Bridge. Pro-free speech etc. Recently, IPv6 geolocation was set to North Korea, so be aware. 07/2024
13 [Incognet](https://incognet.io) Netherlands and USA Yes, on by default Yes They allow Tor exit nodes but you must adhere to their rules https://incognet.io/tor-exits 07/2024
14 [vSys Host](https://vsys.host) Ukraine, Netherlands, USA Yes, on by default Yes Pretty permissive provider registered in Ukraine. Should allow Relay/Exit nodes but nothing in T&C, so better double check. 07/2024
15 [LiteServer](https://liteserver.nl) Netherlands Yes, on by default Yes Very reliable Dutch provider. They do allow Relay nodes but for Exit nodes you need to contact them. Always check T&C https://liteserver.nl/legal 07/2024
16 [TerraHost](https://terrahost.no) Norway Yes, on by default Yes Very reliable Norwegian provider. Only allow exit nodes on Dedicated servers subject to certain caveats (you must open a ticket). Always check T&C https://terrahost.no/avtalebetingelser 07/2024
17 [Mevspace](https://mevspace.com) Poland Yes, on by default Yes Flexible Polish providers with 3 DCs in Poland. They do allow Tor Exit nodes but you may need a dedicated server for this. Make sure you open a ticket to check. As of today's date, they have 48h for 1 EUR tariff 07/2024
18 [Hostiko](https://hostiko.com.ua) Ukraine, Germany Yes, on by default Yes Ukrainian provider. They allow Exit nodes on Germany boxes but limit the bandwidth, you also have to restrict certain ports like 25 and 587. Make sure you open a ticket. 07/2024
19 [Hostslick](https://hostslick.com) Netherlands, Germany Yes, on by default Yes Good amount of bandwidth for the price. Make sure you open the ticket if you want to run Exit node 07/2024
20 [RDP](https://rdp.sh) Netherlands, USA, Poland Yes, on by default Yes German provider. Exit nodes are allowed, policy is here https://rdp.sh/docs/faq/tor ports 25,465,587 must be closed. Make sure you open a ticket before running an exit node. 07/2024
21
22
23
24
25
26
+14
View File
@@ -497,6 +497,13 @@ const config = {
permanent: true,
basePath: false,
},
{
source: "/operators/testing/node-api-check.html",
destination:
"/docs/operators/nodes/performance-and-testing/node-api-check",
permanent: true,
basePath: false,
},
{
source: "/operators/testing/prometheus-grafana.html",
destination:
@@ -656,6 +663,13 @@ const config = {
permanent: true,
basePath: false,
},
{
source: "/docs/operators/testing/node-api-check",
destination:
"/docs/operators/nodes/performance-and-testing/node-api-check",
permanent: true,
basePath: false,
},
{
source: "/docs/operators/nodes/proxy-configuration",
destination:
+3 -3
View File
@@ -37,9 +37,9 @@
"@mui/material": "^5.14.8",
"@nextui-org/accordion": "^2.0.40",
"@nextui-org/react": "^2.4.8",
"@nymproject/contract-clients": ">=1.2.4-rc.2 || ^1",
"@nymproject/mix-fetch-full-fat": ">=1.2.4-rc.2 || ^1",
"@nymproject/sdk-full-fat": ">=1.2.4-rc.2 || ^1",
"@nymproject/contract-clients": ">=1.4.0-rc.0 || ^1",
"@nymproject/mix-fetch-full-fat": ">=1.4.0-rc.0 || ^1",
"@nymproject/sdk-full-fat": ">=1.4.0-rc.0 || ^1",
"@redocly/cli": "^1.25.15",
"@types/mdx": "^2.0.13",
"chain-registry": "^1.19.0",
@@ -3,6 +3,6 @@
The Explorer API is the backend for the [Mixnet Explorer](https://explorer.nymtech.net/).
**This will soon be deprecated in favour of the [Node Status API](ns-api.mdx).**
**This will soon be deprecated in favour of the [Node Status API](../ns-api).**
The code for this service can be found [in our monorepo](https://github.com/nymtech/nym/tree/develop/explorer).
@@ -16,7 +16,7 @@ You can check that your binaries are properly compiled with:
You can check the necessary parameters for the available commands by running:
```
./nym-socks5-client <COMMAND> --help
./nym-client <COMMAND> --help
```
## Initialising a new client instance
@@ -4,7 +4,7 @@
The `nym-cli` binary can be built by running `cargo build --release` in the `nym/tools/nym-cli` directory.
## Usage
See the [commands](commands.mdx) page for an overview of all command options.
See the [commands](./nym-cli/commands.mdx) page for an overview of all command options.
## Staking on someone's behalf (for custodians)
@@ -17,6 +17,8 @@ Sounds great, are there any catches? Well, there are a few (for now):
- For now, `mixFetch` cannot deal with concurrent requests with the same base URL.
Read [this article](https://blog.nym.com/mixfetch-like-the-fetch-api-but-via-the-mixnet-82acfd435c62) to learn more.
<Callout type="info" emoji="️">
Right now Gateways are not required to run a Secure Websocket (WSS) listener, so only a subset of nodes running in Gateway mode have configured their nodes to do so.
@@ -1,8 +1,8 @@
# Network Components
Core components:
* A **Mixnet**, which mixes Sphinx packet traffic so that it cannot be determined who is communicating with whom. Our mixnet is based on a modified version of the [**Loopix** design](concepts/loopix). This is made up of [Nym nodes](architecture/mixnet#nodes) runnning on servers around the world maintained by a decentralised group of Operators.
* Various [**Nym clients**](architecture/mixnet#nym-clients) which manage sending and receiving Sphinx packets, encrypting/decrypting traffic, and providing [cover traffic](./concepts/cover-traffic) to hide 'real' traffic timing.
* A **Mixnet**, which mixes Sphinx packet traffic so that it cannot be determined who is communicating with whom. Our mixnet is based on a modified version of the [**Loopix** design](concepts/loopix). This is made up of [Nym nodes](architecture/mixnet/nodes) runnning on servers around the world maintained by a decentralised group of Operators.
* Various [**Nym clients**](architecture/mixnet/clients) which manage sending and receiving Sphinx packets, encrypting/decrypting traffic, and providing [cover traffic](./concepts/cover-traffic) to hide 'real' traffic timing.
* A CosmWasm-enabled blockchain called [**Nyx**](architecture/nyx), the home of the various smart contracts used by the mixnet. A subset of Nyx Validators run [NymAPI](./architecture/nyx#nymapi) instances, taking part in also producing and verifying [zk-nym credentials](cryptography/zk-nym).
![arch_overview](/images/network/arch/overall-arch.png)
@@ -66,7 +66,7 @@ The Vesting contract has multiple functions:
* Interacting with the Mixnet using vesting (i.e. non-transferable) tokens, allowing users to delegate their unvested tokens.
### Multisig Contract
The multisig contract used by the [NymAPI Quroum](../cryptography/zk-nym/zk-nym-overview) - a subset of the Nyx Validator set taking on the additional work of generating and validating zk-nyms - to execute certain actions in the [zk-nym](../cryptography/zk-nym) contract.
The multisig contract used by the [NymAPI Quroum](../cryptography/zk-nym/zk-nym-overview) - a subset of the Nyx Validator set taking on the additional work of generating and validating zk-nyms - to execute certain actions in the [zk-nym](./ecash.md) contract.
It is essentially an instance of the [canonical](https://github.com/CosmWasm/cw-plus/tree/main/contracts) `cw3-flex-multisig` using the `cw4-group` contract, with one minor change to restrict the addresses allowed to submit proposals.
@@ -1,7 +1,5 @@
import { Steps } from 'nextra/components';
import { Steps } from 'nextra/components'
import { VarInfo } from 'components/variable-info.tsx';
import { Tabs } from 'nextra/components';
import { MyTab } from 'components/generic-tabs.tsx';
# Pre-built Binaries
@@ -20,62 +18,20 @@ If the pre-built binaries don't work or are unavailable for your system, you wil
- Download from your terminal using `curl` or `wget` tool:
```sh
# using curl
curl -L <LINK> -o <PATH>
curl -L <LINK> -o <PATH>/nym-node
# using wget
wget <LINK>
```
In case you want to download binary to your current working directory, drop `<PATH>` from the command
###### 2. Verify the binary `sha256sum` hash
In case you want to download binary to your current working directory, drop `<PATH>` from the command
<div>
<Tabs items={[
<strong>Manual check</strong>,
<strong>One-liner</strong>,
]} defaultIndex="0">
<MyTab>
To see your binary `sha256sum` hash, run:
```sh
sha256sum <BINARY>
```
```sh
# for example
# sha256sum ./nym-wallet_1.2.15_amd64.AppImage
# or
# sha256sum ./nym-node
```
- Download [`hashes.json`](https://github.com/nymtech/nym/releases) file from the same same *Assets* drop down like your binary
- Open it with your text editor or print its content with `cat hashes.json`
- Check it if your binary `sha256sum` output is in `hashes.json` by using the `sha256sum` and searching for it or using `grep` command:
```
grep -i <SHA_HASH>
```
</MyTab>
<MyTab>
- Download [`hashes.json`](https://github.com/nymtech/nym/releases) file from the same same *Assets* drop down like your binary
- Run this command, substituting `<BINARY>` with your the one you want to check:
```sh
sha256sum <BINARY> | awk '{print $1}' | grep -qF "$(jq -r '.assets | to_entries | .[].value.sha256' hashes.json)" && echo "Hash matches an asset in the JSON file." || echo "Hash does not match any asset in the JSON file."
```
```sh
# for example
# sha256sum ./nym-node | awk '{print $1}' | grep -qF "$(jq -r '.assets | to_entries | .[].value.sha256' hashes.json)" && echo "Hash matches an asset in the JSON file." || echo "Hash does not match any asset in the JSON file."
```
</MyTab>
</Tabs>
</div>
- If your have to extract the binary (it would look like like `<BINARY>.tar.gz`) do it:
```sh
tar -xvzf <BINARY>.tar.gz
```
###### 3. Make the binary executable
###### 2. Make the binary executable
- Open terminal in the same directory and run:
```sh
chmod u+x <BINARY>
# for example: chmod u+x nym-node
chmod +x <BINARY>
# for example: chmod +x nym-mixnode
```
</Steps>
+10 -244
View File
@@ -5,6 +5,8 @@ import { VarInfo } from 'components/variable-info.tsx';
import { AccordionTemplate } from 'components/accordion-template.tsx';
import { Steps } from 'nextra/components';
export const TestingSteps = () => (
<div>
Testing steps performed
@@ -30,251 +32,15 @@ export const TunnelManagerCommands = () => (
);
# Changelog
This page displays a full list of all the changes during our release cycle from `v2024.3-eclipse` onward. Operators can find here the newest updates together with links to relevant documentation. The list is sorted so that the newest changes appear first.
**Note:** Any information published on this page was up to date at the time of writing. We do *not* maintain changelog retrospectively.
**Note:** Any information shared on this page was up to date at the time of writing. We do *not* maintain changelog retrospectively.
<VarInfo />
## `v2025.1-reeses`
- [Release Binaries](https://github.com/nymtech/nym/releases/tag/nym-binaries-v2025.1-reeses)
- [`nym-node`](nodes/nym-node.mdx) version `1.3.1`
```
nym-node
Binary Name: nym-node
Build Timestamp: 2025-01-16T11:54:17.079662337Z
Build Version: 1.3.1
Commit SHA: 5ab164d229f85bd2dd27ec6e38292c281df2f678
Commit Date: 2025-01-16T12:51:53.000000000+01:00
Commit Branch: HEAD
rustc Version: 1.84.0
rustc Channel: stable
cargo Profile: release
```
### Operators Updates & Tools
<Callout type="info">
From `nym-node v1.3.0` operators can choose multiple functionalities for their `nym-node` binary (flagged as `--mode`).
**However, the clients are yet to be developed to be able to make a proper selection for multi-mode nodes and therefore we ask operators to sign only one functionality to `--mode` option at a time. Please chose out of: `mixnode` or `entry-gateway` or `exit-gateway`.**
We are developing a design where operators can enable multiple modes, and let the Nym API to position the node according the network's needs in the beginning of each epoch.
</Callout>
- [Node functionality info updated](nodes/nym-node/setup#functionality-mode)
- [Fund your `nym-node` inbuilt Nyx account](nodes/nym-node/bonding#fund-nym-node-client-nyx-account) to prepare for gateway ticket rewarding
- `config.toml` changes:
- Replaced `mode` with `modes` to allow setting the node to run with say, `entry` + `mixnode` roles simultaneously
- Added `maximum_forward_packet_delay` to `mixnet.debug` section
- Moved `verloc` section from `mixnode.verloc` to top level
- Removed `mixnode` section
- Removed `entry_gateway` section to `gateway_tasks`
- Moved `authenticator` section from `gateway_tasks` (`entry_gateway`) to `service_providers` (`exit_gateway`)
- Renamed `exit_gateway` section to `service_providers`
- Moved top level `authenticator` section to `service_providers` so that it'd live alongside NR and IPR
- Added general `debug` section
- Added `metrics` section
- All documentation migrated to a new URL [nym.com/docs](https://nym.com/docs) alongside the rebranding of Nym organisation.
- Updated [network architecture diagrams](https://nym.com/docs/network/architecture)
- New blow-by-blow mixnet [traffic flow](https://nym.com/docs/network/architecture) section
- [Winter Nym Squad League started](https://forum.nym.com/t/nym-squad-league-farewell-fall-welcome-winter/977)
- From this release onward (New Year) Operators Updates & Tools will be on top of each release changelog followed by features, cryptography and bugfix parts.
- All 2024 release notes were moved to the bottom section called [Archived changelog](#archived-changelog)
### Features
- [build(deps): bump micromatch from `4.0.4` to `4.0.8` in `/testnet-faucet`](https://github.com/nymtech/nym/pull/4813): Bumps [micromatch](https://github.com/micromatch/micromatch) from `4.0.4` to `4.0.8`.
- [Move NS client to separate package under NS API](https://github.com/nymtech/nym/pull/5171):
- Moving NS API client code to separate package outside NS Agent makes the code cleaner & more modular
- NS agent now imports NS API client that it uses
- No functionality change in NS API or NS agent
- [Introduced initial internal commands for nym-cli: ecash key and request generation](https://github.com/nymtech/nym/pull/5174)
- [NS API - Gateway stats scraping](https://github.com/nymtech/nym/pull/5180): This PR adds a metrics scraper that fetches metrics from the `metrics/sessions` endpoint on gateway. The entries are stored and served on the API for one year
- [Remove explorer dependency](https://github.com/nymtech/nym/pull/5190)
- [Hopefully final steps of the smoosh™️](https://github.com/nymtech/nym/pull/5201): this PR is a bit more extensive than initially planned because of all the spaghetti that had to be untangled to make it all happen. The general idea is that now it's possible to run your node simultaneously in multiple modes. for example `mixnode` + `entry` or `entry` + `exit`, etc. The modes do the following:
- `mixnode`: allows the node to handle **forward** sphinx packets
- `entry-gateway`: allows the node to accepts websocket connections from incoming clients and to handle **final hop** sphinx packets
- `exit-gateway`: allows the node to handle **final hop** sphinx packets as well as starts NR and IPR service providers
Furthermore, if node runs with `wireguard` enabled, it will start local `authenticator` service provider which will also implicitly enable final hop packet handling regardless of the underlying mode. There are also various of other smaller (and bigger) changes:
- Upon receiving a forward packet, its delay is now clamped so that one could not dos the node by asking it to delay it for, for example, a year
- As mentioned above, node can now run in multiple modes at the same time,
- `/metrics/mixing` endpoint got deprecated (it still provides the same information, however) and might be removed in future release
- Introduced `/metrics/packet-stats` endpoint that provides more extensive information about packets received/sent
- It is now possible to control whether the node should log its statistics to console
- The console logs are also updated with the current packet rates
- All nodes now run the "verloc" protocol and measure every other node on the network
- Metrics got revamped and unified:
- All running metrics are stored in a single `NymNodeMetrics` struct
- There exists a `MetricsAggregator` that listens to any metrics events that might require additional processing
- Any new metrics event types can be easily added by registering a new handler via `register_handler`. the type must implement `MetricsHandler` trait and use unique variant of `MetricsEvent` enum. for example `GatewaySessionStatsHandler`. It is, however, possible to have opaque handlers, such as the already implemented `LegacyMixingStatsUpdater` and `MixnetMetricsCleaner`
- Everything in `mixnode` directory has been removed because there was nothing really left there. The mixing socket listener was unified in `nym-node` and similarly `verloc` was also moved there
- `gateway` directory was similarly reduced in size. Now it also creates appropriate tasks as opposed to the whole gateway process. eventually it might also be further stripped, but today is not that day.
- Removed the generic parameter on the `GatewayStorage` to simplify all the generics down the stack. it wasn't used anyway
CLI:
- Added `--modes` argument to specify all node modes with a single command (or `env` variable). for example: `--modes="mixnode,entry"`. Can't be used alongside `--modes`
- Extended `--mode` argument to allow specifying it multiple times, for example: `--mode mixnode --mode entry`. can't be used alongside `--mode`
Config changes:
- Replaced `mode` with `modes` to allow setting the node to run with say, `entry` + `mixnode` roles simultaneously
- Added `maximum_forward_packet_delay` to `mixnet.debug` section
- Moved `verloc` section from `mixnode.verloc` to top level
- Removed `mixnode` section
- Removed `entry_gateway` section to `gateway_tasks`
- Moved `authenticator` section from `gateway_tasks` (`entry_gateway`) to `service_providers` (`exit_gateway`)
- Renamed `exit_gateway` section to `service_providers`
- Moved top level `authenticator` section to `service_providers` so that it'd live alongside NR and IPR
- Added general `debug` section
- Added `metrics` section
- [Better date serialization](https://github.com/nymtech/nym/pull/5207): Proper date serialization for NS API session stats
- [Restore Location fields](https://github.com/nymtech/nym/pull/5208): restore `latitude` and `longitude` fields
- [Derive serialize for UserAgent](https://github.com/nymtech/nym/pull/5210)
- [Change sqlite journal mode to WAL](https://github.com/nymtech/nym/pull/5213)
- [Shipping raw metrics to PG](https://github.com/nymtech/nym/pull/5216)
- [Extend raw ws fd for gateway client](https://github.com/nymtech/nym/pull/5218)
- [Add fd callback to client core](https://github.com/nymtech/nym/pull/5230)
- [TicketType derive Hash and Eq](https://github.com/nymtech/nym/pull/5233): It's useful to keep `TicketType` in maps, which require `Hash`. Also derive `Eq` since it's useful.
- [Extend swagger docs](https://github.com/nymtech/nym/pull/5235)
- [Add FromStr impl for UserAgent](https://github.com/nymtech/nym/pull/5236): Add `FromStr` implementation for `UserAgent` to make it easier to pass in custom user agent in vpn clients as a CLI argument.
- [Remove unneeded async function annotation](https://github.com/nymtech/nym/pull/5246)
- [Add control messages to `GatewayTransciver`](https://github.com/nymtech/nym/pull/5247)
<AccordionTemplate name={<TestingSteps/>}>
**Review and Testing: Forget Me Implementation**
- Validated the encryption and delivery of `ForgetMe` control messages to the gateway
**Testing: MixTrafficController Integration**
- Verified that the `MixTrafficController` invokes `ForgetMe` logic correctly during shutdown
- Tested behaviour for gateway transceiver failures while sending control messages
**Testing: Gateway Storage Updates**
- Confirmed successful deletion of client data (e.g., inbox messages, bandwidth allocations) from persistent storage
</AccordionTemplate>
- [Add conversion unit tests for auth msg](https://github.com/nymtech/nym/pull/5251)
- [Update TS bindings](https://github.com/nymtech/nym/pull/5255)
- [Removed legacy socks5 listener](https://github.com/nymtech/nym/pull/5259)
- [Add PATCH support to `nym-http-api-client`](https://github.com/nymtech/nym/pull/5260): Add `PATCH` support to `nym-http-api-client` crate
- [Wireguard metrics](https://github.com/nymtech/nym/pull/5278): With this PR on each peer controller update the following global metrics information are also updated:
- total bytes tx
- total bytes rx
- current active peers
- total peers registered
- The former two are exposed with REST endpoints `/api/v1/metrics/wireguard-stats`, while the rest will be accessible via prometheus (soon ™️ ). The wireguard stats are also logged to the console (assuming they're non-zero)
- [Add close to credential storage](https://github.com/nymtech/nym/pull/5283): Add close method to credential storage
<AccordionTemplate name={<TestingSteps/>}>
1. **Review File: `common/credential-storage/src/backends/sqlite.rs`**
- Verified addition of `close` method for the SQLite backend
2. **Review File: `common/credential-storage/src/ephemeral_storage.rs`**
- Confirmed addition of `close` method for ephemeral storage with no action required
3. **Review File: `common/credential-storage/src/persistent_storage/mod.rs`**
- Ensured `close` method integration for persistent storage
4. **Review File: `common/credential-storage/src/storage.rs`**
- Verified updates to the `Storage` trait to include `close` and `cleanup_expired` methods
</AccordionTemplate>
- [Cherry picked \#5286](https://github.com/nymtech/nym/pull/5287)
<AccordionTemplate name={<TestingSteps/>}>
1. **Review File: `common/network-defaults/src/constants.rs`**
- Confirmed updated `mixnet_vpn` constants were added.
2. **Review File: `service-providers/ip-packet-router/src/constants.rs`**
- Checked replacement of legacy `TUN_*` constants with new `mixnet_vpn` constants.
- Validated alignment of routing traffic configurations.
3. **Review File: `service-providers/ip-packet-router/src/ip_packet_router.rs`**
- Ensured new `nym_network_defaults::constants::mixnet_vpn` constants replaced old references.
- Verified `TunDeviceConfig` consistency.
4. **Review File: `service-providers/ip-packet-router/src/util/generate_new_ip.rs`**
- Confirmed substitution of `TUN_DEVICE_*` constants with `NYM_TUN_DEVICE_*` constants.
- Tested functionality for generating random IPs within subnet.
</AccordionTemplate>
- [Expand `nym-node` prometheus metrics](https://github.com/nymtech/nym/pull/5298): This PR undusts the `/api/v1/metrics/prometheus` and introduces the following **37** new data points:
<AccordionTemplate name="New data points">
- mixnet:
- ingress:
- `nym_node_mixnet_ingress_forward_hop_packets_received`
- `nym_node_mixnet_ingress_final_hop_packets_received`
- `nym_node_mixnet_ingress_malformed_packets_received`
- `nym_node_mixnet_ingress_excessive_delay_packets`
- `nym_node_mixnet_ingress_forward_hop_packets_dropped`
- `nym_node_mixnet_ingress_final_hop_packets_dropped`
- `nym_node_mixnet_ingress_forward_hop_packets_received_rate`
- `nym_node_mixnet_ingress_final_hop_packets_received_rate`
- `nym_node_mixnet_ingress_malformed_packets_received_rate`
- `nym_node_mixnet_ingress_excessive_delay_packets_rate`
- `nym_node_mixnet_ingress_forward_hop_packets_dropped_rate`
- `nym_node_mixnet_ingress_final_hop_packets_dropped_rate`
- egress:
- `nym_node_mixnet_egress_stored_on_disk_final_hop_packets`
- `nym_node_mixnet_egress_forward_hop_packets_sent`
- `nym_node_mixnet_egress_ack_packets_sent`
- `nym_node_mixnet_egress_forward_hop_packets_dropped`
- `nym_node_mixnet_egress_forward_hop_packets_sent_rate`
- `nym_node_mixnet_egress_ack_packets_sent_rate`
- `nym_node_mixnet_egress_forward_hop_packets_dropped_rate`
- client sessions
- `nym_node_entry_client_sessions_unique_users`
- `nym_node_entry_client_sessions_sessions_started`
- `nym_node_entry_client_sessions_finished_sessions`
- `nym_node_entry_client_sessions_durations_{TYP}` (histogram), for example `nym_node_entry_client_sessions_durations_vpn`
- wireguard:
- `nym_node_wireguard_bytes_rx`
- `nym_node_wireguard_bytes_tx`
- `nym_node_wireguard_bytes_total_peers`
- `nym_node_wireguard_bytes_active_peers`
- `nym_node_wireguard_bytes_rx_rate`
- `nym_node_wireguard_bytes_tx_rate`
- network
- `nym_node_network_active_ingress_mixnet_connections`
- `nym_node_network_active_ingress_web_socket_connections`
- `nym_node_network_active_egress_mixnet_connections`
- process
- `nym_node_process_forward_hop_packets_being_delayed`
- `nym_node_process_packet_forwarder_queue_size`
- `nym_node_process_topology_query_resolution_latency` (histogram)
- `nym_node_process_final_hop_packets_pending_delivery`
- `nym_node_process_forward_hop_packets_pending_delivery`
</AccordionTemplate>
- [Amend 250gb limit](https://github.com/nymtech/nym/pull/5313): Change bandwidth cap to 250gb
- [Warn users if node is run in exit mode only](https://github.com/nymtech/nym/pull/5320): Throws a warning if node is run in "exit" mode only as by default, this will **NOT** enable entry capabilities, i.e. opening the websocket. thus making it ineligible for rewarded set selection (and rewards)
- [Reduce log severity for number of packets being delayed](https://github.com/nymtech/nym/pull/5321)
- [Apply 1.84 linter suggestions](https://github.com/nymtech/nym/pull/5330)
- [Readjusted --mode behaviour to fix the regression](https://github.com/nymtech/nym/pull/5331)
- [Legacy alert](https://github.com/nymtech/nym/pull/5346): alert moved into `nav` components
### Bugfix
- [Fix overflow](https://github.com/nymtech/nym/pull/5184)
- [Fix overflow again](https://github.com/nymtech/nym/pull/5204)
- [Make sure to update timestamp of last batch verification to prevent double redemption](https://github.com/nymtech/nym/pull/5239)
- [Make sure to apply gateway score filtering when choosing initial node](https://github.com/nymtech/nym/pull/5256)
- [Fixed client session histogram buckets](https://github.com/nymtech/nym/pull/5316)
- [Contract version assignment](https://github.com/nymtech/nym/pull/5318): This PR fixes updates to current nym-node version as well as introduces migration to fix the existing state of the mainnet contract
- [Make sure refresh data key matches bond info](https://github.com/nymtech/nym/pull/5329): This is to forbid operators from reusing the same underlying identity key for multiple nodes by overwriting the describe data. the reported key has to always match what the node has bonded with (and the contract enforces uniqueness)
## Archived Changelog
To allow reading through older changelogs, we store them below sorted by years.
@@ -525,7 +291,7 @@ cargo Profile: release
- `nym-node` has now implemented [IPv6 support for wireguard](https://github.com/nymtech/nym/pull/5059)
- [`network_tunnel_manager.sh` updated](https://raw.githubusercontent.com/nymtech/nym/refs/heads/develop/scripts/network_tunnel_manager.sh): run the commands below to make sure
- [`network_tunnel_manager.sh` updated](network): run the commands below to make sure
<br />
@@ -659,7 +425,7 @@ systemctl daemon-reload && service nym-node restart
- Since `v2024.13-magura`, operators do not update their node version in the wallet. [Manual upgrading steps](nodes/maintenance/manual-upgrade.mdx) has been updated accordingly.
- CLI tool `node_api_check.py`, helping operators to collect all API values about their nodes locally, is not up to date with the API changes introduced with `v2024.13-magura` release version. Please treat it as unstable before we fix it.
- CLI tool [`node_api_check.py`](nodes/performance-and-testing/node-api-check.mdx), helping operators to collect all API values about their nodes locally, is not up to date with the API changes introduced with `v2024.13-magura` release version. Please treat it as unstable before we fix it.
#### Error Log
@@ -967,7 +733,7 @@ Confirm that the deployment workflows work through manual testing
### Operators Updates & Tooling
<Callout type="warning" emoji="⚠️">
**Every operator has to make sure that their nodes self-described endpoint works, otherwise the node will be un-routable and thus won't get any rewards!**
**Every operator has to make sure that their nodes [self-described endpoint works](nodes/performance-and-testing/node-api-check#basic-api-usage), otherwise the node will be un-routable and thus won't get any rewards!**
</Callout>
- **New technical documentation:** All Nym documentation starts from a new entry page [nymtech.net/docs](https://nymtech.net/docs/operators/introduction). To run locally or propose collaboration, start in our [repository](https://github.com/nymtech/nym/tree/develop/documentation)
@@ -1165,7 +931,7 @@ Some endpoints got purposely deprecated without any equivalent reimplemented sin
##### Mixnet Contract
<Callout type="warning" emoji="">
**Every operator has to make sure that their nodes self-described endpoint works, otherwise the node will be un-routable and thus won't get any rewards!**
**Every operator has to make sure that their nodes [self-described endpoint works](nodes/performance-and-testing/node-api-check#basic-api-usage), otherwise the node will be un-routable and thus won't get any rewards!**
</Callout>
###### High Level Changes
@@ -1979,7 +1745,7 @@ done
- Vesting contract functionalities have been purged, users can only remove tokens from vesting
- Migrating from `mixnode` or `gateway` smart contracts to a new unifying `nym-node` smart contract will be available soon using Nym desktop wallet, just like you are used to for bonding and node settings. After this migration all `nym-nodes` will be able to receive delegation and rewards. We will share a step by step guide once this migration will be deployed. No action needed now.
- Nym API Check CLI is upgraded according to the latest API endpoints, output is cleaner and more concise.
- [Nym API Check CLI](testing/node-api-check.md) is upgraded according to the latest API endpoints, output is cleaner and more concise.
#### Operators Tasks
@@ -2868,7 +2634,7 @@ warning: /home/alice/src/nym/nym/common/dkg/Cargo.toml: `default-features` is ig
- Flow [chart](release-cycle.md#release-flow)
- [Sandbox testnet](sandbox.md) guide: teaching Nym node operators how to run their nodes in Nym Sandbox testnet environment.
- [Terms & Conditions flag](nodes/setup.md#terms--conditions)
- Node API Check CLI
- [Node API Check CLI](testing/node-api-check.md)
- [Pruning VPS `syslog` scripts](troubleshooting/vps-isp.md#pruning-logs)
- [Black-xit: Exiting the blacklist](troubleshooting/nodes.md#my-gateway-is-blacklisted)
@@ -2,7 +2,7 @@
## Nym Network
To see different stats about Nym Network live, we recommend you to visit [Nym Harbourmaster](https://harbourmaster.nymtech.net) and dynamic [Nym token page](https://nymtech.net/about/token).
To see different stats about Nym Network live, we recommend you to visit [Nym Harbourmaster](https://harbourmaster.nymtech.net) and dynamic [Nym token page](https://nymtech.net/about/token.
### Is there an explorer for Nym Mixnet?
@@ -12,7 +12,7 @@ Yes, there are..
* [Nym Explorer](https://explorer.nymtech.net/)
* [Sandbox testnet](https://sandbox-explorer.nymtech.net/)
* [Nym Harbourmaster](https://harbourmaster.nymtech.net)
* [Nym Harbourmaster](https://harbourmaster.nymtech.net)
**Built by community**
@@ -43,3 +43,5 @@ At this point the most crucial component needed are [Exit Gateways](../nodes/nym
### Are Nym Nodes whitelisted?
Nope, anyone can run a Nym Node. whether your node is chosen to mix is purely reliant on the node's performance and reputation (self stake + delegations).
+7 -12
View File
@@ -2,7 +2,6 @@ import { Callout } from 'nextra/components';
import { Steps } from 'nextra/components';
import { Tabs } from 'nextra/components';
import NymNodeSpecs from 'components/operators/snippets/nym-node-specs.mdx';
import TermsConditions from 'components/operators/snippets/tc-info.mdx';
# Nym Nodes Operator Guides
@@ -26,25 +25,21 @@ Reserve 45-120 minutes for the initial setup and configuration (depends on your
Outdated nodes are never selected for routing/mixing packets, resulting in not receiving any rewards. You can read more on our [Tokenomics page](tokenomics/mixnet-rewards.mdx) to understand the selection and rewards logic.
<TermsConditions />
Accepting T&Cs is done via a flag `--accept-operator-terms-and-conditions` added explicitly to `nym-node run` command every time. Detailed info and proper syntax is provided on the [setup page](nodes/nym-node/setup#terms--conditions).
## Steps for Nym Node Operators
This is a summary of all needed steps for node operators to setup and configure a `nym-node` and register it to Nym Network:
1. **Start with [Preliminary Steps](nodes/preliminary-steps.mdx), preparing:**
- [VPS](nodes/preliminary-steps/vps-setup.mdx)
- [Nym wallet](nodes/preliminary-steps/wallet-preparation)
1. **Start with [Preliminary Steps](preliminary-steps.mdx), preparing:**
- [VPS](preliminary-steps/vps-setup.mdx)
- [Nym wallet](preliminary-steps/wallet-preparation/mdx)
2. **[Setup](nodes/nym-node/setup.mdx) the node**
2. **[Setup](nym-node/setup.mdx) the node**
3. **[Configure](nodes/nym-node/configuration.mdx) the node and (optionally) automation, Wireguard, WSS, reverse proxy ...**
3. **[Configure](nym-node/configuration.mdx) the node and (optionally) automation, Wireguard, WSS, reverse proxy ...**
4. **[Run](nodes/nym-node/setup.mdx#initialise--run) the node or [the service](nodes/nym-node/configuration.md#systemd)**
4. **[Run](nym-node/setup.mdx#initialise--run) the node or [the service](nym-node/configuration.md#systemd)**
5. **[Bond](nodes/nym-node/bonding.mdx) the node to the Nym API, using Nym wallet**
5. **[Bond](nym-node/bonding.mdx) the node to the Nym API, using Nym wallet**
Make sure to follow the steps thoroughly, in case you find any point difficult don't hesitate to ask in our [Operators channel](https://matrix.to/#/#operators:nymtech.chat).
@@ -101,31 +101,23 @@ For more information about your node's port configuration, check the [port refer
Anything can happen to the server on which your node is running. To back up your `nym-node` keys and configuration protects the operators against the negative impact of unexpected events. To restart your node on another server, two essential pieces are needed:
1. **Node keys to initialise the same node on a new VPS**
2. **Access to the bonding Nyx account (wallet seeds) to update host on the Mixnet smart contract**
1. Node keys to initialise the same node on a new VPS
2. Access to the bonding Nym account (wallet seeds) to edit the IP on smart contract
Optionally, it can be useful to have a backup your configuration directories to setup [reverse proxy and WSS](nym-node/configuration/proxy-configuration.mdx) quickly as well.
Assuming that everyone access their wallets from local machine and does *not* store their seeds on VPS, point \#2 should be a given.
Assuming that everyone access their wallets from local machine and does *not* store their seeds on VPS, point 2. should be a given.
To backup your `nym-node` keys and configuration in the easiest way possible, copy the entire config directory `.nym` from your VPS to your local desktop, using a special copy command `scp`:
<Callout type="warning" emoji="⚠️">
Never store your mnemonic seed anywhere online nor do *not* share it with anyone!
</Callout>
To backup your `nym-node` keys and configuration in the easiest way possible, copy the entire config directory `.nym` from your VPS to your local machine, using a special copy command `scp`:
<Steps>
###### 1. Create a local directory where you want to store your backup
###### 1. Create a directory where you want to store your backup
```sh
mkdir -pv <PATH_TO_TARGET_DIRECTORY>
```
```sh
# for example
# mkdir -pv $HOME/backup/my_nym_node/.nym
```
###### 2. Copy configuration folder `.nym` from your VPS to your newly created backup directory
```sh
@@ -138,96 +130,37 @@ The `scp` command should print logs, an operator can see directly whether it was
</Steps>
Now you have everything needed to restore your `nym-node` on another server. If you are in a need of doing so, follow the steps in [*Restoring a node*](#restoring-a-node) chapter below.
### Backup proxy configuration
If you run your node behind a [reverse proxy and WSS](nym-node/configuration/proxy-configuration.mdx) (suggested for all Exit Gateways), you may find it useful to backup configuration directories for that as well. It's done by following the same logic, using `scp` command for remote copy:
<Callout>
Given that servers hosting a `nym-node` are expected to be a single purpose machines, we assume in this guide, that there are no other `<HOSTNAME>` sub-directories in `/var/www` and `/etc/nginx/sites-available` then the ones relevant to `nym-node`. In case you prefer to only backup configuration for a particular hostname, just append your `<HOSTNAME>` to the path in the commands below.
</ Callout>
<Steps>
###### 1. Create local sub-directories where you want to store your backup
```sh
mkdir -pv <PATH_TO_TARGET_DIRECTORY>
mkdir -pv <PATH_TO_TARGET_DIRECTORY>
```
```sh
# for example
# mkdir -pv $HOME/backup/my_nym_node/var/www
# mkdir -pv $HOME/backup/my_nym_node/etc/nginx/sites-available
```
###### 1. Backup `/var/www` directory
```sh
scp -r <SOURCE_USER_NAME>@<SOURCE_HOST_ADDRESS>:/var/www <PATH_TO_TARGET_DIRECTORY>
```
###### 2. Backup `/etc/nginx/sites-available` directory
```sh
scp -r <SOURCE_USER_NAME>@<SOURCE_HOST_ADDRESS>:/etc/nginx/sites-available <PATH_TO_TARGET_DIRECTORY>
```
###### 3. Verify the success of the backup
The `scp` command should print logs, an operator can see directly whether it was successful or if it encountered any error. However, double check that all your needed configuration is in the backup target directory.
</Steps>
<Callout type="info">
To copy files from the root directory (paths starting with `/`), you need root permissions on the remote machine. If your server is not running as the root user, the `scp` command won't work directly, as using the `sudo` prefix would apply to your local machine instead of the remote server.
To resolve this, you need to SSH into the remote server first, and there use the `sudo cp -r <SOURCE_PATH> <TARGET_PATH>` command to copy the necessary directories to a location outside the root directory. After that, you can run `scp` with the correct source path to transfer the files.
</Callout>
## Restoring a node
In case your VPS was terminated and you have a [backup](#backup-a-node) of your node keys and access to your [bonding](nym-node/bonding) wallet, you can easily restore your node on another server without losing your delegation.
In case your VPS shut down and you have a [backup](#backup-a-node) of your node keys and access to your bonding wallet, you can easily restore your node on another server without losing your delegation.
<Steps>
###### 1. Prepare new VPS
SSH into your new VPS and start with:
- On VPS: Do all [preliminary steps](preliminary-steps.mdx) needed to run a `nym-node`.
- Do all [preliminary steps](preliminary-steps.mdx) needed to run a `nym-node`.
- Create a `.nym/nym-nodes` configuration folder:
- On VPS: Create a `.nym/nym-nodes` configuration folder:
```sh
mkdir -pv ~/.nym/nym-nodes
```
###### 2. Restore your node configuration
Copy the folder with your node configuration and keys from your local machine to the newly created folder on your VPS using `scp` command. Make sure to grab the entire `nym-node` configuration folder, which is called after your local `nym-node` identifier (`<ID>`), the `-r` (recursive) flag will take care of all sub-directories and their content:
From machine where your node is backed up (usually local desktop): Copy the folder with your node keys and configuration to the newly created folder on your VPS using `scp` command. Make sure to grab the entire `nym-node` configuration folder, which is called after your local `nym-node` identifier (`<ID>`), the `-r` (recursive) flag will take care of all sub-directories and their content:
```sh
scp -r <PATH_TO_LOCAL_NODE_CONFIGURATION_FOLDER> <VPS_USER_NAME>@<VPS_HOST_ADDRESS>:~/.nym/nym-nodes/
```
```sh
# for example:
scp -r $HOME/backup/my_nym_node/.nym/nym-nodes/default-nym-node root@my-nym-node:~/.nym/nym-nodes/
```
###### 3. Verify the success of the backup
The `scp` command should print logs, an operator can see directly whether it was successful or if it encountered any error. However, double check that all your needed configuration is in the target directory `.nym/nym-nodes` on your VPS.
###### 4. Configure your node on the new VPS
###### 3. Configure your node on the new VPS
* Edit `~/.nym/nym-nodes/<ID>/config/config.toml` config with the new listening address IP - it's the one under the header `[host]`, called `public_ips = [<PUBLIC_IPS>,]` and add your new location (field `location = <LOCATION>`, formats like: 'Jamaica', or two-letter alpha2 (e.g. 'JM'), three-letter alpha3 (e.g. 'JAM') or three-digit numeric-3 (e.g. '388') can be provided). You can see your IP by running a command `echo "$(curl -4 https://ifconfig.me)"`.
* SSH to your VPS
* Run a command `echo "$(curl -4 https://ifconfig.me)"` to see your public IPv4
* Edit config file located at `~/.nym/nym-nodes/<ID>/config/config.toml` correct IP - it's the field under the header `[host]`, called `public_ips = [<PUBLIC_IPS>,]`
* Add your new location field `location = <LOCATION>`, (formats like: 'Jamaica', or two-letter alpha2 (e.g. 'JM'), three-letter alpha3 (e.g. 'JAM') or three-digit numeric-3 (e.g. '388') can be provided).
* Try to run the node and see if everything works.
* Setup the [systemd](nym-node/configuration.mdx#systemd) automation (don't forget to add the [terms and conditions flag](nym-node/setup.mdx#terms--conditions)) to `ExecStart` command, reload the daemon and run the service.
###### 4. Change the node smart contract info via the wallet interface
@@ -236,63 +169,6 @@ Open Nym Wallet, go to *Bonding*, open *Settings* and change *Host* value to the
</Steps>
Everything should work now. If not, have a look through the `config.toml` file and ensure that there are no details from the past VPS, things like WSS port, landing page asset etc. If you had a configuration with reverse proxy and WSS, continue to set that up as well. If you had it backed up, you can follow the steps to [restore proxy configuration](#restore-proxy-configuration) below, or you can start to [configure proxy from scratch](nym-node/configuration/proxy-configuration.mdx).
### Restoring proxy configuration
If operators moving to a new server had their [proxy configuration backed up](#backup-proxy-configuration), it's possible to simply restore it, following these steps:
<Steps>
###### 1. Copy your backed up directory `/var/www` for landing page to the server
- Start setting up reverse proxy and WSS following [this guide](nym-node/configuration/proxy-configuration.mdx) but instead of creating a new `/var/www/<HOSTNAME>` directory, simply copy there the one from the old server, using `scp` command
```sh
scp -r <PATH_TO_LOCAL_WWW_CONFIGURATION_BACKUP> <VPS_USER_NAME>@<VPS_HOST_ADDRESS>:/var/www/
```
```sh
# for example:
scp -r $HOME/backup/my_nym_node/var/www root@my-nym-node:/var/www
```
###### 2. Make sure that domain is correctly setup
As you see in the guide you use a domain (same like your `<HOSTNAME>`), make sure you have it registered as and redirected to the new IP by logging into your DNS provider and editing the dashboard of the domain.
###### 3. Proceed with all needed `node` configuration
- In the guide follow the steps to [configure your node reverse proxy](nym-node/configuration/proxy-configuration#reverse-proxy-configuration), like installing Nginx, opening the needed ports etc
###### 4. Restore Nginx config
- When you arrive to the point to configure your [`/etc/nginx/sites-available/<HOSTNAME>`](nym-node/configuration/proxy-configuration#2-add-your-endpoint-configuration-to-nginx-by-creating-a-config-file), use again `scp` instead of creating a new one
```sh
scp -r <PATH_TO_LOCAL_NGINX_CONFIGURATION_BACKUP> <VPS_USER_NAME>@<VPS_HOST_ADDRESS>:/etc/nginx/sites-available
```
```sh
# for example:
scp -r $HOME/backup/my_nym_node/etc/nginx/sites-available root@my-nym-node:/etc/nginx/sites-available
```
###### 5. Edit Nginx configuration file with correct hostname
- Open the Nginx config file on your server and see if a domain name needs to be changed to new one. To open the file, use for example nano text editor:
```sh
nano /etc/nginx/sites-available/<HOSTNAME>
```
###### 6. Continue with the Nginx setup
- Go back to the [proxy setup guide](nym-node/configuration/proxy-configuration#3-activate-and-test-nginx-configuration) and continue from the point [\#3](nym-node/configuration/proxy-configuration#3-activate-and-test-nginx-configuration)
###### 7. Edit WSS configuration with the correct hostname
- Open the WSS config file on your server and see if a domain name needs to be changed to new one. To open the file, use for example nano text editor:
```sh
nano /etc/nginx/sites-available/wss-config-nym
```
###### 8. Finish and test the setup
Continue with the [activating and testing](nym-node/configuration/proxy-configuration#2-activate-and-test-nginx-wss-configuration) your reverse proxy and WSS until the end of the guide.
</ Steps>
<Callout type="info">
To copy files to the root directory (paths starting with `/`), you need root permissions on the remote machine. If your server is not running as the root user, the `scp` command won't work directly, as using the `sudo` prefix would apply to your local machine instead of the remote server.
To resolve this, you can SSH into the remote server and create the configuration files manually using the prefix `sudo` and then copy their content from your local backup.
</Callout>
## Moving a node
In case of a need to move a Nym Node from one machine to another and avoiding to lose the delegation, here are few steps how to do it.
@@ -1,8 +1,8 @@
import { VarInfo } from 'components/variable-info.tsx';
import {Callout} from 'nextra/components';
import { VarInfo } from 'components/variable-info.tsx'
# Nym Node
NYM NODE is a tool for running a node within the Nym network. Nym Nodes containing functionality such as `mixnode`, `entry-gateway` and `exit-gateway` are fundamental components of Nym Mixnet architecture. Nym Nodes are ran by decentralised node operators.
To setup any type of Nym Node, start with either building [Nym's platform](../binaries/building-nym.mdx) from source or download [pre-compiled binaries](../binaries/pre-built-binaries.mdx) on the [configured server (VPS)](preliminary-steps/vps-setup.mdx) where you want to run the node. Your Nym Node will need to be bonded before it can be run. We recommend most users use the [Nym desktop wallet](preliminary-steps/wallet-preparation.mdx) for this.
@@ -28,15 +28,11 @@ Do not bond your node to the API if the previous steps weren't finished. Bad con
Any new bonded node will provide only the bare minimum information: host, identity key and optionally custom port of its HTTP API - we highly recommend to set that one up to `8080`. Everything else will be discovered via the self-described API for maximum flexibility. This also includes the sphinx key, meaning if the API is not exposed, the node will be unable to route any traffic.
**Every operator has to make sure that their nodes [self-described endpoint works](../performance-and-testing#functionality--performance-check), otherwise the node will be un-routable and thus won't get any rewards!**
<Callout type="warning" emoji="⚠️">
**Reveal your menominc phrase only in areas out of surveillance of other people and never share it with others. Nym team will never ask you for your mnemonic phrase - in case you were asked by someone it's a scam, do *not* reply to it!**
</Callout>
**Every operator has to make sure that their nodes [self-described endpoint works](nodes/performance-and-testing/node-api-check#basic-api-usage), otherwise the node will be un-routable and thus won't get any rewards!**
## Bond via the Desktop wallet (recommended)
You can bond your `nym-node` via the Desktop wallet. Nym wallet, just like any Nym binary can be downloaded, verified and made executable following [these simple steps](../../binaries/pre-built-binaries#setup-binaries).
You can bond your `nym-node` via the Desktop wallet.
<Steps>
@@ -120,13 +116,18 @@ From `nym-wallet` version `1.2.15` onward the application allows and prompts ope
<Steps>
###### 1. Download the latest wallet from [the release page](https://github.com/nymtech/nym/releases), verify and make executable
###### 1. Download the latest wallet from [the release page](https://github.com/nymtech/nym/releases)
Nym wallet, just like any Nym binary can be downloaded, verified and made executable following [these simple steps](../../binaries/pre-built-binaries#setup-binaries).
###### 2. Verify the binary and extract it if needed
###### 2. Open the wallet and sign in
- Download [`hashes.json`](https://github.com/nymtech/nym/releases/download/nym-wallet-v1.2.15/hashes.json)
- Open it with your text editor or print it's content with `cat hashes.json`
- Run `sha256sum <WALLET_BINARY>` for example `sha256sum ./nym-wallet_1.2.15_amd64.AppImage`
- If your have to extract it (like `.tar.gz`) do it
###### 3. Migrate!
###### 3. Open the wallet and sign in
###### 4. Migrate!
- Go to Bonding and you will be prompted with such message:
@@ -149,48 +150,3 @@ Versions older than `nym-wallet v 1.2.15` will not allow bonding new nodes.
## Bond via the CLI (power users)
If you want to bond your Mix Node via the CLI, then check out the [relevant section in the Nym CLI](../../../developers/tools/nym-cli/usage#usage) docs.
## Fund `nym-node` Client Nyx Account
<Callout type="info">
This is not relevant for operators running exclusively `mixnode` functionality. For any type of gateway functionality this is a preparation requirement for the upcoming [ticket rewarding](../../tokenomics/mixnet-rewards#roadmap).
</ Callout>
Every `nym-node` client contains a mnemonic of a Nyx account, generated with node initialisation (first `run` command creating all configuration and data files). This mnemonic is located in `$HOME/.nym/nym-nodes/<ID>/data/cosmos_mnemonic`. **This is *not* the same account as the one used for bonding!**
This client account will be used for the process of redemption of [zk-nym tickets](../../../network/cryptography/zk-nym) collected by nodes running as `entry-gateway` and `exit-gateway` as the redemption requires gateway to create a multisig proposal on the chain for which the client (node) needs to pay transaction fee.
Giving the low transaction cost on Cosmos, funding your client Nyx account with 25 NYM tokens should be more than enough. To do so, follow these steps:
<Steps>
###### 1. Get your `nym-node` client Nyx account mnemonic phrase
- Make sure your screen is not exposed to other people or recording devices
- To store sensitive credentials use audited and open source password managers, like [KeePassXC](https://keepassxc.org/)
- To print out your node client mnemonic phrase, run:
```sh
cat $HOME/.nym/nym-nodes/<ID>/data/cosmos_mnemonic
```
```sh
# for example
# cat $HOME/.nym/nym-nodes/default-nym-node/data/cosmos_mnemonic
```
- **Alternatively:** You can use `scp` command to copy the file `cosmos_mnemonic` remotely. If this is your preference, use this command:
```sh
scp <USER>@<HOST>:.nym/nym-nodes/<ID>/data/cosmos_mnemonic <LOCAL_TARGET_PATH>
```
- Copy this phrase and save it to your password manager
###### 2. Get the address of your client Nyx account using desktop wallet
- Open desktop wallet and choose to sign in with mnemonic
- Use the phrase from step 1
- Open `Receive` tab and copy Nym account address
- We recommend operators to store this address for future funding
###### 3. Fund the client Nyx account
- Open wallet with Nym tokens and send a minimum of 25 NYM to the address copied in the previous step
- In case you don't have any spare NYM, you can send tokens from any exchange directly to the address copied in the previous step
</ Steps>
Now your `nym-node` client can use inbuilt Nyx account to create a multisig proposal on chain and redeem user tickets.
@@ -7,25 +7,22 @@ import BuildInfo from 'components/outputs/command-outputs/nym-node-build-info.md
import NymNodeHelp from 'components/outputs/command-outputs/nym-node-help.md';
import NymNodeRunHelp from 'components/outputs/command-outputs/nym-node-run-help.md';
import { AccordionTemplate } from 'components/accordion-template.tsx';
import TermsConditions from 'components/operators/snippets/tc-info.mdx';
# Nym Node Setup & Run
This documentation page provides a guide on how to set up and run a [NYM NODE](../nym-node.mdx), along with explanations of available flags, commands, and examples.
<VarInfo/ >
## Current version
```sh
nym-node
Binary Name: nym-node
Build Timestamp: 2025-01-16T11:54:17.079662337Z
Build Version: 1.3.1
Commit SHA: 5ab164d229f85bd2dd27ec6e38292c281df2f678
Commit Date: 2025-01-16T12:51:53.000000000+01:00
Build Timestamp: 2024-12-18T10:18:42.978852430Z
Build Version: 1.2.1
Commit SHA: 8d5a41a790e96ae5e821964865affaa7d3343eab
Commit Date: 2024-12-18T11:07:49.000000000+01:00
Commit Branch: HEAD
rustc Version: 1.84.0
rustc Version: 1.83.0
rustc Channel: stable
cargo Profile: release
```
@@ -34,39 +31,52 @@ cargo Profile: release
<BuildInfo />
*/}
## Functionality (mode)
## Summary
<Callout type="info">
From `nym-node v1.3.0` operators can choose multiple functionalities for their `nym-node` binary (flagged as `--mode`).
**However, the clients are yet to be developed to be able to make a proper selection for multi-mode nodes and therefore we ask operators to sign only one functionality to `--mode` option at a time. Please chose out of: `mixnode` or `entry-gateway` or `exit-gateway`.**
<VarInfo/ >
We are developing a design where operators can enable multiple modes, and let the Nym API to position the node according the network's needs in the beginning of each epoch.
</Callout>
To run a new node, you can simply execute the `nym-node` command without any flags. By default, the node will set necessary configurations. If you later decide to change a setting, you can use the `-w` flag.
Nym Node has three functionalities in the network: `entry-gateway`, `mixnode` and `exit-gateway`.
The most crucial aspect of running the node is specifying the `--mode`. At the moment it can be only one of three: `mixnode`, `entry-gateway`, and `exit-gateway`.
- **Entry Gateway (`--mode entry-gateway`)**: A the node to which local client connects. It checks the bandwidth allowance, using [zk-nyms](../../../network/cryptography/zk-nym) and either sends [Sphinx packets](../../../network/cryptography/sphinx) through the mixnet or directly to Exit Gateway in case of dVPN (2-hop) routing. This node also recieves replies and sends them back to users local client.
Currently the `nym-node` binary can only be run in a single `--mode` at any one time. In the future however, operators will be able to specify multiple modes that a single `nym-node` binary can run. Our goal is to have as many nodes as possible enabling multiple modes, and allow the Nym API to position the node according the network's needs in the beginning of each epoch.
- **Mixnode (`--mode mixnode`)**: Nodes organized in three layers, randmoly selected every epoch (60 minutes), mixing Sphinx packets, adding a slight latency to defend users agains time correlation attacks and sending them further to the next layer or to the Exit Gateway
Every `exit-gateway` mode is basically an `entry-gateway` with NR (Network Requester) and IPR (IP Packet Router) enabled. This means that every `exit-gateway` is automatically seen as an `entry-gateway` but not the opposite.
- **Exit Gateway (`--mode exit-gateway`)**: The final node in the mixnet. It puts all packets together and using inbuilt Network requester and IP packet router, it sends traffic to the open internet. This node also recieves replies and sends them back to the user client.
Gateway operators can check out the node performance, connectivity and much more in our new tool [harbourmaster.nymtech.net](https://harbourmaster.nymtech.net/).
<Callout type="warning">
Exit Gateway is the only node routing data directly to the open internet. Therefore it exposes IP of operators server (VPS) to abuse complains. Before you decide to run an Exit Gateway, please read our [Community Counsel pages](../../community-counsel/exit-gateway) containing more information and some legal content.
</Callout>
To determine which mode your node is running, you can check the `:8080/api/v1/roles` endpoint. For example:
```sh
# sustitude <IPv4_ADDRESS> or <HOSTNAME> with the one corresponding to your node
# for http
http://<IPv4_ADDRESS>:8080/api/v1/roles
# or
http://<IPv4_ADDRESS>/api/v1/roles
Everything essential for each mode exists on `nym-node` by default. For instance, if you run a Mixnode, you'll find that a NR (Network Requester) and IPR (IP Packet Router) addresses exist, but they will be ignored in `mixnode` mode.
# for reversed proxy/WSS
https://<HOSTNAME>/api/v1/roles
```
Note that every `exit-gateway` mode is basically an `entry-gateway` with NR (Network Requester) and IPR (IP Packet Router) enabled. This means that every `exit-gateway` can work as an `entry-gateway` but not the opposite.
Everything necessary will exist on your node by default. For instance, if you're running a mixnode, you'll find that a NR (Network Requester) and IPR (IP Packet Router) address exist, but they will be ignored in `mixnode` mode.
## Command & Examples
For more information about available endpoints and their status, you can refer to:
```sh
# sustitude <IPv4_ADDRESS> or <HOSTNAME> with the one corresponding to your node
# for http
http://<IPv4_ADDRESS>:8080/api/v1/swagger/#/
# or
http://<IPv4_ADDRESS>/api/v1/swagger/#/
**`nym-node` introduces a default human readible ID (local only) `default-nym-node`, which is used if there is not an explicit custom `--id <ID>` specified. All configuration is stored in `~/.nym/nym-nodes/default-nym-node/config/config.toml` or `~/.nym/nym-nodes/<ID>/config/config.toml` respectively.**
# for reversed proxy/WSS
https://<HOSTNAME>/api/v1/swagger/#/
```
## Usage
### Help Command
There are a few changes from the individual binaries used in the past. For example by default `run` command does initialisation function as well, local node `--id` will be set by default (1default-nym-node`) unless specified otherwise.
There are a few changes from the individual binaries used in the past. For example by default `run` command does `init` function as well, local node `--id` will be set by default unless specified otherwise etcetera.
<Callout type="info" emoji="️">
You can always use `--help` flag to see the commands or arguments associated with a given command.
@@ -86,7 +96,13 @@ To list all available flags for each command, run `./nym-node <COMMAND> --help`
The Wireguard flags currently have limited functionality. From version `1.1.6` ([`v2024.9-topdeck`](https://github.com/nymtech/nym/releases/tag/nym-binaries-v2024.9-topdeck)) wireguard is available and recommended to be switched on for nodes running as Gateways. Keep in mind that this option needs a bit of a special [configuration](configuration.md#wireguard-setup).
</Callout>
<TermsConditions />
### Terms & Conditions
<Callout type="info" emoji="️">
From `nym-node` version `1.1.3` onward is required to accept [**Operators Terms & Conditions**](https://nymtech.net/terms-and-conditions/operators/v1.0.0) in order to be part of the active set. Make sure to read them before you add the flag.
</Callout>
There has been a long ongoing discussion whether and how to apply Terms and Conditions for Nym network operators, with an aim to stay aligned with the philosophy of Free Software and provide legal defense for both node operators and Nym developers. To understand better the reasoning behind this decision, you can listen to the first [Nym Operator Town Hall](https://www.youtube.com/live/7hwb8bAZIuc?si=3mQ2ed7AyUA1SsCp&t=915) introducing the T&Cs or to [Operator AMA with CEO Harry Halpin](https://www.youtube.com/watch?v=yIN-zYQw0I0) from June 4th, 2024, explaining pros and cons of T&Cs implementation.
Accepting T&Cs is done via a flag `--accept-operator-terms-and-conditions` added explicitly to `nym-node run` command every time. If you use [systemd](configuration.md#systemd) automation, add the flag to your service file's `ExecStart` line.
@@ -110,6 +126,14 @@ curl -X 'GET' \
}
```
### Commands & Examples
**`nym-node` introduces a default human readible ID (local only) `default-nym-node`, which is used if there is not an explicit custom `--id <ID>` specified. All configuration is stored in `~/.nym/nym-nodes/default-nym-node/config/config.toml` or `~/.nym/nym-nodes/<ID>/config/config.toml` respectively.**
<Callout type="info" emoji="️">
All commands with more options listed below include `--accept-operator-terms-and-conditions` flag, read [Terms & Conditions](#terms--conditions) chapter above before executing these commands.
</Callout>
#### Essential Parameters & Variables
Running a `nym-node` in a `mixnode` mode requires less configuration than a full `exit-gateway` setup, we recommend operators to still follow through with all documented [configuration](configuration.md). Before you scroll down to syntax examples for the mode of your choice please familiarise yourself with the essential [paramters and variables](../../variables.mdx) convention we use in the guide.
@@ -119,13 +143,13 @@ To prevent over-flooding of our documentation we cannot provide with every singl
</Callout>
## Setup & Run
### Initialise & Run
When we use `run` command for the first time the node will initialise all essential configuration and data files (unless specified with a flag `--deny-init`) stored at `$HOME/.nym/nym-nodes/<ID>` where the most important is the `config.toml` file stored at `$HOME/.nym/nym-nodes/<ID>/config/`. Below are some examples of initialising and running `nym-node` with different modes (`--mode`) like `mixnode`, `entry-gateway`, `exit-gateway`.
When we use `run` command the node will do `init` as well, unless we specify with a flag `--deny-init`. Below are some examples of initialising and running `nym-node` with different modes (`--mode`) like `mixnode`, `entry-gateway`, `exit-gateway`.
Please keep in mind that currently we ask operators to run only one functionality (`--mode`) at a time.
Please keep in mind that currently you can run only one functionality (`--mode`) per a `nym-node` instance. We are yet to finalise implement the multi-functionality solution under one node bonded to one Nyx account. Every `exit-gateway` can function as `entry-gateway` by default, not vice versa.
There is a simple default command to initialise and run your node: `./nym-node run --mode <MODE>`, however there quite a few parameters to be configured.
There is a simple default command to initialise and run your node: `./nym-node run --mode <MODE>`, however there quite a few parameters to be configured. When `nym-node` gets to be `run`, these parameters are read by the binary from the configuration file located at `.nym/nym-nodes/<ID>/config/config.toml`.
If an operator specifies any paramteres with optional flags alongside `run` command, these parameters passed in the option will take place over the ones in `config.toml` but they will not overwrite them by default. To overwrite them with the values passed with `run` command, a flag `-w` (`--write-changes`) must be added.
@@ -141,7 +165,7 @@ Alternatively operators can just open a text editor and change these values manu
In such case, you can `run` a node to initalise it or try if everything works, but then stop the proces and paste your entire `run` command syntax (below) to the `ExecStart` line of your `/etc/systemd/system/nym-node.service` and start the node as a [service](configuration.md#following-steps-for-nym-nodes-running-as-systemd-service).
</Callout>
## Migrate
### Migrate
<Callout type="warning">
**Legacy binaries `nym-mixnode` and `nym-gateway` had been deprecated, [`nym-node`](../nym-node.mdx) is the only binary to use for `gateway` or `mixnode` functionalities!**
@@ -162,35 +186,7 @@ Make sure to use `--deny-init` flag to prevent initialisation of a new node.
**After you upgraded your node to the latest release of `nym-node`, make sure that you also follow [the steps to migrate your node in the Mixnet smart contract](bonding#migrate-to-nym-node-in-mixnet-smart-contract), othewise your node will never receive any rewards.**
## Functionality & Performance Check
We have a chapter called [Performance Monitoring & Testing](../nodes/performance-and-testing) including much more information and tooling. If you want to just quickly check your nodes performance, connectivity and much more, visit [harbourmaster.nymtech.net](https://harbourmaster.nymtech.net/).
For more information about available endpoints and their status, you can refer to:
```sh
# sustitude <IPv4_ADDRESS> or <HOSTNAME> with the one corresponding to your node
# for http
http://<IPv4_ADDRESS>:8080/api/v1/swagger/#/
# or
http://<IPv4_ADDRESS>/api/v1/swagger/#/
# for reversed proxy/WSS
https://<HOSTNAME>/api/v1/swagger/#/
```
For example to determine which mode your node is running, you can check the `:8080/api/v1/roles` endpoint:
```sh
# sustitude <IPv4_ADDRESS> or <HOSTNAME> with the one corresponding to your node
# for http
http://<IPv4_ADDRESS>:8080/api/v1/roles
# or
http://<IPv4_ADDRESS>/api/v1/roles
# for reversed proxy/WSS
https://<HOSTNAME>/api/v1/roles
```
## Next steps
### Next steps
If there are any problems checkout the troubleshooting section or report an issue.
@@ -8,34 +8,6 @@ As developers we need to be constantly improving the software. Operators have as
Therefore [monitoring](#monitoring) and [testing](#testing) are essential pieces of our common work. We call out all Nym operators to join the efforts!
## Functionality & Performance Check
If you want to just quickly check your nodes performance, connectivity and much more, visit [harbourmaster.nymtech.net](https://harbourmaster.nymtech.net/).
For more information about available endpoints and their status, you can refer to:
```sh
# sustitude <IPv4_ADDRESS> or <HOSTNAME> with the one corresponding to your node
# for http
http://<IPv4_ADDRESS>:8080/api/v1/swagger/#/
# or
http://<IPv4_ADDRESS>/api/v1/swagger/#/
# for reversed proxy/WSS
https://<HOSTNAME>/api/v1/swagger/#/
```
For example to determine which mode your node is running, you can check the `:8080/api/v1/roles` endpoint:
```sh
# sustitude <IPv4_ADDRESS> or <HOSTNAME> with the one corresponding to your node
# for http
http://<IPv4_ADDRESS>:8080/api/v1/roles
# or
http://<IPv4_ADDRESS>/api/v1/roles
# for reversed proxy/WSS
https://<HOSTNAME>/api/v1/roles
```
## Monitoring
There are multiple ways to monitor performance of nodes and the machines on which they run. For the purpose of maximal privacy and decentralisation of the data - preventing Nym Mixnet from any global adversary takeover - we created these pages as a source of mutual empowerment, a place where operators can share and learn new skills to **setup metrics monitors on their own infrastructure**.
@@ -1,4 +1,5 @@
{
"gateway-probe": "Gateway Probe",
"node-api-check": "Node API Check",
"prometheus-grafana": "Prometheus & Grafana"
}
@@ -4,7 +4,7 @@
There are a couple of steps that need completing before starting to set up your `nym-node`:
1. **[Prepare your wallet](preliminary-steps/wallet-preparation.mdx):** [desktop](https://nym.com/wallet) or [CLI](../../developers/tools/nym-cli/commands.mdx).
1. **[Prepare your wallet](preliminary-steps/wallet-preparation.mdx):** [desktop](https://nym.com/download/wallet) or [CLI](../../developers/tools/nym-cli/commands.mdx).
2. **[Requisition and setup a VPS](preliminary-steps/vps-setup.mdx)** (Virtual Private Server)
Make sure to follow these steps carefully as it prevents a lot of troubleshooting later on.
@@ -2,7 +2,7 @@
## Mainnet
Head to our [website](https://nym.com/wallet) and download Nym wallet for your operating system.
Head to our [website](https://nym.com/download/wallet) and download Nym wallet for your operating system.
{/*
@@ -47,7 +47,7 @@ curl -o sandbox.env -L https://raw.githubusercontent.com/nymtech/nym/develop/env
- In case you downloaded `sandbox.env` to same directory, `<PATH>` is not needed
###### 3. Bond your node to Nym Sandbox environment
- Open [Nym Wallet](https://nym.com/wallet) and switch to testnet
- Open [Nym Wallet](https://nym.com/download/wallet) and switch to testnet
- Go to [faucet.nymtech.net](https://faucet.nymtech.net) and aquire 101 testnet NYM tokens
- Follow the steps on the [bonding page](nodes/nym-node/bonding.mdx)
@@ -113,6 +113,8 @@ There are a few community explorers as well.
Enter your **identity key** to find your node. Check the contents of the `Mixnode stats` and `Routing score` sections.
You can run [Node API Check CLI](../testing/node-api-check.mdx) to query all API endpoints of your node at once.
[Here](https://github.com/cosmos/chain-registry/blob/master/nyx/chain.json#L158-L187) is a dictionary with Nyx chain registry entry regarding all explorers.
If you want more information, or if your node isn't showing up on the explorer of your choice and you want to double-check, here are some examples on how to check if the node is configured properly.
@@ -343,7 +345,7 @@ In case your Gateway appeared on the [blacklist](https://validator.nymtech.net/a
**What to do**
Begin with a sanity check by opening [harbourmaster.nymtech.net](https://harbourmaster.nymtech.net) and check your node there. To see IPv4 and IPv6 routing in real time (harbourmaster can have a cache up to 90 min), run [Gateway Probe CLI](../testing/gateway-probe.mdx).
Begin with a sanity check by opening [harbourmaster.nymtech.net](https://harbourmaster.nymtech.net) and check your node there. To query all API endpoints of your node at once, you can run [Node API Check CLI](../testing/node-api-check.mdx). To see IPv4 and IPv6 routing in real time (harbourmaster can have a cache up to 90 min), run [Gateway Probe CLI](../testing/gateway-probe.mdx).
Then follow these steps:
+4932 -5300
View File
File diff suppressed because it is too large Load Diff
+7 -6
View File
@@ -12,8 +12,7 @@ use js_sys::Promise;
use std::sync::Arc;
use wasm_bindgen::prelude::*;
use wasm_bindgen_futures::future_to_promise;
use wasm_storage::RawDbResult;
use wasm_storage::{Build, Database, VersionChangeEvent, WasmStorage};
use wasm_storage::{IdbVersionChangeEvent, WasmStorage};
use wasm_utils::check_promise_result;
use wasm_utils::error::{PromisableResult, PromisableResultError};
use zeroize::Zeroizing;
@@ -34,8 +33,8 @@ pub struct ExtensionStorage {
}
#[allow(clippy::type_complexity)]
fn db_migration() -> Box<dyn Fn(VersionChangeEvent, Database) -> RawDbResult<()>> {
Box::new(|evt: VersionChangeEvent, db: Database| -> RawDbResult<()> {
fn db_migration() -> Box<dyn Fn(&IdbVersionChangeEvent) -> Result<(), JsValue>> {
Box::new(|evt: &IdbVersionChangeEvent| -> Result<(), JsValue> {
// Even if the web-sys bindings expose the version as a f64, the IndexedDB API
// works with an unsigned integer.
// See <https://github.com/rustwasm/wasm-bindgen/issues/1149>
@@ -43,7 +42,9 @@ fn db_migration() -> Box<dyn Fn(VersionChangeEvent, Database) -> RawDbResult<()>
if old_version < 1 {
// migrating to version 1
db.create_object_store(v1::MNEMONICS_STORE).build()?;
let db = evt.db();
db.create_object_store(v1::MNEMONICS_STORE)?;
}
Ok(())
@@ -121,7 +122,7 @@ impl ExtensionStorage {
.map_err(Into::into)
}
async fn get_all_mnemonic_keys_async(&self) -> Result<Vec<JsValue>, ExtensionStorageError> {
async fn get_all_mnemonic_keys_async(&self) -> Result<js_sys::Array, ExtensionStorageError> {
self.inner
.get_all_keys(v1::MNEMONICS_STORE)
.await
-1
View File
@@ -16,7 +16,6 @@ license = "GPL-3.0"
[dependencies]
async-trait = { workspace = true }
anyhow.workspace = true
arc-swap = { workspace = true }
bip39 = { workspace = true, features = ["zeroize"] }
bs58.workspace = true
celes = { workspace = true } # country codes
+1 -1
View File
@@ -11,13 +11,13 @@ license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
base64 = { workspace = true }
celes = { workspace = true } # country codes
humantime = { workspace = true }
humantime-serde = { workspace = true }
schemars = { workspace = true, features = ["preserve_order"] }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
strum = { workspace = true, features = ["derive"] }
time = { workspace = true, features = ["serde", "formatting", "parsing"] }
thiserror = { workspace = true }
+3 -7
View File
@@ -11,16 +11,16 @@ use async_trait::async_trait;
use nym_bin_common::build_information::BinaryBuildInformationOwned;
use nym_http_api_client::{ApiClient, HttpClientError};
use super::v1::gateway::models::Wireguard;
use super::v1::metrics::models::SessionStats;
use crate::api::v1::authenticator::models::Authenticator;
use crate::api::v1::health::models::NodeHealth;
use crate::api::v1::ip_packet_router::models::IpPacketRouter;
use crate::api::v1::network_requester::exit_policy::models::UsedExitPolicy;
use crate::api::v1::network_requester::models::NetworkRequester;
use crate::api::v1::node_load::models::NodeLoad;
pub use nym_http_api_client::Client;
use super::v1::gateway::models::Wireguard;
use super::v1::metrics::models::SessionStats;
pub type NymNodeApiClientError = HttpClientError<ErrorResponse>;
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
@@ -30,10 +30,6 @@ pub trait NymNodeApiClientExt: ApiClient {
self.get_json_from(routes::api::v1::health_absolute()).await
}
async fn get_node_load(&self) -> Result<NodeLoad, NymNodeApiClientError> {
self.get_json_from(routes::api::v1::load_absolute()).await
}
async fn get_host_information(&self) -> Result<SignedHostInformation, NymNodeApiClientError> {
self.get_json_from(routes::api::v1::host_info_absolute())
.await
@@ -9,4 +9,3 @@ pub mod metrics;
pub mod mixnode;
pub mod network_requester;
pub mod node;
pub mod node_load;
@@ -1,4 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub mod models;
@@ -1,79 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use strum::{Display, EnumString};
#[derive(
Display,
Default,
Serialize,
Deserialize,
Clone,
Copy,
Debug,
EnumString,
PartialEq,
Eq,
JsonSchema,
PartialOrd,
)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub enum Load {
#[default]
Unknown,
// order of the variants matter as we've derived `PartialOrd` on them
Negligible, // 0 - 0.1
Low, // 0.1 - 0.3
Medium, // 0.3 - 0.6
High, // 0.6 - 0.8
VeryHigh, // 0.8 - 0.95
AtCapacity, // >= 0.95
}
impl Load {
// returns load of one tier higher
pub fn increment(&self) -> Self {
match self {
Self::Unknown => Self::Unknown,
Self::Negligible => Self::Low,
Self::Low => Self::Medium,
Self::Medium => Self::High,
Self::High => Self::VeryHigh,
Self::VeryHigh => Self::AtCapacity,
Self::AtCapacity => Self::AtCapacity,
}
}
}
impl From<f64> for Load {
fn from(value: f64) -> Self {
if value == 0. || value.is_nan() || value.is_infinite() {
Self::Unknown
} else if value <= 0.1 {
Self::Negligible
} else if value <= 0.3 {
Self::Low
} else if value <= 0.6 {
Self::Medium
} else if value <= 0.8 {
Self::High
} else if value <= 0.95 {
Self::VeryHigh
} else {
Self::AtCapacity
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone, Copy, JsonSchema)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct NodeLoad {
pub total: Load,
pub machine: Load,
pub network: Load,
}
-2
View File
@@ -35,7 +35,6 @@ pub mod routes {
pub const NODE_DESCRIPTION: &str = "/description";
pub const AUXILIARY: &str = "/auxiliary-details";
pub const HEALTH: &str = "/health";
pub const LOAD: &str = "/load";
pub const SWAGGER: &str = "/swagger";
pub const GATEWAY: &str = "/gateway";
@@ -47,7 +46,6 @@ pub mod routes {
// define helper functions to get absolute routes
absolute_route!(health_absolute, v1_absolute(), HEALTH);
absolute_route!(load_absolute, v1_absolute(), LOAD);
absolute_route!(roles_absolute, v1_absolute(), ROLES);
absolute_route!(build_info_absolute, v1_absolute(), BUILD_INFO);
absolute_route!(host_info_absolute, v1_absolute(), HOST_INFO);
+6 -6
View File
@@ -101,7 +101,7 @@ impl HostArgs {
#[derive(clap::Args, Debug)]
pub(crate) struct HttpArgs {
/// Socket address this node will use for binding its http API.
/// default: `[::]:8080`
/// default: `0.0.0.0:8080`
#[clap(
long,
env = NYMNODE_HTTP_BIND_ADDRESS_ARG
@@ -181,7 +181,7 @@ impl HttpArgs {
#[derive(clap::Args, Debug)]
pub(crate) struct MixnetArgs {
/// Address this node will bind to for listening for mixnet packets
/// default: `[::]:1789`
/// default: `0.0.0.0:1789`
#[clap(
long,
env = NYMNODE_MIXNET_BIND_ADDRESS_ARG
@@ -258,7 +258,7 @@ pub(crate) struct WireguardArgs {
pub(crate) wireguard_enabled: Option<bool>,
/// Socket address this node will use for binding its wireguard interface.
/// default: `[::]:51822`
/// default: `0.0.0.0:51822`
#[clap(
long,
env = NYMNODE_WG_BIND_ADDRESS_ARG
@@ -315,7 +315,7 @@ impl WireguardArgs {
#[derive(clap::Args, Debug)]
pub(crate) struct VerlocArgs {
/// Socket address this node will use for binding its verloc API.
/// default: `[::]:1790`
/// default: `0.0.0.0:1790`
#[clap(
long,
env = NYMNODE_VERLOC_BIND_ADDRESS_ARG
@@ -377,7 +377,7 @@ impl MetricsArgs {
#[derive(clap::Args, Debug, Zeroize, ZeroizeOnDrop)]
pub(crate) struct EntryGatewayArgs {
/// Socket address this node will use for binding its client websocket API.
/// default: `[::]:9000`
/// default: `0.0.0.0:9000`
#[clap(
long,
env = NYMNODE_ENTRY_BIND_ADDRESS_ARG
@@ -431,7 +431,7 @@ impl EntryGatewayArgs {
mut section: config::GatewayTasksConfig,
) -> config::GatewayTasksConfig {
if let Some(bind_address) = self.entry_bind_address {
section.ws_bind_address = bind_address
section.bind_address = bind_address
}
if let Some(ws_port) = self.announce_ws_port {
section.announce_ws_port = Some(ws_port)
+4 -4
View File
@@ -3,7 +3,7 @@
use crate::config::persistence::GatewayTasksPaths;
use nym_config::defaults::{DEFAULT_CLIENT_LISTENING_PORT, TICKETBOOK_VALIDITY_DAYS};
use nym_config::helpers::in6addr_any_init;
use nym_config::helpers::inaddr_any;
use nym_config::serde_helpers::de_maybe_port;
use serde::{Deserialize, Serialize};
use std::net::SocketAddr;
@@ -22,8 +22,8 @@ pub struct GatewayTasksConfig {
pub enforce_zk_nyms: bool,
/// Socket address this node will use for binding its client websocket API.
/// default: `[::]:9000`
pub ws_bind_address: SocketAddr,
/// 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
@@ -188,7 +188,7 @@ impl GatewayTasksConfig {
GatewayTasksConfig {
storage_paths: GatewayTasksPaths::new(data_dir),
enforce_zk_nyms: false,
ws_bind_address: SocketAddr::new(in6addr_any_init(), DEFAULT_WS_PORT),
bind_address: SocketAddr::new(inaddr_any(), DEFAULT_WS_PORT),
announce_ws_port: None,
announce_wss_port: None,
debug: Default::default(),
+1 -1
View File
@@ -13,7 +13,7 @@ fn ephemeral_gateway_config(config: &Config) -> nym_gateway::config::Config {
nym_gateway::config::Config::new(
nym_gateway::config::Gateway {
enforce_zk_nyms: config.gateway_tasks.enforce_zk_nyms,
websocket_bind_address: config.gateway_tasks.ws_bind_address,
websocket_bind_address: config.gateway_tasks.bind_address,
nym_api_urls: config.mixnet.nym_api_urls.clone(),
nyxd_urls: config.mixnet.nyxd_urls.clone(),
},
+8 -18
View File
@@ -13,7 +13,7 @@ use nym_config::defaults::{
WG_TUN_DEVICE_IP_ADDRESS_V6,
};
use nym_config::defaults::{WG_TUN_DEVICE_NETMASK_V4, WG_TUN_DEVICE_NETMASK_V6};
use nym_config::helpers::{in6addr_any_init, inaddr_any};
use nym_config::helpers::inaddr_any;
use nym_config::serde_helpers::de_maybe_port;
use nym_config::serde_helpers::de_maybe_stringified;
use nym_config::{
@@ -444,7 +444,7 @@ pub struct Host {
#[serde(deny_unknown_fields)]
pub struct Http {
/// Socket address this node will use for binding its http API.
/// default: `[::]:8080`
/// default: `0.0.0.0:8080`
pub bind_address: SocketAddr,
/// Path to assets directory of custom landing page of this node.
@@ -470,27 +470,17 @@ pub struct Http {
/// This option is superseded by `expose_system_hardware`
/// default: true
pub expose_crypto_hardware: bool,
/// Specify the cache ttl of the node load.
/// default: 30s
#[serde(with = "humantime_serde")]
pub node_load_cache_ttl: Duration,
}
impl Http {
pub const DEFAULT_NODE_LOAD_CACHE_TTL: Duration = Duration::from_secs(30);
}
impl Default for Http {
fn default() -> Self {
Http {
bind_address: SocketAddr::new(in6addr_any_init(), DEFAULT_HTTP_PORT),
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,
node_load_cache_ttl: Self::DEFAULT_NODE_LOAD_CACHE_TTL,
}
}
}
@@ -500,7 +490,7 @@ impl Default for Http {
#[serde(deny_unknown_fields)]
pub struct Mixnet {
/// Address this node will bind to for listening for mixnet packets
/// default: `[::]:1789`
/// 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
@@ -594,7 +584,7 @@ impl Default for Mixnet {
};
Mixnet {
bind_address: SocketAddr::new(in6addr_any_init(), DEFAULT_MIXNET_PORT),
bind_address: SocketAddr::new(inaddr_any(), DEFAULT_MIXNET_PORT),
announce_port: None,
nym_api_urls,
nyxd_urls,
@@ -607,7 +597,7 @@ impl Default for Mixnet {
#[serde(deny_unknown_fields)]
pub struct Verloc {
/// Socket address this node will use for binding its verloc API.
/// default: `[::]:1790`
/// default: `0.0.0.0:1790`
pub bind_address: SocketAddr,
/// If applicable, custom port announced in the self-described API that other clients and nodes
@@ -628,7 +618,7 @@ impl Verloc {
impl Default for Verloc {
fn default() -> Self {
Verloc {
bind_address: SocketAddr::new(in6addr_any_init(), Self::DEFAULT_VERLOC_PORT),
bind_address: SocketAddr::new(inaddr_any(), Self::DEFAULT_VERLOC_PORT),
announce_port: None,
debug: Default::default(),
}
@@ -697,7 +687,7 @@ pub struct Wireguard {
pub enabled: bool,
/// Socket address this node will use for binding its wireguard interface.
/// default: `[::]:51822`
/// default: `0.0.0.0:51822`
pub bind_address: SocketAddr,
/// Private IPv4 address of the wireguard gateway.
-2
View File
@@ -7,7 +7,6 @@ mod old_config_v3;
mod old_config_v4;
mod old_config_v5;
mod old_config_v6;
mod old_config_v7;
pub use old_config_v1::try_upgrade_config_v1;
pub use old_config_v2::try_upgrade_config_v2;
@@ -15,4 +14,3 @@ pub use old_config_v3::try_upgrade_config_v3;
pub use old_config_v4::try_upgrade_config_v4;
pub use old_config_v5::try_upgrade_config_v5;
pub use old_config_v6::try_upgrade_config_v6;
pub use old_config_v7::try_upgrade_config_v7;
@@ -3,6 +3,11 @@
#![allow(dead_code)]
use crate::config::authenticator::{Authenticator, AuthenticatorDebug};
use crate::config::gateway_tasks::ZkNymTicketHandlerDebug;
use crate::config::service_providers::{
IpPacketRouter, IpPacketRouterDebug, NetworkRequester, NetworkRequesterDebug,
};
use crate::config::*;
use crate::error::NymNodeError;
use celes::Country;
@@ -18,7 +23,6 @@ use nym_config::{
serde_helpers::{de_maybe_port, de_maybe_stringified},
};
use nym_config::{parse_urls, read_config_from_toml_file};
use old_configs::old_config_v7::*;
use persistence::*;
use serde::{Deserialize, Serialize};
use std::env;
@@ -81,13 +85,13 @@ pub enum NodeModeV6 {
ExitGateway,
}
impl From<NodeModeV6> for NodeModesV7 {
impl From<NodeModeV6> for NodeModes {
fn from(config: NodeModeV6) -> Self {
match config {
NodeModeV6::Mixnode => *NodeModesV7::default().with_mixnode(),
NodeModeV6::EntryGateway => *NodeModesV7::default().with_entry(),
NodeModeV6::Mixnode => *NodeModes::default().with_mixnode(),
NodeModeV6::EntryGateway => *NodeModes::default().with_entry(),
// in old version exit implied entry
NodeModeV6::ExitGateway => *NodeModesV7::default().with_entry().with_exit(),
NodeModeV6::ExitGateway => *NodeModes::default().with_entry().with_exit(),
}
}
}
@@ -949,7 +953,7 @@ impl ConfigV6 {
pub async fn try_upgrade_config_v6<P: AsRef<Path>>(
path: P,
prev_config: Option<ConfigV6>,
) -> Result<ConfigV7, NymNodeError> {
) -> Result<Config, NymNodeError> {
debug!("attempting to load v6 config...");
let old_cfg = if let Some(prev_config) = prev_config {
@@ -958,22 +962,22 @@ pub async fn try_upgrade_config_v6<P: AsRef<Path>>(
ConfigV6::read_from_path(&path)?
};
let cfg = ConfigV7 {
let cfg = Config {
save_path: old_cfg.save_path,
id: old_cfg.id,
modes: old_cfg.mode.into(),
host: HostV7 {
host: Host {
public_ips: old_cfg.host.public_ips,
hostname: old_cfg.host.hostname,
location: old_cfg.host.location,
},
mixnet: MixnetV7 {
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: MixnetDebugV7 {
maximum_forward_packet_delay: MixnetDebugV7::DEFAULT_MAXIMUM_FORWARD_PACKET_DELAY,
debug: MixnetDebug {
maximum_forward_packet_delay: MixnetDebug::DEFAULT_MAXIMUM_FORWARD_PACKET_DELAY,
packet_forwarding_initial_backoff: old_cfg
.mixnet
.debug
@@ -987,8 +991,8 @@ pub async fn try_upgrade_config_v6<P: AsRef<Path>>(
unsafe_disable_noise: old_cfg.mixnet.debug.unsafe_disable_noise,
},
},
storage_paths: NymNodePathsV7 {
keys: KeysPathsV7 {
storage_paths: NymNodePaths {
keys: KeysPaths {
private_ed25519_identity_key_file: old_cfg
.storage_paths
.keys
@@ -1016,7 +1020,7 @@ pub async fn try_upgrade_config_v6<P: AsRef<Path>>(
},
description: old_cfg.storage_paths.description,
},
http: HttpV7 {
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,
@@ -1024,10 +1028,10 @@ pub async fn try_upgrade_config_v6<P: AsRef<Path>>(
expose_system_hardware: old_cfg.http.expose_system_hardware,
expose_crypto_hardware: old_cfg.http.expose_crypto_hardware,
},
verloc: VerlocV7 {
verloc: Verloc {
bind_address: old_cfg.mixnode.verloc.bind_address,
announce_port: old_cfg.mixnode.verloc.announce_port,
debug: VerlocDebugV7 {
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,
@@ -1037,7 +1041,7 @@ pub async fn try_upgrade_config_v6<P: AsRef<Path>>(
retry_timeout: old_cfg.mixnode.verloc.debug.retry_timeout,
},
},
wireguard: WireguardV7 {
wireguard: Wireguard {
enabled: old_cfg.wireguard.enabled,
bind_address: old_cfg.wireguard.bind_address,
private_ipv4: old_cfg.wireguard.private_ipv4,
@@ -1045,7 +1049,7 @@ pub async fn try_upgrade_config_v6<P: AsRef<Path>>(
announced_port: old_cfg.wireguard.announced_port,
private_network_prefix_v4: old_cfg.wireguard.private_network_prefix_v4,
private_network_prefix_v6: old_cfg.wireguard.private_network_prefix_v6,
storage_paths: WireguardPathsV7 {
storage_paths: WireguardPaths {
private_diffie_hellman_key_file: old_cfg
.wireguard
.storage_paths
@@ -1056,8 +1060,8 @@ pub async fn try_upgrade_config_v6<P: AsRef<Path>>(
.public_diffie_hellman_key_file,
},
},
gateway_tasks: GatewayTasksConfigV7 {
storage_paths: GatewayTasksPathsV7 {
gateway_tasks: GatewayTasksConfig {
storage_paths: GatewayTasksPaths {
clients_storage: old_cfg.entry_gateway.storage_paths.clients_storage,
stats_storage: old_cfg.entry_gateway.storage_paths.stats_storage,
cosmos_mnemonic: old_cfg.entry_gateway.storage_paths.cosmos_mnemonic,
@@ -1066,9 +1070,9 @@ pub async fn try_upgrade_config_v6<P: AsRef<Path>>(
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: GatewayTasksConfigDebugV7 {
debug: gateway_tasks::Debug {
message_retrieval_limit: old_cfg.entry_gateway.debug.message_retrieval_limit,
zk_nym_tickets: ZkNymTicketHandlerDebugV7 {
zk_nym_tickets: ZkNymTicketHandlerDebug {
revocation_bandwidth_penalty: old_cfg
.entry_gateway
.debug
@@ -1094,11 +1098,11 @@ pub async fn try_upgrade_config_v6<P: AsRef<Path>>(
..Default::default()
},
},
service_providers: ServiceProvidersConfigV7 {
storage_paths: ServiceProvidersPathsV7 {
service_providers: ServiceProvidersConfig {
storage_paths: ServiceProvidersPaths {
clients_storage: old_cfg.exit_gateway.storage_paths.clients_storage,
stats_storage: old_cfg.exit_gateway.storage_paths.stats_storage,
network_requester: NetworkRequesterPathsV7 {
network_requester: NetworkRequesterPaths {
private_ed25519_identity_key_file: old_cfg
.exit_gateway
.storage_paths
@@ -1135,7 +1139,7 @@ pub async fn try_upgrade_config_v6<P: AsRef<Path>>(
.network_requester
.gateway_registrations,
},
ip_packet_router: IpPacketRouterPathsV7 {
ip_packet_router: IpPacketRouterPaths {
private_ed25519_identity_key_file: old_cfg
.exit_gateway
.storage_paths
@@ -1172,7 +1176,7 @@ pub async fn try_upgrade_config_v6<P: AsRef<Path>>(
.ip_packet_router
.gateway_registrations,
},
authenticator: AuthenticatorPathsV7 {
authenticator: AuthenticatorPaths {
private_ed25519_identity_key_file: old_cfg
.exit_gateway
.storage_paths
@@ -1212,8 +1216,8 @@ pub async fn try_upgrade_config_v6<P: AsRef<Path>>(
},
open_proxy: old_cfg.exit_gateway.open_proxy,
upstream_exit_policy_url: old_cfg.exit_gateway.upstream_exit_policy_url,
network_requester: NetworkRequesterV7 {
debug: NetworkRequesterDebugV7 {
network_requester: NetworkRequester {
debug: NetworkRequesterDebug {
enabled: old_cfg.exit_gateway.network_requester.debug.enabled,
disable_poisson_rate: old_cfg
.exit_gateway
@@ -1223,8 +1227,8 @@ pub async fn try_upgrade_config_v6<P: AsRef<Path>>(
client_debug: old_cfg.exit_gateway.network_requester.debug.client_debug,
},
},
ip_packet_router: IpPacketRouterV7 {
debug: IpPacketRouterDebugV7 {
ip_packet_router: IpPacketRouter {
debug: IpPacketRouterDebug {
enabled: old_cfg.exit_gateway.ip_packet_router.debug.enabled,
disable_poisson_rate: old_cfg
.exit_gateway
@@ -1234,19 +1238,19 @@ pub async fn try_upgrade_config_v6<P: AsRef<Path>>(
client_debug: old_cfg.exit_gateway.ip_packet_router.debug.client_debug,
},
},
authenticator: AuthenticatorV7 {
debug: AuthenticatorDebugV7 {
authenticator: Authenticator {
debug: AuthenticatorDebug {
enabled: old_cfg.authenticator.debug.enabled,
disable_poisson_rate: old_cfg.authenticator.debug.disable_poisson_rate,
client_debug: old_cfg.authenticator.debug.client_debug,
},
},
debug: ServiceProvidersConfigDebugV7 {
debug: service_providers::Debug {
message_retrieval_limit: old_cfg.exit_gateway.debug.message_retrieval_limit,
},
},
metrics: Default::default(),
logging: LoggingSettingsV7 {},
logging: LoggingSettings {},
debug: Default::default(),
};
Ok(cfg)
File diff suppressed because it is too large Load Diff
+1
View File
@@ -30,6 +30,7 @@ pub struct ServiceProvidersConfig {
#[serde(default)]
pub debug: Debug,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Debug {
+6 -6
View File
@@ -46,7 +46,7 @@ location = '{{ host.location }}'
[mixnet]
# Address this node will bind to for listening for mixnet packets
# default: `[::]:1789`
# default: `0.0.0.0:1789`
bind_address = '{{ mixnet.bind_address }}'
# If applicable, custom port announced in the self-described API that other clients and nodes
@@ -95,7 +95,7 @@ public_x25519_noise_key_file = '{{ storage_paths.keys.public_x25519_noise_key_fi
[http]
# Socket address this node will use for binding its http API.
# default: `[::]:8080`
# default: `0.0.0.0:8080`
bind_address = '{{ http.bind_address }}'
# Path to assets directory of custom landing page of this node
@@ -126,7 +126,7 @@ expose_crypto_hardware = {{ http.expose_crypto_hardware }}
enabled = {{ wireguard.enabled }}
# Socket address this node will use for binding its wireguard interface.
# default: `[::]:51822`
# default: `0.0.0.0:51822`
bind_address = '{{ wireguard.bind_address }}'
# Private IP address of the wireguard gateway.
@@ -161,7 +161,7 @@ public_diffie_hellman_key_file = '{{ wireguard.storage_paths.public_diffie_hellm
[verloc]
# Socket address this node will use for binding its verloc API.
# default: `[::]:1790`
# default: `0.0.0.0:1790`
bind_address = '{{ verloc.bind_address }}'
# If applicable, custom port announced in the self-described API that other clients and nodes
@@ -179,8 +179,8 @@ announce_port ={{#if verloc.announce_port }} {{ verloc.announce_port }} {{else}}
enforce_zk_nyms = {{ gateway_tasks.enforce_zk_nyms }}
# Socket address this node will use for binding its client websocket API.
# default: `[::]:9000`
ws_bind_address = '{{ gateway_tasks.ws_bind_address }}'
# default: `0.0.0.0:9000`
bind_address = '{{ gateway_tasks.bind_address }}'
# Custom announced port for listening for websocket client traffic.
# If unspecified, the value from the `bind_address` will be used instead
+1 -2
View File
@@ -14,8 +14,7 @@ async fn try_upgrade_config(path: &Path) -> Result<(), NymNodeError> {
let cfg = try_upgrade_config_v3(path, cfg).await.ok();
let cfg = try_upgrade_config_v4(path, cfg).await.ok();
let cfg = try_upgrade_config_v5(path, cfg).await.ok();
let cfg = try_upgrade_config_v6(path, cfg).await.ok();
match try_upgrade_config_v7(path, cfg).await {
match try_upgrade_config_v6(path, cfg).await {
Ok(cfg) => cfg.save(),
Err(e) => {
tracing::error!("Failed to finish upgrade - {e}");
@@ -72,7 +72,7 @@ pub(crate) fn get_system_info(
system_name: System::name(),
kernel_version: System::kernel_version(),
os_version: System::os_version(),
cpu_arch: Some(System::cpu_arch()),
cpu_arch: System::cpu_arch(),
hardware,
}
}
@@ -14,8 +14,8 @@ use nym_node_requests::api::v1::health::models::NodeHealth;
tag = "Health",
responses(
(status = 200, content(
(NodeHealth = "application/json"),
(NodeHealth = "application/yaml")
(Vec<NodeHealth> = "application/json"),
(Vec<NodeHealth> = "application/yaml")
), description = "the api is available and healthy")
),
params(OutputParams)
@@ -1,33 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::node::http::state::AppState;
use axum::extract::{Query, State};
use nym_http_api_common::{FormattedResponse, OutputParams};
use nym_node_requests::api::v1::node_load::models::NodeLoad;
/// Returns current relative load this node.
#[utoipa::path(
get,
path = "/load",
context_path = "/api/v1",
tag = "Node",
responses(
(status = 200, content(
(NodeLoad = "application/json"),
(NodeLoad = "application/yaml")
), description = "current node load")
),
params(OutputParams)
)]
pub(crate) async fn root_load(
Query(output): Query<OutputParams>,
State(state): State<AppState>,
) -> NodeLoadResponse {
let output = output.output.unwrap_or_default();
let load = state.cached_load.current_load();
output.to_response(load)
}
pub type NodeLoadResponse = FormattedResponse<NodeLoad>;
@@ -10,7 +10,6 @@ pub mod authenticator;
pub mod gateway;
pub mod health;
pub mod ip_packet_router;
pub mod load;
pub mod metrics;
pub mod mixnode;
pub mod network_requester;
@@ -31,7 +30,6 @@ pub struct Config {
pub(super) fn routes(config: Config) -> Router<AppState> {
Router::new()
.route(v1::HEALTH, get(health::root_health))
.route(v1::LOAD, get(load::root_load))
.nest(v1::METRICS, metrics::routes(config.metrics))
.nest(v1::GATEWAY, gateway::routes(config.gateway))
.nest(v1::MIXNODE, mixnode::routes(config.mixnode))
@@ -24,7 +24,6 @@ use utoipa_swagger_ui::SwaggerUi;
api::v1::metrics::verloc::verloc_stats,
api::v1::metrics::prometheus::prometheus_metrics,
api::v1::health::root_health,
api::v1::load::root_load,
api::v1::gateway::root::root_gateway,
api::v1::gateway::client_interfaces::client_interfaces,
api::v1::gateway::client_interfaces::mixnet_websockets,
@@ -39,8 +38,6 @@ use utoipa_swagger_ui::SwaggerUi;
nym_http_api_common::OutputParams,
api_requests::v1::health::models::NodeHealth,
api_requests::v1::health::models::NodeStatus,
api_requests::v1::node_load::models::NodeLoad,
api_requests::v1::node_load::models::Load,
api_requests::v1::node::models::BinaryBuildInformationOwned,
api_requests::v1::node::models::HostInformation,
api_requests::v1::node::models::HostKeys,
-209
View File
@@ -1,209 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use arc_swap::ArcSwap;
use nym_node_requests::api::v1::node_load::models::{Load, NodeLoad};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::time::Duration;
use sysinfo::{CpuRefreshKind, MemoryRefreshKind, Networks, System};
use time::OffsetDateTime;
use tracing::debug;
#[derive(Clone)]
pub struct CachedNodeLoad {
ttl: Duration,
being_updated: Arc<AtomicBool>,
inner: Arc<ArcSwap<CachedNodeLoadInner>>,
}
impl CachedNodeLoad {
pub(crate) fn new(ttl: Duration) -> Self {
CachedNodeLoad {
ttl,
being_updated: Arc::new(Default::default()),
inner: Arc::new(ArcSwap::new(Arc::new(CachedNodeLoadInner::initial()))),
}
}
pub(crate) fn current_load(&self) -> NodeLoad {
let now = OffsetDateTime::now_utc();
let inner = self.inner.load();
if inner.timestamp + self.ttl < now {
// new
let already_being_updated = self.being_updated.swap(true, Ordering::SeqCst);
if already_being_updated {
// use the 'stale' entry because it is already being updated by another thread
inner.load
} else {
self.update_cache()
}
} else {
inner.load
}
}
fn update_cache(&self) -> NodeLoad {
let old = self.inner.load();
let current_load = CachedNodeLoadInner::update(&old);
let load = current_load.load;
self.inner.store(Arc::new(current_load));
self.being_updated.store(false, Ordering::SeqCst);
load
}
}
pub struct CachedNodeLoadInner {
timestamp: OffsetDateTime,
last_transmitted: u64,
last_received: u64,
load: NodeLoad,
}
struct RawUsage {
load_per_cpu: f64,
memory_usage: f64,
swap_usage: f64,
eth_transmitted: u64,
eth_received: u64,
total_swap: u64,
}
impl RawUsage {
fn current() -> RawUsage {
let mut system = sysinfo::System::new();
let networks = Networks::new_with_refreshed_list();
system.refresh_memory_specifics(MemoryRefreshKind::everything());
system.refresh_cpu_specifics(CpuRefreshKind::nothing());
let average_load = System::load_average();
let cpu_count = system.cpus().len();
let load_per_cpu = average_load.five / cpu_count as f64;
let total_memory = system.total_memory();
let used_memory = system.used_memory();
let memory_usage = used_memory as f64 / total_memory as f64;
let total_swap = system.total_swap();
let used_swap = system.free_swap();
let swap_usage = used_swap as f64 / total_swap as f64;
let mut eth_transmitted = 0;
let mut eth_received = 0;
// we're only interested in interfaces with 'eth' or 'en' prefix
// (that's a very weak assumption, but that's just first iteration of this endpoint)
for (interface, data) in networks.list() {
if interface.starts_with("eth") || interface.starts_with("en") {
eth_transmitted += data.total_transmitted();
eth_received += data.total_received();
}
}
debug!(average_load = ?average_load, memory_usage=memory_usage, swap_usage = swap_usage, "current load");
RawUsage {
load_per_cpu,
memory_usage,
swap_usage,
eth_transmitted,
eth_received,
total_swap,
}
}
}
impl CachedNodeLoadInner {
pub fn initial() -> CachedNodeLoadInner {
let timestamp = OffsetDateTime::now_utc();
let raw_usage = RawUsage::current();
let mut base_load = Load::from(raw_usage.load_per_cpu);
let memory_load = Load::from(raw_usage.memory_usage);
// if memory load is of higher tier, increment the base load by one level
// (i.e. for example from 'Low' to 'Medium')
if memory_load > base_load {
base_load = base_load.increment();
}
if raw_usage.total_swap > 1024 * 1024 * 1024 {
// same with swap
let swap_load = Load::from(raw_usage.swap_usage);
if swap_load > base_load {
base_load = base_load.increment();
}
}
CachedNodeLoadInner {
timestamp,
last_transmitted: raw_usage.eth_transmitted,
last_received: raw_usage.eth_received,
load: NodeLoad {
total: base_load,
machine: base_load,
network: Load::Unknown,
},
}
}
pub fn update(previous: &Self) -> CachedNodeLoadInner {
let timestamp = OffsetDateTime::now_utc();
let raw_usage = RawUsage::current();
let time_delta = (timestamp - previous.timestamp).as_seconds_f64();
let tx_delta = raw_usage.eth_transmitted - previous.last_transmitted;
let rx_delta = raw_usage.eth_received - previous.last_received;
let tx_bs = tx_delta as f64 / time_delta;
let rx_bs = rx_delta as f64 / time_delta;
// currently we consider value of 1Gbps to be maximum load
// in the future we should allow specifying custom sizes of network cards
// (Gbps = Bytes/s * 0.000000008)
let tx_gbps = tx_bs * 0.000000008;
let rx_gbps = rx_bs * 0.000000008;
let network_load = Load::from(tx_gbps.max(rx_gbps));
debug!(tx_gbps = tx_gbps, rx_gbps = rx_gbps, "network load");
let mut base_load = Load::from(raw_usage.load_per_cpu);
let memory_load = Load::from(raw_usage.memory_usage);
// if memory load is of higher tier, increment the base load by one level
// (i.e. for example from 'Low' to 'Medium')
if memory_load > base_load {
base_load = base_load.increment();
}
if raw_usage.total_swap > 1024 * 1024 * 1024 {
// same with swap
let swap_load = Load::from(raw_usage.swap_usage);
if swap_load > base_load {
base_load = base_load.increment();
}
}
let total_load = if base_load > network_load {
base_load
} else {
base_load.increment()
};
CachedNodeLoadInner {
timestamp,
last_transmitted: raw_usage.eth_transmitted,
last_received: raw_usage.eth_received,
load: NodeLoad {
total: total_load,
machine: base_load,
network: network_load,
},
}
}
}
+1 -11
View File
@@ -1,39 +1,29 @@
// Copyright 2023-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::node::http::state::load::CachedNodeLoad;
use crate::node::http::state::metrics::MetricsAppState;
use nym_node_metrics::NymNodeMetrics;
use nym_verloc::measurements::SharedVerlocStats;
use std::time::Duration;
use tokio::time::Instant;
pub mod load;
pub mod metrics;
#[derive(Clone)]
pub struct AppState {
pub(crate) startup_time: Instant,
pub(crate) cached_load: CachedNodeLoad,
pub(crate) metrics: MetricsAppState,
}
impl AppState {
#[allow(clippy::new_without_default)]
pub fn new(
metrics: NymNodeMetrics,
verloc: SharedVerlocStats,
load_cache_ttl: Duration,
) -> Self {
pub fn new(metrics: NymNodeMetrics, verloc: SharedVerlocStats) -> Self {
AppState {
// is it 100% accurate?
// no.
// does it have to be?
// also no.
startup_time: Instant::now(),
cached_load: CachedNodeLoad::new(load_cache_ttl),
metrics: MetricsAppState { metrics, verloc },
}
}
+4 -8
View File
@@ -560,7 +560,7 @@ impl NymNode {
.config
.gateway_tasks
.announce_ws_port
.unwrap_or(self.config.gateway_tasks.ws_bind_address.port());
.unwrap_or(self.config.gateway_tasks.bind_address.port());
Ok(nym_topology::RoutingNode {
node_id: u32::MAX,
@@ -606,7 +606,7 @@ impl NymNode {
if self.modes().entry {
info!(
"starting the clients websocket... on {}",
self.config.gateway_tasks.ws_bind_address
self.config.gateway_tasks.bind_address
);
let websocket = gateway_tasks_builder
.build_websocket_listener(active_clients_store.clone())
@@ -711,7 +711,7 @@ impl NymNode {
.config
.gateway_tasks
.announce_ws_port
.unwrap_or(self.config.gateway_tasks.ws_bind_address.port()),
.unwrap_or(self.config.gateway_tasks.bind_address.port()),
wss_port: self.config.gateway_tasks.announce_wss_port,
});
let gateway_details = api_requests::v1::gateway::models::Gateway {
@@ -785,11 +785,7 @@ impl NymNode {
config.api.v1_config.node.roles.ip_packet_router_enabled = true;
}
let app_state = AppState::new(
self.metrics.clone(),
self.verloc_stats.clone(),
self.config.http.node_load_cache_ttl,
);
let app_state = AppState::new(self.metrics.clone(), self.verloc_stats.clone());
Ok(NymNodeRouter::new(config, app_state)
.build_server(&self.config.http.bind_address)
+37 -59
View File
@@ -806,19 +806,19 @@ version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32560304ab4c365791fd307282f76637213d8083c1a98490c35159cd67852237"
dependencies = [
"prost 0.12.3",
"prost",
"prost-types",
"tendermint-proto 0.34.0",
]
[[package]]
name = "cosmos-sdk-proto"
version = "0.26.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "462e1f6a8e005acc8835d32d60cbd7973ed65ea2a8d8473830e675f050956427"
version = "0.22.0-pre"
source = "git+https://github.com/cosmos/cosmos-rust?rev=4b1332e6d8258ac845cef71589c8d362a669675a#4b1332e6d8258ac845cef71589c8d362a669675a"
dependencies = [
"prost 0.13.4",
"tendermint-proto 0.40.1",
"prost",
"prost-types",
"tendermint-proto 0.37.0",
]
[[package]]
@@ -843,12 +843,11 @@ dependencies = [
[[package]]
name = "cosmrs"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "210fbe6f98594963b46cc980f126a9ede5db9a3848ca65b71303bebdb01afcd9"
version = "0.17.0-pre"
source = "git+https://github.com/cosmos/cosmos-rust?rev=4b1332e6d8258ac845cef71589c8d362a669675a#4b1332e6d8258ac845cef71589c8d362a669675a"
dependencies = [
"bip32",
"cosmos-sdk-proto 0.26.1",
"cosmos-sdk-proto 0.22.0-pre",
"ecdsa",
"eyre",
"k256",
@@ -857,7 +856,7 @@ dependencies = [
"serde_json",
"signature",
"subtle-encoding",
"tendermint 0.40.1",
"tendermint 0.37.0",
"tendermint-rpc",
"thiserror 1.0.64",
]
@@ -3073,7 +3072,7 @@ name = "nym-api-requests"
version = "0.1.0"
dependencies = [
"bs58",
"cosmrs 0.21.0",
"cosmrs 0.17.0-pre",
"cosmwasm-std",
"ecdsa",
"getset",
@@ -3091,7 +3090,7 @@ dependencies = [
"serde",
"serde_json",
"sha2 0.10.8",
"tendermint 0.40.1",
"tendermint 0.37.0",
"thiserror 1.0.64",
"time",
"utoipa",
@@ -3329,6 +3328,7 @@ name = "nym-node-requests"
version = "0.1.0"
dependencies = [
"async-trait",
"base64 0.22.1",
"celes",
"humantime 2.1.0",
"humantime-serde",
@@ -3340,7 +3340,6 @@ dependencies = [
"schemars",
"serde",
"serde_json",
"strum 0.26.3",
"thiserror 1.0.64",
"time",
"utoipa",
@@ -3406,7 +3405,7 @@ name = "nym-types"
version = "1.0.0"
dependencies = [
"base64 0.22.1",
"cosmrs 0.21.0",
"cosmrs 0.17.0-pre",
"cosmwasm-std",
"eyre",
"hmac",
@@ -3439,7 +3438,7 @@ dependencies = [
"bip32",
"bip39",
"colored 2.0.4",
"cosmrs 0.21.0",
"cosmrs 0.17.0-pre",
"cosmwasm-std",
"cw-controllers",
"cw-utils",
@@ -3464,7 +3463,7 @@ dependencies = [
"nym-network-defaults",
"nym-serde-helpers",
"nym-vesting-contract-common",
"prost 0.13.4",
"prost",
"reqwest 0.12.4",
"serde",
"serde_json",
@@ -3547,7 +3546,7 @@ dependencies = [
"bip39",
"cfg-if",
"colored 2.0.4",
"cosmrs 0.21.0",
"cosmrs 0.17.0-pre",
"cosmwasm-std",
"dirs 4.0.0",
"dotenvy",
@@ -4187,17 +4186,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "146c289cda302b98a28d40c8b3b90498d6e526dd24ac2ecea73e4e491685b94a"
dependencies = [
"bytes",
"prost-derive 0.12.3",
]
[[package]]
name = "prost"
version = "0.13.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c0fef6c4230e4ccf618a35c59d7ede15dea37de8427500f50aff708806e42ec"
dependencies = [
"bytes",
"prost-derive 0.13.4",
"prost-derive",
]
[[package]]
@@ -4213,26 +4202,13 @@ dependencies = [
"syn 2.0.96",
]
[[package]]
name = "prost-derive"
version = "0.13.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "157c5a9d7ea5c2ed2d9fb8f495b64759f7816c7eaea54ba3978f0d63000162e3"
dependencies = [
"anyhow",
"itertools 0.13.0",
"proc-macro2",
"quote",
"syn 2.0.85",
]
[[package]]
name = "prost-types"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "193898f59edcf43c26227dcd4c8427f00d99d61e95dcde58dabd49fa291d470e"
dependencies = [
"prost 0.12.3",
"prost",
]
[[package]]
@@ -5652,7 +5628,7 @@ dependencies = [
"k256",
"num-traits",
"once_cell",
"prost 0.12.3",
"prost",
"prost-types",
"ripemd",
"serde",
@@ -5670,9 +5646,9 @@ dependencies = [
[[package]]
name = "tendermint"
version = "0.40.1"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9703e34d940c2a293804752555107f8dbe2b84ec4c6dd5203831235868105d2"
checksum = "954496fbc9716eb4446cdd6d00c071a3e2f22578d62aa03b40c7e5b4fda3ed42"
dependencies = [
"bytes",
"digest 0.10.7",
@@ -5683,7 +5659,8 @@ dependencies = [
"k256",
"num-traits",
"once_cell",
"prost 0.13.4",
"prost",
"prost-types",
"ripemd",
"serde",
"serde_bytes",
@@ -5693,21 +5670,21 @@ dependencies = [
"signature",
"subtle 2.5.0",
"subtle-encoding",
"tendermint-proto 0.40.1",
"tendermint-proto 0.37.0",
"time",
"zeroize",
]
[[package]]
name = "tendermint-config"
version = "0.40.1"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89cc3ea9a39b7ee34eefcff771cc067ecaa0c988c1c5ac08defd878471a06f76"
checksum = "f84b11b57d20ee4492a1452faff85f5c520adc36ca9fe5e701066935255bb89f"
dependencies = [
"flex-error",
"serde",
"serde_json",
"tendermint 0.40.1",
"tendermint 0.37.0",
"toml 0.8.19",
"url",
]
@@ -5722,7 +5699,7 @@ dependencies = [
"flex-error",
"num-derive",
"num-traits",
"prost 0.12.3",
"prost",
"prost-types",
"serde",
"serde_bytes",
@@ -5732,13 +5709,14 @@ dependencies = [
[[package]]
name = "tendermint-proto"
version = "0.40.1"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ae9e1705aa0fa5ecb2c6aa7fb78c2313c4a31158ea5f02048bf318f849352eb"
checksum = "dc87024548c7f3da479885201e3da20ef29e85a3b13d04606b380ac4c7120d87"
dependencies = [
"bytes",
"flex-error",
"prost 0.13.4",
"prost",
"prost-types",
"serde",
"serde_bytes",
"subtle-encoding",
@@ -5747,9 +5725,9 @@ dependencies = [
[[package]]
name = "tendermint-rpc"
version = "0.40.1"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "835a52aa504c63ec05519e31348d3f4ba2fe79493c588e2cad5323d5e81b161a"
checksum = "dfdc2281e271277fda184d96d874a6fe59f569b130b634289257baacfc95aa85"
dependencies = [
"async-trait",
"bytes",
@@ -5766,9 +5744,9 @@ dependencies = [
"serde_json",
"subtle 2.5.0",
"subtle-encoding",
"tendermint 0.40.1",
"tendermint 0.37.0",
"tendermint-config",
"tendermint-proto 0.40.1",
"tendermint-proto 0.37.0",
"thiserror 1.0.64",
"time",
"tokio",
+1 -1
View File
@@ -50,7 +50,7 @@ base64 = "0.13"
zeroize = { version = "1.5", features = ["zeroize_derive", "serde"] }
cosmwasm-std = "1.3.0"
cosmrs = { version = "0.21.0" }
cosmrs = { git = "https://github.com/cosmos/cosmos-rust", rev = "4b1332e6d8258ac845cef71589c8d362a669675a" }
nym-node-requests = { path = "../../nym-node/nym-node-requests" }
nym-validator-client = { path = "../../common/client-libs/validator-client" }
-5334
View File
File diff suppressed because it is too large Load Diff
@@ -40,7 +40,6 @@ async fn main() {
client.disconnect().await;
}
#[derive(Clone)]
#[allow(unused)]
struct MockClientStorage {
pub key_store: MockKeyStore,
@@ -97,7 +96,6 @@ impl MixnetClientStorage for MockClientStorage {
}
}
#[derive(Clone)]
struct MockKeyStore;
#[async_trait]
@@ -117,7 +115,6 @@ impl KeyStore for MockKeyStore {
}
}
#[derive(Clone)]
struct MockGatewayDetailsStore;
#[async_trait]
+6 -6
View File
@@ -17,22 +17,22 @@ use zeroize::Zeroizing;
/// The way to create this client is by calling
/// [`crate::mixnet::DisconnectedMixnetClient::create_bandwidth_client`] on the associated mixnet
/// client.
pub struct BandwidthAcquireClient<St: Storage + Clone> {
pub struct BandwidthAcquireClient<'a, St: Storage> {
client: DirectSigningHttpRpcNyxdClient,
storage: St,
storage: &'a St,
client_id: Zeroizing<Vec<u8>>,
ticketbook_type: TicketType,
}
impl<St> BandwidthAcquireClient<St>
impl<'a, St> BandwidthAcquireClient<'a, St>
where
St: Storage + Clone,
St: Storage,
<St as Storage>::StorageError: Send + Sync + 'static,
{
pub(crate) fn new(
network_details: NymNetworkDetails,
mnemonic: String,
storage: St,
storage: &'a St,
client_id: Vec<u8>,
ticketbook_type: TicketType,
) -> Result<Self> {
@@ -55,7 +55,7 @@ where
pub async fn acquire(&self) -> Result<()> {
issue_credential(
&self.client,
&self.storage,
self.storage,
self.client_id.deref(),
self.ticketbook_type,
)
+6 -6
View File
@@ -25,7 +25,7 @@ use nym_client_core::client::{
};
use nym_client_core::config::{DebugConfig, StatsReporting};
use nym_client_core::error::ClientCoreError;
use nym_client_core::init::helpers::gateways_for_init;
use nym_client_core::init::helpers::current_gateways;
use nym_client_core::init::setup_gateway;
use nym_client_core::init::types::{GatewaySelectionSpecification, GatewaySetup};
use nym_client_core::ForgetMe;
@@ -108,7 +108,7 @@ impl MixnetClientBuilder<OnDiskPersistent> {
impl<S> MixnetClientBuilder<S>
where
S: MixnetClientStorage + Clone + 'static,
S: MixnetClientStorage + 'static,
S::ReplyStore: Send + Sync,
S::GatewaysDetailsStore: Sync,
<S::ReplyStore as ReplyStorageBackend>::StorageError: Sync + Send,
@@ -326,7 +326,7 @@ where
/// client.
pub struct DisconnectedMixnetClient<S>
where
S: MixnetClientStorage + Clone,
S: MixnetClientStorage,
{
/// Client configuration
config: Config,
@@ -371,7 +371,7 @@ where
impl<S> DisconnectedMixnetClient<S>
where
S: MixnetClientStorage + Clone + 'static,
S: MixnetClientStorage + 'static,
S::ReplyStore: Send + Sync,
S::GatewaysDetailsStore: Sync,
<S::ReplyStore as ReplyStorageBackend>::StorageError: Sync + Send,
@@ -513,7 +513,7 @@ where
let user_agent = self.user_agent.clone();
let mut rng = OsRng;
let available_gateways = gateways_for_init(
let available_gateways = current_gateways(
&mut rng,
&nym_api_endpoints,
user_agent,
@@ -622,7 +622,7 @@ where
BandwidthAcquireClient::new(
self.config.network_details.clone(),
mnemonic,
self.storage.credential_store().clone(),
self.storage.credential_store(),
client_id,
ticketbook_type,
)
-22448
View File
File diff suppressed because it is too large Load Diff