Compare commits

..

9 Commits

Author SHA1 Message Date
dynco-nym d474a2840f All agents use the same key
- remove assigned_agent column
- remove logic which would stop agents with
  the same key to connect
- as a safety measure, add cap to total no. of agents
2024-11-21 00:21:14 +01:00
dynco-nym bacf68697c Update cargo version 2024-11-20 17:43:36 +01:00
dynco-nym 6c4333a962 Suppress ts-rs serde warnings 2024-11-20 15:42:59 +01:00
dynco-nym 03f40a9ce2 Uniform request authentication 2024-11-20 15:42:02 +01:00
dynco-nym 65f4c08050 Fix testrun cleanup bug
- introduce a new column last_assigned which is different than
  created_at so that stale testruns get cleaned up based on
  last_assigned
- created_at is still useful for determining the "oldest" testrun
  to be picked up
2024-11-20 12:31:26 +01:00
dynco-nym 1b52856fe5 Replay protection 2024-11-20 12:31:17 +01:00
dynco-nym e4df6416f5 /request better authentication
- moved agent API calls to Client struct
2024-11-20 12:29:29 +01:00
dynco-nym f5ab647a7a /submit with better auth
- also adjust agent run script to authenticate, even in parallel
2024-11-20 12:24:39 +01:00
dynco-nym ff53cd6e1d Agents authenticate with NSAPI 2024-11-20 12:24:32 +01:00
152 changed files with 468 additions and 1623 deletions
+1 -1
View File
@@ -11,7 +11,7 @@ on:
jobs:
check-schema:
name: Generate and check schema
runs-on: ubuntu-20.04
runs-on: arc-ubuntu-20.04
env:
CARGO_TERM_COLOR: always
steps:
+1 -1
View File
@@ -13,7 +13,7 @@ on:
jobs:
matrix_prep:
runs-on: ubuntu-20.04
runs-on: arc-ubuntu-20.04
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
+18 -14
View File
@@ -25,27 +25,31 @@ jobs:
git config --global user.email "lawrence@nymtech.net"
git config --global user.name "Lawrence Stalder"
- name: Get version from cargo.toml
uses: mikefarah/yq@v4.44.5
- name: Get version from package.json
uses: sergeysova/jq-action@v2
id: get_version
with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/nym-network-monitor/Cargo.toml
cmd: jq -r '.version' ${{ env.WORKING_DIRECTORY }}/package.json
- name: Check if tag exists
run: |
if git rev-parse ${{ steps.get_version.outputs.value }} >/dev/null 2>&1; then
echo "Tag ${{ steps.get_version.outputs.value }} already exists"
fi
- name: Remove existing tag if exists
run: |
echo "Checking if tag ${{ env.CONTAINER_NAME }}-${{ steps.get_version.outputs.result }} exists..."
if git rev-parse ${{ env.CONTAINER_NAME }}-${{ steps.get_version.outputs.result }} >/dev/null 2>&1; then
echo "Tag ${{ env.CONTAINER_NAME }}-${{ steps.get_version.outputs.result }} already exists"
git push --delete origin ${{ env.CONTAINER_NAME }}-${{ steps.get_version.outputs.result }}
git tag -d ${{ env.CONTAINER_NAME }}-${{ steps.get_version.outputs.result }}
if git rev-parse ${{ steps.get_version.outputs.value }} >/dev/null 2>&1; then
git push --delete origin ${{ steps.get_version.outputs.value }}
git tag -d ${{ steps.get_version.outputs.value }}
fi
- name: Create tag
run: |
git tag -a ${{ env.CONTAINER_NAME }}-${{ steps.get_version.outputs.result }} -m "Version ${{ steps.get_version.outputs.result }}"
git push origin ${{ env.CONTAINER_NAME }}-${{ steps.get_version.outputs.result }}
git tag -a ${{ steps.get_version.outputs.value }} -m "Version ${{ steps.get_version.outputs.value }}"
git push origin ${{ steps.get_version.outputs.value }}
- name: BuildAndPushImageOnHarbor
run: |
docker build -f nym-network-monitor.dockerfile ${{ env.WORKING_DIRECTORY }} -t harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }}:${{ steps.get_version.outputs.result }} -t harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }}:latest
docker push harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }} --all-tags
docker build -f nym-network-monitor.dockerfile ${{ env.WORKING_DIRECTORY }} -t harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }}:${{ steps.get_version.outputs.value }} -t harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }}:latest
docker push harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }} --all-tags
@@ -8,7 +8,7 @@ on:
description: Which gateway probe git ref to build the image with
env:
WORKING_DIRECTORY: "nym-node-status-api/nym-node-status-agent"
WORKING_DIRECTORY: "nym-node-status-agent"
CONTAINER_NAME: "node-status-agent"
jobs:
@@ -58,4 +58,4 @@ jobs:
- name: BuildAndPushImageOnHarbor
run: |
docker build --build-arg GIT_REF=${{ github.event.inputs.gateway_probe_git_ref }} -f ${{ env.WORKING_DIRECTORY }}/Dockerfile . -t harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }}:${{ steps.get_version.outputs.result }}-${{ steps.cleanup_gateway_probe_ref.outputs.git_ref }}
docker push harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }} --all-tags
docker push harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }} --all-tags
+1 -1
View File
@@ -3,7 +3,7 @@ on:
workflow_dispatch:
env:
WORKING_DIRECTORY: "nym-node-status-api/nym-node-status-api"
WORKING_DIRECTORY: "nym-node-status-api"
CONTAINER_NAME: "node-status-api"
jobs:
-6
View File
@@ -4,12 +4,6 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
## [Unreleased]
## [2024.13-magura-patched] (2024-11-22)
- [experimental] allow clients to change between deterministic route selection based on packet headers and a pseudorandom distribution
- Introduced a configurable limit on retransmission frequency of packets if ACKs are not received
- Filtered out invalid IP addresses on nym-api
## [2024.13-magura] (2024-11-18)
- Limit race probability ([#5145])
Generated
+25 -26
View File
@@ -4470,7 +4470,6 @@ dependencies = [
"cw2",
"cw3",
"cw4",
"dashmap",
"dirs",
"futures",
"getset",
@@ -4752,14 +4751,12 @@ dependencies = [
"nym-coconut-dkg-common",
"nym-config",
"nym-contracts-common",
"nym-credential-proxy-requests",
"nym-credential-storage",
"nym-credential-utils",
"nym-credentials",
"nym-credentials-interface",
"nym-crypto",
"nym-ecash-contract-common",
"nym-ecash-time",
"nym-id",
"nym-mixnet-contract-common",
"nym-multisig-contract-common",
@@ -4784,7 +4781,7 @@ dependencies = [
[[package]]
name = "nym-client"
version = "1.1.44"
version = "1.1.43"
dependencies = [
"bs58",
"clap 4.5.20",
@@ -5009,6 +5006,16 @@ dependencies = [
"nym-multisig-contract-common",
]
[[package]]
name = "nym-common-models"
version = "0.1.0"
dependencies = [
"anyhow",
"bincode",
"nym-crypto",
"serde",
]
[[package]]
name = "nym-compact-ecash"
version = "0.1.0"
@@ -5861,7 +5868,7 @@ dependencies = [
[[package]]
name = "nym-network-monitor"
version = "1.0.2"
version = "0.1.0"
dependencies = [
"anyhow",
"axum 0.7.7",
@@ -5891,7 +5898,7 @@ dependencies = [
[[package]]
name = "nym-network-requester"
version = "1.1.45"
version = "1.1.44"
dependencies = [
"addr",
"anyhow",
@@ -5942,7 +5949,7 @@ dependencies = [
[[package]]
name = "nym-node"
version = "1.1.11"
version = "1.1.10"
dependencies = [
"anyhow",
"bip39",
@@ -6048,23 +6055,30 @@ name = "nym-node-status-agent"
version = "1.0.0-rc.1"
dependencies = [
"anyhow",
"bincode",
"chrono",
"clap 4.5.20",
"nym-bin-common",
"nym-common-models",
"nym-crypto",
"nym-node-status-client",
"rand",
"reqwest 0.12.4",
"serde",
"serde_json",
"tempfile",
"tokio",
"tokio-util",
"tracing",
"tracing-subscriber",
]
[[package]]
name = "nym-node-status-api"
version = "1.0.0-rc.3"
version = "1.0.0-rc.1"
dependencies = [
"anyhow",
"axum 0.7.7",
"bincode",
"chrono",
"clap 4.5.20",
"cosmwasm-std",
@@ -6072,11 +6086,11 @@ dependencies = [
"futures-util",
"moka",
"nym-bin-common",
"nym-common-models",
"nym-crypto",
"nym-explorer-client",
"nym-network-defaults",
"nym-node-requests",
"nym-node-status-client",
"nym-task",
"nym-validator-client",
"regex",
@@ -6099,21 +6113,6 @@ dependencies = [
"utoipauto",
]
[[package]]
name = "nym-node-status-client"
version = "0.1.0"
dependencies = [
"anyhow",
"bincode",
"chrono",
"nym-crypto",
"nym-http-api-client",
"reqwest 0.12.4",
"serde",
"serde_json",
"tracing",
]
[[package]]
name = "nym-node-tester-utils"
version = "0.1.0"
@@ -6306,7 +6305,7 @@ dependencies = [
[[package]]
name = "nym-socks5-client"
version = "1.1.44"
version = "1.1.43"
dependencies = [
"bs58",
"clap 4.5.20",
+6 -5
View File
@@ -61,6 +61,7 @@ members = [
"common/ip-packet-requests",
"common/ledger",
"common/mixnode-common",
"common/models",
"common/network-defaults",
"common/node-tester-utils",
"common/nonexhaustive-delayqueue",
@@ -125,9 +126,8 @@ members = [
"nym-node",
"nym-node/nym-node-http-api",
"nym-node/nym-node-requests",
"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-status-api",
"nym-node-status-agent",
"nym-outfox",
"nym-validator-rewarder",
"tools/echo-server",
@@ -155,6 +155,7 @@ members = [
default-members = [
"clients/native",
"clients/socks5",
"common/models",
"explorer-api",
"gateway",
"mixnode",
@@ -162,9 +163,9 @@ default-members = [
"nym-credential-proxy/nym-credential-proxy",
"nym-data-observatory",
"nym-node",
"nym-node-status-api/nym-node-status-agent",
"nym-node-status-api/nym-node-status-api",
"nym-node-status-api",
"nym-validator-rewarder",
"nym-node-status-api",
"service-providers/authenticator",
"service-providers/ip-packet-router",
"service-providers/network-requester",
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-client"
version = "1.1.44"
version = "1.1.43"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
description = "Implementation of the Nym Client"
edition = "2021"
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-socks5-client"
version = "1.1.44"
version = "1.1.43"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
edition = "2021"
+1 -10
View File
@@ -393,20 +393,13 @@ pub struct Traffic {
/// poisson distribution.
pub disable_main_poisson_packet_distribution: bool,
/// Specify whether route selection should be determined by the packet header.
pub deterministic_route_selection: bool,
/// Specify how many times particular packet can be retransmitted
/// None - no limit
pub maximum_number_of_retransmissions: Option<u32>,
/// Specifies the packet size used for sent messages.
/// Do not override it unless you understand the consequences of that change.
pub primary_packet_size: PacketSize,
/// Specifies the optional auxiliary packet size for optimizing message streams.
/// Note that its use decreases overall anonymity.
/// Do not set it unless you understand the consequences of that change.
/// Do not set it it unless you understand the consequences of that change.
pub secondary_packet_size: Option<PacketSize>,
pub packet_type: PacketType,
@@ -431,8 +424,6 @@ impl Default for Traffic {
average_packet_delay: DEFAULT_AVERAGE_PACKET_DELAY,
message_sending_average_delay: DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY,
disable_main_poisson_packet_distribution: false,
deterministic_route_selection: false,
maximum_number_of_retransmissions: None,
primary_packet_size: PacketSize::RegularPacket,
secondary_packet_size: None,
packet_type: PacketType::Mix,
@@ -111,7 +111,6 @@ impl From<ConfigV5> for Config {
primary_packet_size: value.debug.traffic.primary_packet_size,
secondary_packet_size: value.debug.traffic.secondary_packet_size,
packet_type: value.debug.traffic.packet_type,
..Default::default()
},
cover_traffic: CoverTraffic {
loop_cover_traffic_average_delay: value
@@ -30,8 +30,7 @@ pub(crate) enum Action {
InsertPending(Vec<PendingAcknowledgement>),
/// Removes given `PendingAcknowledgement` from the 'shared' state. Also cancels the retransmission timer.
/// Initiated by `AcknowledgementListener` upon receiving the acknowledgement. Also by `RetransmissionRequestListener`
/// upon deciding to abandon the data.
/// Initiated by `AcknowledgementListener`
RemovePending(FragmentIdentifier),
/// Starts the retransmission timer on given `PendingAcknowledgement` with the `Duration` based on
@@ -42,7 +41,7 @@ pub(crate) enum Action {
/// Updates the expected delay of given `PendingAcknowledgement` with the new provided `SphinxDelay`.
/// Initiated by `RetransmissionRequestListener`
UpdatePendingAck(FragmentIdentifier, SphinxDelay),
UpdateDelay(FragmentIdentifier, SphinxDelay),
}
impl Action {
@@ -58,8 +57,8 @@ impl Action {
Action::StartTimer(frag_id)
}
pub(crate) fn new_update_pending_ack(frag_id: FragmentIdentifier, delay: SphinxDelay) -> Self {
Action::UpdatePendingAck(frag_id, delay)
pub(crate) fn new_update_delay(frag_id: FragmentIdentifier, delay: SphinxDelay) -> Self {
Action::UpdateDelay(frag_id, delay)
}
}
@@ -136,7 +135,7 @@ impl ActionController {
}
fn handle_start_timer(&mut self, frag_id: FragmentIdentifier) {
trace!("{frag_id} is starting its timer");
trace!("{} is starting its timer", frag_id);
if let Some((pending_ack_data, queue_key)) = self.pending_acks_data.get_mut(&frag_id) {
// the fact that this branch is now POSSIBLE is a sign of a need to refactor this whole
@@ -194,7 +193,7 @@ impl ActionController {
// initiated basically as a first step of retransmission. At first data has its delay updated
// (as new sphinx packet was created with new expected delivery time)
fn handle_update_pending_ack(&mut self, frag_id: FragmentIdentifier, delay: SphinxDelay) {
fn handle_update_delay(&mut self, frag_id: FragmentIdentifier, delay: SphinxDelay) {
trace!("{} is updating its delay", frag_id);
// TODO: is it possible to solve this without either locking or temporarily removing the value?
if let Some((pending_ack_data, queue_key)) = self.pending_acks_data.remove(&frag_id) {
@@ -203,7 +202,7 @@ impl ActionController {
// reference to this Arc. HOWEVER, before the Action was pushed onto the queue, the reference
// was dropped hence this unwrap is safe.
let mut inner_data = Arc::try_unwrap(pending_ack_data).unwrap();
inner_data.update_retransmitted(delay);
inner_data.update_delay(delay);
self.pending_acks_data
.insert(frag_id, (Arc::new(inner_data), queue_key));
@@ -226,7 +225,7 @@ impl ActionController {
// about it. Perhaps just reschedule it at later point?
let frag_id = expired_ack.into_inner();
trace!("{frag_id} has expired");
trace!("{} has expired", frag_id);
if let Some((pending_ack_data, queue_key)) = self.pending_acks_data.get_mut(&frag_id) {
if queue_key.is_none() {
@@ -259,9 +258,7 @@ impl ActionController {
Action::InsertPending(pending_acks) => self.handle_insert(pending_acks),
Action::RemovePending(frag_id) => self.handle_remove(frag_id),
Action::StartTimer(frag_id) => self.handle_start_timer(frag_id),
Action::UpdatePendingAck(frag_id, delay) => {
self.handle_update_pending_ack(frag_id, delay)
}
Action::UpdateDelay(frag_id, delay) => self.handle_update_delay(frag_id, delay),
}
}
@@ -71,7 +71,6 @@ pub(crate) struct PendingAcknowledgement {
delay: SphinxDelay,
destination: PacketDestination,
mix_hops: Option<u8>,
retransmissions: u32,
}
impl PendingAcknowledgement {
@@ -87,7 +86,6 @@ impl PendingAcknowledgement {
delay,
destination: PacketDestination::KnownRecipient(recipient.into()),
mix_hops,
retransmissions: 0,
}
}
@@ -107,7 +105,6 @@ impl PendingAcknowledgement {
// Messages sent using SURBs are using the number of mix hops set by the recipient when
// they provided the SURBs, so it doesn't make sense to include it here.
mix_hops: None,
retransmissions: 0,
}
}
@@ -119,9 +116,8 @@ impl PendingAcknowledgement {
self.message_chunk.clone()
}
fn update_retransmitted(&mut self, new_delay: SphinxDelay) {
fn update_delay(&mut self, new_delay: SphinxDelay) {
self.delay = new_delay;
self.retransmissions += 1;
}
}
@@ -167,9 +163,6 @@ impl AcknowledgementControllerConnectors {
/// Configurable parameters of the `AcknowledgementController`
pub(super) struct Config {
/// Specify how many times particular packet can be retransmitted
maximum_retransmissions: Option<u32>,
/// Given ack timeout in the form a * BASE_DELAY + b, it specifies the additive part `b`
ack_wait_addition: Duration,
@@ -181,13 +174,8 @@ pub(super) struct Config {
}
impl Config {
pub(super) fn new(
maximum_retransmissions: Option<u32>,
ack_wait_addition: Duration,
ack_wait_multiplier: f64,
) -> Self {
pub(super) fn new(ack_wait_addition: Duration, ack_wait_multiplier: f64) -> Self {
Config {
maximum_retransmissions,
ack_wait_addition,
ack_wait_multiplier,
packet_size: Default::default(),
@@ -250,7 +238,6 @@ where
// will listen for any ack timeouts and trigger retransmission
let retransmission_request_listener = RetransmissionRequestListener::new(
config.maximum_retransmissions,
connectors.ack_action_sender.clone(),
message_handler,
retransmission_rx,
@@ -20,7 +20,6 @@ use std::sync::{Arc, Weak};
// responsible for packet retransmission upon fired timer
pub(super) struct RetransmissionRequestListener<R> {
maximum_retransmissions: Option<u32>,
action_sender: AckActionSender,
message_handler: MessageHandler<R>,
request_receiver: RetransmissionRequestReceiver,
@@ -32,14 +31,12 @@ where
R: CryptoRng + Rng,
{
pub(super) fn new(
maximum_retransmissions: Option<u32>,
action_sender: AckActionSender,
message_handler: MessageHandler<R>,
request_receiver: RetransmissionRequestReceiver,
reply_controller_sender: ReplyControllerSender,
) -> Self {
RetransmissionRequestListener {
maximum_retransmissions,
action_sender,
message_handler,
request_receiver,
@@ -80,18 +77,6 @@ where
}
};
let frag_id = timed_out_ack.message_chunk.fragment_identifier();
if let Some(limit) = self.maximum_retransmissions {
if timed_out_ack.retransmissions >= limit {
warn!("reached maximum number of allowed retransmissions for the packet");
self.action_sender
.unbounded_send(Action::new_remove(frag_id))
.unwrap();
return;
}
}
let maybe_prepared_fragment = match &timed_out_ack.destination {
PacketDestination::Anonymous {
recipient_tag,
@@ -116,6 +101,8 @@ where
}
};
let frag_id = timed_out_ack.message_chunk.fragment_identifier();
let prepared_fragment = match maybe_prepared_fragment {
Ok(prepared_fragment) => prepared_fragment,
Err(err) => {
@@ -149,7 +136,7 @@ where
// with the additional poisson delay.
// And since Actions are executed in order `UpdateTimer` will HAVE TO be executed before `StartTimer`
self.action_sender
.unbounded_send(Action::new_update_pending_ack(frag_id, new_delay))
.unbounded_send(Action::new_update_delay(frag_id, new_delay))
.unwrap();
// send to `OutQueueControl` to eventually send to the mix network
@@ -91,9 +91,6 @@ pub(crate) struct Config {
/// and surb-based are going to be sent.
sender_address: Recipient,
/// Specify whether route selection should be determined by the packet header.
deterministic_route_selection: bool,
/// Average delay a data packet is going to get delay at a single mixnode.
average_packet_delay: Duration,
@@ -117,12 +114,10 @@ impl Config {
sender_address: Recipient,
average_packet_delay: Duration,
average_ack_delay: Duration,
deterministic_route_selection: bool,
) -> Self {
Config {
ack_key,
sender_address,
deterministic_route_selection,
average_packet_delay,
average_ack_delay,
num_mix_hops: DEFAULT_NUM_MIX_HOPS,
@@ -181,7 +176,6 @@ where
{
let message_preparer = MessagePreparer::new(
rng,
config.deterministic_route_selection,
config.sender_address,
config.average_packet_delay,
config.average_ack_delay,
@@ -640,7 +634,7 @@ where
pub(crate) fn update_ack_delay(&self, id: FragmentIdentifier, new_delay: Delay) {
self.action_sender
.unbounded_send(Action::UpdatePendingAck(id, new_delay))
.unbounded_send(Action::UpdateDelay(id, new_delay))
.expect("action control task has died")
}
@@ -65,7 +65,6 @@ pub struct Config {
impl<'a> From<&'a Config> for acknowledgement_control::Config {
fn from(cfg: &'a Config) -> Self {
acknowledgement_control::Config::new(
cfg.traffic.maximum_number_of_retransmissions,
cfg.acks.ack_wait_addition,
cfg.acks.ack_wait_multiplier,
)
@@ -98,7 +97,6 @@ impl<'a> From<&'a Config> for message_handler::Config {
cfg.self_recipient,
cfg.traffic.average_packet_delay,
cfg.acks.average_ack_delay,
cfg.traffic.deterministic_route_selection,
)
.with_custom_primary_packet_size(cfg.traffic.primary_packet_size)
.with_custom_secondary_packet_size(cfg.traffic.secondary_packet_size)
@@ -98,13 +98,6 @@ impl DirectSecp256k1HdWallet {
Ok((private_key, public_key))
}
pub fn derive_extended_private_key(
&self,
hd_path: &DerivationPath,
) -> Result<XPrv, DirectSecp256k1HdWalletError> {
Ok(XPrv::derive_from_path(self.seed, hd_path)?)
}
pub fn try_derive_accounts(&self) -> Result<Vec<AccountData>, DirectSecp256k1HdWalletError> {
let mut accounts = Vec::with_capacity(self.accounts.len());
for derivation_info in &self.accounts {
-2
View File
@@ -48,7 +48,6 @@ nym-vesting-contract-common = { path = "../cosmwasm-smart-contracts/vesting-cont
nym-coconut-dkg-common = { path = "../cosmwasm-smart-contracts/coconut-dkg" }
nym-multisig-contract-common = { path = "../cosmwasm-smart-contracts/multisig-contract" }
nym-ecash-contract-common = { path = "../cosmwasm-smart-contracts/ecash-contract" }
nym-ecash-time = { path = "../../common/ecash-time" }
nym-sphinx = { path = "../../common/nymsphinx" }
nym-client-core = { path = "../../common/client-core" }
nym-config = { path = "../../common/config" }
@@ -57,7 +56,6 @@ nym-credentials-interface = { path = "../../common/credentials-interface" }
nym-credential-storage = { path = "../../common/credential-storage" }
nym-credential-utils = { path = "../../common/credential-utils" }
nym-id = { path = "../nym-id" }
nym-credential-proxy-requests = { path = "../../nym-credential-proxy/nym-credential-proxy-requests" }
nym-pemstore = { path = "../../common/pemstore", version = "0.3.0" }
nym-types = { path = "../../common/types" }
@@ -1,41 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::Parser;
use log::trace;
use nym_credentials_interface::{generate_keypair_user, generate_keypair_user_from_seed, Base58};
use serde::{Deserialize, Serialize};
use std::io::stdout;
#[derive(Serialize, Deserialize)]
pub struct Bs58EncodedKeys {
pub secret_key: String,
pub public_key: String,
}
#[derive(Debug, Parser)]
pub struct Args {
/// Secret value that's used for deriving underlying ecash keypair
#[clap(long)]
pub(crate) bs58_encoded_client_secret: Option<String>,
}
pub fn generate_ecash_keypair(args: Args) -> anyhow::Result<()> {
trace!("args: {args:?}");
let keypair = if let Some(secret) = args.bs58_encoded_client_secret {
let seed = bs58::decode(&secret).into_vec()?;
generate_keypair_user_from_seed(&seed)
} else {
generate_keypair_user()
};
let encoded = Bs58EncodedKeys {
secret_key: keypair.secret_key().to_bs58(),
public_key: keypair.public_key().to_bs58(),
};
serde_json::to_writer_pretty(stdout(), &encoded)?;
Ok(())
}
-23
View File
@@ -1,23 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::{Args, Subcommand};
pub mod generate_keypair;
pub mod withdrawal_request;
#[derive(Debug, Args)]
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
pub struct InternalEcash {
#[clap(subcommand)]
pub command: InternalEcashCommands,
}
#[derive(Debug, Subcommand)]
pub enum InternalEcashCommands {
/// Generate a dummy withdrawal request
GenerateWithdrawalRequest(withdrawal_request::Args),
/// Generate dummy ecash keypair
GenerateKeypair(generate_keypair::Args),
}
@@ -1,78 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::Parser;
use log::trace;
use nym_credential_proxy_requests::api::v1::ticketbook::models::TicketbookRequest;
use nym_credentials_interface::{
generate_keypair_user, withdrawal_request, Base58, SecretKeyUser, TicketType,
};
use nym_ecash_time::{ecash_default_expiration_date, EcashTime};
use serde::{Deserialize, Serialize};
use std::io::stdout;
use time::macros::format_description;
use time::Date;
use zeroize::Zeroizing;
fn parse_date(raw: &str) -> Result<Date, time::error::Parse> {
let format = format_description!("[year]-[month]-[day]");
Date::parse(raw, &format)
}
#[derive(Serialize, Deserialize)]
pub struct Bs58EncodedOutput {
pub ecash_proxy_request: TicketbookRequest,
pub ecash_secret: String,
/// Needed to later unblind shares
pub ecash_request_info_bs58: String,
}
#[derive(Debug, Parser)]
pub struct Args {
/// Specify which type of ticketbook
#[clap(long, default_value_t = TicketType::V1MixnetEntry)]
pub(crate) ticketbook_type: TicketType,
/// Set expiration date for the ticketbook
#[clap(long, value_parser = parse_date, default_value_t = ecash_default_expiration_date())]
pub(crate) expiration_date: Date,
/// Provide ecash secret key (or generate a fresh one)
#[clap(long)]
pub(crate) ecash_secret_key_bs58: Option<String>,
}
pub async fn generate_withdrawal_request(args: Args) -> anyhow::Result<()> {
trace!("args: {args:?}");
let ecash_keypair = if let Some(secret_key) = args.ecash_secret_key_bs58 {
let secret_key = Zeroizing::new(bs58::decode(Zeroizing::new(secret_key)).into_vec()?);
let sk = SecretKeyUser::from_bytes(&secret_key)?;
sk.into()
} else {
generate_keypair_user()
};
let (withdrawal_request, request_info) = withdrawal_request(
ecash_keypair.secret_key(),
args.expiration_date.ecash_unix_timestamp(),
args.ticketbook_type.encode(),
)?;
let encoded = Bs58EncodedOutput {
ecash_proxy_request: TicketbookRequest {
withdrawal_request: withdrawal_request.into(),
ecash_pubkey: ecash_keypair.public_key(),
expiration_date: args.expiration_date,
ticketbook_type: args.ticketbook_type,
is_freepass_request: false,
},
ecash_secret: ecash_keypair.secret_key().to_bs58(),
ecash_request_info_bs58: request_info.to_bs58(),
};
serde_json::to_writer_pretty(stdout(), &encoded)?;
Ok(())
}
-19
View File
@@ -1,19 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::{Args, Subcommand};
pub mod ecash;
#[derive(Debug, Args)]
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
pub struct Internal {
#[clap(subcommand)]
pub command: InternalCommands,
}
#[derive(Debug, Subcommand)]
pub enum InternalCommands {
/// Ecash related internal commands
Ecash(ecash::InternalEcash),
}
-1
View File
@@ -3,6 +3,5 @@
pub mod context;
pub mod ecash;
pub mod internal;
pub mod utils;
pub mod validator;
+1 -9
View File
@@ -221,15 +221,7 @@ impl From<PayInfo> for NymPayInfo {
}
#[derive(
Copy,
Clone,
Debug,
PartialEq,
Serialize,
Deserialize,
strum::Display,
strum::EnumString,
strum::EnumIter,
Copy, Clone, Debug, PartialEq, Serialize, Deserialize, strum::Display, strum::EnumString,
)]
#[serde(rename_all = "kebab-case")]
#[strum(serialize_all = "kebab-case")]
-126
View File
@@ -282,48 +282,6 @@ impl Client {
}
}
pub async fn create_delete_request<K, V>(
&self,
path: PathSegments<'_>,
params: Params<'_, K, V>,
) -> RequestBuilder
where
K: AsRef<str>,
V: AsRef<str>,
{
let url = sanitize_url(&self.base_url, path, params);
self.reqwest_client.delete(url)
}
pub async fn send_delete_request<K, V, E>(
&self,
path: PathSegments<'_>,
params: Params<'_, K, V>,
) -> Result<Response, HttpClientError<E>>
where
K: AsRef<str>,
V: AsRef<str>,
E: Display,
{
tracing::trace!("Sending DELETE request");
let url = sanitize_url(&self.base_url, path, params);
#[cfg(target_arch = "wasm32")]
{
Ok(wasmtimer::tokio::timeout(
self.request_timeout,
self.reqwest_client.delete(url).send(),
)
.await
.map_err(|_timeout| HttpClientError::RequestTimeout)??)
}
#[cfg(not(target_arch = "wasm32"))]
{
Ok(self.reqwest_client.delete(url).send().await?)
}
}
#[instrument(level = "debug", skip_all)]
pub async fn get_json<T, K, V, E>(
&self,
@@ -357,21 +315,6 @@ impl Client {
parse_response(res, true).await
}
pub async fn delete_json<T, K, V, E>(
&self,
path: PathSegments<'_>,
params: Params<'_, K, V>,
) -> Result<T, HttpClientError<E>>
where
for<'a> T: Deserialize<'a>,
K: AsRef<str>,
V: AsRef<str>,
E: Display + DeserializeOwned,
{
let res = self.send_delete_request(path, params).await?;
parse_response(res, false).await
}
#[instrument(level = "debug", skip_all)]
pub async fn get_json_endpoint<T, S, E>(&self, endpoint: S) -> Result<T, HttpClientError<E>>
where
@@ -437,35 +380,6 @@ impl Client {
parse_response(res, true).await
}
pub async fn delete_json_endpoint<T, S, E>(&self, endpoint: S) -> Result<T, HttpClientError<E>>
where
for<'a> T: Deserialize<'a>,
E: Display + DeserializeOwned,
S: AsRef<str>,
{
#[cfg(target_arch = "wasm32")]
let res = {
wasmtimer::tokio::timeout(
self.request_timeout,
self.reqwest_client
.delete(self.base_url.join(endpoint.as_ref())?)
.send(),
)
.await
.map_err(|_timeout| HttpClientError::RequestTimeout)??
};
#[cfg(not(target_arch = "wasm32"))]
let res = {
self.reqwest_client
.delete(self.base_url.join(endpoint.as_ref())?)
.send()
.await?
};
parse_response(res, false).await
}
}
// define those methods on the trait for nicer extensions (and not having to type the thing twice)
@@ -498,17 +412,6 @@ pub trait ApiClient {
V: AsRef<str> + Sync,
E: Display + DeserializeOwned;
async fn delete_json<T, K, V, E>(
&self,
path: PathSegments<'_>,
params: Params<'_, K, V>,
) -> Result<T, HttpClientError<E>>
where
for<'a> T: Deserialize<'a>,
K: AsRef<str> + Sync,
V: AsRef<str> + Sync,
E: Display + DeserializeOwned;
/// `get` json data from the provided absolute endpoint, i.e. for example `"/api/v1/mixnodes?since=12345"`
async fn get_json_from<T, S, E>(&self, endpoint: S) -> Result<T, HttpClientError<E>>
where
@@ -526,12 +429,6 @@ pub trait ApiClient {
for<'a> T: Deserialize<'a>,
E: Display + DeserializeOwned,
S: AsRef<str> + Sync + Send;
async fn delete_json_from<T, S, E>(&self, endpoint: S) -> Result<T, HttpClientError<E>>
where
for<'a> T: Deserialize<'a>,
E: Display + DeserializeOwned,
S: AsRef<str> + Sync + Send;
}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
@@ -567,20 +464,6 @@ impl ApiClient for Client {
self.post_json(path, params, json_body).await
}
async fn delete_json<T, K, V, E>(
&self,
path: PathSegments<'_>,
params: Params<'_, K, V>,
) -> Result<T, HttpClientError<E>>
where
for<'a> T: Deserialize<'a>,
K: AsRef<str> + Sync,
V: AsRef<str> + Sync,
E: Display + DeserializeOwned,
{
self.delete_json(path, params).await
}
async fn get_json_from<T, S, E>(&self, endpoint: S) -> Result<T, HttpClientError<E>>
where
for<'a> T: Deserialize<'a>,
@@ -603,15 +486,6 @@ impl ApiClient for Client {
{
self.post_json_endpoint(endpoint, json_body).await
}
async fn delete_json_from<T, S, E>(&self, endpoint: S) -> Result<T, HttpClientError<E>>
where
for<'a> T: Deserialize<'a>,
E: Display + DeserializeOwned,
S: AsRef<str> + Sync + Send,
{
self.delete_json_endpoint(endpoint).await
}
}
// utility function that should solve the double slash problem in API urls forever.
+17
View File
@@ -0,0 +1,17 @@
[package]
name = "nym-common-models"
version = "0.1.0"
authors.workspace = true
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
edition.workspace = true
license.workspace = true
rust-version.workspace = true
readme.workspace = true
[dependencies]
anyhow = { workspace = true }
bincode = { workspace = true }
nym-crypto = { path = "../crypto", features = ["asymmetric", "serde"] }
serde = { workspace = true, features = ["derive"] }
+1
View File
@@ -0,0 +1 @@
pub mod ns_api;
@@ -1,9 +1,7 @@
use nym_crypto::asymmetric::ed25519::{PublicKey, Signature};
use nym_crypto::asymmetric::ed25519::{PublicKey, Signature, SignatureError};
use serde::{Deserialize, Serialize};
pub mod get_testrun {
use crate::auth::SignedRequest;
use super::*;
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Payload {
@@ -42,8 +40,6 @@ pub struct TestrunAssignment {
}
pub mod submit_results {
use crate::auth::SignedRequest;
use super::*;
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Payload {
@@ -74,3 +70,32 @@ pub mod submit_results {
}
}
}
pub trait SignedRequest {
type Payload: serde::Serialize;
fn public_key(&self) -> &PublicKey;
fn signature(&self) -> &Signature;
fn payload(&self) -> &Self::Payload;
}
pub trait VerifiableRequest: SignedRequest {
type Error: From<bincode::Error> + From<SignatureError>;
fn verify_signature(&self) -> Result<(), Self::Error> {
bincode::serialize(self.payload())
.map_err(Self::Error::from)
.and_then(|serialized| {
self.public_key()
.verify(serialized, self.signature())
.map_err(Self::Error::from)
})
}
}
impl<T> VerifiableRequest for T
where
T: SignedRequest,
{
type Error = anyhow::Error;
}
-40
View File
@@ -157,46 +157,6 @@ impl NymNetworkDetails {
}
}
#[rustfmt::skip]
#[cfg(feature = "env")]
pub fn export_to_env(self) {
use crate::var_names;
use std::env::set_var;
fn set_optional_var(var_name: &str, value: Option<String>) {
if let Some(value) = value {
set_var(var_name, value);
}
}
set_var(var_names::NETWORK_NAME, self.network_name);
set_var(var_names::BECH32_PREFIX, self.chain_details.bech32_account_prefix);
set_var(var_names::MIX_DENOM, self.chain_details.mix_denom.base);
set_var(var_names::MIX_DENOM_DISPLAY, self.chain_details.mix_denom.display);
set_var(var_names::STAKE_DENOM, self.chain_details.stake_denom.base);
set_var(var_names::STAKE_DENOM_DISPLAY, self.chain_details.stake_denom.display);
set_var(var_names::DENOMS_EXPONENT, self.chain_details.mix_denom.display_exponent.to_string());
if let Some(e) = self.endpoints.first() {
set_var(var_names::NYXD, e.nyxd_url.clone());
set_optional_var(var_names::NYM_API, e.api_url.clone());
set_optional_var(var_names::NYXD_WEBSOCKET, e.websocket_url.clone());
}
set_optional_var(var_names::MIXNET_CONTRACT_ADDRESS, self.contracts.mixnet_contract_address);
set_optional_var(var_names::VESTING_CONTRACT_ADDRESS, self.contracts.vesting_contract_address);
set_optional_var(var_names::ECASH_CONTRACT_ADDRESS, self.contracts.ecash_contract_address);
set_optional_var(var_names::GROUP_CONTRACT_ADDRESS, self.contracts.group_contract_address);
set_optional_var(var_names::MULTISIG_CONTRACT_ADDRESS, self.contracts.multisig_contract_address);
set_optional_var(var_names::COCONUT_DKG_CONTRACT_ADDRESS, self.contracts.coconut_dkg_contract_address);
set_optional_var(var_names::EXPLORER_API, self.explorer_api);
set_optional_var(var_names::NYM_VPN_API, self.nym_vpn_api_url);
}
pub fn default_gas_price_amount(&self) -> f64 {
GAS_PRICE_AMOUNT
}
+4 -14
View File
@@ -29,9 +29,6 @@ pub struct NodeTester<R> {
packet_size: PacketSize,
/// Specify whether route selection should be determined by the packet header.
deterministic_route_selection: bool,
/// Average delay a data packet is going to get delay at a single mixnode.
average_packet_delay: Duration,
@@ -51,13 +48,11 @@ impl<R> NodeTester<R>
where
R: Rng + CryptoRng,
{
#[allow(clippy::too_many_arguments)]
pub fn new(
rng: R,
base_topology: NymTopology,
self_address: Option<Recipient>,
packet_size: PacketSize,
deterministic_route_selection: bool,
average_packet_delay: Duration,
average_ack_delay: Duration,
ack_key: Arc<AckKey>,
@@ -67,7 +62,6 @@ where
base_topology,
self_address,
packet_size,
deterministic_route_selection,
average_packet_delay,
average_ack_delay,
num_mix_hops: DEFAULT_NUM_MIX_HOPS,
@@ -295,18 +289,10 @@ where
impl<R: CryptoRng + Rng> FragmentPreparer for NodeTester<R> {
type Rng = R;
fn deterministic_route_selection(&self) -> bool {
self.deterministic_route_selection
}
fn rng(&mut self) -> &mut Self::Rng {
&mut self.rng
}
fn nonce(&self) -> i32 {
1
}
fn num_mix_hops(&self) -> u8 {
self.num_mix_hops
}
@@ -318,4 +304,8 @@ impl<R: CryptoRng + Rng> FragmentPreparer for NodeTester<R> {
fn average_ack_delay(&self) -> Duration {
self.average_ack_delay
}
fn nonce(&self) -> i32 {
1
}
}
@@ -530,15 +530,6 @@ impl From<KeyPairUser> for SecretKeyUser {
}
}
impl From<SecretKeyUser> for KeyPairUser {
fn from(value: SecretKeyUser) -> Self {
KeyPairUser {
public_key: value.public_key(),
secret_key: value,
}
}
}
impl KeyPairUser {
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
+10 -28
View File
@@ -18,7 +18,7 @@ use nym_sphinx_params::{PacketType, ReplySurbKeyDigestAlgorithm, DEFAULT_NUM_MIX
use nym_sphinx_types::{Delay, NymPacket};
use nym_topology::{NymTopology, NymTopologyError};
use rand::{CryptoRng, Rng, SeedableRng};
use rand_chacha::ChaCha8Rng;
use rand_chacha::ChaCha20Rng;
use nym_sphinx_chunking::monitoring;
use std::time::Duration;
@@ -51,7 +51,6 @@ impl From<PreparedFragment> for MixPacket {
pub trait FragmentPreparer {
type Rng: CryptoRng + Rng;
fn deterministic_route_selection(&self) -> bool;
fn rng(&mut self) -> &mut Self::Rng;
fn nonce(&self) -> i32;
fn num_mix_hops(&self) -> u8;
@@ -202,7 +201,9 @@ pub trait FragmentPreparer {
// could perform diffie-hellman with its own keys followed by a kdf to re-derive
// the packet encryption key
let fragment_header = fragment.header();
let seed = fragment.seed().wrapping_mul(self.nonce());
let mut rng = ChaCha20Rng::seed_from_u64(seed as u64);
let destination = packet_recipient.gateway();
let hops = mix_hops.unwrap_or(self.num_mix_hops());
monitoring::fragment_sent(&fragment, self.nonce(), *destination, hops);
@@ -240,18 +241,8 @@ pub trait FragmentPreparer {
};
// generate pseudorandom route for the packet
log::trace!("Preparing chunk for sending with {hops} mix hops");
let route = if self.deterministic_route_selection() {
log::trace!("using deterministic route selection");
let seed = fragment_header.seed().wrapping_mul(self.nonce());
let mut rng = ChaCha8Rng::seed_from_u64(seed as u64);
topology.random_route_to_gateway(&mut rng, hops, destination)?
} else {
log::trace!("using pseudorandom route selection");
let mut rng = self.rng();
topology.random_route_to_gateway(&mut rng, hops, destination)?
};
log::trace!("Preparing chunk for sending with {} mix hops", hops);
let route = topology.random_route_to_gateway(&mut rng, hops, destination)?;
let destination = packet_recipient.as_sphinx_destination();
// including set of delays
@@ -322,9 +313,6 @@ pub struct MessagePreparer<R> {
/// Instance of a cryptographically secure random number generator.
rng: R,
/// Specify whether route selection should be determined by the packet header.
deterministic_route_selection: bool,
/// Address of this client which also represent an address to which all acknowledgements
/// and surb-based are going to be sent.
sender_address: Recipient,
@@ -348,7 +336,6 @@ where
{
pub fn new(
rng: R,
deterministic_route_selection: bool,
sender_address: Recipient,
average_packet_delay: Duration,
average_ack_delay: Duration,
@@ -357,7 +344,6 @@ where
let nonce = rng.gen();
MessagePreparer {
rng,
deterministic_route_selection,
sender_address,
average_packet_delay,
average_ack_delay,
@@ -471,18 +457,10 @@ where
impl<R: CryptoRng + Rng> FragmentPreparer for MessagePreparer<R> {
type Rng = R;
fn deterministic_route_selection(&self) -> bool {
self.deterministic_route_selection
}
fn rng(&mut self) -> &mut Self::Rng {
&mut self.rng
}
fn nonce(&self) -> i32 {
self.nonce
}
fn num_mix_hops(&self) -> u8 {
self.num_mix_hops
}
@@ -494,6 +472,10 @@ impl<R: CryptoRng + Rng> FragmentPreparer for MessagePreparer<R> {
fn average_ack_delay(&self) -> Duration {
self.average_ack_delay
}
fn nonce(&self) -> i32 {
self.nonce
}
}
/*
-11
View File
@@ -162,13 +162,6 @@ pub struct TrafficWasm {
/// a loop cover message is sent instead in order to preserve the rate.
pub message_sending_average_delay_ms: u32,
/// Specify how many times particular packet can be retransmitted
/// None - no limit
pub maximum_number_of_retransmissions: Option<u32>,
/// Specify whether route selection should be determined by the packet header.
pub deterministic_route_selection: bool,
/// Controls whether the main packet stream constantly produces packets according to the predefined
/// poisson distribution.
pub disable_main_poisson_packet_distribution: bool,
@@ -203,8 +196,6 @@ impl From<TrafficWasm> for ConfigTraffic {
message_sending_average_delay: Duration::from_millis(
traffic.message_sending_average_delay_ms as u64,
),
deterministic_route_selection: traffic.deterministic_route_selection,
maximum_number_of_retransmissions: traffic.maximum_number_of_retransmissions,
disable_main_poisson_packet_distribution: traffic
.disable_main_poisson_packet_distribution,
primary_packet_size: PacketSize::RegularPacket,
@@ -220,8 +211,6 @@ impl From<ConfigTraffic> for TrafficWasm {
average_packet_delay_ms: traffic.average_packet_delay.as_millis() as u32,
message_sending_average_delay_ms: traffic.message_sending_average_delay.as_millis()
as u32,
deterministic_route_selection: traffic.deterministic_route_selection,
maximum_number_of_retransmissions: traffic.maximum_number_of_retransmissions,
disable_main_poisson_packet_distribution: traffic
.disable_main_poisson_packet_distribution,
use_extended_packet_size: traffic.secondary_packet_size.is_some(),
@@ -88,14 +88,6 @@ pub struct TrafficWasmOverride {
#[tsify(optional)]
pub message_sending_average_delay_ms: Option<u32>,
/// Specify how many times particular packet can be retransmitted
#[tsify(optional)]
pub maximum_number_of_retransmissions: Option<u32>,
/// Specify whether route selection should be determined by the packet header.
#[tsify(optional)]
pub deterministic_route_selection: Option<bool>,
/// Controls whether the main packet stream constantly produces packets according to the predefined
/// poisson distribution.
#[tsify(optional)]
@@ -121,10 +113,6 @@ impl From<TrafficWasmOverride> for TrafficWasm {
message_sending_average_delay_ms: value
.message_sending_average_delay_ms
.unwrap_or(def.message_sending_average_delay_ms),
maximum_number_of_retransmissions: value.maximum_number_of_retransmissions,
deterministic_route_selection: value
.deterministic_route_selection
.unwrap_or(def.deterministic_route_selection),
disable_main_poisson_packet_distribution: value
.disable_main_poisson_packet_distribution
.unwrap_or(def.disable_main_poisson_packet_distribution),
@@ -1 +1 @@
Monday, November 25th 2024, 13:24:04 UTC
Tuesday, November 19th 2024, 11:34:21 UTC
-12
View File
@@ -772,18 +772,6 @@ const config = {
permanent: true,
basePath: false,
},
{
source: "/operators/troubleshooting/nodes.html",
destination: "/docs/operators/troubleshooting/nodes",
permanent: true,
basePath: false,
},
{
source: "/operators/nodes/manual-upgrade.html",
destination: "/docs/operators/nodes/maintenance/manual-upgrade",
permanent: true,
basePath: false,
},
{
source: "/developers/faq/general-faq.html",
destination: "/docs/operators/faq/general-faq",
@@ -14,4 +14,4 @@ MultiSURBs were implemented in `v1.1.4`. Clients, when sending a message to anot
If a reply is too large still (i.e. it would use more space than the available combined payload of the SURBs sent with the original message), the receiver will use a SURB to ask the sender for more SURBs.
You can read more about SURBs in §4.5 of the [Nym Whitepaper](https://nymtech.net/nym-whitepaper.pdf) as well as the [SURB traffic page](../traffic/anonymous-replies) to learn more about how SURBs are used in the Mixnet, known attacks relying on SURBs, etc.
You can read more about SURBs in §4.5 of the [Nym Whitepaper](https://nymtech.net/nym-whitepaper.pdf) as well as the [SURB format page](../traffic/anonymous-replies).
@@ -0,0 +1,13 @@
# Anonymous Replies with SURBs
> SURBs are the Sphinx equivalent of "onion addresses" in Tor, with the caveat that a SURB can only
be used once (to prevent replay attacks) and within its epoch of validity (the mix node public keys used to
prepare the SURB are only valid for a limited period).
> ...
> A SURB effectively contains: (1) the encrypted headers of a Sphinx message that, if sent to the mixnet, will be routed back to the original sender; (2) the address of the first-layer mix node where the message should be sent; and (3) a cryptographic key to encrypt the reply payload.
>
> [Nym Whitepaper](https://nymtech.net/nym-whitepaper.pdf) §4.6
As outlined in the [concepts](../concepts/anonymous-replies) section, SURBs are layer encrypted sets of Sphinx headers detailing a reply path ending in the sending client's [Nym address](../traffic/addressing-system). Clients receiving messages with SURBs attached are able to write a payload to the provided headers without ever learning about anything other than the first hop back into the Mixnet - the Gateway they are currently connected to.
There is a balance to be struck between the amount of SURBs to compute to send along with messages (which takes computation resources) and not sending enough SURBs initially, thus having to wait for a SURB to be sent from the receiver to the sender, requesting more SURBs be sent.
@@ -1,78 +0,0 @@
import { Callout } from 'nextra/components'
# Anonymous Replies with SURBs
> SURBs are pre-computed Sphinx packet headers encoding a mixnet route that ends in the participant that created the SURB. A sender can generate one or more SURBs and include them in their Sphinx message to a recipient. The recipient can use the SURBs as Sphinx headers to send back replies or acknowledgements that anonymously reach back the original sender after going through the mixnet.
>
> SURBs are the Sphinx equivalent of "onion addresses" in Tor, with the caveat that a SURB can only be used once (to prevent replay attacks) and within its epoch of validity (the mix node public keys used to prepare the SURB are only valid for a limited period). SURB headers are encrypted by the sender, so the recipient sending it back cannot infer from it any information about the message route, the per-hop latency, or the senders address, which is encoded in the innermost (last) routing layer of the SURB. SURBs ('Single Use Reply Blocks') allow clients to reply to incoming messages anonymously.
>
> ...
>
> A SURB effectively contains: (1) the encrypted headers of a Sphinx message that, if sent to the mixnet, will be routed back to the original sender; (2) the address of the first-layer mix node where the message should be sent; and (3) a cryptographic key to encrypt the reply payload.
> [Nym Whitepaper](https://nymtech.net/nym-whitepaper.pdf) §4.5
As outlined in the [concepts](../concepts/anonymous-replies) section, SURBs are layer encrypted sets of Sphinx headers detailing a reply path ending in the sending client's [Nym address](../traffic/addressing-system). Clients receiving messages with SURBs attached are able to write a payload to the provided headers without ever learning about anything other than the first hop back into the Mixnet - the Gateway they (the sender of the reply) are currently registered with.
Put simply, client A sends client B a request of some kind. Before sending the request through the Mixnet, it creates a number of SURBs and sends those along with the request. Client B writes the response to the request to the payload of the SURBs, and then sends these through the Mixnet. Since Sphinx packets are multiply route-encrypted, the first destination of the packet is the `nym-node` running as an Entry Gateway that client B has registered with already, so no information regarding either the path through the Mixnet or the destination of the SURBs (aka the Nym address of client A) is revealed to client B.
## Anatomy of a SURB
Diagramatic representation coming soon™️.
## Sender Tags
For a session between two clients, the sending client generates a random alphanumeric string, referred to as a `sender tag` which is sent along with the SURBs to the receiver of its message(s). The `sender tag` is generated randomly, and does not refer in any way to any identifiers of the sending client.
This is done so that receiving clients have some way of differentiating incoming SURBs from multiple clients and can split them into different 'buckets' in order to facilitate concurrent anonymous replies.
## Replenishing SURBs
Since each SURB is just a pre-computed Sphinx packet header, and Sphinx packets only have a finite payload size, then the size of a possible reply for the amount of SURBs sent with a request is `# SURBs * payload size in bytes`. However it is often the case that replies may be variable in size and larger than the alloted payload size, and/or the sending client did not want to compute many Sphinx headers to send with its initial request.
As such, when a client is running out of SURBs to use for replying, it will use a SURB to send a request to the initial sending client. This request is a request for more SURBs to be sent to it.
```mermaid
---
config:
theme: neo-dark
layout: elk
---
sequenceDiagram
participant Nym Client (Sender)
participant Mixnet Nodes
participant Nym Client (Receiver)
Nym Client (Sender) ->> Nym Client (Sender): Create Sphinx packets with request payload & Sphinx packets with N SURBs
Nym Client (Sender) ->> Mixnet Nodes: Sphinx packets (with payload + with SURBs)
Mixnet Nodes ->> Nym Client (Receiver): Sphinx packets (with payload + with SURBs)
Nym Client (Receiver) ->> Nym Client (Receiver): Decrypt packets
Nym Client (Receiver) ->> Nym Client (Receiver): Perform computation
Nym Client (Receiver) ->> Nym Client (Receiver): Prepare response - problem! Response requires > N * SURB payloads
Nym Client (Receiver) ->> Mixnet Nodes: Sphinx packets (SURB request)
Mixnet Nodes ->> Nym Client (Sender): Sphinx packets (SURB request)
Nym Client (Sender) ->> Nym Client (Sender): Create more SURBs
Nym Client (Sender) ->> Mixnet Nodes: Sphinx packets (SURBs)
Mixnet Nodes ->> Nym Client (Receiver): Sphinx packets (SURBs)
Nym Client (Receiver) ->> Nym Client (Receiver): Write response to SURB payloads
Nym Client (Receiver) ->> Mixnet Nodes: SURBs with payload
Mixnet Nodes ->> Nym Client (Sender): SURBs with payload
Nym Client (Sender) ->> Nym Client (Sender): Decrypt - anonymous response received
```
There is a balance to be struck between the amount of SURBs to compute to send along with messages (aka the sending client A spending computation resources) and not sending enough SURBs initially, thus having to wait for a SURB to be sent from the receiving client B to client A requesting more SURBs be sent, which themselves have to be then sent through the Mixnet to client B to be written to and sent back through the Mixnet again.
If you are able to spend the extra resources upfront and send a lot of SURBs, fewer trips through the Mixnet are required for traffic to go back and forth. However, bear in mind that SURBs in the future will have a finite lifespan (see the section below) so precomputing a very large number to send with the initial message (assuming you are expecting a large reponse, or several messages back and forth) will not work. Furthermore, sending huge amounts of SURBs might open your app up to [possible attacks](#anatomy-of-a-surb).
## SURB Lifetimes
<Callout type="warning">
At the time of writing, SURBs themselves are valid indefinitely, but clients purge their local DB of SURBs that are older than a day on restart. SURBs are valid between topology changes over epochs as `nym-nodes` have a single static publick key, so unless a node goes offline, SURBs will still work even after a topology change.
We still have a few features to add to the Mixnet to add some extra security which will dramatically limit the amount of time SURBs are valid for, but will increase the overall security of the network, so do **not** build with the current status in mind. Instead, check out the information below, and build with this in mind.
</Callout>
Once node key rotation (part of the larger [forward secrecy](https://en.wikipedia.org/wiki/Forward_secrecy) work) and [replay protection](https://www.kaspersky.com/resource-center/definitions/replay-attack) is implemented, SURBs will only be valid for the length of the key epoch (aka for the length of time a `nym-node` retains a particular public key between rotations). The length of the key epoch is still to be decided.
Although this means that there will probably be more back-and-forth between clients sending large volumes of traffic, this will make the network more secure overall.
## Known Attacks Using SURBs
There is a known attack in which a malicious service provider / client continually requests that a sending client sends more and more SURBs to them, accruing a large number of them. The attacker then sends all SURBs back to the sending clients at once in order to try and see permutations in the traffic exiting the Mixnet to the sending client, in order to work out which Gateway they are using as their Entry Gateway.
This attack however relies upon the attacker already being able to actively scan all `nym-node`s running as Gateways and capture that traffic, as well as (once the [zk-nym scheme]() is enabled, spend money to send that traffic through the Mixnet. Furthermore, this is an _active attack_ and requires a client to be either be running a malicious service, or be in a position to request multiple bundles of SURBs from clients via some service.
+20 -150
View File
@@ -33,136 +33,6 @@ This page displays a full list of all the changes during our release cycle from
<VarInfo />
## `v2024.13-magura-patched`
- [Release binaries](https://github.com/nymtech/nym/releases/tag/nym-binaries-v2024.13-magura-patched)
- [`nym-node`](nodes/nym-node.mdx) version `1.1.11`
```sh
nym-node
Binary Name: nym-node
Build Timestamp: 2024-11-22T14:30:48.067329245Z
Build Version: 1.1.11
Commit SHA: 01c7b2819ee3d328deccd303b4113ff415d7e276
Commit Date: 2024-11-22T10:50:59.000000000+01:00
Commit Branch: HEAD
rustc Version: 1.82.0
rustc Channel: stable
cargo Profile: release
```
<Callout type="warning" emoji="⚠️">
After changes coming along with `v2024.13-magura` (`nym-node v1.1.10`), Nym Explorer is no longer picking all values correctly. Insstead of fixing this outdated explorer, we are working on a new one, coming out soon.
[Nym Harbourmaster](https://harbourmaster.nymtech.net) has cache of 90min, expect your values to be updated with delay. We are aware of some issues with Nym Harbourmaster and working hard to resolve them in the upcoming explorer v2. To check your routing values in real time, you can use [`nym-gateway-probe`](nodes/performance-and-testing/gateway-probe).
</Callout>
### Operators Updates & Tools
- Updated [`network_tunnel_manager.sh`](https://github.com/nymtech/nym/blob/develop/scripts/network_tunnel_manager.sh) (moved to our monorepo) helps operators to configure their IP tables rules for `nymtun` and `wireguard` routing.
- **Please re-run [routing configuration steps](https://nymtech.net/docs/operators/nodes/nym-node/configuration#routing-configuration) to update your routing settings.**
- We found out that some operators have a wrong value for wireguard IP. Follow these steps to ensure your value is set to `10.1.0.1` (default on new nodes):
<Steps>
###### 1. Open your node config file:
```sh
nano $HOME/.nym/nym-nodes/<ID>/config/config.toml
# change <ID> for your local nym moniker for example:
# nano $HOME/.nym/nym-nodes/default-nym-node/config/config.toml
```
###### 2. Control or change the value of wireguard private IP
- Scroll down to section starting with `[wireguard]`
- Find line `private_ip` and ensure it's set to value `10.1.0.1`
- The section will look like this:
```toml
[wireguard]
# Specifies whether the wireguard service is enabled on this node.
enabled = true
# Socket address this node will use for binding its wireguard interface.
# default: `0.0.0.0:51822`
bind_address = '0.0.0.0:51822'
# Private IP address of the wireguard gateway.
# default: `10.1.0.1`
private_ip = '10.1.0.1'
```
###### 3. Save, exit and restart the service
- If you used `nano` editor - press `ctrl` + `x` and confirm the changes
- Run these commands to update the service with new values and restart your node process:
```sh
systemctl daemon-reload && service nym-node restart
```
</Steps>
- New manual how to [run `nym-node` as non-root](nodes/nym-node/configuration#running-nym-node-as-a-non-root)
- 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`](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
In case you encounter this error:
```
[ERROR] nym-node/src/node/mod.rs:628: the exit gateway subtask has failed with the following message: failed to start authenticator: internal wireguard error no private IP set for peer..
```
You can follow these steps to make a workaround:
<br />
<AccordionTemplate name="Authenticator error fix">
<Steps>
###### 1. Find the error
- In the node logs, locate the ERROR message which says `the exit gateway subtask has failed with the following message: failed to start authenticator: internal wireguard error no private IP set for peer KN5GPvkC+p6G/SM4PD2Z3ObAtRGiDjHPRnQOPpbdUQk=`
- Copy the end part of that peer, later denoted as `<WG_PEER_STRING_END>` (in our example `GiDjHPRnQOPpbdUQk=`) to use later in the sql commands
###### 2. Fix the issue in sqlite3 db
<Callout type="warning" emoji="⚠️">
Be careful when running commands within sqlite database.
</Callout>
- Navigate to the data directory:
```sh
cd $HOME/.nym/nym-nodes/<ID>/data
```
- Enter the database:
```sh
sqlite3 clients.sqlite
```
- Run these commands:
```sh
# Change with your unique <PEER_STRING_END>
select * from wireguard_peer where public_key like "%<WG_PEER_STRING_END>%"
# Make sure that only ONE line is returned and it contains the key
delete from wireguard_peer where public_key like "%<WG_PEER_STRING_END>%";
```
- Confirm that peer has been removed by running this again:
```sh
select * from wireguard_peer where public_key like "%<WG_PEER_STRING_END>%";
```
###### 3. Exit and restart the service
Run `.quit` and:
```sh
systemctl restart nym-node.service
```
</Steps>
</AccordionTemplate>
## `v2024.13-magura`
Magura release represents a bigger milestone in [project Smoosh](archive/faq/smoosh-faq.mdx) development where `nym-node` is one binary able to perform any function in Nym Mixnet. This release is especially crucial for operators, please pay attention to the section [*Operators Updates & Tooling*](#operators-updates--tooling) below.
@@ -200,15 +70,15 @@ cargo Profile: release
- [Switch over the last set of jobs to arc runners](https://github.com/nymtech/nym/pull/4938): Switch over the remaining GH jobs using 16-core runners to self-hosted arc runners. Since we can't currently use Docker on the ubuntu-20.04 runners, remove the matrix notification steps
<AccordionTemplate name={<TestingSteps/>}>
<AccordionTemplate name={<TestingSteps/>}>
Confirm that the deployment workflows work through manual testing
- [x] cd-docs
- [x] publish-sdk-npm
</AccordionTemplate>
- [V2 performance monitoring feature flag](https://github.com/nymtech/nym/pull/4943): Feature flag to use v2 network monitor results in rewarding
- [V2 performance monitoring feature flag](https://github.com/nymtech/nym/pull/4943): Feature flag to use v2 network monitor results in rewarding
- [Add `utoipa` feature to nym-node](https://github.com/nymtech/nym/pull/4945): `cargo build -p nym-node` was failing, since its depending on `QueryParams` having `utoipa` traits derived
- [Add `utoipa` feature to nym-node](https://github.com/nymtech/nym/pull/4945): `cargo build -p nym-node` was failing, since its depending on `QueryParams` having `utoipa` traits derived
- [Ticket type storage](https://github.com/nymtech/nym/pull/4947)
@@ -233,8 +103,8 @@ Confirm that the deployment workflows work through manual testing
- [chore: remove unused rocket code](https://github.com/nymtech/nym/pull/4968)
- [add Dockerfile for nym node](https://github.com/nymtech/nym/pull/4972)
- [`Product Data` Add session type based on ecash ticket received](https://github.com/nymtech/nym/pull/4974): Fire an `EcashTicket` event for the `GatewayStatisticsCollector`, when an Ecash ticket is being accepted. This allows to mark an active session as being a mixnet session or a vpn session. It also changes the format of the related self-described data, to accommodate that new session type.
- [`Product Data` Add session type based on ecash ticket received](https://github.com/nymtech/nym/pull/4974): Fire an `EcashTicket` event for the `GatewayStatisticsCollector`, when an Ecash ticket is being accepted. This allows to mark an active session as being a mixnet session or a vpn session. It also changes the format of the related self-described data, to accommodate that new session type.
- [Top up bandwidth](https://github.com/nymtech/nym/pull/4975)
@@ -277,7 +147,7 @@ Confirm that the deployment workflows work through manual testing
- log gw identity key
- better agent testrun logging
- log responses on server side
- change response code for agents
- change response code for agents
- update sqlx data
- fix agent - probe gw bug
@@ -295,7 +165,7 @@ Confirm that the deployment workflows work through manual testing
- [Allow custom http port to be reset](https://github.com/nymtech/nym/pull/5073)
- [Fix gateway decreasing bandwidth](https://github.com/nymtech/nym/pull/5075): Make sure to update the storage after each decrease with the new values. Also set the storage values to 0 on restart for existing peers, as kernel peers can't have those values set to 0
- [Fix gateway decreasing bandwidth](https://github.com/nymtech/nym/pull/5075): Make sure to update the storage after each decrease with the new values. Also set the storage values to 0 on restart for existing peers, as kernel peers can't have those values set to 0
- [Fix expiration date as today + 7 days](https://github.com/nymtech/nym/pull/5076)
@@ -317,7 +187,7 @@ Confirm that the deployment workflows work through manual testing
- [Add NYM_VPN_API to env files](https://github.com/nymtech/nym/pull/5099)
- [Feature/force refresh node](https://github.com/nymtech/nym/pull/5101): currently if nodes update their role from say mixnode to entry-gateway, it might take quite a while for `nym-api` to pick up the change and thus they might be losing performance. With this change, the node will be force refreshed on its startup
- [Feature/force refresh node](https://github.com/nymtech/nym/pull/5101): currently if nodes update their role from say mixnode to entry-gateway, it might take quite a while for `nym-api` to pick up the change and thus they might be losing performance. With this change, the node will be force refreshed on its startup
- [`nym-credential-proxy-requests`: reqwest use rustls-tls](https://github.com/nymtech/nym/pull/5116)
@@ -335,7 +205,7 @@ Confirm that the deployment workflows work through manual testing
- [Fix critical issues SI84 and SI85 from Cure53](https://github.com/nymtech/nym/pull/4758): This pull request fixes the following issues:
- NYM-01-009 WP5: BLS12-381 EC signature bypasses in Coconut library (Critical)
- NYM-01-014 WP5: Partial signature bypass in offline eCash (Critical)
- [bugfix: correctly paginate through 'search_tx' endpoint](https://github.com/nymtech/nym/pull/4936): when `results.append(&mut res.txs);` was called, `res.txs` was always empty thus it was impossible to return more than page size number of results
- [Fix broken build after merge](https://github.com/nymtech/nym/pull/4937)
@@ -344,7 +214,7 @@ Confirm that the deployment workflows work through manual testing
- dedicated commands to request specific blocks for processing
- decreased websocket failure timeout
- ensuring we do actually have sufficient number of blocks to process rewarding for given epoch
- additional error logging
- additional error logging
- [bugfix: fix expected return type on /v1/gateways endpoint](https://github.com/nymtech/nym/pull/4965)
@@ -354,7 +224,7 @@ Confirm that the deployment workflows work through manual testing
- Faulty aggregation to invalid offline eCash signatures
- Signature forgery of Pointcheval-Sanders schema
- [bugfix: client memory leak](https://github.com/nymtech/nym/pull/4991): This fixes memory leaks in all the clients. however, they were most prominent in `nym-api` during network monitoring due to the sheer amount of packets being pushed
- [bugfix: client memory leak](https://github.com/nymtech/nym/pull/4991): This fixes memory leaks in all the clients. however, they were most prominent in `nym-api` during network monitoring due to the sheer amount of packets being pushed
- [Fix rustfmt in nym-credential-proxy](https://github.com/nymtech/nym/pull/4992)
@@ -409,7 +279,7 @@ Confirm that the deployment workflows work through manual testing
- [bugfix: make sure to assign correct node_id and identity during 'gateway_details' table migration](https://github.com/nymtech/nym/pull/5142)
- [bugifx: assign 'node_id' when converting from 'GatewayDetails' to 'TestNode'](https://github.com/nymtech/nym/pull/5143)
### Operators Updates & Tooling
<Callout type="warning" emoji="⚠️">
@@ -422,7 +292,7 @@ Confirm that the deployment workflows work through manual testing
- **[Operators release & rewards roadmap](tokenomics/mixnet-rewards.mdx#roadmap)**
- **New [Operators landing page](https://nymtech.net/operators)**
- **New [Operators landing pag e](https://nymtech.net/operators)
- [Nym Harbourmaster](https://harbourmaster.nymtech.net) had a new tab `NODE SEARCH` where operators can easily search nodes by identity keys and owner accounts and get all public information listed.
@@ -465,7 +335,7 @@ Confirm that the deployment workflows work through manual testing
- Confirm the transaction
###### 5. Welcome to new episode of `nym-node`!
###### 5. Welcome to new episode of `nym-node`!
</ Steps>
</AccordionTemplate>
@@ -490,13 +360,13 @@ Confirm that the deployment workflows work through manual testing
- Currently in *Native rewarding*, the rewards are split equally across the [rewarded set of nodes](https://validator.nymtech.net/api/v1/epoch/reward_params) (which now = active set and it's size is 240 nodes) for both Mixnet mode and dVPN mode. Every node being assigned 1 / 240 work factor (hence *naive rewarding*).
#### Directory Services v2.1: API & Mixnet Contract Changes
#### Directory Services v2.1: API & Mixnet Contract Changes
Magura release brings [breaking changes on API](https://github.com/nymtech/nym/pull/4903) logic of Nym. New APIs will only communicate with `nym-node` from this release and newer. Also old version of APIs won't be able to communicate with the new version of `nym-node`. We are also moving towards completely removing Nym Explorer API, which now has been only used to report nodes location.
Magura release brings [breaking changes on API](https://github.com/nymtech/nym/pull/4903) logic of Nym. New APIs will only communicate with `nym-node` from this release and newer. Also old version of APIs won't be able to communicate with the new version of `nym-node`. We are also moving towards completely removing Nym Explorer API, which now has been only used to report nodes location.
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. Furthermore, this allows to arbitrary change of `nym-node` from mixnode into a gateway modes (and vice versa) without losing any delegations.
The contract changes also mean any node functionality can get rewards. Rather than just with assigned mixing roles, gateways now also added into the pool. However, to be eligible for gateway rewarding, one must [migrate into a `nym-node`](#wallet-changes) on a smart contract level (or bond a new node).
The contract changes also mean any node functionality can get rewards. Rather than just with assigned mixing roles, gateways now also added into the pool. However, to be eligible for gateway rewarding, one must [migrate into a `nym-node`](#wallet-changes) on a smart contract level (or bond a new node).
##### API High Level Changes
@@ -1439,7 +1309,7 @@ Every `nym-node` should be upgraded to the latest version! Operators can test us
**`nym-node`**
- Make sure to fill in basic description info, into the file located at `.nym/nym-nodes/<ID>/data/description.toml` (all nodes)
- Configure wireguard routing with new [`network_tunnel_manager.sh`](https://github.com/nymtech/nym/blob/develop/scripts/network_tunnel_manager.sh) following [these steps](nodes/configuration.md#routing-configuration) (Gateways only for the time being)
- Configure wireguard routing with new [`network_tunnel_manager.sh`](https://gist.github.com/tommyv1987/ccf6ca00ffb3d7e13192edda61bb2a77) following [these steps](nodes/configuration.md#routing-configuration) (Gateways only for the time being)
- Enable Wireguard with `--wireguard-enabled true` flag included in your run command (Gateways only for the time being)
- Note: On some VPS this setup may not be enough to get the correct results as some ISPs have their own security groups setup below the individual VPS. In that case a ticket to ISP will have to be issued to open the needed settings. We are working on a template for such ticket.
- Setup [reverse proxy and WSS](nodes/proxy-configuration.md) on `nym-node` (Gateways only for the time being)
@@ -1681,9 +1551,9 @@ ufw status
**Step 2: Download and Prepare the Network Tunnel Manager Script**
1. Download the [`network_tunnel_manager.sh`](https://github.com/nymtech/nym/blob/develop/scripts/network_tunnel_manager.sh) script:
1. Download the [`network_tunnel_manager.sh`](https://gist.github.com/tommyv1987/ccf6ca00ffb3d7e13192edda61bb2a77) script:
```bash
curl -L -o network_tunnel_manager.sh https://github.com/nymtech/nym/blob/develop/scripts/network_tunnel_manager.sh
curl -L -o network_tunnel_manager.sh https://gist.githubusercontent.com/tommyv1987/ccf6ca00ffb3d7e13192edda61bb2a77/raw/3c0a38c1416f8fdf22906c013299dd08d1497183/network_tunnel_manager.sh
```
2. Make the script executable:
@@ -14,27 +14,30 @@ This page explains how to upgrade [`nym-node`](#nym-node-upgrade) or [`validator
## Nym node Upgrade
Since `v2024.13-magura` (`nym-node v1.1.10`), **operators NO longer update node information in the Mixnet smart contract** (wallet version information), **only upgrade node binary** (on VPS), resulting in `~/.nym/nym-nodes/<ID>/config/config.toml` update.
**Upgrading your node is a straight forward two-step process:**
<Steps>
#### 1. Updating the binary and `~/.nym/nym-nodes/<ID>/config/config.toml` on your VPS
#### 2. Updating the node information in the [mixnet smart contract](https://nymtech.net/docs/nyx/mixnet-contract.html). This is the information that is present on the [mixnet explorer](https://explorer.nymtech.net).
</Steps>
Below are detailed steps how to do it:
<Steps>
###### 1. Upgrade `nym-node` binary
###### 1. Upgrading node binary and information in config file
- Pause your node process.
- If you run your node as `systemd` service (recommended), run: `service nym-node stop`
- Otherwise open the terminal window with your node logs and press once `ctrl + c` and wait for the node to terminate gracefully
- Otherwise open the terminal window with your node logs and press `ctrl + c`
- Replace the existing `nym-node` binary with the newest binary (which you can either [compile yourself](../../binaries/building-nym.mdx) or [download](../../binaries/pre-built-binaries.mdx).
- To verify node version, run `./nym-node --version`
###### 2. Restart the node
- [Re-run with the same values](../nym-node/setup.mdx#initialise--run) as you use to run your `nym-node`. If you want keep changes in your config file, use flag `-w` (`--write-changes`), **This will just update the config file, it will not overwrite existing keys**.
- If you automated your node with `systemd` (recommended), make sure you have all needed flags in `ExecStart` line of the service config file, and run:
- If you automated your node with `systemd` (recommended) run:
```sh
systemctl daemon-reload
service nym-node start
@@ -45,19 +48,25 @@ service nym-node start
journalctl -f -u nym-node.service
```
###### 2. Updating your node information in the smart contract
###### 3. Check if your node is reporting the version correctly
Follow these steps to update the information about your `nym-node` which is publicly available from the [`nym-api`](https://validator.nymtech.net/api/swagger/index.html) and information displayed on the [Mixnet explorer](https://explorer.nymtech.net).
- Open [Nym Harbourbourmaster](https://harbourmaster.nymtech.net), search your node and verify that everything is working as expected and your node shows expected version.
You can either do this graphically via the Desktop Wallet, or the CLI.
<div>
<Tabs items={[
<strong>Desktop Wallet (recommended)</strong>,
<strong>CLI (superusers)</strong>,
]} defaultIndex="0">
<MyTab><DesktopWalletUpdate/></MyTab>
<MyTab><CliUpdate/></MyTab>
</Tabs>
</div>
</Steps>
<Callout type="warning" emoji="⚠️">
After changes coming along with `v2024.13-magura` (`nym-node v1.1.10`), Nym Explorer is no longer picking all values correctly. Insstead of fixing this outdated explorer, we are working on a new one, coming out soon.
[Nym Harbourmaster](https://harbourmaster.nymtech.net) has cache of 90min, expect your values to be updated with delay. We are aware of some issues with Nym Harbourmaster and working hard to resolve them in the upcoming explorer v2. To check your routing values in real time, you can use [`nym-gateway-probe`](../performance-and-testing/gateway-probe).
</Callout>
## Validator Upgrade
Upgrading from `v0.31.1` -> `v0.32.0` process is fairly simple. Grab the `v0.32.0` release tarball from the [`nyxd` releases page](https://github.com/nymtech/nyxd/releases), and untar it. Inside are two files:
@@ -83,3 +92,4 @@ The most common reason for your validator being jailed is that it runs out of me
Running the command `df -H` will return the size of the various partitions of your VPS.
If the `/dev/sda` partition is almost full, try pruning some of the `.gz` syslog archives and restart your validator process.
@@ -268,7 +268,7 @@ Make sure to keep your IPv4 address enabled while setting up IPv6, as the majori
### Routing Configuration
While we're working on Rust implementation to have these settings as a part of the binary build, to solve these connectivity requirements in the meantime we wrote a script [`network_tunnel_manager.sh`](https://github.com/nymtech/nym/blob/develop/scripts/network_tunnel_manager.sh) to support operators to configure their servers and address all the connectivity requirements.
While we're working on Rust implementation to have these settings as a part of the binary build, to solve these connectivity requirements in the meantime we wrote a script [`network_tunnel_manager.sh`](https://gist.github.com/tommyv1987/ccf6ca00ffb3d7e13192edda61bb2a77) to support operators to configure their servers and address all the connectivity requirements.
Networking configuration across different ISPs and various operation systems does not have a generic solution. If the provided configuration setup doesn't solve your problem check out [IPv6 troubleshooting](../../troubleshooting/vps-isp.mdx#ipv6-troubleshooting) page. Be aware that you may have to do more research, customised adjustments or contact your ISP to change settings for your VPS.
@@ -285,7 +285,7 @@ The script should be used in a context where `nym-node` is running to fully util
###### 1. Download `network_tunnel_manager.sh`, make executable and run:
```sh
curl -L https://raw.githubusercontent.com/nymtech/nym/refs/heads/develop/scripts/network_tunnel_manager.sh -o network_tunnel_manager.sh && \
curl -L https://gist.githubusercontent.com/tommyv1987/ccf6ca00ffb3d7e13192edda61bb2a77/raw/74241cc06492b955e582052939090f57a285a65e/network_tunnel_manager.sh -o network_tunnel_manager.sh && \
chmod +x network_tunnel_manager.sh && \
./network_tunnel_manager.sh
```
@@ -426,95 +426,6 @@ journalctl -u nym-node.service -f -n 100
Make sure that you get the validation of all connectivity. If there are still any problems, please refer to [troubleshooting section](../../troubleshooting/vps-isp.mdx#incorrect-gateway-network-check).
## Running `nym-node` as a non-root
Some operators prefer to run `nym-node` without root privileges. It's possible but still `nym-node` binary needs higher privileges for network-level operations demanding these permissions. Below is a guide how to go about such setup:
<Callout type="warning" emoji="⚠️">
Copying nodes database and the `.nym/` directories from `/root/.nym` to `/home/<USER>/.nym/` should be treated as experimental, therefore we would advise this section for operators starting new nodes, rather than tweaking an existing one. We will publish a detailed guide for changing permissions of an existing node soon.
</Callout>
<Steps>
###### 1. Setup a new user
- Define a variable `user_name` using your desired user name:
```sh
user_name="<USER>"
```
- Run:
```sh
user_home="/home/$user_name"
if ! id "$user_name" &>/dev/null; then
sudo adduser --home "$user_home" --disabled-login --gecos "" "$user_name"
else
echo "user $user_name already exists"
fi
```
- And follow by:
```sh
sudo usermod -aG sudo "$user_name"
```
- Optional: Add to sudoers group:
```sh
echo "$user_name ALL=(ALL) NOPASSWD:ALL" | sudo tee -a /etc/sudoers.d/$user_name
```
###### 2. Grant needed permissions for network-level operations
While `nym-node` will be set as a user process, it requires higher privileges for network-level operations, set them up with this command:
```sh
sudo setcap 'cap_net_bind_service=+ep cap_net_admin=+ep' nym-node
```
**After replacing or upgrading the binary, you must reapply these permissions each time!**
###### 3. Edit service config file
- Add these new lines to your `/etc/systemd/system/nym-node.service` [service config file](#systemd)
- `After=network.target`
- `Group=<USER>`
- `Type=simple`
- Your service file will then look like this:
```ini
[Unit]
Description=Nym Node
After=network.target
StartLimitInterval=350
StartLimitBurst=10
[Service]
User=<USER>
Group=<USER>
Type=simple
LimitNOFILE=65536
ExecStart=<PATH>/nym-node run <ARGUMENTS> # add all the flags you use to run your node
KillSignal=SIGINT
Restart=on-failure
RestartSec=30
[Install]
WantedBy=multi-user.target
```
###### 4. Reload and restart the service
```sh
systemctl daemon-reload && service nym-node restart
```
- If you want to follow the logs, run:
```sh
journalctl -u nym-node -f
```
</Steps>
## Next Steps
@@ -17,16 +17,15 @@ This documentation page provides a guide on how to set up and run a [NYM NODE](.
```sh
nym-node
Binary Name: nym-node
Build Timestamp: 2024-11-22T14:30:48.067329245Z
Build Version: 1.1.11
Commit SHA: 01c7b2819ee3d328deccd303b4113ff415d7e276
Commit Date: 2024-11-22T10:50:59.000000000+01:00
Build Timestamp: 2024-11-18T17:02:50.947941194Z
Build Version: 1.1.10
Commit SHA: b49ef643df86f0c670672429812c632fbbaf6cf1
Commit Date: 2024-11-18T17:56:57.000000000+01:00
Commit Branch: HEAD
rustc Version: 1.82.0
rustc Channel: stable
cargo Profile: release
```
{/* COMMENTING THIS OUT ASS WE HAVE TO FIGURE OUT HOW TO SHOW THE LATEST VERSION FROM MASTER BRANCH
<BuildInfo />
*/}
@@ -8,10 +8,6 @@ import NodeApiCheckQueryHelp from 'components/outputs/command-outputs/node-api-c
# Node API Check
<Callout type="warning" emoji="⚠️">
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.
</Callout>
<VarInfo />
Operating a `nym-node` is not a *"set and forget"* endeavor, it takes some work. To diagnose node network performance through querying APIs, is a good knowledge to have. These are the main places to look for API endpoints regarding `nym-node`:
@@ -97,9 +93,9 @@ python --version
</Tabs>
</div>
###### 2. Install `node_api_check.py` and make executable
###### 2. Install `node_api_check.py` and make executable
To run the program you neet to have [`node_api_check.py`](https://github.com/nymtech/nym/tree/develop/scripts/node_api_check.py) and [`api_endpoints.json`](https://github.com/nymtech/nym/tree/develop/scripts/api_endpoints.json).
To run the program you neet to have [`node_api_check.py`](https://github.com/nymtech/nym/tree/develop/scripts/node_api_check.py) and [`api_endpoints.json`](https://github.com/nymtech/nym/tree/develop/scripts/api_endpoints.json).
- If you [compiled from source](../../binaries/building-nym.mdx), you already have both of these files. Note that the latest version of this program may be on `develop` branch.
@@ -163,3 +159,4 @@ Another command is `version_count` where at least one `nym-node` version is requ
```sh
./node_api_check version_count 1.1.0 1.1.1 1.1.2 1.1.3 --markdown
```
@@ -232,9 +232,6 @@ username soft nofile 4096
Then reboot your server and restart your node.
## Running `nym-node` as a non-root
Some operators prefer to run `nym-node` without root privileges. It's possible but still `nym-node` binary needs higher privileges for network-level operations demanding these permissions. If you are starting a new `nym-node` and want to run it as a non-root, follow [this guide](../nym-node/configuration#running-nym-node-as-a-non-root) before you proceed with the node setup sections.
## Ports reference tables
@@ -161,7 +161,7 @@ In dVPN (2-hop) mode every node which meets the performance criteria, including
In both cases, the selection algorithm also looks whether the node runs with [Terms & Conditions](nodes/nym-node/setup.mdx#terms--conditions) accepted **AND** if it's not a legacy binary version. In case either of these criterias are not met, the node will have be excluded from the rewarded set selection.
To read more about rewards calculation, please see next page [*Nym Operators Rewards*](tokenomics/mixnet-rewards.md).
To read more about rewards calculation, please see next page [*Nym Operators Rewards*](mixnet-rewards.md).
## Query Validator API
@@ -153,7 +153,7 @@ The entire active set selection probablity:
For a comparison we made an example with 5 nodes, where first number is node performance and second stake saturation (assuming all of them `config_score` = 1 and not 0):
<br />
<AccordionTemplate name="✏️ Example: Reward set selection probability calculation">
<AccordionTemplate name="✏️ Example: Reward set selection">
> node_1 = 1.00 ^ 20 \* 1.0 = 1 <br />
> node_2 = 1.00 ^ 20 \* 0.5 = 0.5 <br />
> node_3 = 0.99 ^ 20 \* 1.0 = 0.818 <br />
-1
View File
@@ -20,7 +20,6 @@ bloomfilter = { workspace = true }
cfg-if = { workspace = true }
clap = { workspace = true, features = ["cargo", "derive", "env"] }
console-subscriber = { workspace = true, optional = true } # validator-api needs to be built with RUSTFLAGS="--cfg tokio_unstable"
dashmap = { workspace = true }
dirs = { workspace = true }
futures = { workspace = true }
itertools = { workspace = true }
+2 -2
View File
@@ -13,7 +13,7 @@ use crate::node_status_api::NodeStatusCache;
use crate::nym_contract_cache::cache::NymContractCache;
use crate::support::caching::cache::SharedCache;
use crate::support::config;
use crate::support::http::state::{AppState, ForcedRefresh};
use crate::support::http::state::AppState;
use crate::support::storage::NymApiStorage;
use async_trait::async_trait;
use axum::Router;
@@ -1264,7 +1264,7 @@ struct TestFixture {
impl TestFixture {
fn build_app_state(storage: NymApiStorage) -> AppState {
AppState {
forced_refresh: ForcedRefresh::new(true),
forced_refresh: Default::default(),
nym_contract_cache: NymContractCache::new(),
node_status_cache: NodeStatusCache::new(),
circulating_supply_cache: CirculatingSupplyCache::new("unym".to_owned()),
+5 -1
View File
@@ -14,6 +14,7 @@ use nym_sphinx::params::PacketType;
use nym_sphinx::receiver::MessageReceiver;
use nym_task::TaskClient;
use std::collections::{HashMap, HashSet};
use std::process;
use tokio::time::{sleep, Duration, Instant};
use tracing::{debug, error, info, trace};
@@ -94,7 +95,10 @@ impl<R: MessageReceiver + Send> Monitor<R> {
)
.await
{
error!("Failed to submit monitor run information to the database: {err}",);
error!("Failed to submit monitor run information to the database - {err}",);
// TODO: slightly more graceful shutdown here
process::exit(1);
}
}
@@ -121,7 +121,6 @@ impl PacketPreparer {
test_route.topology().clone(),
self_address,
PacketSize::RegularPacket,
false,
DEFAULT_AVERAGE_PACKET_DELAY,
DEFAULT_AVERAGE_ACK_DELAY,
self.ack_key.clone(),
+12 -37
View File
@@ -57,9 +57,6 @@ pub enum NodeDescribeCacheError {
// TODO: perhaps include more details here like whether key/signature/payload was malformed
#[error("could not verify signed host information for node {node_id}")]
MissignedHostInformation { node_id: NodeId },
#[error("node {node_id} is announcing an illegal ip address")]
IllegalIpAddress { node_id: NodeId },
}
// this exists because I've been moving things around quite a lot and now the place that holds the type
@@ -202,18 +199,13 @@ impl DescribedNodes {
pub struct NodeDescriptionProvider {
contract_cache: NymContractCache,
allow_all_ips: bool,
batch_size: usize,
}
impl NodeDescriptionProvider {
pub(crate) fn new(
contract_cache: NymContractCache,
allow_all_ips: bool,
) -> NodeDescriptionProvider {
pub(crate) fn new(contract_cache: NymContractCache) -> NodeDescriptionProvider {
NodeDescriptionProvider {
contract_cache,
allow_all_ips,
batch_size: DEFAULT_NODE_DESCRIBE_BATCH_SIZE,
}
}
@@ -278,7 +270,6 @@ async fn try_get_client(
async fn try_get_description(
data: RefreshData,
allow_all_ips: bool,
) -> Result<NymNodeDescription, NodeDescribeCacheError> {
let client = try_get_client(&data.host, data.node_id, data.port).await?;
@@ -295,12 +286,6 @@ async fn try_get_description(
});
}
if !allow_all_ips && !host_info.data.check_ips() {
return Err(NodeDescribeCacheError::IllegalIpAddress {
node_id: data.node_id,
});
}
let node_info = query_for_described_data(&client, data.node_id).await?;
let description = node_info.into_node_description(host_info.data);
@@ -372,8 +357,8 @@ impl RefreshData {
self.node_id
}
pub(crate) async fn try_refresh(self, allow_all_ips: bool) -> Option<NymNodeDescription> {
match try_get_description(self, allow_all_ips).await {
pub(crate) async fn try_refresh(self) -> Option<NymNodeDescription> {
match try_get_description(self).await {
Ok(description) => Some(description),
Err(err) => {
debug!("failed to obtain node self-described data: {err}");
@@ -427,15 +412,11 @@ impl CacheItemProvider for NodeDescriptionProvider {
}
}
let nodes = stream::iter(
nodes_to_query
.into_iter()
.map(|n| n.try_refresh(self.allow_all_ips)),
)
.buffer_unordered(self.batch_size)
.filter_map(|x| async move { x.map(|d| (d.node_id, d)) })
.collect::<HashMap<_, _>>()
.await;
let nodes = stream::iter(nodes_to_query.into_iter().map(|n| n.try_refresh()))
.buffer_unordered(self.batch_size)
.filter_map(|x| async move { x.map(|d| (d.node_id, d)) })
.collect::<HashMap<_, _>>()
.await;
info!("refreshed self described data for {} nodes", nodes.len());
@@ -451,11 +432,8 @@ pub(crate) fn new_refresher(
) -> CacheRefresher<DescribedNodes, NodeDescribeCacheError> {
CacheRefresher::new(
Box::new(
NodeDescriptionProvider::new(
contract_cache,
config.debug.node_describe_allow_illegal_ips,
)
.with_batch_size(config.debug.node_describe_batch_size),
NodeDescriptionProvider::new(contract_cache)
.with_batch_size(config.debug.node_describe_batch_size),
),
config.debug.node_describe_caching_interval,
)
@@ -468,11 +446,8 @@ pub(crate) fn new_refresher_with_initial_value(
) -> CacheRefresher<DescribedNodes, NodeDescribeCacheError> {
CacheRefresher::new_with_initial_value(
Box::new(
NodeDescriptionProvider::new(
contract_cache,
config.debug.node_describe_allow_illegal_ips,
)
.with_batch_size(config.debug.node_describe_batch_size),
NodeDescriptionProvider::new(contract_cache)
.with_batch_size(config.debug.node_describe_batch_size),
),
config.debug.node_describe_caching_interval,
initial,
@@ -88,6 +88,7 @@ pub(crate) async fn submit_gateway_monitoring_results(
match state
.storage
.manager
.submit_gateway_statuses_v2(message.results())
.await
{
@@ -132,6 +133,7 @@ pub(crate) async fn submit_node_monitoring_results(
match state
.storage
.manager
.submit_mixnode_statuses_v2(message.results())
.await
{
+1 -2
View File
@@ -86,9 +86,8 @@ async fn refresh_described(
}
// to make sure you can't ddos the endpoint while a request is in progress
state.forced_refresh.set_last_refreshed(node_id).await;
let allow_all_ips = state.forced_refresh.allow_all_ip_addresses;
if let Some(updated_data) = refresh_data.try_refresh(allow_all_ips).await {
if let Some(updated_data) = refresh_data.try_refresh().await {
let Ok(mut describe_cache) = state.described_nodes_cache.write().await else {
return Err(AxumErrorResponse::service_unavailable());
};
+2 -6
View File
@@ -21,9 +21,7 @@ use crate::status::{ApiStatusState, SignerState};
use crate::support::caching::cache::SharedCache;
use crate::support::config::helpers::try_load_current_config;
use crate::support::config::Config;
use crate::support::http::state::{
AppState, ForcedRefresh, ShutdownHandles, TASK_MANAGER_TIMEOUT_S,
};
use crate::support::http::state::{AppState, ShutdownHandles, TASK_MANAGER_TIMEOUT_S};
use crate::support::http::RouterBuilder;
use crate::support::nyxd;
use crate::support::storage::runtime_migrations::m001_directory_services_v2_1::migrate_to_directory_services_v2_1;
@@ -190,9 +188,7 @@ async fn start_nym_api_tasks_axum(config: &Config) -> anyhow::Result<ShutdownHan
};
let router = router.with_state(AppState {
forced_refresh: ForcedRefresh::new(
config.topology_cacher.debug.node_describe_allow_illegal_ips,
),
forced_refresh: Default::default(),
nym_contract_cache: nym_contract_cache_state.clone(),
node_status_cache: node_status_cache_state.clone(),
circulating_supply_cache: circulating_supply_cache.clone(),
-3
View File
@@ -439,8 +439,6 @@ pub struct TopologyCacherDebug {
pub node_describe_caching_interval: Duration,
pub node_describe_batch_size: usize,
pub node_describe_allow_illegal_ips: bool,
}
impl Default for TopologyCacherDebug {
@@ -449,7 +447,6 @@ impl Default for TopologyCacherDebug {
caching_interval: DEFAULT_TOPOLOGY_CACHE_INTERVAL,
node_describe_caching_interval: DEFAULT_NODE_DESCRIBE_CACHE_INTERVAL,
node_describe_batch_size: DEFAULT_NODE_DESCRIBE_BATCH_SIZE,
node_describe_allow_illegal_ips: false,
}
}
}
+1 -9
View File
@@ -82,20 +82,12 @@ pub(crate) struct AppState {
pub(crate) node_info_cache: unstable::NodeInfoCache,
}
#[derive(Clone)]
#[derive(Clone, Default)]
pub(crate) struct ForcedRefresh {
pub(crate) allow_all_ip_addresses: bool,
pub(crate) refreshes: Arc<RwLock<HashMap<NodeId, OffsetDateTime>>>,
}
impl ForcedRefresh {
pub(crate) fn new(allow_all_ip_addresses: bool) -> ForcedRefresh {
ForcedRefresh {
allow_all_ip_addresses,
refreshes: Arc::new(Default::default()),
}
}
pub(crate) async fn last_refreshed(&self, node_id: NodeId) -> Option<OffsetDateTime> {
self.refreshes.read().await.get(&node_id).copied()
}
+96 -96
View File
@@ -7,7 +7,6 @@ use crate::support::storage::models::{
ActiveGateway, ActiveMixnode, GatewayDetails, HistoricalUptime, MixnodeDetails, NodeStatus,
RewardingReport, TestedGatewayStatus, TestedMixnodeStatus, TestingRoute,
};
use crate::support::storage::DbIdCache;
use nym_mixnet_contract_common::{EpochId, IdentityKey, NodeId};
use nym_types::monitoring::NodeResult;
use sqlx::FromRow;
@@ -52,7 +51,24 @@ impl AvgGatewayReliability {
// all SQL goes here
impl StorageManager {
pub(super) async fn get_all_avg_mix_reliability_in_last_24hr(
pub(crate) async fn get_mixnode_mix_ids_by_identity(
&self,
identity: &str,
) -> Result<Vec<NodeId>, sqlx::Error> {
let ids = sqlx::query!(
r#"SELECT mix_id as "mix_id: NodeId" FROM mixnode_details WHERE identity_key = ?"#,
identity
)
.fetch_all(&self.connection_pool)
.await?
.into_iter()
.map(|row| row.mix_id)
.collect();
Ok(ids)
}
pub(crate) async fn get_all_avg_mix_reliability_in_last_24hr(
&self,
end_ts_secs: i64,
) -> Result<Vec<AvgMixnodeReliability>, sqlx::Error> {
@@ -61,7 +77,7 @@ impl StorageManager {
.await
}
pub(super) async fn get_all_avg_gateway_reliability_in_last_24hr(
pub(crate) async fn get_all_avg_gateway_reliability_in_last_24hr(
&self,
end_ts_secs: i64,
) -> Result<Vec<AvgGatewayReliability>, sqlx::Error> {
@@ -70,7 +86,7 @@ impl StorageManager {
.await
}
pub(super) async fn get_all_avg_mix_reliability_in_time_interval(
pub(crate) async fn get_all_avg_mix_reliability_in_time_interval(
&self,
start_ts_secs: i64,
end_ts_secs: i64,
@@ -98,7 +114,7 @@ impl StorageManager {
Ok(result)
}
pub(super) async fn get_all_avg_gateway_reliability_in_interval(
pub(crate) async fn get_all_avg_gateway_reliability_in_interval(
&self,
start_ts_secs: i64,
end_ts_secs: i64,
@@ -131,7 +147,7 @@ impl StorageManager {
/// # Arguments
///
/// * `mix_id`: mix-id (as assigned by the smart contract) of the mixnode.
pub(super) async fn get_mixnode_database_id(
pub(crate) async fn get_mixnode_database_id(
&self,
mix_id: NodeId,
) -> Result<Option<i64>, sqlx::Error> {
@@ -143,7 +159,7 @@ impl StorageManager {
Ok(id)
}
pub(super) async fn get_gateway_database_id(
pub(crate) async fn get_gateway_database_id(
&self,
node_id: NodeId,
) -> Result<Option<i64>, sqlx::Error> {
@@ -156,7 +172,7 @@ impl StorageManager {
}
/// Tries to obtain row id of given gateway given its identity
pub(super) async fn get_gateway_database_id_by_identity(
pub(crate) async fn get_gateway_database_id_by_identity(
&self,
identity: &str,
) -> Result<Option<i64>, sqlx::Error> {
@@ -171,7 +187,7 @@ impl StorageManager {
Ok(id)
}
pub(super) async fn get_gateway_node_id_from_identity_key(
pub(crate) async fn get_gateway_node_id_from_identity_key(
&self,
identity: &str,
) -> Result<Option<NodeId>, sqlx::Error> {
@@ -186,7 +202,7 @@ impl StorageManager {
Ok(node_id)
}
pub(super) async fn get_gateway_identity_key(
pub(crate) async fn get_gateway_identity_key(
&self,
node_id: NodeId,
) -> Result<Option<IdentityKey>, sqlx::Error> {
@@ -206,7 +222,7 @@ impl StorageManager {
/// # Arguments
///
/// * `mix_id`: mix-id (as assigned by the smart contract) of the mixnode.
pub(super) async fn get_mixnode_identity_key(
pub(crate) async fn get_mixnode_identity_key(
&self,
mix_id: NodeId,
) -> Result<Option<IdentityKey>, sqlx::Error> {
@@ -228,7 +244,7 @@ impl StorageManager {
///
/// * `mix_id`: mix-id (as assigned by the smart contract) of the mixnode.
/// * `timestamp`: unix timestamp of the lower bound of the selection.
pub(super) async fn get_mixnode_statuses_since(
pub(crate) async fn get_mixnode_statuses_since(
&self,
mix_id: NodeId,
timestamp: i64,
@@ -256,7 +272,7 @@ impl StorageManager {
///
/// * `identity`: identity (base58-encoded public key) of the gateway.
/// * `timestamp`: unix timestamp of the lower bound of the selection.
pub(super) async fn get_gateway_statuses_since(
pub(crate) async fn get_gateway_statuses_since(
&self,
node_id: NodeId,
timestamp: i64,
@@ -282,7 +298,7 @@ impl StorageManager {
/// # Arguments
///
/// * `mix_id`: mix-id (as assigned by the smart contract) of the mixnode.
pub(super) async fn get_mixnode_historical_uptimes(
pub(crate) async fn get_mixnode_historical_uptimes(
&self,
mix_id: NodeId,
) -> Result<Vec<ApiHistoricalUptime>, sqlx::Error> {
@@ -320,7 +336,7 @@ impl StorageManager {
/// # Arguments
///
/// * `identity`: identity (base58-encoded public key) of the gateway.
pub(super) async fn get_gateway_historical_uptimes(
pub(crate) async fn get_gateway_historical_uptimes(
&self,
node_id: NodeId,
) -> Result<Vec<ApiHistoricalUptime>, sqlx::Error> {
@@ -353,7 +369,7 @@ impl StorageManager {
Ok(uptimes)
}
pub(super) async fn get_historical_mix_uptime_on(
pub(crate) async fn get_historical_mix_uptime_on(
&self,
contract_node_id: i64,
date: Date,
@@ -377,7 +393,7 @@ impl StorageManager {
.await
}
pub(super) async fn get_historical_gateway_uptime_on(
pub(crate) async fn get_historical_gateway_uptime_on(
&self,
contract_node_id: i64,
date: Date,
@@ -408,7 +424,7 @@ impl StorageManager {
///
/// * `since`: unix timestamp indicating the lower bound interval of the selection.
/// * `until`: unix timestamp indicating the upper bound interval of the selection.
pub(super) async fn get_mixnode_statuses_by_database_id(
pub(crate) async fn get_mixnode_statuses_by_database_id(
&self,
id: i64,
since: i64,
@@ -429,7 +445,7 @@ impl StorageManager {
.await
}
pub(super) async fn get_mixnode_average_reliability_in_interval(
pub(crate) async fn get_mixnode_average_reliability_in_interval(
&self,
id: i64,
start: i64,
@@ -491,7 +507,7 @@ impl StorageManager {
///
/// * `since`: unix timestamp indicating the lower bound interval of the selection.
/// * `until`: unix timestamp indicating the upper bound interval of the selection.
pub(super) async fn get_gateway_statuses_by_database_id(
pub(crate) async fn get_gateway_statuses_by_database_id(
&self,
id: i64,
since: i64,
@@ -518,36 +534,27 @@ impl StorageManager {
///
/// * `timestamp`: unix timestamp indicating when the measurements took place.
/// * `mixnode_results`: reliability results of each node that got tested.
pub(super) async fn submit_mixnode_statuses(
pub(crate) async fn submit_mixnode_statuses(
&self,
timestamp: i64,
mixnode_results: Vec<NodeResult>,
id_cache: &DbIdCache,
) -> Result<(), sqlx::Error> {
// insert it all in a transaction to make sure all nodes are updated at the same time
// (plus it's a nice guard against new nodes)
let mut tx = self.connection_pool.begin().await?;
for mixnode_result in mixnode_results {
let mixnode_id = match id_cache.mixnode_db_id(mixnode_result.node_id) {
Some(id) => id,
None => {
let mixnode_id = sqlx::query!(
r#"
INSERT OR IGNORE INTO mixnode_details(mix_id, identity_key) VALUES (?, ?);
SELECT id FROM mixnode_details WHERE mix_id = ?;
"#,
mixnode_result.node_id,
mixnode_result.identity,
mixnode_result.node_id,
)
.fetch_one(&mut *tx)
.await?
.id;
id_cache.set_mixnode_db_id(mixnode_result.node_id, mixnode_id);
mixnode_id
}
};
let mixnode_id = sqlx::query!(
r#"
INSERT OR IGNORE INTO mixnode_details(mix_id, identity_key) VALUES (?, ?);
SELECT id FROM mixnode_details WHERE mix_id = ?;
"#,
mixnode_result.node_id,
mixnode_result.identity,
mixnode_result.node_id,
)
.fetch_one(&mut *tx)
.await?
.id;
// insert the actual status
sqlx::query!(
@@ -566,7 +573,7 @@ impl StorageManager {
tx.commit().await
}
pub(super) async fn submit_mixnode_statuses_v2(
pub(crate) async fn submit_mixnode_statuses_v2(
&self,
mixnode_results: &[NodeResult],
) -> Result<(), sqlx::Error> {
@@ -613,11 +620,10 @@ impl StorageManager {
///
/// * `timestamp`: unix timestamp indicating when the measurements took place.
/// * `gateway_results`: reliability results of each node that got tested.
pub(super) async fn submit_gateway_statuses(
pub(crate) async fn submit_gateway_statuses(
&self,
timestamp: i64,
gateway_results: Vec<NodeResult>,
id_cache: &DbIdCache,
) -> Result<(), sqlx::Error> {
// insert it all in a transaction to make sure all nodes are updated at the same time
// (plus it's a nice guard against new nodes)
@@ -625,45 +631,39 @@ impl StorageManager {
for gateway_result in gateway_results {
// if gateway info doesn't exist, insert it and get its id
// same ID "problem" as described for mixnode insertion
let gateway_id = match id_cache.gateway_db_id(gateway_result.node_id) {
Some(id) => id,
None => {
let gateway_id = sqlx::query!(
r#"
INSERT OR IGNORE INTO gateway_details(node_id, identity) VALUES (?, ?);
SELECT id FROM gateway_details WHERE identity = ?;
"#,
gateway_result.node_id,
gateway_result.identity,
gateway_result.identity,
)
.fetch_one(&mut *tx)
.await?
.id;
id_cache.set_gateway_db_id(gateway_result.node_id, gateway_id);
gateway_id
}
};
let gateway_id = sqlx::query!(
r#"
INSERT OR IGNORE INTO gateway_details(node_id, identity) VALUES (?, ?);
SELECT id FROM gateway_details WHERE identity = ?;
"#,
gateway_result.node_id,
gateway_result.identity,
gateway_result.identity,
)
.fetch_one(&mut *tx)
.await?
.id;
// insert the actual status
sqlx::query!(
r#"
INSERT INTO gateway_status (gateway_details_id, reliability, timestamp) VALUES (?, ?, ?);
"#,
gateway_id,
gateway_result.reliability,
timestamp
)
.execute(&mut *tx)
.await?;
r#"
INSERT INTO gateway_status (gateway_details_id, reliability, timestamp) VALUES (?, ?, ?);
"#,
gateway_id,
gateway_result.reliability,
timestamp
)
.execute(&mut *tx)
.await?;
}
// finally commit the transaction
tx.commit().await
}
pub(super) async fn submit_gateway_statuses_v2(
pub(crate) async fn submit_gateway_statuses_v2(
&self,
gateway_results: &[NodeResult],
) -> Result<(), sqlx::Error> {
@@ -714,7 +714,7 @@ impl StorageManager {
/// # Arguments
///
/// * `testing_route`: test route used for this particular network monitor run.
pub(super) async fn submit_testing_route_used(
pub(crate) async fn submit_testing_route_used(
&self,
testing_route: TestingRoute,
) -> Result<(), sqlx::Error> {
@@ -742,7 +742,7 @@ impl StorageManager {
///
/// * `db_mixnode_id`: id (as saved in the database) of the mixnode.
/// * `since`: unix timestamp indicating the lower bound interval of the selection.
pub(super) async fn get_mixnode_testing_route_presence_count_since(
pub(crate) async fn get_mixnode_testing_route_presence_count_since(
&self,
db_mixnode_id: i64,
since: i64,
@@ -781,7 +781,7 @@ impl StorageManager {
///
/// * `gateway_id`: id (as saved in the database) of the gateway.
/// * `since`: unix timestamp indicating the lower bound interval of the selection.
pub(super) async fn get_gateway_testing_route_presence_count_since(
pub(crate) async fn get_gateway_testing_route_presence_count_since(
&self,
gateway_id: i64,
since: i64,
@@ -813,7 +813,7 @@ impl StorageManager {
}
/// Checks whether there are already any historical uptimes with this particular date.
pub(super) async fn check_for_historical_uptime_existence(
pub(crate) async fn check_for_historical_uptime_existence(
&self,
today_iso_8601: &str,
) -> Result<bool, sqlx::Error> {
@@ -833,7 +833,7 @@ impl StorageManager {
/// * `node_id`: id of the mixnode (as inserted in `mixnode_details_id` table).
/// * `date`: date associated with the uptime represented in ISO 8601, i.e. YYYY-MM-DD.
/// * `uptime`: the actual uptime of the node during the specified day.
pub(super) async fn insert_mixnode_historical_uptime(
pub(crate) async fn insert_mixnode_historical_uptime(
&self,
mix_id: i64,
date: &str,
@@ -855,7 +855,7 @@ impl StorageManager {
/// * `node_id`: id of the gateway (as inserted in `gateway_details_id` table).
/// * `date`: date associated with the uptime represented in ISO 8601, i.e. YYYY-MM-DD.
/// * `uptime`: the actual uptime of the node during the specified day.
pub(super) async fn insert_gateway_historical_uptime(
pub(crate) async fn insert_gateway_historical_uptime(
&self,
db_id: i64,
date: &str,
@@ -876,7 +876,7 @@ impl StorageManager {
/// # Arguments
///
/// * `timestamp`: unix timestamp at which the monitor test run has occurred
pub(super) async fn insert_monitor_run(&self, timestamp: i64) -> Result<i64, sqlx::Error> {
pub(crate) async fn insert_monitor_run(&self, timestamp: i64) -> Result<i64, sqlx::Error> {
let res = sqlx::query!("INSERT INTO monitor_run(timestamp) VALUES (?)", timestamp)
.execute(&self.connection_pool)
.await?;
@@ -889,7 +889,7 @@ impl StorageManager {
///
/// * `since`: unix timestamp indicating the lower bound interval of the selection.
/// * `until`: unix timestamp indicating the upper bound interval of the selection.
pub(super) async fn get_monitor_runs_count(
pub(crate) async fn get_monitor_runs_count(
&self,
since: i64,
until: i64,
@@ -911,7 +911,7 @@ impl StorageManager {
/// # Arguments
///
/// * `until`: timestamp specifying the purge cutoff.
pub(super) async fn purge_old_mixnode_statuses(
pub(crate) async fn purge_old_mixnode_statuses(
&self,
timestamp: i64,
) -> Result<(), sqlx::Error> {
@@ -927,7 +927,7 @@ impl StorageManager {
/// # Arguments
///
/// * `until`: timestamp specifying the purge cutoff.
pub(super) async fn purge_old_gateway_statuses(
pub(crate) async fn purge_old_gateway_statuses(
&self,
timestamp: i64,
) -> Result<(), sqlx::Error> {
@@ -944,7 +944,7 @@ impl StorageManager {
///
/// * `since`: indicates the lower bound timestamp for deciding whether given mixnode is active
/// * `until`: indicates the upper bound timestamp for deciding whether given mixnode is active
pub(super) async fn get_all_active_mixnodes_in_interval(
pub(crate) async fn get_all_active_mixnodes_in_interval(
&self,
since: i64,
until: i64,
@@ -978,7 +978,7 @@ impl StorageManager {
///
/// * `since`: indicates the lower bound timestamp for deciding whether given gateway is active
/// * `until`: indicates the upper bound timestamp for deciding whether given gateway is active
pub(super) async fn get_all_active_gateways_in_interval(
pub(crate) async fn get_all_active_gateways_in_interval(
&self,
since: i64,
until: i64,
@@ -1025,7 +1025,7 @@ impl StorageManager {
///
/// * `report`: report to insert into the database
#[allow(unused)]
pub(super) async fn insert_rewarding_report(
pub(crate) async fn insert_rewarding_report(
&self,
report: RewardingReport,
) -> Result<(), sqlx::Error> {
@@ -1044,7 +1044,7 @@ impl StorageManager {
}
#[allow(unused)]
pub(super) async fn get_rewarding_report(
pub(crate) async fn get_rewarding_report(
&self,
absolute_epoch_id: EpochId,
) -> Result<Option<RewardingReport>, sqlx::Error> {
@@ -1069,7 +1069,7 @@ impl StorageManager {
///
/// * `since`: unix timestamp indicating the lower bound interval of the selection.
/// * `until`: unix timestamp indicating the upper bound interval of the selection.
pub(super) async fn get_all_active_mixnodes_statuses_in_interval(
pub(crate) async fn get_all_active_mixnodes_statuses_in_interval(
&self,
since: i64,
until: i64,
@@ -1102,7 +1102,7 @@ impl StorageManager {
///
/// * `since`: unix timestamp indicating the lower bound interval of the selection.
/// * `until`: unix timestamp indicating the upper bound interval of the selection.
pub(super) async fn get_all_active_gateways_statuses_in_interval(
pub(crate) async fn get_all_active_gateways_statuses_in_interval(
&self,
since: i64,
until: i64,
@@ -1129,7 +1129,7 @@ impl StorageManager {
Ok(active_day_statuses)
}
pub(super) async fn get_mixnode_details_by_db_id(
pub(crate) async fn get_mixnode_details_by_db_id(
&self,
id: i64,
) -> Result<Option<MixnodeDetails>, sqlx::Error> {
@@ -1142,7 +1142,7 @@ impl StorageManager {
.await
}
pub(super) async fn get_gateway_details_by_db_id(
pub(crate) async fn get_gateway_details_by_db_id(
&self,
id: i64,
) -> Result<Option<GatewayDetails>, sqlx::Error> {
@@ -1154,7 +1154,7 @@ impl StorageManager {
.await
}
pub(super) async fn get_mixnode_statuses_count(&self, db_id: i64) -> Result<i32, sqlx::Error> {
pub(crate) async fn get_mixnode_statuses_count(&self, db_id: i64) -> Result<i32, sqlx::Error> {
sqlx::query!(
r#"
SELECT COUNT(*) as count
@@ -1170,7 +1170,7 @@ impl StorageManager {
.map(|record| record.count)
}
pub(super) async fn get_mixnode_statuses(
pub(crate) async fn get_mixnode_statuses(
&self,
mix_id: NodeId,
limit: u32,
@@ -1206,7 +1206,7 @@ impl StorageManager {
.await
}
pub(super) async fn get_gateway_statuses_count(&self, db_id: i64) -> Result<i32, sqlx::Error> {
pub(crate) async fn get_gateway_statuses_count(&self, db_id: i64) -> Result<i32, sqlx::Error> {
sqlx::query!(
r#"
SELECT COUNT(*) as count
@@ -1222,7 +1222,7 @@ impl StorageManager {
.map(|record| record.count)
}
pub(super) async fn get_gateway_statuses(
pub(crate) async fn get_gateway_statuses(
&self,
gateway_identity: &str,
limit: u32,
+28 -85
View File
@@ -1,7 +1,6 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use self::manager::{AvgGatewayReliability, AvgMixnodeReliability};
use crate::network_monitor::test_route::TestRoute;
use crate::node_status_api::models::{
GatewayStatusReport, GatewayUptimeHistory, HistoricalUptime as ApiHistoricalUptime,
@@ -13,71 +12,37 @@ use crate::storage::models::{NodeStatus, TestingRoute};
use crate::support::storage::models::{
GatewayDetails, HistoricalUptime, MixnodeDetails, TestedGatewayStatus, TestedMixnodeStatus,
};
use dashmap::DashMap;
use nym_mixnet_contract_common::NodeId;
use nym_types::monitoring::NodeResult;
use sqlx::ConnectOptions;
use std::path::Path;
use std::sync::Arc;
use std::time::Duration;
use time::{Date, OffsetDateTime};
use tracing::log::LevelFilter;
use tracing::{error, info, warn};
use self::manager::{AvgGatewayReliability, AvgMixnodeReliability};
pub(crate) mod manager;
pub(crate) mod models;
pub(crate) mod runtime_migrations;
#[derive(Default)]
pub(crate) struct DbIdCache {
pub mixnodes_v1: DashMap<NodeId, i64>,
pub gateways_v1: DashMap<NodeId, i64>,
}
impl DbIdCache {
pub(crate) fn mixnode_db_id(&self, node_id: NodeId) -> Option<i64> {
self.mixnodes_v1.get(&node_id).map(|v| *v)
}
pub(crate) fn gateway_db_id(&self, node_id: NodeId) -> Option<i64> {
self.gateways_v1.get(&node_id).map(|v| *v)
}
pub(crate) fn set_mixnode_db_id(&self, node_id: NodeId, db_id: i64) {
self.mixnodes_v1.insert(node_id, db_id);
}
pub(crate) fn set_gateway_db_id(&self, node_id: NodeId, db_id: i64) {
self.gateways_v1.insert(node_id, db_id);
}
}
// note that clone here is fine as upon cloning the same underlying pool will be used
#[derive(Clone)]
pub(crate) struct NymApiStorage {
pub manager: StorageManager,
pub db_id_cache: Arc<DbIdCache>,
}
impl NymApiStorage {
pub async fn init<P: AsRef<Path>>(database_path: P) -> Result<Self, NymApiStorageError> {
// TODO: we can inject here more stuff based on our nym-api global config
// struct. Maybe different pool size or timeout intervals?
let connect_opts = sqlx::sqlite::SqliteConnectOptions::new()
let opts = sqlx::sqlite::SqliteConnectOptions::new()
.filename(database_path)
.create_if_missing(true)
.log_statements(LevelFilter::Trace)
.log_slow_statements(LevelFilter::Warn, Duration::from_millis(250));
.disable_statement_logging();
// TODO: do we want auto_vacuum ?
let pool_opts = sqlx::sqlite::SqlitePoolOptions::new()
.min_connections(5)
.max_connections(25)
.acquire_timeout(Duration::from_secs(60));
let connection_pool = match pool_opts.connect_with(connect_opts).await {
let connection_pool = match sqlx::SqlitePool::connect_with(opts).await {
Ok(db) => db,
Err(err) => {
error!("Failed to connect to SQLx database: {err}");
@@ -94,38 +59,32 @@ impl NymApiStorage {
let storage = NymApiStorage {
manager: StorageManager { connection_pool },
db_id_cache: Arc::new(Default::default()),
};
Ok(storage)
}
pub(crate) async fn get_mixnode_database_id(
#[allow(unused)]
pub(crate) async fn mix_identity_to_mix_ids(
&self,
node_id: NodeId,
) -> Result<Option<i64>, NymApiStorageError> {
if let Some(cached) = self.db_id_cache.mixnode_db_id(node_id) {
return Ok(Some(cached));
}
if let Some(retrieved) = self.manager.get_mixnode_database_id(node_id).await? {
self.db_id_cache.set_mixnode_db_id(node_id, retrieved);
return Ok(Some(retrieved));
}
Ok(None)
identity: &str,
) -> Result<Vec<NodeId>, NymApiStorageError> {
Ok(self
.manager
.get_mixnode_mix_ids_by_identity(identity)
.await?)
}
pub(crate) async fn get_gateway_database_id(
#[allow(unused)]
pub(crate) async fn mix_identity_to_latest_mix_id(
&self,
node_id: NodeId,
) -> Result<Option<i64>, NymApiStorageError> {
if let Some(cached) = self.db_id_cache.gateway_db_id(node_id) {
return Ok(Some(cached));
}
if let Some(retrieved) = self.manager.get_gateway_database_id(node_id).await? {
self.db_id_cache.set_gateway_db_id(node_id, retrieved);
return Ok(Some(retrieved));
}
Ok(None)
identity: &str,
) -> Result<Option<NodeId>, NymApiStorageError> {
Ok(self
.mix_identity_to_mix_ids(identity)
.await?
.into_iter()
.max())
}
pub(crate) async fn get_all_avg_gateway_reliability_in_last_24hr(
@@ -617,6 +576,7 @@ impl NymApiStorage {
// we MUST have those entries in the database, otherwise the route wouldn't have been chosen
// in the first place
let layer1_mix_db_id = self
.manager
.get_mixnode_database_id(test_route.layer_one_mix().mix_id)
.await?
.ok_or_else(|| NymApiStorageError::DatabaseInconsistency {
@@ -624,6 +584,7 @@ impl NymApiStorage {
})?;
let layer2_mix_db_id = self
.manager
.get_mixnode_database_id(test_route.layer_two_mix().mix_id)
.await?
.ok_or_else(|| NymApiStorageError::DatabaseInconsistency {
@@ -631,6 +592,7 @@ impl NymApiStorage {
})?;
let layer3_mix_db_id = self
.manager
.get_mixnode_database_id(test_route.layer_three_mix().mix_id)
.await?
.ok_or_else(|| NymApiStorageError::DatabaseInconsistency {
@@ -638,6 +600,7 @@ impl NymApiStorage {
})?;
let gateway_db_id = self
.manager
.get_gateway_database_id(test_route.gateway().node_id)
.await?
.ok_or_else(|| NymApiStorageError::DatabaseInconsistency {
@@ -738,11 +701,11 @@ impl NymApiStorage {
let monitor_run_id = self.manager.insert_monitor_run(now).await?;
self.manager
.submit_mixnode_statuses(now, mixnode_results, &self.db_id_cache)
.submit_mixnode_statuses(now, mixnode_results)
.await?;
self.manager
.submit_gateway_statuses(now, gateway_results, &self.db_id_cache)
.submit_gateway_statuses(now, gateway_results)
.await?;
for test_route in test_routes {
@@ -752,26 +715,6 @@ impl NymApiStorage {
Ok(())
}
pub(crate) async fn submit_mixnode_statuses_v2(
&self,
mixnode_results: &[NodeResult],
) -> Result<(), NymApiStorageError> {
self.manager
.submit_mixnode_statuses_v2(mixnode_results)
.await?;
Ok(())
}
pub(crate) async fn submit_gateway_statuses_v2(
&self,
gateway_results: &[NodeResult],
) -> Result<(), NymApiStorageError> {
self.manager
.submit_gateway_statuses_v2(gateway_results)
.await?;
Ok(())
}
/// Obtains number of network monitor test runs that have occurred within the specified interval.
///
/// # Arguments
-1
View File
@@ -6,7 +6,6 @@ RUN cargo build --release
FROM locustio/locust
EXPOSE 8089
EXPOSE 8080
COPY --from=builder /usr/src/nym/target/release/nym-network-monitor /bin/nym-network-monitor
COPY --from=builder /usr/src/nym/nym-network-monitor/locustfile.py locustfile.py
COPY --from=builder /usr/src/nym/nym-network-monitor/entrypoint.sh entrypoint.sh
+2 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-network-monitor"
version = "1.0.2"
version = "0.1.0"
authors.workspace = true
repository.workspace = true
homepage.workspace = true
@@ -13,7 +13,7 @@ license.workspace = true
[dependencies]
anyhow = { workspace = true }
axum = { workspace = true, features = ["json"] }
clap = { workspace = true, features = ["derive", "env"] }
clap = { workspace = true, features = ["derive"] }
dashmap = { workspace = true }
futures = { workspace = true }
log = { workspace = true }
+1 -1
View File
@@ -15,7 +15,7 @@ nnm_pid=$!
sleep 10
python -m locust -H http://${NYM_NETWORK_MONITOR_HOST}:${NYM_NETWORK_MONITOR_PORT} --processes "${processes}" --autostart --autoquit 60 -u "${users}" -t "${timeout}"s &
python -m locust -H http://127.0.0.1:8080 --processes "${processes}" --autostart --autoquit 60 -u "${users}" -t "${timeout}"s &
locust_pid=$!
wait $locust_pid
+2 -2
View File
@@ -107,11 +107,11 @@ struct Args {
client_lifetime: u64,
/// Port to listen on
#[arg(long, default_value_t = 8080, env = "NYM_NETWORK_MONITOR_PORT")]
#[arg(long, default_value_t = 8080)]
port: u16,
/// Host to listen on
#[arg(long, default_value = "127.0.0.1", env = "NYM_NETWORK_MONITOR_HOST")]
#[arg(long, default_value = "127.0.0.1")]
host: String,
/// Path to the topology file
+2
View File
@@ -0,0 +1,2 @@
nym-gateway-probe
keys/
@@ -1,6 +1,7 @@
# Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
# SPDX-License-Identifier: Apache-2.0
[package]
name = "nym-node-status-agent"
version = "1.0.0-rc.1"
@@ -15,14 +16,20 @@ readme.workspace = true
[dependencies]
anyhow = { workspace = true}
bincode = { workspace = true }
chrono = { workspace = true }
clap = { workspace = true, features = ["derive", "env"] }
nym-bin-common = { path = "../../common/bin-common", features = ["models"]}
nym-node-status-client = { path = "../nym-node-status-client" }
nym-crypto = { path = "../../common/crypto", features = ["asymmetric", "rand"] }
nym-bin-common = { path = "../common/bin-common", features = ["models"]}
nym-common-models = { path = "../common/models" }
nym-crypto = { path = "../common/crypto", features = ["asymmetric", "rand"] }
rand = { workspace = true }
tokio = { workspace = true, features = ["macros", "rt-multi-thread", "process"] }
tokio-util = { workspace = true }
tracing = { workspace = true }
tracing-subscriber = { workspace = true, features = ["env-filter"] }
reqwest = { workspace = true, features = ["json"] }
serde = { workspace = true }
serde_json = { workspace = true }
[dev-dependencies]
tempfile = { workspace = true }
@@ -3,14 +3,14 @@
set -eu
export ENVIRONMENT=${ENVIRONMENT:-"sandbox"}
probe_git_ref="nym-vpn-core-v1.0.0-rc.14"
probe_git_ref="nym-vpn-core-v1.0.0-rc.6"
crate_root=$(dirname $(realpath "$0"))
monorepo_root=$(realpath "${crate_root}/../..")
monorepo_root=$(dirname "${crate_root}")
echo "Expecting nym-vpn-client repo at a sibling level of nym monorepo dir"
gateway_probe_src=$(dirname "${monorepo_root}")/nym-vpn-client/nym-vpn-core
echo "gateway_probe_src=$gateway_probe_src"
echo "crate_root=$crate_root"
set -a
source "${monorepo_root}/envs/${ENVIRONMENT}.env"
@@ -30,11 +30,9 @@ function copy_gw_probe() {
pushd $gateway_probe_src
git fetch -a
git checkout $probe_git_ref
cargo build --release --package nym-gateway-probe
cp target/release/nym-gateway-probe "$crate_root"
$crate_root/nym-gateway-probe --version
popd
}
@@ -46,7 +44,7 @@ function swarm() {
local workers=$1
for ((i = 1; i <= workers; i++)); do
${monorepo_root}/target/release/nym-node-status-agent run-probe &
../target/release/nym-node-status-agent run-probe &
done
wait
@@ -1,35 +1,62 @@
use crate::models::{get_testrun, submit_results, TestrunAssignment};
use anyhow::bail;
use api::ApiPaths;
use anyhow::{bail, Context};
use nym_common_models::ns_api::{get_testrun, submit_results, TestrunAssignment};
use nym_crypto::asymmetric::ed25519::{PrivateKey, Signature};
use std::fmt::Display;
use tracing::instrument;
mod api;
pub mod auth;
pub mod models;
use crate::cli::GwProbe;
pub struct NsApiClient {
api: ApiPaths,
const INTERNAL_TESTRUNS: &str = "internal/testruns";
pub(crate) async fn run_probe(
server_ip: &str,
server_port: u16,
ns_api_auth_key: &str,
probe_path: &str,
) -> anyhow::Result<()> {
let auth_key = PrivateKey::from_base58_string(ns_api_auth_key)
.context("Couldn't parse auth key, exiting")?;
let ns_api_client = Client::new(server_ip, server_port, auth_key);
let probe = GwProbe::new(probe_path.to_string());
let version = probe.version().await;
tracing::info!("Probe version:\n{}", version);
if let Some(testrun) = ns_api_client.request_testrun().await? {
let log = probe.run_and_get_log(&Some(testrun.gateway_identity_key));
ns_api_client
.submit_results(testrun.testrun_id, log, testrun.assigned_at_utc)
.await?;
} else {
tracing::info!("No testruns available, exiting")
}
Ok(())
}
struct Client {
server_address: String,
client: reqwest::Client,
auth_key: PrivateKey,
}
impl NsApiClient {
impl Client {
pub fn new(server_ip: &str, server_port: u16, auth_key: PrivateKey) -> Self {
let server_address = format!("{}:{}", server_ip, server_port);
let api = ApiPaths::new(server_address);
let client = reqwest::Client::new();
Self {
api,
server_address,
client,
auth_key,
}
}
#[instrument(level = "debug", skip_all)]
pub async fn request_testrun(&self) -> anyhow::Result<Option<TestrunAssignment>> {
let target_url = self.api.request_testrun();
pub(crate) async fn request_testrun(&self) -> anyhow::Result<Option<TestrunAssignment>> {
let target_url = self.api_with_subpath(None::<String>);
let payload = get_testrun::Payload {
agent_public_key: self.auth_key.public_key(),
@@ -66,13 +93,13 @@ impl NsApiClient {
}
#[instrument(level = "debug", skip(self, probe_result))]
pub async fn submit_results(
pub(crate) async fn submit_results(
&self,
testrun_id: i64,
probe_result: String,
assigned_at_utc: i64,
) -> anyhow::Result<()> {
let target_url = self.api.submit_results(testrun_id);
let target_url = self.api_with_subpath(Some(testrun_id));
let payload = submit_results::Payload {
probe_result,
@@ -102,4 +129,12 @@ impl NsApiClient {
let signed = self.auth_key.sign(&serialized);
Ok(signed)
}
fn api_with_subpath(&self, subpath: Option<impl Display>) -> String {
if let Some(subpath) = subpath {
format!("{}/{}/{}", self.server_address, INTERNAL_TESTRUNS, subpath)
} else {
format!("{}/{}", self.server_address, INTERNAL_TESTRUNS)
}
}
}
+1 -2
View File
@@ -1,7 +1,6 @@
nym-node-status-agent/nym-gateway-probe
nym-node-status-agent/keys/
data/
enter_db.sh
nym-gateway-probe
nym-node-status-api
*.sqlite
*.sqlite-journal

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