Compare commits

...

31 Commits

Author SHA1 Message Date
Tommy Verrall b45eb16783 Update ci-build-upload-binaries.yml 2024-08-05 11:48:07 +02:00
Tommy Verrall f8523dc7d1 Update publish-nym-binaries.yml 2024-08-05 11:47:00 +02:00
Bogdan-Ștefan Neacşu 3d5ac0b883 Fix version 1 not having template correspondent initially (#4733) 2024-07-29 12:38:27 +02:00
benedettadavico de0fb7459d update changelog 2024-07-26 15:28:06 +02:00
benedettadavico e2ead6dbe1 bumping binaries versions 2024-07-26 15:25:47 +02:00
Jędrzej Stuczyński 7b10d92ca4 Merge pull request #4731 from nymtech/chore/1.80-lints
chore: fix 1.80 lint issues
2024-07-26 11:51:23 +01:00
Jędrzej Stuczyński 2c6e5eb673 cherry-pick: fix build issues 2024-07-26 11:11:52 +01:00
Jon Häggblad 02fde4e530 Handle clients with different versions in IPR (#4723)
* Add signable_request function

* Export key type in function signature

* Cargo.lock

* Track client version and respond using it

* Internally use v7 and then down convert if needed

* Local response type

* Streamline

* Strong type for client version

* Remove commented out code

* rustfmt

* Ignore sign verification fail for v6
2024-07-24 15:35:59 +02:00
import this cc25fc1f32 [DOCs/operators]: Changelog for v2024.8 wispa & guide syntax edits (#4728)
* changelog for release v2024.8-wispa

* clarify syntax

* typo fix
2024-07-24 12:38:25 +00:00
benedetta davico c971e486b5 Merge pull request #4726 from nymtech/release/2024.8-wispa
Release/2024.8 wispa into develop
2024-07-24 12:48:57 +02:00
import this 96a9eb6f6a [DOCs/docs]: Commnet out extra stubs (#4727)
* commnet out stubs

* fix broken links - ready to merge
2024-07-24 11:58:14 +02:00
benedetta davico 9eeb61ea0a Merge branch 'develop' into release/2024.8-wispa 2024-07-24 10:56:03 +02:00
John Smith 08042c61ad [DOCs/operators]: Update troubleshooting/vps-isp.md with manual IPv6 configuration (#4651)
* Update vps-isp.md

Added an extra diagnostic step, which helped me to debug lack of routability.

* Update vps-isp.md

Implementing serinko's comments

* Update vps-isp.md

Changed possibly to possible and added how to find IPv6 Gateway.

* Update vps-isp.md

Fixed ifup/ifdown link
2024-07-24 08:53:49 +00:00
Stefano Piermatteo 36c74f30e5 [DOCs/operators]: Syntax fix in setup.md (#4682) 2024-07-24 08:37:33 +00:00
Bogdan-Ștefan Neacşu fd1d437211 Add 1GB/day/user bandwidth cap (#4717)
* Add check for 1GB/day/user and remove stale check

* Use saturated_sub

* Remove from wg peers

* Use 10 seconds instead of 1

* Query bandwidth message

* Ad client query message too

* Keep stale check

* Make bandwidth cap value public

* Fix consumed vs available bug

* Don't overwrite existing registrations

* Use self pub key instead of peer's
2024-07-23 20:49:49 +02:00
Tommy Verrall 4956d13bdc fix conflicts 2024-07-23 17:32:49 +02:00
Jędrzej Stuczyński 6478736654 Merge pull request #4706 from nymtech/chore/remove-old-migration-code
removed mixnode/gateway config migration code and disabled cli without explicit flag
2024-07-23 15:11:36 +01:00
benedettadavico d9f6c0723e updating versions 2024-07-23 15:37:04 +02:00
Jon Häggblad f86050d916 Default construct NodeRole for backwards compatibility (#4722) 2024-07-22 16:04:29 +02:00
Tommy Verrall 52f5656190 Merge pull request #4721 from nymtech/jon/node-role-default
Default construct NodeRole
2024-07-22 15:09:27 +02:00
Jon Häggblad 21cd90f238 Default construct NodeRole for backwards compatibility 2024-07-22 14:59:18 +02:00
import this 4e51188d35 [DOCs/operators]: Guide to back up and restore nym-node (#4720)
* add node backup & node restore guides

* finished: ready to review

* finished: ready to review
2024-07-22 11:17:55 +00:00
John Smith 22eb199936 Update isp-sheet.csv (#4718)
* Update isp-sheet.csv

Added a few known VPS providers which (a) support crypto payment (b) allow TOR in some shape or form (c) more or less know for their stability. Will add more eventually.

* Update isp-sheet.csv

added a few more providers
2024-07-22 10:57:57 +00:00
Tommy Verrall a2fc1bbc96 Merge pull request #4719 from nymtech/serinko/bug-fix/wss-guide
[DOCs/operators]: BugFix - add ssl cert to WSS server block
2024-07-19 12:48:36 +02:00
import this 621599692f add ssl cert to WSS server block 2024-07-19 10:43:02 +00:00
Bogdan-Ștefan Neacşu 72e243042e Add upgrades to nym-node for authenticator changes (#4703)
* Add iterative upgrades to nym-node

* Authenticator correct configuration

* Add info log

* Enable auth opts on entry gw

* Move ephemeral config from exit_gateway

* Fix fmt

* Fix clippy

* Pass custom transceiver for authenticator

* Fix non-linux build

* Feature gate wg_api

* Change naming from semver to simple incremental

* Move opts unwrap inside the mutable function

* Remove unneeded authenticator_description
2024-07-12 12:02:22 +02:00
Jędrzej Stuczyński c253b22f69 fixed the positional argument 2024-07-11 16:20:20 +01:00
Jędrzej Stuczyński 66f3a3e9a8 removed mixnode/gateway config migration code and disabled commands without explicit flag 2024-07-11 15:53:10 +01:00
benedettadavico c2ad4e5bb4 Update changelog and bump versions 2024-07-10 11:01:20 +02:00
Tommy Verrall 5f7f5ef92d Merge pull request #4699 from nymtech/release/2024.7-doubledecker
Release/2024.7 doubledecker
2024-07-10 10:46:03 +02:00
benedetta davico 008afe7a85 Merge pull request #4676 from nymtech/release/2024.6-chomp
Release/2024.6-chomp to master
2024-06-27 11:47:55 +02:00
75 changed files with 1246 additions and 2504 deletions
@@ -8,11 +8,6 @@ on:
required: true
default: false
type: boolean
enable_wireguard:
description: "Add --features wireguard"
required: true
default: false
type: boolean
enable_deb:
description: "True to enable cargo-deb installation and .deb package building"
required: false
@@ -42,7 +37,7 @@ jobs:
strategy:
fail-fast: false
matrix:
platform: [ubuntu-20.04]
platform: [ ubuntu-20.04 ]
runs-on: ${{ matrix.platform }}
env:
@@ -70,9 +65,6 @@ jobs:
- name: Set CARGO_FEATURES
run: |
echo 'CARGO_FEATURES=--features wireguard' >> $GITHUB_ENV
if: >
github.event_name == 'schedule' ||
(github.event_name == 'workflow_dispatch' && inputs.enable_wireguard == true)
- name: Install Rust stable
uses: actions-rs/toolchain@v1
+6 -2
View File
@@ -51,6 +51,10 @@ jobs:
echo 'RUSTFLAGS="--cfg tokio_unstable"' >> $GITHUB_ENV
if: github.event_name == 'workflow_dispatch' && inputs.add_tokio_unstable == true
- name: Set CARGO_FEATURES
run: |
echo 'CARGO_FEATURES=--features wireguard' >> $GITHUB_ENV
- name: Install Rust stable
uses: actions-rs/toolchain@v1
with:
@@ -60,8 +64,8 @@ jobs:
uses: actions-rs/cargo@v1
with:
command: build
args: --workspace --release
args: --workspace --release ${{ env.CARGO_FEATURES }}
- name: Upload Artifact
uses: actions/upload-artifact@v3
with:
+38 -1
View File
@@ -4,6 +4,44 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
## [Unreleased]
## [2024.9-topdeck] (2024-07-26)
- chore: fix 1.80 lint issues ([#4731])
- Handle clients with different versions in IPR ([#4723])
- Add 1GB/day/user bandwidth cap ([#4717])
- Feature/merge back ([#4710])
- removed mixnode/gateway config migration code and disabled cli without explicit flag ([#4706])
[#4731]: https://github.com/nymtech/nym/pull/4731
[#4723]: https://github.com/nymtech/nym/pull/4723
[#4717]: https://github.com/nymtech/nym/pull/4717
[#4710]: https://github.com/nymtech/nym/pull/4710
[#4706]: https://github.com/nymtech/nym/pull/4706
## [2024.8-wispa] (2024-07-10)
- add event parsing to support cosmos_sdk > 0.50 ([#4697])
- Fix NR config compatibility ([#4690])
- Remove UserAgent constructor since it's weakly typed ([#4689])
- [bugfix]: Node_api_check CLI looked over roles on blacklisted nodes ([#4687])
- Add mixnodes to self describing api cache ([#4684])
- Move and whole bump of crates to workspace and upgrade some ([#4680])
- Remove code that refers to removed nym-network-statistics ([#4679])
- Remove nym-network-statistics ([#4678])
- Create UserAgent that can be passed from the binary to the nym api client ([#4677])
- Add authenticator ([#4667])
[#4697]: https://github.com/nymtech/nym/pull/4697
[#4690]: https://github.com/nymtech/nym/pull/4690
[#4689]: https://github.com/nymtech/nym/pull/4689
[#4687]: https://github.com/nymtech/nym/pull/4687
[#4684]: https://github.com/nymtech/nym/pull/4684
[#4680]: https://github.com/nymtech/nym/pull/4680
[#4679]: https://github.com/nymtech/nym/pull/4679
[#4678]: https://github.com/nymtech/nym/pull/4678
[#4677]: https://github.com/nymtech/nym/pull/4677
[#4667]: https://github.com/nymtech/nym/pull/4667
## [2024.7-doubledecker] (2024-07-04)
- Add an early return in `parse_raw_str_logs` for empty raw log strings. ([#4686])
@@ -513,7 +551,6 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
[#3187]: https://github.com/nymtech/nym/issues/3187
[#3203]: https://github.com/nymtech/nym/pull/3203
[#3199]: https://github.com/nymtech/nym/pull/3199
>>>>>>> master
## [v1.1.13] (2023-03-15)
Generated
+11 -25
View File
@@ -2093,7 +2093,7 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
[[package]]
name = "explorer-api"
version = "1.1.36"
version = "1.1.38"
dependencies = [
"chrono",
"clap 4.5.4",
@@ -3849,7 +3849,7 @@ dependencies = [
[[package]]
name = "nym-api"
version = "1.1.40"
version = "1.1.42"
dependencies = [
"anyhow",
"async-trait",
@@ -4057,7 +4057,7 @@ dependencies = [
[[package]]
name = "nym-cli"
version = "1.1.38"
version = "1.1.40"
dependencies = [
"anyhow",
"base64 0.13.1",
@@ -4136,7 +4136,7 @@ dependencies = [
[[package]]
name = "nym-client"
version = "1.1.37"
version = "1.1.39"
dependencies = [
"bs58 0.5.1",
"clap 4.5.4",
@@ -4950,7 +4950,7 @@ dependencies = [
[[package]]
name = "nym-network-requester"
version = "1.1.38"
version = "1.1.40"
dependencies = [
"addr",
"anyhow",
@@ -4999,26 +4999,9 @@ dependencies = [
"zeroize",
]
[[package]]
name = "nym-network-statistics"
version = "1.1.34"
dependencies = [
"dirs 4.0.0",
"log",
"nym-bin-common",
"nym-statistics-common",
"nym-task",
"pretty_env_logger",
"rocket",
"serde",
"sqlx",
"thiserror",
"tokio",
]
[[package]]
name = "nym-node"
version = "1.1.4"
version = "1.1.6"
dependencies = [
"anyhow",
"bip39",
@@ -5281,7 +5264,7 @@ dependencies = [
[[package]]
name = "nym-socks5-client"
version = "1.1.37"
version = "1.1.39"
dependencies = [
"bs58 0.5.1",
"clap 4.5.4",
@@ -5759,6 +5742,7 @@ name = "nym-wireguard"
version = "0.1.0"
dependencies = [
"base64 0.21.7",
"chrono",
"dashmap",
"defguard_wireguard_rs",
"ip_network",
@@ -5767,6 +5751,7 @@ dependencies = [
"nym-network-defaults",
"nym-task",
"nym-wireguard-types",
"thiserror",
"tokio",
"tokio-stream",
"x25519-dalek",
@@ -5794,7 +5779,7 @@ dependencies = [
[[package]]
name = "nymvisor"
version = "0.1.3"
version = "0.1.5"
dependencies = [
"anyhow",
"bytes",
@@ -9454,6 +9439,7 @@ dependencies = [
name = "wasm-utils"
version = "0.1.0"
dependencies = [
"console_error_panic_hook",
"futures",
"getrandom 0.2.15",
"gloo-net",
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-client"
version = "1.1.37"
version = "1.1.39"
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.37"
version = "1.1.39"
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"
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
use nym_sphinx::addressing::Recipient;
use nym_wireguard_types::{GatewayClient, InitMessage};
use nym_wireguard_types::{GatewayClient, InitMessage, PeerPublicKey};
use serde::{Deserialize, Serialize};
use crate::make_bincode_serializer;
@@ -57,6 +57,19 @@ impl AuthenticatorRequest {
)
}
pub fn new_query_request(peer_public_key: PeerPublicKey, reply_to: Recipient) -> (Self, u64) {
let request_id = generate_random();
(
Self {
version: VERSION,
data: AuthenticatorRequestData::QueryBandwidth(peer_public_key),
reply_to,
request_id,
},
request_id,
)
}
pub fn to_bytes(&self) -> Result<Vec<u8>, bincode::Error> {
use bincode::Options;
make_bincode_serializer().serialize(self)
@@ -67,4 +80,5 @@ impl AuthenticatorRequest {
pub enum AuthenticatorRequestData {
Initial(InitMessage),
Final(GatewayClient),
QueryBandwidth(PeerPublicKey),
}
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
use nym_sphinx::addressing::Recipient;
use nym_wireguard_types::registration::RegistrationData;
use nym_wireguard_types::registration::{RegistrationData, RegistredData, RemainingBandwidthData};
use serde::{Deserialize, Serialize};
use crate::make_bincode_serializer;
@@ -33,10 +33,31 @@ impl AuthenticatorResponse {
}
}
pub fn new_registered(reply_to: Recipient, request_id: u64) -> Self {
pub fn new_registered(
registred_data: RegistredData,
reply_to: Recipient,
request_id: u64,
) -> Self {
Self {
version: VERSION,
data: AuthenticatorResponseData::Registered(RegisteredResponse {
reply: registred_data,
reply_to,
request_id,
}),
reply_to,
}
}
pub fn new_remaining_bandwidth(
remaining_bandwidth_data: Option<RemainingBandwidthData>,
reply_to: Recipient,
request_id: u64,
) -> Self {
Self {
version: VERSION,
data: AuthenticatorResponseData::RemainingBandwidth(RemainingBandwidthResponse {
reply: remaining_bandwidth_data,
reply_to,
request_id,
}),
@@ -64,6 +85,7 @@ impl AuthenticatorResponse {
match &self.data {
AuthenticatorResponseData::PendingRegistration(response) => Some(response.request_id),
AuthenticatorResponseData::Registered(response) => Some(response.request_id),
AuthenticatorResponseData::RemainingBandwidth(response) => Some(response.request_id),
}
}
}
@@ -72,6 +94,7 @@ impl AuthenticatorResponse {
pub enum AuthenticatorResponseData {
PendingRegistration(PendingRegistrationResponse),
Registered(RegisteredResponse),
RemainingBandwidth(RemainingBandwidthResponse),
}
#[derive(Clone, Debug, Serialize, Deserialize)]
@@ -85,4 +108,12 @@ pub struct PendingRegistrationResponse {
pub struct RegisteredResponse {
pub request_id: u64,
pub reply_to: Recipient,
pub reply: RegistredData,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct RemainingBandwidthResponse {
pub request_id: u64,
pub reply_to: Recipient,
pub reply: Option<RemainingBandwidthData>,
}
@@ -1,6 +1,11 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
// TEMPORARY WORKAROUND:
// those features are expected as the below should only get activated whenever
// the corresponding features in tendermint-rpc are enabled transitively
#![allow(unexpected_cfgs)]
use crate::nyxd::cosmwasm_client::client_traits::SigningCosmWasmClient;
use crate::nyxd::error::NyxdError;
use crate::nyxd::{Config, GasPrice, Hash, Height};
@@ -1,6 +1,11 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
// TEMPORARY WORKAROUND:
// those features are expected as the below should only get activated whenever
// the corresponding features in tendermint-rpc are enabled transitively
#![allow(unexpected_cfgs)]
use crate::nyxd::contract_traits::{NymContractsProvider, TypedNymContracts};
use crate::nyxd::cosmwasm_client::types::{
ChangeAdminResult, ContractCodeId, ExecuteResult, InstantiateOptions, InstantiateResult,
@@ -1,6 +1,11 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
// TEMPORARY WORKAROUND:
// those features are expected as the below should only get activated whenever
// the corresponding features in tendermint-rpc are enabled transitively
#![allow(unexpected_cfgs)]
use async_trait::async_trait;
use cosmrs::tendermint::{self, abci, block::Height, evidence::Evidence, Genesis, Hash};
use serde::{de::DeserializeOwned, Serialize};
@@ -0,0 +1,69 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::{v6, v7};
impl From<v7::response::StaticConnectFailureReason> for v6::response::StaticConnectFailureReason {
fn from(failure: v7::response::StaticConnectFailureReason) -> Self {
match failure {
v7::response::StaticConnectFailureReason::RequestedIpAlreadyInUse => {
v6::response::StaticConnectFailureReason::RequestedIpAlreadyInUse
}
v7::response::StaticConnectFailureReason::RequestedNymAddressAlreadyInUse => {
v6::response::StaticConnectFailureReason::RequestedNymAddressAlreadyInUse
}
v7::response::StaticConnectFailureReason::OutOfDateTimestamp => {
v6::response::StaticConnectFailureReason::Other("out of date timestamp".to_string())
}
v7::response::StaticConnectFailureReason::Other(reason) => {
v6::response::StaticConnectFailureReason::Other(reason)
}
}
}
}
impl From<v7::response::DynamicConnectFailureReason> for v6::response::DynamicConnectFailureReason {
fn from(failure: v7::response::DynamicConnectFailureReason) -> Self {
match failure {
v7::response::DynamicConnectFailureReason::RequestedNymAddressAlreadyInUse => {
v6::response::DynamicConnectFailureReason::RequestedNymAddressAlreadyInUse
}
v7::response::DynamicConnectFailureReason::NoAvailableIp => {
v6::response::DynamicConnectFailureReason::NoAvailableIp
}
v7::response::DynamicConnectFailureReason::Other(err) => {
v6::response::DynamicConnectFailureReason::Other(err)
}
}
}
}
impl From<v7::response::InfoResponseReply> for v6::response::InfoResponseReply {
fn from(reply: v7::response::InfoResponseReply) -> Self {
match reply {
v7::response::InfoResponseReply::Generic { msg } => {
v6::response::InfoResponseReply::Generic { msg }
}
v7::response::InfoResponseReply::VersionMismatch {
request_version,
response_version,
} => v6::response::InfoResponseReply::VersionMismatch {
request_version,
response_version,
},
v7::response::InfoResponseReply::ExitPolicyFilterCheckFailed { dst } => {
v6::response::InfoResponseReply::ExitPolicyFilterCheckFailed { dst }
}
}
}
}
impl From<v7::response::InfoLevel> for v6::response::InfoLevel {
fn from(level: v7::response::InfoLevel) -> Self {
match level {
v7::response::InfoLevel::Info => v6::response::InfoLevel::Info,
v7::response::InfoLevel::Warn => v6::response::InfoLevel::Warn,
v7::response::InfoLevel::Error => v6::response::InfoLevel::Error,
}
}
}
+1
View File
@@ -1,3 +1,4 @@
pub mod conversion;
pub mod request;
pub mod response;
@@ -198,6 +198,17 @@ impl IpPacketRequestData {
| IpPacketRequestData::Health(_) => None,
}
}
pub fn signable_request(&self) -> Option<Result<Vec<u8>, SignatureError>> {
match self {
IpPacketRequestData::StaticConnect(request) => Some(request.request()),
IpPacketRequestData::DynamicConnect(request) => Some(request.request()),
IpPacketRequestData::Disconnect(request) => Some(request.request()),
IpPacketRequestData::Data(_) => None,
IpPacketRequestData::Ping(_) => None,
IpPacketRequestData::Health(_) => None,
}
}
}
// A static connect request is when the client provides the internal IP address it will use on the
-1
View File
@@ -44,7 +44,6 @@ pub enum NymTopologyError {
PayloadBuilder,
#[error("Outfox: {0}")]
#[cfg(feature = "outfox")]
Outfox(#[from] nym_sphinx_types::OutfoxError),
#[error("{0}")]
@@ -7,6 +7,7 @@ use nym_sphinx_routing::SphinxRouteMaker;
use nym_sphinx_types::Node;
use rand::{CryptoRng, Rng};
#[allow(dead_code)]
pub struct NymTopologyRouteProvider<R> {
rng: R,
inner: NymTopology,
+3 -1
View File
@@ -17,6 +17,8 @@ gloo-utils = { workspace = true }
gloo-net = { workspace = true, features = ["websocket"], optional = true }
#gloo-net = { path = "../../../../gloo/crates/net", features = ["websocket"], optional = true }
console_error_panic_hook = { workspace = true, optional = true }
# we don't want entire tokio-tungstenite, tungstenite itself is just fine - we just want message and error enums
[dependencies.tungstenite]
workspace = true
@@ -28,7 +30,7 @@ workspace = true
optional = true
[features]
default = ["sleep"]
default = ["sleep", "console_error_panic_hook"]
sleep = ["web-sys", "web-sys/Window"]
websocket = [
"getrandom",
@@ -27,12 +27,15 @@ pub type HmacSha256 = Hmac<Sha256>;
pub type Nonce = u64;
pub type Taken = Option<SystemTime>;
pub const BANDWIDTH_CAP_PER_DAY: u64 = 1024 * 1024 * 1024; // 1 GB
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(tag = "type", rename_all = "camelCase")]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub enum ClientMessage {
Initial(InitMessage),
Final(GatewayClient),
Query(PeerPublicKey),
}
#[derive(Serialize, Deserialize, Debug, Clone)]
@@ -60,6 +63,19 @@ pub struct RegistrationData {
pub wg_port: u16,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct RegistredData {
pub pub_key: PeerPublicKey,
pub private_ip: IpAddr,
pub wg_port: u16,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct RemainingBandwidthData {
pub available_bandwidth: u64,
pub suspended: bool,
}
/// Client that wants to register sends its PublicKey bytes mac digest encrypted with a DH shared secret.
/// Gateway/Nym node can then verify pub_key payload using the same process
#[derive(Serialize, Deserialize, Debug, Clone)]
+2
View File
@@ -12,6 +12,7 @@ license.workspace = true
[dependencies]
base64 = { workspace = true }
chrono = { workspace = true }
dashmap = { workspace = true }
defguard_wireguard_rs = { workspace = true }
# The latest version on crates.io at the time of writing this (6.0.0) has a
@@ -24,5 +25,6 @@ nym-crypto = { path = "../crypto", features = ["asymmetric"] }
nym-network-defaults = { path = "../network-defaults" }
nym-task = { path = "../task" }
nym-wireguard-types = { path = "../wireguard-types" }
thiserror = { workspace = true }
tokio = { workspace = true, features = ["rt-multi-thread", "net", "io-util"] }
tokio-stream = { workspace = true }
+11
View File
@@ -0,0 +1,11 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("peers in wireguard don't match with in-memory ")]
PeerMismatch,
#[error("{0}")]
Defguard(#[from] defguard_wireguard_rs::error::WireguardInterfaceError),
}
+25 -14
View File
@@ -1,3 +1,6 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
#![cfg_attr(not(target_os = "linux"), allow(dead_code))]
// #![warn(clippy::pedantic)]
// #![warn(clippy::expect_used)]
@@ -6,13 +9,14 @@
use dashmap::DashMap;
use defguard_wireguard_rs::{host::Peer, key::Key, net::IpAddrMask, WGApi};
use nym_crypto::asymmetric::encryption::KeyPair;
use nym_wireguard_types::{Config, Error, GatewayClient, GatewayClientRegistry};
use peer_controller::PeerControlMessage;
use nym_wireguard_types::{Config, Error, GatewayClient, GatewayClientRegistry, PeerPublicKey};
use peer_controller::PeerControlRequest;
use std::sync::Arc;
use tokio::sync::mpsc::{self, UnboundedReceiver};
use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender};
const WG_TUN_NAME: &str = "nymwg";
pub(crate) mod error;
pub mod peer_controller;
pub struct WgApiWrapper {
@@ -39,14 +43,14 @@ pub struct WireguardGatewayData {
config: Config,
keypair: Arc<KeyPair>,
client_registry: Arc<GatewayClientRegistry>,
peer_tx: mpsc::UnboundedSender<PeerControlMessage>,
peer_tx: UnboundedSender<PeerControlRequest>,
}
impl WireguardGatewayData {
pub fn new(
config: Config,
keypair: Arc<KeyPair>,
) -> (Self, mpsc::UnboundedReceiver<PeerControlMessage>) {
) -> (Self, UnboundedReceiver<PeerControlRequest>) {
let (peer_tx, peer_rx) = mpsc::unbounded_channel();
(
WireguardGatewayData {
@@ -75,20 +79,26 @@ impl WireguardGatewayData {
let mut peer = Peer::new(Key::new(client.pub_key.to_bytes()));
peer.allowed_ips
.push(IpAddrMask::new(client.private_ip, 32));
let msg = PeerControlMessage::AddPeer(peer);
let msg = PeerControlRequest::AddPeer(peer);
self.peer_tx.send(msg).map_err(|_| Error::PeerModifyStopped)
}
pub fn remove_peer(&self, client: &GatewayClient) -> Result<(), Error> {
let key = Key::new(client.pub_key().to_bytes());
let msg = PeerControlMessage::RemovePeer(key);
let msg = PeerControlRequest::RemovePeer(key);
self.peer_tx.send(msg).map_err(|_| Error::PeerModifyStopped)
}
pub fn query_bandwidth(&self, peer_public_key: PeerPublicKey) -> Result<(), Error> {
let key = Key::new(peer_public_key.to_bytes());
let msg = PeerControlRequest::QueryBandwidth(key);
self.peer_tx.send(msg).map_err(|_| Error::PeerModifyStopped)
}
}
pub struct WireguardData {
pub inner: WireguardGatewayData,
pub peer_rx: UnboundedReceiver<PeerControlMessage>,
pub peer_rx: UnboundedReceiver<PeerControlRequest>,
}
/// Start wireguard device
@@ -96,6 +106,7 @@ pub struct WireguardData {
pub async fn start_wireguard(
task_client: nym_task::TaskClient,
wireguard_data: WireguardData,
control_tx: UnboundedSender<peer_controller::PeerControlResponse>,
) -> Result<std::sync::Arc<WgApiWrapper>, Box<dyn std::error::Error + Send + Sync + 'static>> {
use base64::{prelude::BASE64_STANDARD, Engine};
use defguard_wireguard_rs::{InterfaceConfiguration, WireguardInterfaceApi};
@@ -135,13 +146,13 @@ pub async fn start_wireguard(
wg_api.configure_peer_routing(&[catch_all_peer])?;
let wg_api = std::sync::Arc::new(WgApiWrapper::new(wg_api));
let mut controller = PeerController::new(wg_api.clone(), wireguard_data.peer_rx);
let mut controller = PeerController::new(
wg_api.clone(),
interface_config.peers,
wireguard_data.peer_rx,
control_tx,
);
tokio::spawn(async move { controller.run(task_client).await });
Ok(wg_api)
}
#[cfg(not(target_os = "linux"))]
pub async fn start_wireguard() {
todo!("WireGuard is currently only supported on Linux");
}
+128 -37
View File
@@ -1,91 +1,182 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::{
sync::Arc,
time::{Duration, SystemTime},
};
use defguard_wireguard_rs::{
host::{Host, Peer},
key::Key,
WGApi, WireguardInterfaceApi,
};
use chrono::{Timelike, Utc};
use defguard_wireguard_rs::{host::Peer, key::Key, WireguardInterfaceApi};
use nym_wireguard_types::registration::{RemainingBandwidthData, BANDWIDTH_CAP_PER_DAY};
use std::time::SystemTime;
use std::{collections::HashMap, sync::Arc, time::Duration};
use tokio::sync::mpsc;
use tokio_stream::{wrappers::IntervalStream, StreamExt};
use crate::error::Error;
use crate::WgApiWrapper;
const DEFAULT_PEER_TIMEOUT: Duration = Duration::from_secs(60 * 60); // 1 hour
// To avoid any problems, keep this stale check time bigger (>2x) then the bandwidth cap
// reset time (currently that one is 24h, at UTC midnight)
const DEFAULT_PEER_TIMEOUT: Duration = Duration::from_secs(60 * 60 * 24 * 3); // 3 days
const DEFAULT_PEER_TIMEOUT_CHECK: Duration = Duration::from_secs(60); // 1 minute
pub enum PeerControlMessage {
pub enum PeerControlRequest {
AddPeer(Peer),
RemovePeer(Key),
QueryBandwidth(Key),
}
pub enum PeerControlResponse {
AddPeer {
success: bool,
},
RemovePeer {
success: bool,
},
QueryBandwidth {
bandwidth_data: Option<RemainingBandwidthData>,
},
}
pub struct PeerController {
peer_rx: mpsc::UnboundedReceiver<PeerControlMessage>,
request_rx: mpsc::UnboundedReceiver<PeerControlRequest>,
response_tx: mpsc::UnboundedSender<PeerControlResponse>,
wg_api: Arc<WgApiWrapper>,
timeout_check_interval: IntervalStream,
active_peers: HashMap<Key, Peer>,
suspended_peers: HashMap<Key, Peer>,
last_seen_bandwidth: HashMap<Key, u64>,
}
impl PeerController {
pub fn new(
wg_api: Arc<WgApiWrapper>,
peer_rx: mpsc::UnboundedReceiver<PeerControlMessage>,
peers: Vec<Peer>,
request_rx: mpsc::UnboundedReceiver<PeerControlRequest>,
response_tx: mpsc::UnboundedSender<PeerControlResponse>,
) -> Self {
let timeout_check_interval = tokio_stream::wrappers::IntervalStream::new(
tokio::time::interval(DEFAULT_PEER_TIMEOUT_CHECK),
);
let active_peers = peers
.into_iter()
.map(|peer| (peer.public_key.clone(), peer))
.collect();
PeerController {
wg_api,
peer_rx,
request_rx,
response_tx,
timeout_check_interval,
active_peers,
suspended_peers: HashMap::new(),
last_seen_bandwidth: HashMap::new(),
}
}
fn remove_stale_peers(wg_api: &WGApi, host: Host) {
let current_timestamp = SystemTime::now();
for (key, peer) in host.peers.iter() {
if let Some(timestamp) = peer.last_handshake {
if let Ok(duration_since_handshake) = current_timestamp.duration_since(timestamp) {
if duration_since_handshake > DEFAULT_PEER_TIMEOUT {
if let Err(e) = wg_api.remove_peer(key) {
log::error!("Could not remove stale peer: {:?}", e);
} else {
log::debug!("Removed stale peer {:?}", key);
}
}
fn check_stale_peer(&self, peer: &Peer, current_timestamp: SystemTime) -> Result<bool, Error> {
if let Some(timestamp) = peer.last_handshake {
if let Ok(duration_since_handshake) = current_timestamp.duration_since(timestamp) {
if duration_since_handshake > DEFAULT_PEER_TIMEOUT {
self.wg_api.inner.remove_peer(&peer.public_key)?;
return Ok(true);
}
}
}
Ok(false)
}
fn check_suspend_peer(&mut self, peer: &Peer) -> Result<(), Error> {
let prev_peer = self
.active_peers
.get(&peer.public_key)
.ok_or(Error::PeerMismatch)?;
let data_usage =
(peer.rx_bytes + peer.tx_bytes).saturating_sub(prev_peer.rx_bytes + prev_peer.tx_bytes);
if data_usage > BANDWIDTH_CAP_PER_DAY {
self.wg_api.inner.remove_peer(&peer.public_key)?;
let (moved_key, moved_peer) = self
.active_peers
.remove_entry(&peer.public_key)
.ok_or(Error::PeerMismatch)?;
self.suspended_peers.insert(moved_key, moved_peer);
}
Ok(())
}
fn check_peers(&mut self) -> Result<(), Error> {
// Add 10 seconds to cover edge cases. At worst, we give ten free seconds worth of bandwidth
// by resetting the bandwidth twice
let reset = Utc::now().num_seconds_from_midnight() as u64
<= DEFAULT_PEER_TIMEOUT_CHECK.as_secs() + 10;
if reset {
for (_, peer) in self.suspended_peers.drain() {
self.wg_api.inner.configure_peer(&peer)?;
}
}
let host = self.wg_api.inner.read_interface_data()?;
self.last_seen_bandwidth = host
.peers
.iter()
.map(|(key, peer)| (key.clone(), peer.rx_bytes + peer.tx_bytes))
.collect();
if reset {
self.active_peers = host.peers;
} else {
let current_timestamp = SystemTime::now();
for peer in host.peers.values() {
if !self.check_stale_peer(peer, current_timestamp)? {
self.check_suspend_peer(peer)?;
}
}
}
Ok(())
}
pub async fn run(&mut self, mut task_client: nym_task::TaskClient) {
loop {
tokio::select! {
_ = self.timeout_check_interval.next() => {
match self.wg_api.inner.read_interface_data() {
Ok(host) => Self::remove_stale_peers(&self.wg_api.inner, host),
Err(e) => { log::error!("Could not read peer data: {:?}", e); },
if let Err(e) = self.check_peers() {
log::error!("Error while periodically checking peers: {:?}", e);
}
}
_ = task_client.recv() => {
log::trace!("PeerController handler: Received shutdown");
break;
}
msg = self.peer_rx.recv() => {
msg = self.request_rx.recv() => {
match msg {
Some(PeerControlMessage::AddPeer(peer)) => {
if let Err(e) = self.wg_api.inner.configure_peer(&peer) {
Some(PeerControlRequest::AddPeer(peer)) => {
let success = if let Err(e) = self.wg_api.inner.configure_peer(&peer) {
log::error!("Could not configure peer: {:?}", e);
}
false
} else {
self.active_peers.insert(peer.public_key.clone(), peer);
true
};
self.response_tx.send(PeerControlResponse::AddPeer { success }).ok();
}
Some(PeerControlMessage::RemovePeer(peer_pubkey)) => {
if let Err(e) = self.wg_api.inner.remove_peer(&peer_pubkey) {
Some(PeerControlRequest::RemovePeer(peer_pubkey)) => {
let success = if let Err(e) = self.wg_api.inner.remove_peer(&peer_pubkey) {
log::error!("Could not remove peer: {:?}", e);
}
false
} else {
self.active_peers.remove(&peer_pubkey);
self.suspended_peers.remove(&peer_pubkey);
true
};
self.response_tx.send(PeerControlResponse::RemovePeer { success }).ok();
}
Some(PeerControlRequest::QueryBandwidth(peer_pubkey)) => {
let msg = if self.suspended_peers.contains_key(&peer_pubkey) {
PeerControlResponse::QueryBandwidth { bandwidth_data: Some(RemainingBandwidthData{ available_bandwidth: 0, suspended: true }) }
} else if let Some(&consumed_bandwidth) = self.last_seen_bandwidth.get(&peer_pubkey) {
PeerControlResponse::QueryBandwidth { bandwidth_data: Some(RemainingBandwidthData{ available_bandwidth: BANDWIDTH_CAP_PER_DAY - consumed_bandwidth, suspended: false })}
} else {
PeerControlResponse::QueryBandwidth { bandwidth_data: None }
};
self.response_tx.send(msg).ok();
}
None => {
log::trace!("PeerController [main loop]: stopping since channel closed");
-4
View File
@@ -15,10 +15,6 @@
# Nodes
- [Node Types (Previously Setup Guides)](nodes/overview.md)
- [Mix Node](nodes/mixnode.md)
- [Gateway](nodes/gateway.md)
- [Network Requester](nodes/network-requester.md)
- [Validator](nodes/validator.md)
# Wallet
- [Desktop Wallet](wallet/desktop-wallet.md)
@@ -9,31 +9,31 @@ At a high level, our technologies include:
* a **mixnet**, which encrypts and mixes Sphinx packet traffic so that it cannot be determined who is communicating with whom. Our mixnet is based on a modified version of the **Loopix** design.
* a privacy enhancing signature scheme called **Coconut**. Coconut allows a shift in thinking about resource access control, from an identity-based paradigm based on _who you are_ to a privacy-preserving paradigm based on _right to use_.
* **Sphinx**, a way of transmitting armoured, layer-encrypted information packets which are indistinguishable from each other at a binary level.
* the **Nyx** blockchain, a general-purpose CosmWasm-enabled smart contract platform, and the home of the smart contracts which keep track of the mixnet.
* the **Nyx** blockchain, a general-purpose CosmWasm-enabled smart contract platform, and the home of the smart contracts which keep track of the mixnet.
The most important thing to note is that these technologies ensure privacy at two different levels of the stack: **network data transmission**, and **transactions**.
Here's an overview diagram of the different types of nodes making up the network:
Here's an overview diagram of the different types of nodes making up the network:
![Nym Platform](../images/nym-platform-dark.png)
Developers can think of the network as being comprised of **infrastructure nodes** and **clients** for interacting with this infrastructure via **P**rivacy-**e**nhanced **app**lications (PEApps).
Developers can think of the network as being comprised of **infrastructure nodes** and **clients** for interacting with this infrastructure via **P**rivacy-**e**nhanced **app**lications (PEApps).
## Mixnet Infrastructure
## Mixnet Infrastructure
The mixnet - the different pieces of software that your traffic will pass through when using an privacy-enhanced app (PEApp) - is made up of several different types of nodes:
* **Mix Nodes** provide network security for network content _and_ metadata, making it impossible to see who is communicating with who, by performing packet-mixing on traffic travelling through the network.
* **Mix Nodes** provide network security for network content _and_ metadata, making it impossible to see who is communicating with who, by performing packet-mixing on traffic travelling through the network.
* **Gateways** act as message storage for clients which may go offline and come back online again, and defend against denial of service attacks. The default gateway implementation included in the Nym platform code holds packets for later retrieval. For many applications (such as simple chat), this is usable out of the box, as it provides a place that potentially offline clients can retrieve packets from. The access token allows clients to pull messages from the gateway node.
* **Services** are applications that communicate with nym clients, listening and sending traffic to the mixnet. This is an umbrella term for a variety of different pieces of code, such as the [network requester](../nodes/network-requester.md) binary.
* **Services** are applications that communicate with nym clients, listening and sending traffic to the mixnet. This is an umbrella term for a variety of different pieces of code, such as the network requester binary.
* **Nyx Blockchain Validators** secure the network with proof-of-stake Sybil defenses, determine which nodes are included within the network, and work together to create Coconut threshold credentials which provide anonymous access to data and resources. They also produce blocks and secure the Nyx Blockchain. Initially, this chain was used only to house the CosmWasm smart contracts keeping track of Nym's network topology, token vesting contracts, and the `NYM` token itself. In recent months, we've decided to expand the role of Nyx and instead expand its role by making it an open smart contract platform for anyone to upload CosmWasm smart contracts to. Validators also provide privacy-enhanced credentials based on the testimony of a set of decentralized, blockchain-based issuing authorities. Nym validators use the [Coconut](https://arxiv.org/abs/1802.07344) [signature scheme](https://en.wikipedia.org/wiki/Digital_signature) to issue credentials. This allows privacy apps to generate anonymous resource claims through decentralised authorities, then use them with Service Providers.
## Privacy-enhanced applications (PEApps)
## Privacy-enhanced applications (PEApps)
PEApps use a Nym client to connect to the network in order to get the available Network Topology for traffic routing, and send/receive packets to other users and services. Clients, in order to send traffic through the mixnet, connect to gateways. Since applications may go online and offline, a client's gateway provides a sort of mailbox where apps can receive their messages.
Nym clients connect to gateways. Messages are automatically piped to connected clients and deleted from the gateway's disk storage. If a client is offline when a message arrives, it will be stored for later retrieval. When the client connects, all messages will be delivered, and deleted from the gateway's disk.
Nym clients connect to gateways. Messages are automatically piped to connected clients and deleted from the gateway's disk storage. If a client is offline when a message arrives, it will be stored for later retrieval. When the client connects, all messages will be delivered, and deleted from the gateway's disk.
When it starts up, a client registers itself with a gateway, and the gateway returns an access token. The access token plus the gateway's IP can then be used as a form of addressing for delivering packets.
@@ -46,4 +46,4 @@ Service Providers (SPs) may interact with external systems on behalf of a user.
There is also a special category of Service Provider, namely SPs that do not visibly interact with any external systems. You might think of these as crypto-utopiapps: they're doing something, but it's not possible from outside to say with any certainty what their function is, or who is interacting with them.
All apps talk with gateways using Sphinx packets and a small set of simple control messages. These messages are sent to gateways over websockets. Each app client has a long-lived relationship with its gateway; Nym defines messages for clients registering and authenticating with gateways, as well as sending encrypted Sphinx packets.
All apps talk with gateways using Sphinx packets and a small set of simple control messages. These messages are sent to gateways over websockets. Each app client has a long-lived relationship with its gateway; Nym defines messages for clients registering and authenticating with gateways, as well as sending encrypted Sphinx packets.
+10 -11
View File
@@ -8,7 +8,7 @@ Nym has two main codebases:
- the [Nym platform](https://github.com/nymtech/nym), written in Rust. This contains all of our code _except_ for the validators.
- the [Nym validators](https://github.com/nymtech/nyxd), written in Go.
> This page details how to build the main Nym platform code. **If you want to build and run a validator, [go here](../nodes/validator.md) instead.**
> This page details how to build the main Nym platform code. **If you want to build and run a validator, [go here](https://nymtech.net/operators/nodes/validator-setup.html) instead.**
## Prerequisites
- Debian/Ubuntu: `pkg-config`, `build-essential`, `libssl-dev`, `curl`, `jq`, `git`
@@ -54,18 +54,17 @@ cargo build --release # build your binaries with **mainnet** configuration
Quite a bit of stuff gets built. The key working parts are:
* [mix node](../nodes/mixnode.md): `nym-mixnode`
* [gateway node](../nodes/gateway.md): `nym-gateway`
* [websocket client](https://nymtech.net/developers/clients/websocket-client.md): `nym-client`
* [socks5 client](https://nymtech.net/developers/clients/socks5-client.md): `nym-socks5-client`
* [network requester](../nodes/network-requester.md): `nym-network-requester`
* [nym-cli tool](../tools/nym-cli.md): `nym-cli`
* [Nym Node](https://nymtech.net/operators/nodes/nym-node.html): `nym-node`
* [Validator](https://nymtech.net/operators/nodes/validator-setup.html)
* [websocket client](https://nymtech.net/docs/clients/websocket-client.html): `nym-client`
* [socks5 client](https://nymtech.net/docs/clients/socks5-client.html): `nym-socks5-client`
* [webassembly client](https://nymtech.net/docs/clients/webassembly-client.html): `webassembly-client`
* [nym-cli tool](https://nymtech.net/docs/tools/nym-cli.html): `nym-cli`
* [nym-api](https://nymtech.net/operators/nodes/nym-api.html): `nym-api`
[//]: # (* [nymvisor]&#40;https://nymtech.net/operators/nodes/nymvisor-upgrade.html&#41;: `nymvisor`)
* [nymvisor](https://nymtech.net/operators/nodes/nymvisor-upgrade.html): `nymvisor`
The repository also contains Typescript applications which aren't built in this process. These can be built by following the instructions on their respective docs pages.
* [Nym Wallet](../wallet/desktop-wallet.md)
* [Network Explorer UI](../explorers/mixnet-explorer.md)
* [Nym Wallet](https://nymtech.net/docs/wallet/desktop-wallet.html)
* [Network Explorer UI](https://nymtech.net/docs/explorers/mixnet-explorer.html)
> You cannot build from GitHub's .zip or .tar.gz archive files on the releases page - the Nym build scripts automatically include the current git commit hash in the built binary during compilation, so the build will fail if you use the archive code (which isn't a Git repository). Check the code out from github using `git clone` instead.
+4 -6
View File
@@ -1,9 +1,7 @@
# Node Types
> This section was previously the node setup guides. These have been migrated to their own [**Operator Guides**](https://nymtech.net/operators) book.
>
> For setup and maintenance guides, go to the Operators book linked above.
>
> This section is a little spartan for the moment, but we will be adding detailed information about how exactly each node functions, as well as references to any literature and technical specs in the near future.
```admonish info
We are working on a detailed description of how each component of Nym Mixnet and Nyx blockchain functions, as well as references to any literature and technical specs.
This section contains information on the different node types of the mixnet and blockchain.
Meanwhile please refer to our [**Operators Guide**](https://nymtech.net/operators) book.
```
+211 -1
View File
@@ -2,6 +2,216 @@
This page displays a full list of all the changes during our release cycle from [`v2024.3-eclipse`](https://github.com/nymtech/nym/blob/nym-binaries-v2024.3-eclipse/CHANGELOG.md) onwards. Operators can find here the newest updates together with links to relevant documentation. The list is sorted so that the newest changes appear first.
## `v2024.8-wispa`
- [Release binaries](https://github.com/nymtech/nym/releases/tag/nym-binaries-v2024.8-wispa)
- [Release CHANGELOG.md](https://github.com/nymtech/nym/blob/nym-binaries-v2024.8-wispa/CHANGELOG.md)
- [`nym-node`](nodes/nym-node.md) version `1.1.5`
~~~admonish example collapsible=true title='CHANGELOG.md'
- add event parsing to support cosmos_sdk > 0.50 ([#4697])
- Fix NR config compatibility ([#4690])
- Remove UserAgent constructor since it's weakly typed ([#4689])
- [bugfix]: Node_api_check CLI looked over roles on blacklisted nodes ([#4687])
- Add mixnodes to self describing api cache ([#4684])
- Move and whole bump of crates to workspace and upgrade some ([#4680])
- Remove code that refers to removed nym-network-statistics ([#4679])
- Remove nym-network-statistics ([#4678])
- Create UserAgent that can be passed from the binary to the nym api client ([#4677])
- Add authenticator ([#4667])
[#4697]: https://github.com/nymtech/nym/pull/4697
[#4690]: https://github.com/nymtech/nym/pull/4690
[#4689]: https://github.com/nymtech/nym/pull/4689
[#4687]: https://github.com/nymtech/nym/pull/4687
[#4684]: https://github.com/nymtech/nym/pull/4684
[#4680]: https://github.com/nymtech/nym/pull/4680
[#4679]: https://github.com/nymtech/nym/pull/4679
[#4678]: https://github.com/nymtech/nym/pull/4678
[#4677]: https://github.com/nymtech/nym/pull/4677
[#4667]: https://github.com/nymtech/nym/pull/4667
~~~
### Features
* [Default construct NodeRole](https://github.com/nymtech/nym/pull/4721): To preserve compatibility with newer clients interacting with older `nym-api`
~~~admonish example collapsible=true title='Testing steps performed'
1. Reviewed the changes in the `nym-api-requests/src/models.rs` file.
2. Verified that the `NymNodeDescription` struct includes the new `role` field with a default value set by `default_node_role`.
3. Checked the implementation of the `default_node_role` function to ensure it returns `NodeRole::Inactive`.
4. Ran the updated code in the sandbox environment.
5. Monitored the sandbox environment for any issues or errors related to the changes.
**Notes (if any):**
The test was successful. No issues were flagged during the testing in the sandbox environment. The new default value for `NodeRole` ensures backward compatibility without causing disruptions.
~~~
* [Default construct NodeRole for backwards compatibility (apply [\#4721](https://github.com/nymtech/nym/pull/4721) on develop)](https://github.com/nymtech/nym/pull/4722)
* [Add upgrades to `nym-node` for `authenticator` changes](https://github.com/nymtech/nym/pull/4703)
~~~admonish example collapsible=true title='Testing steps performed'
1. Reviewed the changes in the `gateway/src/error.rs` and `gateway/src/node/mod.rs` files.
2. Verified the new error enum `AuthenticatorStartupFailure` was added to `GatewayError`.
3. Confirmed the implementation of the `StartedAuthenticator` struct and its usage in the `start_authenticator` function.
4. Ran the updated code in the canary environment.
5. Monitored the canary environment for any issues or errors related to the changes.
~~~
* [Add event parsing to support `cosmos_sdk` > `0.50`](https://github.com/nymtech/nym/pull/4697)
~~~admonish example collapsible=true title='Testing steps performed'
1. Reviewed the changes in `common/client-libs/validator-client/src/nyxd/cosmwasm_client/client_traits/signing_client.rs`, `logs.rs`, `types.rs`, and `nym-api/src/coconut/tests/mod.rs` files.
2. Verified the addition of event parsing in the relevant functions and structs.
3. Ensured that the `find_attribute` function correctly parses event attributes.
4. Ran the updated code in the sandbox environment.
5. Broadcasted transactions on the sandbox network to test the changes.
6. Monitored the sandbox network for any malformed responses or errors after the test chain upgrade.
~~~
* [Send bandwidth status messages when connecting](https://github.com/nymtech/nym/pull/4691): When connecting to the gateway we get received the available bandwidth left. Emit a status messages for this, for consumption by the application layer.
~~~admonish example collapsible=true title='Testing steps performed'
1. Reviewed the changes in `common/bandwidth-controller/src/event.rs`, `common/bandwidth-controller/src/lib.rs`, and `common/client-libs/gateway-client/src/client.rs` files.
2. Verified the implementation of `BandwidthStatusMessage` enum for emitting status messages.
3. Ensured `GatewayClient` is updated to send bandwidth status messages when connecting.
4. Deployed the updated code on the canary environment.
5. Connected to the gateway and checked for the emission of bandwidth status messages.
6. Verified that the messages were correctly parsed and consumed by the application layer.
7. Ran the VPN client to observe the parsed events.
~~~
* [Fix NR config compatibility](https://github.com/nymtech/nym/pull/4690): Recently we deleted the old statistics service provider. This fixes some issues where old configs didn't work with the latest changes.
- Make NR able to read config with old keys in
- Remove deleted config keys from NR template
~~~admonish example collapsible=true title='Testing steps performed'
1. Reviewed the changes in the `service-providers/network-requester/src/config/mod.rs` and `service-providers/network-requester/src/config/template.rs` files.
2. Ensured `NetworkRequester` config is able to read old keys for compatibility.
3. Removed old and deleted config keys from the `NetworkRequester` template.
4. Compiled the project to verify no issues or warnings appeared.
5. Ran all tests to ensure that the changes did not affect the functionality.
6. Validated that no leftover code from the old statistics service provider caused any issues.
~~~
* [Remove `UserAgent` constructor since it's weakly typed](https://github.com/nymtech/nym/pull/4689):
~~~admonish example collapsible=true title='Testing steps performed'
1. Reviewed the changes in `common/http-api-client/src/user_agent.rs` file.
2. Verified the removal of the `UserAgent` constructor and ensured that all instances of `UserAgent::new` are updated accordingly.
3. Checked the implementation of `UserAgent` struct using `BinaryBuildInformation` and `BinaryBuildInformationOwned`.
4. Deployed the updated code across different environments (QA, sandbox, and canary).
5. Ran tests to ensure that the `UserAgent` struct functions correctly without the constructor.
~~~
* [Add mixnodes to self describing api cache](https://github.com/nymtech/nym/pull/4684):
- Abstracts getting the self describing info a bit
- Adds mixnodes to the cache refresher as well
- Adds `role` field to the `NodeDescription` struct, to be able to distinguish between mixnodes and gateways
- Switched to using `NodeStatusCache` instead of `ContractCache`
~~~admonish example collapsible=true title='Testing steps performed'
Called the new `/mixnodes/described` endpoint as well as the existing `/gateways/described` endpoint and verified that the data returned for each was correct based on the settings that different nodes have when they are setup.
For gateway endpoint, the “role” for now does not differentiate between entry and exit gateways, this will be implemented in the future.
~~~
* [Move and whole bump of crates to workspace and upgrade some](https://github.com/nymtech/nym/pull/4680):
- Fix cargo warning for `default_features`
- Move dirs 4.0 to workspace
- Use workspace `base64` dep
- Move `rand_chacha` and `x25519-dalek` to workspace
- Use workspace `ed25519-dalek` dep
- Move `itertools` to workspace deps and upgrade
- Move a few partial deps to workspace while preserving versions
~~~admonish example collapsible=true title='Testing steps performed'
1. Reviewed the changes to move and upgrade crates to the workspace.
2. Verified the updated dependencies:
- Moved `dirs` to version 4.0 in the workspace.
- Updated the `base64` dependency to use the workspace version.
- Moved `rand_chacha` and `x25519-dalek` to the workspace.
- Updated `ed25519-dalek` to use the workspace version.
- Moved and upgraded `itertools` in the workspace.
- Moved other partial dependencies to the workspace while preserving their versions.
3. Ensured the `Cargo.toml` files across the project reflect these changes correctly.
4. Compiled the entire project to check for any issues or warnings.
5. Verified that all tests pass successfully after the changes.
~~~
* [Remove `nym-network-statistics`](https://github.com/nymtech/nym/pull/4678): Remove `nym-network-statistics` service provider that is no longer used.
~~~admonish example collapsible=true title='Testing steps performed'
1. Reviewed the project to identify all references to `nym-network-statistics`.
2. Removed all code and dependencies associated with `nym-network-statistics`.
3. Ensured that no references to `nym-network-statistics` remain in the codebase, including comments, imports, and configuration files.
4. Compiled the project to check for any issues or warnings.
5. Ran all tests to ensure the removal did not affect the functionality of the project.
~~~
* [Remove code that refers to removed `nym-network-statistics`](https://github.com/nymtech/nym/pull/4679): Follow up to [\#4678](https://github.com/nymtech/nym/pull/4678) where all code interacting with it is removed.
~~~admonish example collapsible=true title='Testing steps performed'
1. Reviewed the project to identify all references to `nym-network-statistics`.
2. Removed all code and dependencies associated with `nym-network-statistics`.
3. Ensured that no references to `nym-network-statistics` remain in the codebase, including comments, imports, and configuration files.
4. Compiled the project to check for any issues or warnings.
5. Ran all tests to ensure the removal did not affect the functionality of the project.
~~~
* [Create `UserAgent` that can be passed from the binary to the `nym-api` client](https://github.com/nymtech/nym/pull/4677):
- Support setting `UserAgent` for the validator client
- Support setting `UserAgent` in the SDK `MixnetClient`
- Set `UserAgent` when getting the list of gateways and topology in
- `nym-client`
- `nym-socks5-client`
- Standalone `ip-packet-router`
~~~admonish example collapsible=true title='Testing steps performed'
Used the nym-vpn-cli to test this, and we can visibly see the `UserAgent`, no issues with the comments mentioned above.
Example of the user agent sent:
`nym-client/1.1.36/x86_64-unknown-linux-gnu/e18bb70`
<img width="1435" alt="image" src="https://github.com/nymtech/nym/assets/60836166/5d4cc76f-84e6-45cb-9102-adc2b58a25d9">
Connected with no problems
~~~
* [Add `authenticator`](https://github.com/nymtech/nym/pull/4667)
### Bugfix
* [`Node_api_check.py` CLI looked over roles on blacklisted nodes](https://github.com/nymtech/nym/pull/4687): Removing/correcting this redundant function which results in unwanted error print, will resolve in the program not looking up the `roles` endpoint for blacklisted GWs, instead just ignores the role description and still return all other endpoints.
### Operators Guide updates
* [Create a guide to backup and restore `nym-node`](https://nymtech.net/operators/nodes/maintenance.html#backup-a-node), PR [\#4720](https://github.com/nymtech/nym/pull/4720)
* [Add manual IPv6 ifup/down network configuration](https://nymtech.net/operators/troubleshooting/vps-isp.html#network-configuration), PR [\#4651](https://github.com/nymtech/nym/pull/4651)
* [Extend ISP list](https://nymtech.net/operators/legal/isp-list.html)
* [Add SSL cert bot block to WSS setup](https://nymtech.net/operators/nodes/proxy-configuration.html#web-secure-socket-setup), [PR here](https://github.com/nymtech/nym/commits/develop/): WSS setup fully works!
* [Correct `HTTP API port` in bonding page](https://nymtech.net/operators/nodes/bonding.html#bond-via-the-desktop-wallet-recommended) , [PR \#4707](https://github.com/nymtech/nym/pull/4707): Change `HTTP API port` to `8080` on every `nym-node` by opening `config.toml` and making sure that your binding addresses and ports are as in the block below. Then go to desktop wallet and open the box called `Show advanced options` and make sure all your ports are set correctly (usually this means to change `HTTP api port` to `8080` for `mixnode` mode).
~~~admonish example collapsible=true title='snap of binding addresses and ports in `config.toml`'
```toml
[host]
public_ips = [
'<YOUR_PUBLIC_IPv4>'
]
[mixnet]
bind_address = '0.0.0.0:1789'
[http]
bind_address = '0.0.0.0:8080'
[mixnode]
[mixnode.verloc]
bind_address = '0.0.0.0:1790'
[entry_gateway]
bind_address = '0.0.0.0:9000'
```
~~~
* [Comment our deprecated node pages in `/docs`](https://github.com/nymtech/nym/pull/4727)
- Fixes [issue \#4632](https://github.com/nymtech/nym/issues/4632)
* [Remove redundant syntax from the setup guide](https://github.com/nymtech/nym/pull/4682)
---
## `v2024.7-doubledecker`
- [Release binaries](https://github.com/nymtech/nym/releases/tag/nym-binaries-v2024.7-doubledecker)
@@ -42,7 +252,7 @@ This page displays a full list of all the changes during our release cycle from
### Features
- [Remove the `nym-mixnode` and `nym-gateway` binaries from the CI upload builds action](https://github.com/nymtech/nym/pull/4693):
- [Remove the `nym-mixnode` and `nym-gateway` binaries from the CI upload builds action](https://github.com/nymtech/nym/pull/4693)
- [Add an early return in `parse_raw_str_logs` for empty raw log strings.](https://github.com/nymtech/nym/pull/4686): This accommodates for the v50 + chain upgrade.
- [Bump braces from `3.0.2` to `3.0.3` in `/wasm/mix-fetch/internal-dev`](https://github.com/nymtech/nym/pull/4672): Version update of [braces](https://github.com/micromatch/braces)
- [Bump braces from `3.0.2` to `3.0.3` in `/clients/native/examples/js-examples/websocket`](https://github.com/nymtech/nym/pull/4663): Version update of [braces](https://github.com/micromatch/braces).
@@ -8,3 +8,19 @@
[HostSailor](https://hostsailor.com),"USA","Yes, based on ticket","Yes","The IPv6 setup needs custom research and is not documented","05/2024"
[Misaka](https://www.misaka.io/),"South Africa","Yes, native support","No","Very Expensive","05/2024"
[IsHosting](https://ishosting.com/en),"Brazil, Netherlands","Yes, based on ticket","Yes","Expensive","05/2024"
[AlexHost](https://alexhost.com),"Moldova, Bulgaria, Sweden, Netherlands","Yes, on by default","Yes","They allow TOR Bridges, Relays. Exit nodes are only allowed on dedicated servers (prices start from 26 EUR)","07/2024"
[iHostArt](https://ihostart.com),"Romania","Yes, on by default","Yes","Super permissive provider. They do allow Tor Exit/Relay/Bridge. Pro-free speech etc. Recently, IPv6 geolocation was set to North Korea, so be aware.","07/2024"
[Incognet](https://incognet.io),"Netherlands and USA","Yes, on by default","Yes","They allow Tor exit nodes but you must adhere to their rules https://incognet.io/tor-exits","07/2024"
[vSys Host](https://vsys.host),"Ukraine, Netherlands, USA","Yes, on by default","Yes","Pretty permissive provider registered in Ukraine. Should allow Relay/Exit nodes but nothing in T&C, so better double check.","07/2024"
[LiteServer](https://liteserver.nl),"Netherlands","Yes, on by default","Yes","Very reliable Dutch provider. They do allow Relay nodes but for Exit nodes you need to contact them. Always check T&C https://liteserver.nl/legal","07/2024"
[TerraHost](https://terrahost.no),"Norway","Yes, on by default","Yes","Very reliable Norwegian provider. Only allow exit nodes on Dedicated servers subject to certain caveats (you must open a ticket). Always check T&C https://terrahost.no/avtalebetingelser","07/2024"
[Mevspace](https://mevspace.com),"Poland","Yes, on by default","Yes","Flexible Polish providers with 3 DCs in Poland. They do allow Tor Exit nodes but you may need a dedicated server for this. Make sure you open a ticket to check. As of today's date, they have 48h for 1 EUR tariff","07/2024"
[Hostiko](https://hostiko.com.ua),"Ukraine, Germany","Yes, on by default","Yes","Ukrainian provider. They allow Exit nodes on Germany boxes but limit the bandwidth, you also have to restrict certain ports like 25 and 587. Make sure you open a ticket.","07/2024"
[Hostslick](https://hostslick.com),"Netherlands, Germany","Yes, on by default","Yes","Good amount of bandwidth for the price. Make sure you open the ticket if you want to run Exit node","07/2024"
[RDP](https://rdp.sh),"Netherlands, USA, Poland","Yes, on by default","Yes","German provider. Exit nodes are allowed, policy is here https://rdp.sh/docs/faq/tor ports 25,465,587 must be closed. Make sure you open a ticket before running an exit node.","07/2024"
1 **ISP** **Locations** **Public IPv6** **Crypto Payments** **Comments** **Last Updated**
8 [HostSailor](https://hostsailor.com) USA Yes, based on ticket Yes The IPv6 setup needs custom research and is not documented 05/2024
9 [Misaka](https://www.misaka.io/) South Africa Yes, native support No Very Expensive 05/2024
10 [IsHosting](https://ishosting.com/en) Brazil, Netherlands Yes, based on ticket Yes Expensive 05/2024
11 [AlexHost](https://alexhost.com) Moldova, Bulgaria, Sweden, Netherlands Yes, on by default Yes They allow TOR Bridges, Relays. Exit nodes are only allowed on dedicated servers (prices start from 26 EUR) 07/2024
12 [iHostArt](https://ihostart.com) Romania Yes, on by default Yes Super permissive provider. They do allow Tor Exit/Relay/Bridge. Pro-free speech etc. Recently, IPv6 geolocation was set to North Korea, so be aware. 07/2024
13 [Incognet](https://incognet.io) Netherlands and USA Yes, on by default Yes They allow Tor exit nodes but you must adhere to their rules https://incognet.io/tor-exits 07/2024
14 [vSys Host](https://vsys.host) Ukraine, Netherlands, USA Yes, on by default Yes Pretty permissive provider registered in Ukraine. Should allow Relay/Exit nodes but nothing in T&C, so better double check. 07/2024
15 [LiteServer](https://liteserver.nl) Netherlands Yes, on by default Yes Very reliable Dutch provider. They do allow Relay nodes but for Exit nodes you need to contact them. Always check T&C https://liteserver.nl/legal 07/2024
16 [TerraHost](https://terrahost.no) Norway Yes, on by default Yes Very reliable Norwegian provider. Only allow exit nodes on Dedicated servers subject to certain caveats (you must open a ticket). Always check T&C https://terrahost.no/avtalebetingelser 07/2024
17 [Mevspace](https://mevspace.com) Poland Yes, on by default Yes Flexible Polish providers with 3 DCs in Poland. They do allow Tor Exit nodes but you may need a dedicated server for this. Make sure you open a ticket to check. As of today's date, they have 48h for 1 EUR tariff 07/2024
18 [Hostiko](https://hostiko.com.ua) Ukraine, Germany Yes, on by default Yes Ukrainian provider. They allow Exit nodes on Germany boxes but limit the bandwidth, you also have to restrict certain ports like 25 and 587. Make sure you open a ticket. 07/2024
19 [Hostslick](https://hostslick.com) Netherlands, Germany Yes, on by default Yes Good amount of bandwidth for the price. Make sure you open the ticket if you want to run Exit node 07/2024
20 [RDP](https://rdp.sh) Netherlands, USA, Poland Yes, on by default Yes German provider. Exit nodes are allowed, policy is here https://rdp.sh/docs/faq/tor ports 25,465,587 must be closed. Make sure you open a ticket before running an exit node. 07/2024
21
22
23
24
25
26
@@ -122,6 +122,10 @@ RestartSec=30
WantedBy=multi-user.target
```
```admonish warning title=""
[Accepting T&Cs](setup.md#terms--conditions) is done via a flag `--accept-operator-terms-and-conditions` added explicitly to `nym-node run` command every time. If you use systemd automation, add the flag to your service file's `ExecStart` line.
```
3. Save the file
```admonish note
@@ -114,6 +114,8 @@ tmux attach-session
#### systemd
> Configuration of `systemd` service files for `nym-node` is under [Nym Node - Configuration](configuration.md#systemd) page.
##### For Nymvisor
> Since you're running your node via a Nymvisor instance, as well as creating a Nymvisor `.service` file, you will also want to **stop any previous node automation process you already have running**.
@@ -200,9 +202,58 @@ systemctl status <NODE>.service
systemctl daemon-reload
```
This lets your operating system know it's ok to reload the service configuration. Then restart your `<NODE>`.
This lets your operating system know it's OK to reload the service configuration. Then restart your `<NODE>`.
## Backup a node
Anything can happen to the server on which your node is running. To back up your `nym-node` keys and configuration protects the operators against the negative impact of unexpected events. To restart your node on another server, two essential pieces are needed:
1. Node keys to initialise the same node on a new VPS
2. Access to the bonding Nym account (wallet seeds) to edit the IP on smart contract
Assuming that everyone access their wallets from local machine and does *not* store their seeds on VPS, point \#2 should be a given. To backup your `nym-node` keys and configuration in the easiest way possible, copy the entire config directory `.nym` from your VPS to your local desktop, using a special copy command `scp`:
1. Create a directory where you want to store your backup
```sh
mkdir -pv <PATH_TO_TARGET_DIRECTORY>
```
2. Copy configuration folder `.nym` from your VPS to your newly created backup directory:
```sh
scp -r <SOURCE_USER_NAME>@<SOURCE_HOST_ADDRESS>:~/.nym/nym-nodes/<YOUR_NODE_ID> <PATH_TO_TARGET_DIRECTORY>
```
3. The `scp` command should print logs, an operator can see directly whether it was successful or if it encountered any error. However, double check that all your needed configuration is in the backup directory.
## Restoring a node
In case your VPS shut down and you have a [backup](#backup-a-node) of your node keys and access to your bonding wallet, you can easily restore your node on another server without losing your delegation.
1. On VPS: Do all [preliminary steps](preliminary-steps.md) needed to run a `nym-node`.
2. On VPS: Create a `.nym/nym-nodes` configuration folder:
```sh
mkdir -pv ~/.nym/nym-nodes
```
3. From machine where your node is backed up (usually local desktop): Copy the folder with your node keys and configuration to the newly created folder on your VPS using `scp` command. Make sure to grab the entire `nym-node` configuration folder, which is called after your local `nym-node` ID, the `-r` flag will take care of all sub-directories and their content:
```sh
scp -r <LOCAL_NODE_CONFIGURATION_FOLDER> <VPS_USER_NAME>@<VPS_HOST_ADDRESS>:~/.nym/nym-nodes/
```
4. The `scp` command should print logs, an operator can see directly whether it was successful or if it encountered any error. However, double check that all your needed configuration is in the target directory `.nym/nym-nodes` on your VPS.
5. Configure your node on the new VPS:
* Edit `~/.nym/nym-nodes/<ID>/config/config.toml` config with the new listening address IP - it's the one under the header `[host]`, called `public_ips = ['<YOUR_PUBLIC_IP>',]` and add your new location (field `location = <LOCATION>`, formats like: 'Jamaica', or two-letter alpha2 (e.g. 'JM'), three-letter alpha3 (e.g. 'JAM') or three-digit numeric-3 (e.g. '388') can be provided). You can see your IP by running a command `echo "$(curl -4 https://ifconfig.me)"`.
* Try to run the node and see if everything works.
* Setup the [systemd](#systemd) automation (don't forget to add the [terms and conditions flag](setup.md#terms--conditions)) to `ExecStart` command, reload the daemon and run the service.
6. And finally change the node smart contract info via the wallet interface. Open Nym Wallet, go to *Bonding*, open *Gateway Settings* or *Mixnode Settings* and change *Host* value to the new `nym-node` IP address. Otherwise the keys will point to the old IP address in the smart contract, and the node will not be able to be connected, and it will fail up-time checks, returning zero performance.
## Moving a node
In case of a need to move a Nym Node from one machine to another and avoiding to lose the delegation, here are few steps how to do it.
@@ -224,12 +275,23 @@ mkdir ~/.nym/nym-nodes
scp -r -3 <SOURCE_USER_NAME>@<SOURCE_HOST_ADDRESS>:~/.nym/nym-nodes <TARGET_USER_NAME>@<TARGET_HOST_ADDRESS>:~/.nym/nym-nodes/
```
**On new/target machine**
**On new/target VPS**
* Edit `~/.nym/nym-nodes/<ID>/config/config.toml` config with the new listening address IP - it's the one under the header `[host]`, called `public_ips = ['<YOUR_PUBLIC_IP>',]` and add your new location (field `location = <LOCATION>`, formats like: 'Jamaica', or two-letter alpha2 (e.g. 'JM'), three-letter alpha3 (e.g. 'JAM') or three-digit numeric-3 (e.g. '388') can be provided). You can see your IP by running a command `echo "$(curl -4 https://ifconfig.me)"`.
* Try to run the node and see if everything works.
* Setup the [systemd](#systemd) automation. If you want to use the exact same service config file, you can also copy it from one VPS to another following the same logic by opening your **local terminal** (as that one's ssh key is authorized in both of the VPS) and running:
```sh
scp -r -3 <SOURCE_USER_NAME>@<SOURCE_HOST_ADDRESS>:/etc/systemd/system/nym-node.service <TARGET_USER_NAME>@<TARGET_HOST_ADDRESS>:/etc/systemd/system/nym-node.service
```
Note: [Accepting T&Cs](setup.md#terms--conditions) is done via a flag `--accept-operator-terms-and-conditions` added explicitly to `nym-node run` command every time. If you use [systemd](configuration.md#systemd) automation, add the flag to your service file's `ExecStart` line.
**In your desktop wallet**
* Edit `~/.nym/nym-nodes/<ID>/config/config.toml` config with the new listening address IP. You can get that one running a command `echo "$(curl -4 https://ifconfig.me)"`.
* Setup the [systemd](#systemd) automation, reload the daemon and run the service, or just simply run the node if you don't use automation
* Change the node smart contract info via the wallet interface. Open Nym Wallet, go to *Bonding*, open *Gateway Settings* or *Mixnode Settings* and change *Host* value to the new `nym-node` IP address. Otherwise the keys will point to the old IP address in the smart contract, and the node will not be able to be connected, and it will fail up-time checks, returning zero performance.
Make sure to stop the old node.
## Rename node local ID
Local node ID (not the identity key) is a name chosen by operators which defines where the nodes configuration data will be stored, where the ID determines the path to `~/.nym/nym-nodes/<ID>/`. This ID is never shared on the network.
@@ -592,15 +592,28 @@ server {
# WSS configuration
server {
listen ${port_value};
listen ${port_value} ssl http2;
listen [::]:${port_value} ssl http2;
server_name ${host_name};
ssl_certificate /etc/letsencrypt/live/${host_name}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/${host_name}/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
location / {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, HEAD';
add_header 'Access-Control-Allow-Headers' '*';
add_header 'Access-Control-Allow-Headers' '*';
proxy_http_version 1.1;
proxy_http_version 1.1;
proxy_set_header Upgrade \$http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header X-Forwarded-For \$remote_addr;
@@ -699,9 +712,9 @@ We made the landing page customization directory in [*Preliminary steps*](#preli
3. Configure your site to work with `nginx`. Open a new text file `/etc/nginx/sites-available/<HOSTNAME>` and paste the block below. Don't forget to insert your correct values.
~~~admonish example collapsible=true title="Site configuration `/etc/nginx/sites-available/<HOSTNAME>`"
```bash
#################################################################
# EXCHANGE ALL <HOSTNAME>, <WSS_PORT> AND <PATH_TO> VARIABLES ! #
#################################################################
#####################################################
# EXCHANGE ALL <HOSTNAME> & <WSS_PORT> VARIABLES ! #
####################################################
# Reversed proxy configuration for landing page
server {
@@ -744,15 +757,27 @@ server {
# WSS configuration
server {
listen <WSS_PORT>;
listen <WSS_PORT> ssl http2;
listen [::]:<WSS_PORT> ssl http2;
server_name <HOSTNAME>;
ssl_certificate /etc/letsencrypt/live/<HOSTNAME>/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/<HOSTNAME>/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
location / {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, HEAD';
add_header 'Access-Control-Allow-Headers' '*';
add_header 'Access-Control-Allow-Headers' '*';
proxy_http_version 1.1;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header X-Forwarded-For $remote_addr;
+1 -6
View File
@@ -114,7 +114,7 @@ From `nym-node` version `1.1.3` onward is required to accept [**Operators Terms
There has been a long ongoing discussion whether and how to apply Terms and Conditions for Nym network operators, with an aim to stay aligned with the philosophy of Free Software and provide legal defense for both node operators and Nym developers. To understand better the reasoning behind this decision, you can listen to the first [Nym Operator Town Hall](https://www.youtube.com/live/7hwb8bAZIuc?si=3mQ2ed7AyUA1SsCp&t=915) introducing the T&Cs or to [Operator AMA with CEO Harry Halpin](https://www.youtube.com/watch?v=yIN-zYQw0I0) from June 4th, 2024, explaining pros and cons of T&Cs implementation.
Accepting T&Cs is done via an explicit flag `--accept-operator-terms-and-conditions` added to `nym-node run` command.
Accepting T&Cs is done via a flag `--accept-operator-terms-and-conditions` added explicitly to `nym-node run` command every time. If you use [systemd](configuration.md#systemd) automation, add the flag to your service file's `ExecStart` line.
To check whether any node has T&Cs accepted or not can be done by querying Swagger API endpoint `/auxiliary_details` via one of these ports (depending on node setup):
```sh
@@ -230,11 +230,6 @@ Run the node with custom `--id` without initialising:
./nym-node run --id <ID> --deny-init --mode mixnode --accept-operator-terms-and-conditions
```
Run the node with custom `--id` without initialising:
```sh
./nym-node run --id <ID> --deny-init --mode entry-gateway --accept-operator-terms-and-conditions
```
### Migrate
```admonish caution
@@ -23,8 +23,48 @@ Begin with the steps listed in [*Connectivity Test and Configuration*](../nodes/
4. Search or ask your ISP for additional documentation related to IPv6 routing and ask them to provide you with `IPv6 IP address` and `IPv6 IP gateway address`
- For example Digital Ocean setup isn't the most straight forward, but it's [well documented](https://docs.digitalocean.com/products/networking/ipv6/how-to/enable/#on-existing-droplets) and it works.
5. Search for guides regarding your particular system and distribution. For Debian based distributions using systemd, some generic guides such as [this one](https://cloudzy.com/blog/configure-ipv6-on-ubuntu/) work as well.
5. Search for guides regarding your particular system and distribution. For Debian based distributions using systemd, some generic guides such as [this one](https://cloudzy.com/blog/configure-ipv6-on-ubuntu/) or [this one](https://help.ovhcloud.com/csm/en-ie-vps-configuring-ipv6?id=kb_article_view&sysparm_article=KB0047567) work as well.
### Network configuration
On modern Debian based Linux distributions, the network is configured by either [Netplan](https://netplan.io/) or [ifup/ifdown](https://manpages.debian.org/testing/ifupdown/ifup.8.en.html) utilities . It is very easy to check which one you have.
1. If you have the following folder `/etc/netplan` which has got a YAML file - you are likely to have Netplan.
2. If you have the following folder `/etc/network` and it is not empty - you are likely to have ifup/down.
Most contemporary Ubuntu/Debian distributions come with Netplan, however it is possible that your hosting provider is using a custom version of ISO. For example, Debian 12 (latest version as of June 2024) may come with ifup/down.
Nym operator community members have tested a VPS with Netplan and where in some cases `nym-node --mode exit-gateway` was not routing IP packets properly even after running `network_tunnel_manager.sh` script. We are working on a guide to setup Netplan configuration manually for such cases.
Configuration of ifup/ifdown is a bit simpler. If the `network_tunnel_manager.sh` script doesn't do the job, open `/etc/network/interfaces` file (research if your system uses a different naming for it) and configure it similarly to this:
```sh
auto lo
iface lo inet loopback
auto eth0
iface eth0 inet static
address <YOUR_IPV4_ADDRESS>
netmask NETMASK
gateway <YOUR_IPV4_GATEWAY>
iface eth0 inet6 static
accept_ra 0
address <YOUR_IPV6_ADDRESS>
netmask 64
gateway <YOUR_IPV6_GATEWAY>
post-up /sbin/ip -r route add <YOUR_IPV6_GATEWAY> dev eth0
post-up /sbin/ip -r route add default via <YOUR_IPV6_GATEWAY>
```
Last two lines are particularly important as they enable IPv6 routing. You can find YOUR_IPV6_GATEWAY using your server's control panel. There is no single way to find the gateway, so please access your control panel to find yours or open a support ticket. Here is an example of how it looks on [OVH](https://help.ovhcloud.com/csm/en-ie-vps-configuring-ipv6?id=kb_article_view&sysparm_article=KB0047567).
```admonish warning title="WARNING"
Be extra careful editing this file since you may lock yourself out of the server. If it happens, you can always access the server via the hoster's VNC panel.
```
Once finished, save the file and reboot the server. Now, running `ip a` command should return correct IPv4 and IPv6 addresses.
Finally re-run `network_tunnel_manager.sh` script, following the steps in node [IPv6 configuration chapter](https://github.com/nymtech/nym/nodes/configuration.md#ipv6-configuration).
## Other VPS troubleshooting
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "explorer-api"
version = "1.1.36"
version = "1.1.38"
edition = "2021"
license.workspace = true
-3
View File
@@ -1,7 +1,6 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::commands::upgrade_helpers;
use log::{error, info};
use nym_config::{save_formatted_config_to_file, OptionalSet};
use nym_crypto::asymmetric::identity;
@@ -122,8 +121,6 @@ pub(crate) fn ensure_correct_bech32_prefix(address: &AccountId) -> Result<(), Ga
}
pub(crate) fn try_load_current_config(id: &str) -> Result<Config, GatewayError> {
upgrade_helpers::try_upgrade_config(id)?;
Config::read_from_default_path(id).map_err(|err| {
error!(
"Failed to load config for {id}. Are you sure you have run `init` before? (Error was: {err})",
+8 -2
View File
@@ -2,9 +2,10 @@
// SPDX-License-Identifier: GPL-3.0-only
use crate::Cli;
use anyhow::bail;
use clap::CommandFactory;
use clap::Subcommand;
use log::warn;
use log::{error, warn};
use nym_bin_common::completions::{fig_generate, ArgShell};
use std::io::IsTerminal;
use std::time::Duration;
@@ -17,7 +18,6 @@ pub(crate) mod run;
pub(crate) mod setup_ip_packet_router;
pub(crate) mod setup_network_requester;
pub(crate) mod sign;
mod upgrade_helpers;
#[derive(Subcommand)]
pub(crate) enum Commands {
@@ -55,6 +55,12 @@ pub(crate) enum Commands {
pub(crate) async fn execute(args: Cli) -> anyhow::Result<()> {
let bin_name = "nym-gateway";
if !args.force_run {
let msg = "standalone gateways have been deprecated - please migrate to a `nym-node` via `nym-node migrate gateways` command";
error!("{msg}");
bail!("{msg}")
}
warn!("standalone gateways have been deprecated - please consider migrating it to a `nym-node` via `nym-node migrate gateway` command");
if std::io::stdout().is_terminal() {
// if user is running it in terminal session,
-153
View File
@@ -1,153 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use log::info;
use nym_gateway::config::old_config_v1_1_20::ConfigV1_1_20;
use nym_gateway::config::old_config_v1_1_28::ConfigV1_1_28;
use nym_gateway::config::old_config_v1_1_29::ConfigV1_1_29;
use nym_gateway::config::old_config_v1_1_31::ConfigV1_1_31;
use nym_gateway::config::old_config_v1_1_36::ConfigV1_1_36;
use nym_gateway::config::{default_config_filepath, Config};
use nym_gateway::error::GatewayError;
fn try_upgrade_v1_1_20_config(id: &str) -> Result<bool, GatewayError> {
use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
// explicitly load it as v1.1.20 (which is incompatible with the current, i.e. 1.1.21+)
let Ok(old_config) = ConfigV1_1_20::load_from_file(id) else {
// if we failed to load it, there might have been nothing to upgrade
// or maybe it was an even older file. in either way. just ignore it and carry on with our day
return Ok(false);
};
info!("It seems the gateway is using <= v1.1.20 config template.");
info!("It is going to get updated to the current specification.");
let updated_step1: ConfigV1_1_28 = old_config.into();
let updated_step2: ConfigV1_1_29 = updated_step1.into();
let updated_step3: ConfigV1_1_31 = updated_step2.into();
let updated_step4: ConfigV1_1_36 = updated_step3.into();
let updated: Config = updated_step4.into();
updated
.save_to_default_location()
.map_err(|err| GatewayError::ConfigSaveFailure {
path: default_config_filepath(id),
id: id.to_string(),
source: err,
})?;
Ok(true)
}
fn try_upgrade_v1_1_28_config(id: &str) -> Result<bool, GatewayError> {
// explicitly load it as v1.1.28 (which is incompatible with the current, i.e. 1.1.29+)
let Ok(old_config) = ConfigV1_1_28::read_from_default_path(id) else {
// if we failed to load it, there might have been nothing to upgrade
// or maybe it was an even older file. in either way. just ignore it and carry on with our day
return Ok(false);
};
info!("It seems the gateway is using <= v1.1.28 config template.");
info!("It is going to get updated to the current specification.");
let updated_step1: ConfigV1_1_29 = old_config.into();
let updated_step2: ConfigV1_1_31 = updated_step1.into();
let updated_step3: ConfigV1_1_36 = updated_step2.into();
let updated: Config = updated_step3.into();
updated
.save_to_default_location()
.map_err(|err| GatewayError::ConfigSaveFailure {
path: default_config_filepath(id),
id: id.to_string(),
source: err,
})?;
Ok(true)
}
fn try_upgrade_v1_1_29_config(id: &str) -> Result<bool, GatewayError> {
// explicitly load it as v1.1.29 (which is incompatible with the current, i.e. 1.1.30+)
let Ok(old_config) = ConfigV1_1_29::read_from_default_path(id) else {
// if we failed to load it, there might have been nothing to upgrade
// or maybe it was an even older file. in either way. just ignore it and carry on with our day
return Ok(false);
};
info!("It seems the gateway is using <= v1.1.29 config template.");
info!("It is going to get updated to the current specification.");
let updated_step1: ConfigV1_1_31 = old_config.into();
let updated_step2: ConfigV1_1_36 = updated_step1.into();
let updated: Config = updated_step2.into();
updated
.save_to_default_location()
.map_err(|err| GatewayError::ConfigSaveFailure {
path: default_config_filepath(id),
id: id.to_string(),
source: err,
})?;
Ok(true)
}
fn try_upgrade_v1_1_31_config(id: &str) -> Result<bool, GatewayError> {
// explicitly load it as v1.1.31 (which is incompatible with the current, i.e. 1.1.32+)
let Ok(old_config) = ConfigV1_1_31::read_from_default_path(id) else {
// if we failed to load it, there might have been nothing to upgrade
// or maybe it was an even older file. in either way. just ignore it and carry on with our day
return Ok(false);
};
info!("It seems the gateway is using <= v1.1.31 config template.");
info!("It is going to get updated to the current specification.");
let updated_step1: ConfigV1_1_36 = old_config.into();
let updated: Config = updated_step1.into();
updated
.save_to_default_location()
.map_err(|err| GatewayError::ConfigSaveFailure {
path: default_config_filepath(id),
id: id.to_string(),
source: err,
})?;
Ok(true)
}
fn try_upgrade_v1_1_36_config(id: &str) -> Result<bool, GatewayError> {
// explicitly load it as v1.1.36 (which is incompatible with the current, i.e. 1.1.37+)
let Ok(old_config) = ConfigV1_1_36::read_from_default_path(id) else {
// if we failed to load it, there might have been nothing to upgrade
// or maybe it was an even older file. in either way. just ignore it and carry on with our day
return Ok(false);
};
info!("It seems the gateway is using <= v1.1.36 config template.");
info!("It is going to get updated to the current specification.");
let updated: Config = old_config.into();
updated
.save_to_default_location()
.map_err(|err| GatewayError::ConfigSaveFailure {
path: default_config_filepath(id),
id: id.to_string(),
source: err,
})?;
Ok(true)
}
pub(crate) fn try_upgrade_config(id: &str) -> Result<(), GatewayError> {
if try_upgrade_v1_1_20_config(id)? {
return Ok(());
}
if try_upgrade_v1_1_28_config(id)? {
return Ok(());
}
if try_upgrade_v1_1_29_config(id)? {
return Ok(());
}
if try_upgrade_v1_1_31_config(id)? {
return Ok(());
}
if try_upgrade_v1_1_36_config(id)? {
return Ok(());
}
Ok(())
}
-5
View File
@@ -22,11 +22,6 @@ use zeroize::{Zeroize, ZeroizeOnDrop};
pub use crate::config::persistence::paths::GatewayPaths;
pub mod old_config_v1_1_20;
pub mod old_config_v1_1_28;
pub mod old_config_v1_1_29;
pub mod old_config_v1_1_31;
pub mod old_config_v1_1_36;
pub mod persistence;
mod template;
-203
View File
@@ -1,203 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::config::old_config_v1_1_28::{
ConfigV1_1_28, DebugV1_1_28, GatewayPathsV1_1_28, GatewayV1_1_28, KeysPathsV1_1_28,
LoggingSettingsV1_1_28,
};
use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
use nym_validator_client::nyxd;
use serde::{Deserialize, Serialize};
use std::net::{IpAddr, Ipv4Addr};
use std::path::PathBuf;
use std::str::FromStr;
use std::time::Duration;
use url::Url;
const STATISTICS_SERVICE_DOMAIN_ADDRESS: &str = "https://mainnet-stats.nymte.ch:8090/";
const NYXD_URL: &str = "https://rpc.nymtech.net";
const NYM_API: &str = "https://validator.nymtech.net/api/";
const DEFAULT_MIX_LISTENING_PORT: u16 = 1789;
const DEFAULT_CLIENT_LISTENING_PORT: u16 = 9000;
const DEFAULT_PRESENCE_SENDING_DELAY: Duration = Duration::from_millis(10_000);
const DEFAULT_PACKET_FORWARDING_INITIAL_BACKOFF: Duration = Duration::from_millis(10_000);
const DEFAULT_PACKET_FORWARDING_MAXIMUM_BACKOFF: Duration = Duration::from_millis(300_000);
const DEFAULT_INITIAL_CONNECTION_TIMEOUT: Duration = Duration::from_millis(1_500);
const DEFAULT_MAXIMUM_CONNECTION_BUFFER_SIZE: usize = 2000;
const DEFAULT_STORED_MESSAGE_FILENAME_LENGTH: u16 = 16;
const DEFAULT_MESSAGE_RETRIEVAL_LIMIT: i64 = 100;
/// returns a `0.0.0.0` / INADDR_ANY
fn bind_all_address() -> IpAddr {
IpAddr::V4(Ipv4Addr::UNSPECIFIED)
}
#[derive(Debug, Default, Deserialize, PartialEq, Serialize)]
pub struct ConfigV1_1_20 {
gateway: GatewayV1_1_20,
#[serde(default)]
logging: LoggingV1_1_20,
#[serde(default)]
debug: DebugV1_1_20,
}
impl From<ConfigV1_1_20> for ConfigV1_1_28 {
fn from(value: ConfigV1_1_20) -> Self {
ConfigV1_1_28 {
gateway: GatewayV1_1_28 {
version: value.gateway.version,
id: value.gateway.id,
only_coconut_credentials: value.gateway.only_coconut_credentials,
listening_address: value.gateway.listening_address,
mix_port: value.gateway.mix_port,
clients_port: value.gateway.clients_port,
enabled_statistics: value.gateway.enabled_statistics,
nym_api_urls: value.gateway.nym_api_urls,
nyxd_urls: value.gateway.nyxd_urls,
statistics_service_url: value.gateway.statistics_service_url,
cosmos_mnemonic: value.gateway.cosmos_mnemonic,
},
storage_paths: GatewayPathsV1_1_28 {
keys: KeysPathsV1_1_28 {
private_identity_key_file: value.gateway.private_identity_key_file,
public_identity_key_file: value.gateway.public_identity_key_file,
private_sphinx_key_file: value.gateway.private_sphinx_key_file,
public_sphinx_key_file: value.gateway.public_sphinx_key_file,
},
clients_storage: value.gateway.persistent_storage,
},
logging: value.logging.into(),
debug: value.debug.into(),
}
}
}
impl MigrationNymConfig for ConfigV1_1_20 {
fn default_root_directory() -> PathBuf {
// unless this is run on some esoteric system, it should not fail thus the expect is fine
#[allow(clippy::expect_used)]
dirs::home_dir()
.expect("Failed to evaluate $HOME value")
.join(".nym")
.join("gateways")
}
}
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize)]
pub struct GatewayV1_1_20 {
version: String,
id: String,
#[serde(default)]
only_coconut_credentials: bool,
#[serde(default = "bind_all_address")]
listening_address: IpAddr,
announce_address: String,
mix_port: u16,
clients_port: u16,
private_identity_key_file: PathBuf,
public_identity_key_file: PathBuf,
private_sphinx_key_file: PathBuf,
public_sphinx_key_file: PathBuf,
enabled_statistics: bool,
statistics_service_url: Url,
#[serde(alias = "validator_api_urls")]
nym_api_urls: Vec<Url>,
#[serde(alias = "validator_nymd_urls")]
nyxd_urls: Vec<Url>,
cosmos_mnemonic: bip39::Mnemonic,
nym_root_directory: PathBuf,
persistent_storage: PathBuf,
wallet_address: Option<nyxd::AccountId>,
}
impl Default for GatewayV1_1_20 {
fn default() -> Self {
// allow usage of `expect` here as our default mainnet values should have been well-formed.
#[allow(clippy::expect_used)]
GatewayV1_1_20 {
version: env!("CARGO_PKG_VERSION").to_string(),
id: "".to_string(),
only_coconut_credentials: false,
listening_address: bind_all_address(),
announce_address: "127.0.0.1".to_string(),
mix_port: DEFAULT_MIX_LISTENING_PORT,
clients_port: DEFAULT_CLIENT_LISTENING_PORT,
private_identity_key_file: Default::default(),
public_identity_key_file: Default::default(),
private_sphinx_key_file: Default::default(),
public_sphinx_key_file: Default::default(),
enabled_statistics: false,
statistics_service_url: Url::from_str(STATISTICS_SERVICE_DOMAIN_ADDRESS)
.expect("Invalid default statistics service URL"),
nym_api_urls: vec![Url::from_str(NYM_API).expect("Invalid default API URL")],
nyxd_urls: vec![Url::from_str(NYXD_URL).expect("Invalid default nyxd URL")],
cosmos_mnemonic: bip39::Mnemonic::generate(24)
.expect("failed to generate fresh mnemonic"),
nym_root_directory: ConfigV1_1_20::default_root_directory(),
persistent_storage: Default::default(),
wallet_address: None,
}
}
}
#[derive(Debug, Default, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
struct LoggingV1_1_20 {}
impl From<LoggingV1_1_20> for LoggingSettingsV1_1_28 {
fn from(_value: LoggingV1_1_20) -> Self {
LoggingSettingsV1_1_28 {}
}
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(default)]
struct DebugV1_1_20 {
#[serde(with = "humantime_serde")]
packet_forwarding_initial_backoff: Duration,
#[serde(with = "humantime_serde")]
packet_forwarding_maximum_backoff: Duration,
#[serde(with = "humantime_serde")]
initial_connection_timeout: Duration,
maximum_connection_buffer_size: usize,
#[serde(with = "humantime_serde")]
presence_sending_delay: Duration,
stored_messages_filename_length: u16,
message_retrieval_limit: i64,
use_legacy_framed_packet_version: bool,
}
impl From<DebugV1_1_20> for DebugV1_1_28 {
fn from(value: DebugV1_1_20) -> Self {
DebugV1_1_28 {
packet_forwarding_initial_backoff: value.packet_forwarding_initial_backoff,
packet_forwarding_maximum_backoff: value.packet_forwarding_maximum_backoff,
initial_connection_timeout: value.initial_connection_timeout,
maximum_connection_buffer_size: value.maximum_connection_buffer_size,
presence_sending_delay: value.presence_sending_delay,
stored_messages_filename_length: value.stored_messages_filename_length,
message_retrieval_limit: value.message_retrieval_limit,
use_legacy_framed_packet_version: value.use_legacy_framed_packet_version,
}
}
}
impl Default for DebugV1_1_20 {
fn default() -> Self {
DebugV1_1_20 {
packet_forwarding_initial_backoff: DEFAULT_PACKET_FORWARDING_INITIAL_BACKOFF,
packet_forwarding_maximum_backoff: DEFAULT_PACKET_FORWARDING_MAXIMUM_BACKOFF,
initial_connection_timeout: DEFAULT_INITIAL_CONNECTION_TIMEOUT,
presence_sending_delay: DEFAULT_PRESENCE_SENDING_DELAY,
maximum_connection_buffer_size: DEFAULT_MAXIMUM_CONNECTION_BUFFER_SIZE,
stored_messages_filename_length: DEFAULT_STORED_MESSAGE_FILENAME_LENGTH,
message_retrieval_limit: DEFAULT_MESSAGE_RETRIEVAL_LIMIT,
// TODO: remember to change it in one of future releases!!
use_legacy_framed_packet_version: true,
}
}
}
-233
View File
@@ -1,233 +0,0 @@
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::config::old_config_v1_1_29::{
ConfigV1_1_29, DebugV1_1_29, GatewayPathsV1_1_29, GatewayV1_1_29, KeysPathsV1_1_29,
LoggingSettingsV1_1_29,
};
use nym_config::{
must_get_home, read_config_from_toml_file, DEFAULT_CONFIG_DIR, DEFAULT_CONFIG_FILENAME, NYM_DIR,
};
use serde::{Deserialize, Serialize};
use std::io;
use std::net::IpAddr;
use std::path::{Path, PathBuf};
use std::time::Duration;
use url::Url;
const DEFAULT_GATEWAYS_DIR: &str = "gateways";
// 'DEBUG'
// where applicable, the below are defined in milliseconds
const DEFAULT_PRESENCE_SENDING_DELAY: Duration = Duration::from_millis(10_000);
const DEFAULT_PACKET_FORWARDING_INITIAL_BACKOFF: Duration = Duration::from_millis(10_000);
const DEFAULT_PACKET_FORWARDING_MAXIMUM_BACKOFF: Duration = Duration::from_millis(300_000);
const DEFAULT_INITIAL_CONNECTION_TIMEOUT: Duration = Duration::from_millis(1_500);
const DEFAULT_MAXIMUM_CONNECTION_BUFFER_SIZE: usize = 2000;
const DEFAULT_STORED_MESSAGE_FILENAME_LENGTH: u16 = 16;
const DEFAULT_MESSAGE_RETRIEVAL_LIMIT: i64 = 100;
/// Derive default path to gateway's config directory.
/// It should get resolved to `$HOME/.nym/gateways/<id>/config`
pub fn default_config_directory<P: AsRef<Path>>(id: P) -> PathBuf {
must_get_home()
.join(NYM_DIR)
.join(DEFAULT_GATEWAYS_DIR)
.join(id)
.join(DEFAULT_CONFIG_DIR)
}
/// Derive default path to gateways's config file.
/// It should get resolved to `$HOME/.nym/gateways/<id>/config/config.toml`
pub fn default_config_filepath<P: AsRef<Path>>(id: P) -> PathBuf {
default_config_directory(id).join(DEFAULT_CONFIG_FILENAME)
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct KeysPathsV1_1_28 {
pub private_identity_key_file: PathBuf,
pub public_identity_key_file: PathBuf,
pub private_sphinx_key_file: PathBuf,
pub public_sphinx_key_file: PathBuf,
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct GatewayPathsV1_1_28 {
pub keys: KeysPathsV1_1_28,
#[serde(alias = "persistent_storage")]
pub clients_storage: PathBuf,
}
#[derive(Debug, Default, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct LoggingSettingsV1_1_28 {}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ConfigV1_1_28 {
pub gateway: GatewayV1_1_28,
pub storage_paths: GatewayPathsV1_1_28,
#[serde(default)]
pub logging: LoggingSettingsV1_1_28,
#[serde(default)]
pub debug: DebugV1_1_28,
}
impl ConfigV1_1_28 {
pub fn read_from_default_path<P: AsRef<Path>>(id: P) -> io::Result<Self> {
read_config_from_toml_file(default_config_filepath(id))
}
}
impl From<ConfigV1_1_28> for ConfigV1_1_29 {
fn from(value: ConfigV1_1_28) -> Self {
ConfigV1_1_29 {
// \/ ADDED
save_path: None,
// /\ ADDED
gateway: GatewayV1_1_29 {
version: value.gateway.version,
id: value.gateway.id,
only_coconut_credentials: value.gateway.only_coconut_credentials,
listening_address: value.gateway.listening_address,
mix_port: value.gateway.mix_port,
clients_port: value.gateway.clients_port,
enabled_statistics: value.gateway.enabled_statistics,
nym_api_urls: value.gateway.nym_api_urls,
nyxd_urls: value.gateway.nyxd_urls,
statistics_service_url: value.gateway.statistics_service_url,
cosmos_mnemonic: value.gateway.cosmos_mnemonic,
},
storage_paths: GatewayPathsV1_1_29 {
keys: KeysPathsV1_1_29 {
private_identity_key_file: value.storage_paths.keys.private_identity_key_file,
public_identity_key_file: value.storage_paths.keys.public_identity_key_file,
private_sphinx_key_file: value.storage_paths.keys.private_sphinx_key_file,
public_sphinx_key_file: value.storage_paths.keys.public_sphinx_key_file,
},
clients_storage: value.storage_paths.clients_storage,
// \/ ADDED
network_requester_config: None,
// /\ ADDED
},
// \/ ADDED
network_requester: Default::default(),
// /\ ADDED
logging: LoggingSettingsV1_1_29 {},
debug: DebugV1_1_29 {
packet_forwarding_initial_backoff: value.debug.packet_forwarding_initial_backoff,
packet_forwarding_maximum_backoff: value.debug.packet_forwarding_maximum_backoff,
initial_connection_timeout: value.debug.initial_connection_timeout,
maximum_connection_buffer_size: value.debug.maximum_connection_buffer_size,
presence_sending_delay: value.debug.presence_sending_delay,
stored_messages_filename_length: value.debug.stored_messages_filename_length,
message_retrieval_limit: value.debug.message_retrieval_limit,
use_legacy_framed_packet_version: value.debug.use_legacy_framed_packet_version,
},
}
}
}
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize)]
pub struct GatewayV1_1_28 {
/// Version of the gateway for which this configuration was created.
pub version: String,
/// ID specifies the human readable ID of this particular gateway.
pub id: String,
/// Indicates whether this gateway is accepting only coconut credentials for accessing the
/// the mixnet, or if it also accepts non-paying clients
#[serde(default)]
pub only_coconut_credentials: bool,
/// Address to which this mixnode will bind to and will be listening for packets.
pub listening_address: IpAddr,
/// Port used for listening for all mixnet traffic.
/// (default: 1789)
pub mix_port: u16,
/// Port used for listening for all client-related traffic.
/// (default: 9000)
pub clients_port: u16,
/// Whether gateway collects and sends anonymized statistics
pub enabled_statistics: bool,
/// Domain address of the statistics service
pub statistics_service_url: Url,
/// Addresses to APIs from which the node gets the view of the network.
#[serde(alias = "validator_api_urls")]
pub nym_api_urls: Vec<Url>,
/// Addresses to validators which the node uses to check for double spending of ERC20 tokens.
#[serde(alias = "validator_nymd_urls")]
pub nyxd_urls: Vec<Url>,
/// Mnemonic of a cosmos wallet used in checking for double spending.
// #[deprecated(note = "move to storage")]
// TODO: I don't think this should be stored directly in the config...
pub cosmos_mnemonic: bip39::Mnemonic,
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(default)]
pub struct DebugV1_1_28 {
/// Initial value of an exponential backoff to reconnect to dropped TCP connection when
/// forwarding sphinx packets.
#[serde(with = "humantime_serde")]
pub packet_forwarding_initial_backoff: Duration,
/// Maximum value of an exponential backoff to reconnect to dropped TCP connection when
/// forwarding sphinx packets.
#[serde(with = "humantime_serde")]
pub packet_forwarding_maximum_backoff: Duration,
/// Timeout for establishing initial connection when trying to forward a sphinx packet.
#[serde(with = "humantime_serde")]
pub initial_connection_timeout: Duration,
/// Maximum number of packets that can be stored waiting to get sent to a particular connection.
pub maximum_connection_buffer_size: usize,
/// Delay between each subsequent presence data being sent.
#[serde(with = "humantime_serde")]
pub presence_sending_delay: Duration,
/// Length of filenames for new client messages.
pub stored_messages_filename_length: u16,
/// Number of messages from offline client that can be pulled at once from the storage.
pub message_retrieval_limit: i64,
/// Specifies whether the mixnode should be using the legacy framing for the sphinx packets.
// it's set to true by default. The reason for that decision is to preserve compatibility with the
// existing nodes whilst everyone else is upgrading and getting the code for handling the new field.
// It shall be disabled in the subsequent releases.
pub use_legacy_framed_packet_version: bool,
}
impl Default for DebugV1_1_28 {
fn default() -> Self {
DebugV1_1_28 {
packet_forwarding_initial_backoff: DEFAULT_PACKET_FORWARDING_INITIAL_BACKOFF,
packet_forwarding_maximum_backoff: DEFAULT_PACKET_FORWARDING_MAXIMUM_BACKOFF,
initial_connection_timeout: DEFAULT_INITIAL_CONNECTION_TIMEOUT,
presence_sending_delay: DEFAULT_PRESENCE_SENDING_DELAY,
maximum_connection_buffer_size: DEFAULT_MAXIMUM_CONNECTION_BUFFER_SIZE,
stored_messages_filename_length: DEFAULT_STORED_MESSAGE_FILENAME_LENGTH,
message_retrieval_limit: DEFAULT_MESSAGE_RETRIEVAL_LIMIT,
use_legacy_framed_packet_version: false,
}
}
}
-282
View File
@@ -1,282 +0,0 @@
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::config::Host;
use nym_config::{
must_get_home, read_config_from_toml_file, DEFAULT_CONFIG_DIR, DEFAULT_CONFIG_FILENAME, NYM_DIR,
};
use serde::{Deserialize, Deserializer, Serialize};
use std::io;
use std::net::IpAddr;
use std::path::{Path, PathBuf};
use std::time::Duration;
use url::Url;
use super::old_config_v1_1_31::{
ConfigV1_1_31, DebugV1_1_31, GatewayPathsV1_1_31, GatewayV1_1_31, KeysPathsV1_1_31,
LoggingSettingsV1_1_31, NetworkRequesterV1_1_31,
};
const DEFAULT_GATEWAYS_DIR: &str = "gateways";
// 'DEBUG'
// where applicable, the below are defined in milliseconds
const DEFAULT_PRESENCE_SENDING_DELAY: Duration = Duration::from_millis(10_000);
const DEFAULT_PACKET_FORWARDING_INITIAL_BACKOFF: Duration = Duration::from_millis(10_000);
const DEFAULT_PACKET_FORWARDING_MAXIMUM_BACKOFF: Duration = Duration::from_millis(300_000);
const DEFAULT_INITIAL_CONNECTION_TIMEOUT: Duration = Duration::from_millis(1_500);
const DEFAULT_MAXIMUM_CONNECTION_BUFFER_SIZE: usize = 2000;
const DEFAULT_STORED_MESSAGE_FILENAME_LENGTH: u16 = 16;
const DEFAULT_MESSAGE_RETRIEVAL_LIMIT: i64 = 100;
/// Derive default path to gateway's config directory.
/// It should get resolved to `$HOME/.nym/gateways/<id>/config`
pub fn default_config_directory<P: AsRef<Path>>(id: P) -> PathBuf {
must_get_home()
.join(NYM_DIR)
.join(DEFAULT_GATEWAYS_DIR)
.join(id)
.join(DEFAULT_CONFIG_DIR)
}
/// Derive default path to gateways's config file.
/// It should get resolved to `$HOME/.nym/gateways/<id>/config/config.toml`
pub fn default_config_filepath<P: AsRef<Path>>(id: P) -> PathBuf {
default_config_directory(id).join(DEFAULT_CONFIG_FILENAME)
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct KeysPathsV1_1_29 {
pub private_identity_key_file: PathBuf,
pub public_identity_key_file: PathBuf,
pub private_sphinx_key_file: PathBuf,
pub public_sphinx_key_file: PathBuf,
}
fn de_maybe_path<'de, D>(deserializer: D) -> Result<Option<PathBuf>, D::Error>
where
D: Deserializer<'de>,
{
let path = PathBuf::deserialize(deserializer)?;
if path.as_os_str().is_empty() {
Ok(None)
} else {
Ok(Some(path))
}
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct GatewayPathsV1_1_29 {
pub keys: KeysPathsV1_1_29,
#[serde(alias = "persistent_storage")]
pub clients_storage: PathBuf,
#[serde(deserialize_with = "de_maybe_path")]
pub network_requester_config: Option<PathBuf>,
}
#[derive(Debug, Default, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct LoggingSettingsV1_1_29 {}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ConfigV1_1_29 {
#[serde(skip)]
pub save_path: Option<PathBuf>,
pub gateway: GatewayV1_1_29,
pub storage_paths: GatewayPathsV1_1_29,
pub network_requester: NetworkRequesterV1_1_29,
#[serde(default)]
pub logging: LoggingSettingsV1_1_29,
#[serde(default)]
pub debug: DebugV1_1_29,
}
impl ConfigV1_1_29 {
pub fn read_from_default_path<P: AsRef<Path>>(id: P) -> io::Result<Self> {
read_config_from_toml_file(default_config_filepath(id))
}
}
impl From<ConfigV1_1_29> for ConfigV1_1_31 {
fn from(value: ConfigV1_1_29) -> Self {
ConfigV1_1_31 {
save_path: value.save_path,
// \/ ADDED
host: Host {
// this is a very bad default!
public_ips: vec![value.gateway.listening_address],
hostname: None,
},
// /\ ADDED
// \/ ADDED
http: Default::default(),
// /\ ADDED
gateway: GatewayV1_1_31 {
version: value.gateway.version,
id: value.gateway.id,
only_coconut_credentials: value.gateway.only_coconut_credentials,
listening_address: value.gateway.listening_address,
mix_port: value.gateway.mix_port,
clients_port: value.gateway.clients_port,
// \/ ADDED
clients_wss_port: None,
// /\ ADDED
enabled_statistics: value.gateway.enabled_statistics,
nym_api_urls: value.gateway.nym_api_urls,
nyxd_urls: value.gateway.nyxd_urls,
statistics_service_url: value.gateway.statistics_service_url,
cosmos_mnemonic: value.gateway.cosmos_mnemonic,
},
// \/ ADDED
wireguard: Default::default(),
// /\ ADDED
storage_paths: GatewayPathsV1_1_31 {
keys: KeysPathsV1_1_31 {
private_identity_key_file: value.storage_paths.keys.private_identity_key_file,
public_identity_key_file: value.storage_paths.keys.public_identity_key_file,
private_sphinx_key_file: value.storage_paths.keys.private_sphinx_key_file,
public_sphinx_key_file: value.storage_paths.keys.public_sphinx_key_file,
},
clients_storage: value.storage_paths.clients_storage,
network_requester_config: value.storage_paths.network_requester_config,
},
network_requester: NetworkRequesterV1_1_31 {
enabled: value.network_requester.enabled,
},
logging: LoggingSettingsV1_1_31 {},
debug: DebugV1_1_31 {
packet_forwarding_initial_backoff: value.debug.packet_forwarding_initial_backoff,
packet_forwarding_maximum_backoff: value.debug.packet_forwarding_maximum_backoff,
initial_connection_timeout: value.debug.initial_connection_timeout,
maximum_connection_buffer_size: value.debug.maximum_connection_buffer_size,
presence_sending_delay: value.debug.presence_sending_delay,
stored_messages_filename_length: value.debug.stored_messages_filename_length,
message_retrieval_limit: value.debug.message_retrieval_limit,
use_legacy_framed_packet_version: value.debug.use_legacy_framed_packet_version,
},
}
}
}
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize)]
pub struct GatewayV1_1_29 {
/// Version of the gateway for which this configuration was created.
pub version: String,
/// ID specifies the human readable ID of this particular gateway.
pub id: String,
/// Indicates whether this gateway is accepting only coconut credentials for accessing the
/// the mixnet, or if it also accepts non-paying clients
#[serde(default)]
pub only_coconut_credentials: bool,
/// Address to which this mixnode will bind to and will be listening for packets.
pub listening_address: IpAddr,
/// Port used for listening for all mixnet traffic.
/// (default: 1789)
pub mix_port: u16,
/// Port used for listening for all client-related traffic.
/// (default: 9000)
pub clients_port: u16,
/// Whether gateway collects and sends anonymized statistics
pub enabled_statistics: bool,
/// Domain address of the statistics service
pub statistics_service_url: Url,
/// Addresses to APIs from which the node gets the view of the network.
#[serde(alias = "validator_api_urls")]
pub nym_api_urls: Vec<Url>,
/// Addresses to validators which the node uses to check for double spending of ERC20 tokens.
#[serde(alias = "validator_nymd_urls")]
pub nyxd_urls: Vec<Url>,
/// Mnemonic of a cosmos wallet used in checking for double spending.
// #[deprecated(note = "move to storage")]
// TODO: I don't think this should be stored directly in the config...
pub cosmos_mnemonic: bip39::Mnemonic,
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(default)]
pub struct NetworkRequesterV1_1_29 {
pub enabled: bool,
}
#[allow(clippy::derivable_impls)]
impl Default for NetworkRequesterV1_1_29 {
fn default() -> Self {
NetworkRequesterV1_1_29 { enabled: false }
}
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(default)]
pub struct DebugV1_1_29 {
/// Initial value of an exponential backoff to reconnect to dropped TCP connection when
/// forwarding sphinx packets.
#[serde(with = "humantime_serde")]
pub packet_forwarding_initial_backoff: Duration,
/// Maximum value of an exponential backoff to reconnect to dropped TCP connection when
/// forwarding sphinx packets.
#[serde(with = "humantime_serde")]
pub packet_forwarding_maximum_backoff: Duration,
/// Timeout for establishing initial connection when trying to forward a sphinx packet.
#[serde(with = "humantime_serde")]
pub initial_connection_timeout: Duration,
/// Maximum number of packets that can be stored waiting to get sent to a particular connection.
pub maximum_connection_buffer_size: usize,
/// Delay between each subsequent presence data being sent.
#[serde(with = "humantime_serde")]
pub presence_sending_delay: Duration,
/// Length of filenames for new client messages.
pub stored_messages_filename_length: u16,
/// Number of messages from offline client that can be pulled at once from the storage.
pub message_retrieval_limit: i64,
/// Specifies whether the mixnode should be using the legacy framing for the sphinx packets.
// it's set to true by default. The reason for that decision is to preserve compatibility with the
// existing nodes whilst everyone else is upgrading and getting the code for handling the new field.
// It shall be disabled in the subsequent releases.
pub use_legacy_framed_packet_version: bool,
}
impl Default for DebugV1_1_29 {
fn default() -> Self {
DebugV1_1_29 {
packet_forwarding_initial_backoff: DEFAULT_PACKET_FORWARDING_INITIAL_BACKOFF,
packet_forwarding_maximum_backoff: DEFAULT_PACKET_FORWARDING_MAXIMUM_BACKOFF,
initial_connection_timeout: DEFAULT_INITIAL_CONNECTION_TIMEOUT,
presence_sending_delay: DEFAULT_PRESENCE_SENDING_DELAY,
maximum_connection_buffer_size: DEFAULT_MAXIMUM_CONNECTION_BUFFER_SIZE,
stored_messages_filename_length: DEFAULT_STORED_MESSAGE_FILENAME_LENGTH,
message_retrieval_limit: DEFAULT_MESSAGE_RETRIEVAL_LIMIT,
use_legacy_framed_packet_version: false,
}
}
}
-362
View File
@@ -1,362 +0,0 @@
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use nym_config::{
must_get_home, read_config_from_toml_file, DEFAULT_CONFIG_DIR, DEFAULT_CONFIG_FILENAME, NYM_DIR,
};
use nym_network_defaults::WG_PORT;
use serde::{Deserialize, Deserializer, Serialize};
use std::io;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::path::{Path, PathBuf};
use std::time::Duration;
use url::Url;
use super::old_config_v1_1_36::{
ConfigV1_1_36, DebugV1_1_36, GatewayPathsV1_1_36, GatewayV1_1_36, KeysPathsV1_1_36,
LoggingSettingsV1_1_36, NetworkRequesterV1_1_36, WireguardPathsV1_1_36, WireguardV1_1_36,
};
use super::{Host, Http};
const DEFAULT_GATEWAYS_DIR: &str = "gateways";
// 'DEBUG'
// where applicable, the below are defined in milliseconds
const DEFAULT_PRESENCE_SENDING_DELAY: Duration = Duration::from_millis(10_000);
const DEFAULT_PACKET_FORWARDING_INITIAL_BACKOFF: Duration = Duration::from_millis(10_000);
const DEFAULT_PACKET_FORWARDING_MAXIMUM_BACKOFF: Duration = Duration::from_millis(300_000);
const DEFAULT_INITIAL_CONNECTION_TIMEOUT: Duration = Duration::from_millis(1_500);
const DEFAULT_MAXIMUM_CONNECTION_BUFFER_SIZE: usize = 2000;
const DEFAULT_STORED_MESSAGE_FILENAME_LENGTH: u16 = 16;
const DEFAULT_MESSAGE_RETRIEVAL_LIMIT: i64 = 100;
fn de_maybe_port<'de, D>(deserializer: D) -> Result<Option<u16>, D::Error>
where
D: Deserializer<'de>,
{
let port = u16::deserialize(deserializer)?;
if port == 0 {
Ok(None)
} else {
Ok(Some(port))
}
}
fn de_maybe_path<'de, D>(deserializer: D) -> Result<Option<PathBuf>, D::Error>
where
D: Deserializer<'de>,
{
let path = PathBuf::deserialize(deserializer)?;
if path.as_os_str().is_empty() {
Ok(None)
} else {
Ok(Some(path))
}
}
/// Derive default path to gateway's config directory.
/// It should get resolved to `$HOME/.nym/gateways/<id>/config`
pub fn default_config_directory<P: AsRef<Path>>(id: P) -> PathBuf {
must_get_home()
.join(NYM_DIR)
.join(DEFAULT_GATEWAYS_DIR)
.join(id)
.join(DEFAULT_CONFIG_DIR)
}
/// Derive default path to gateways's config file.
/// It should get resolved to `$HOME/.nym/gateways/<id>/config/config.toml`
pub fn default_config_filepath<P: AsRef<Path>>(id: P) -> PathBuf {
default_config_directory(id).join(DEFAULT_CONFIG_FILENAME)
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ConfigV1_1_31 {
// additional metadata holding on-disk location of this config file
#[serde(skip)]
pub(crate) save_path: Option<PathBuf>,
pub host: Host,
#[serde(default)]
pub http: Http,
pub gateway: GatewayV1_1_31,
#[serde(default)]
// currently not really used for anything useful
pub wireguard: WireguardV1_1_31,
pub storage_paths: GatewayPathsV1_1_31,
pub network_requester: NetworkRequesterV1_1_31,
#[serde(default)]
pub logging: LoggingSettingsV1_1_31,
#[serde(default)]
pub debug: DebugV1_1_31,
}
impl ConfigV1_1_31 {
pub fn read_from_default_path<P: AsRef<Path>>(id: P) -> io::Result<Self> {
read_config_from_toml_file(default_config_filepath(id))
}
}
impl From<ConfigV1_1_31> for ConfigV1_1_36 {
fn from(value: ConfigV1_1_31) -> Self {
Self {
save_path: value.save_path,
host: value.host,
http: value.http,
gateway: GatewayV1_1_36 {
version: value.gateway.version,
id: value.gateway.id,
only_coconut_credentials: value.gateway.only_coconut_credentials,
listening_address: value.gateway.listening_address,
mix_port: value.gateway.mix_port,
clients_port: value.gateway.clients_port,
clients_wss_port: value.gateway.clients_wss_port,
enabled_statistics: value.gateway.enabled_statistics,
statistics_service_url: value.gateway.statistics_service_url,
nym_api_urls: value.gateway.nym_api_urls,
nyxd_urls: value.gateway.nyxd_urls,
cosmos_mnemonic: value.gateway.cosmos_mnemonic,
},
wireguard: WireguardV1_1_36 {
enabled: value.wireguard.enabled,
bind_address: value.wireguard.bind_address,
announced_port: value.wireguard.announced_port,
private_network_prefix: Default::default(),
storage_paths: WireguardPathsV1_1_36 {
// no fields (yet)
},
},
storage_paths: GatewayPathsV1_1_36 {
keys: KeysPathsV1_1_36 {
private_identity_key_file: value.storage_paths.keys.private_identity_key_file,
public_identity_key_file: value.storage_paths.keys.public_identity_key_file,
private_sphinx_key_file: value.storage_paths.keys.private_sphinx_key_file,
public_sphinx_key_file: value.storage_paths.keys.public_sphinx_key_file,
},
clients_storage: value.storage_paths.clients_storage,
network_requester_config: value.storage_paths.network_requester_config,
// \/ ADDED
ip_packet_router_config: Default::default(),
// /\ ADDED
},
network_requester: NetworkRequesterV1_1_36 {
enabled: value.network_requester.enabled,
},
// \/ ADDED
ip_packet_router: Default::default(),
// /\ ADDED
logging: LoggingSettingsV1_1_36 {
// no fields (yet)
},
debug: DebugV1_1_36 {
packet_forwarding_initial_backoff: value.debug.packet_forwarding_initial_backoff,
packet_forwarding_maximum_backoff: value.debug.packet_forwarding_maximum_backoff,
initial_connection_timeout: value.debug.initial_connection_timeout,
maximum_connection_buffer_size: value.debug.maximum_connection_buffer_size,
presence_sending_delay: value.debug.presence_sending_delay,
stored_messages_filename_length: value.debug.stored_messages_filename_length,
message_retrieval_limit: value.debug.message_retrieval_limit,
use_legacy_framed_packet_version: value.debug.use_legacy_framed_packet_version,
},
}
}
}
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize)]
pub struct GatewayV1_1_31 {
/// Version of the gateway for which this configuration was created.
pub version: String,
/// ID specifies the human readable ID of this particular gateway.
pub id: String,
/// Indicates whether this gateway is accepting only coconut credentials for accessing the
/// the mixnet, or if it also accepts non-paying clients
#[serde(default)]
pub only_coconut_credentials: bool,
/// Address to which this mixnode will bind to and will be listening for packets.
pub listening_address: IpAddr,
/// Port used for listening for all mixnet traffic.
/// (default: 1789)
pub mix_port: u16,
/// Port used for listening for all client-related traffic.
/// (default: 9000)
pub clients_port: u16,
/// If applicable, announced port for listening for secure websocket client traffic.
/// (default: None)
#[serde(deserialize_with = "de_maybe_port")]
pub clients_wss_port: Option<u16>,
/// Whether gateway collects and sends anonymized statistics
pub enabled_statistics: bool,
/// Domain address of the statistics service
pub statistics_service_url: Url,
/// Addresses to APIs from which the node gets the view of the network.
#[serde(alias = "validator_api_urls")]
pub nym_api_urls: Vec<Url>,
/// Addresses to validators which the node uses to check for double spending of ERC20 tokens.
#[serde(alias = "validator_nymd_urls")]
pub nyxd_urls: Vec<Url>,
/// Mnemonic of a cosmos wallet used in checking for double spending.
// #[deprecated(note = "move to storage")]
// TODO: I don't think this should be stored directly in the config...
pub cosmos_mnemonic: bip39::Mnemonic,
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(default)]
#[serde(deny_unknown_fields)]
pub struct WireguardV1_1_31 {
/// Specifies whether the wireguard service is enabled on this node.
pub enabled: bool,
/// Socket address this node will use for binding its wireguard interface.
/// default: `0.0.0.0:51820`
pub bind_address: SocketAddr,
/// Port announced to external clients wishing to connect to the wireguard interface.
/// Useful in the instances where the node is behind a proxy.
pub announced_port: u16,
/// Paths for wireguard keys, client registries, etc.
pub storage_paths: WireguardPathsV1_1_31,
}
impl Default for WireguardV1_1_31 {
fn default() -> Self {
Self {
enabled: false,
bind_address: SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), WG_PORT),
announced_port: WG_PORT,
storage_paths: WireguardPathsV1_1_31 {},
}
}
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct WireguardPathsV1_1_31 {
// pub keys:
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct GatewayPathsV1_1_31 {
pub keys: KeysPathsV1_1_31,
/// Path to sqlite database containing all persistent data: messages for offline clients,
/// derived shared keys and available client bandwidths.
#[serde(alias = "persistent_storage")]
pub clients_storage: PathBuf,
/// Path to the configuration of the embedded network requester.
#[serde(deserialize_with = "de_maybe_path")]
pub network_requester_config: Option<PathBuf>,
// pub node_description: PathBuf,
// pub cosmos_bip39_mnemonic: PathBuf,
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
pub struct KeysPathsV1_1_31 {
/// Path to file containing private identity key.
pub private_identity_key_file: PathBuf,
/// Path to file containing public identity key.
pub public_identity_key_file: PathBuf,
/// Path to file containing private sphinx key.
pub private_sphinx_key_file: PathBuf,
/// Path to file containing public sphinx key.
pub public_sphinx_key_file: PathBuf,
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(default)]
pub struct NetworkRequesterV1_1_31 {
/// Specifies whether network requester service is enabled in this process.
pub enabled: bool,
}
#[allow(clippy::derivable_impls)]
impl Default for NetworkRequesterV1_1_31 {
fn default() -> Self {
Self { enabled: false }
}
}
#[derive(Debug, Default, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct LoggingSettingsV1_1_31 {
// well, we need to implement something here at some point...
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(default)]
pub struct DebugV1_1_31 {
/// Initial value of an exponential backoff to reconnect to dropped TCP connection when
/// forwarding sphinx packets.
#[serde(with = "humantime_serde")]
pub packet_forwarding_initial_backoff: Duration,
/// Maximum value of an exponential backoff to reconnect to dropped TCP connection when
/// forwarding sphinx packets.
#[serde(with = "humantime_serde")]
pub packet_forwarding_maximum_backoff: Duration,
/// Timeout for establishing initial connection when trying to forward a sphinx packet.
#[serde(with = "humantime_serde")]
pub initial_connection_timeout: Duration,
/// Maximum number of packets that can be stored waiting to get sent to a particular connection.
pub maximum_connection_buffer_size: usize,
/// Delay between each subsequent presence data being sent.
#[serde(with = "humantime_serde")]
pub presence_sending_delay: Duration,
/// Length of filenames for new client messages.
pub stored_messages_filename_length: u16,
/// Number of messages from offline client that can be pulled at once from the storage.
pub message_retrieval_limit: i64,
/// Specifies whether the mixnode should be using the legacy framing for the sphinx packets.
// it's set to true by default. The reason for that decision is to preserve compatibility with the
// existing nodes whilst everyone else is upgrading and getting the code for handling the new field.
// It shall be disabled in the subsequent releases.
pub use_legacy_framed_packet_version: bool,
}
impl Default for DebugV1_1_31 {
fn default() -> Self {
Self {
packet_forwarding_initial_backoff: DEFAULT_PACKET_FORWARDING_INITIAL_BACKOFF,
packet_forwarding_maximum_backoff: DEFAULT_PACKET_FORWARDING_MAXIMUM_BACKOFF,
initial_connection_timeout: DEFAULT_INITIAL_CONNECTION_TIMEOUT,
presence_sending_delay: DEFAULT_PRESENCE_SENDING_DELAY,
maximum_connection_buffer_size: DEFAULT_MAXIMUM_CONNECTION_BUFFER_SIZE,
stored_messages_filename_length: DEFAULT_STORED_MESSAGE_FILENAME_LENGTH,
message_retrieval_limit: DEFAULT_MESSAGE_RETRIEVAL_LIMIT,
use_legacy_framed_packet_version: false,
}
}
}
-376
View File
@@ -1,376 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::config::persistence::paths::GatewayPaths;
use nym_bin_common::logging::LoggingSettings;
use nym_config::{
must_get_home, read_config_from_toml_file, DEFAULT_CONFIG_DIR, DEFAULT_CONFIG_FILENAME, NYM_DIR,
};
use nym_network_defaults::WG_PORT;
use serde::{Deserialize, Deserializer, Serialize};
use std::io;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::path::{Path, PathBuf};
use std::time::Duration;
use url::Url;
use super::persistence::paths::KeysPaths;
use super::{Config, Debug, Gateway, Host, Http, NetworkRequester};
const DEFAULT_GATEWAYS_DIR: &str = "gateways";
// 'DEBUG'
// where applicable, the below are defined in milliseconds
const DEFAULT_PRESENCE_SENDING_DELAY: Duration = Duration::from_millis(10_000);
const DEFAULT_PACKET_FORWARDING_INITIAL_BACKOFF: Duration = Duration::from_millis(10_000);
const DEFAULT_PACKET_FORWARDING_MAXIMUM_BACKOFF: Duration = Duration::from_millis(300_000);
const DEFAULT_INITIAL_CONNECTION_TIMEOUT: Duration = Duration::from_millis(1_500);
const DEFAULT_MAXIMUM_CONNECTION_BUFFER_SIZE: usize = 2000;
const DEFAULT_STORED_MESSAGE_FILENAME_LENGTH: u16 = 16;
const DEFAULT_MESSAGE_RETRIEVAL_LIMIT: i64 = 100;
fn de_maybe_port<'de, D>(deserializer: D) -> Result<Option<u16>, D::Error>
where
D: Deserializer<'de>,
{
let port = u16::deserialize(deserializer)?;
if port == 0 {
Ok(None)
} else {
Ok(Some(port))
}
}
fn de_maybe_path<'de, D>(deserializer: D) -> Result<Option<PathBuf>, D::Error>
where
D: Deserializer<'de>,
{
let path = PathBuf::deserialize(deserializer)?;
if path.as_os_str().is_empty() {
Ok(None)
} else {
Ok(Some(path))
}
}
/// Derive default path to gateway's config directory.
/// It should get resolved to `$HOME/.nym/gateways/<id>/config`
pub fn default_config_directory<P: AsRef<Path>>(id: P) -> PathBuf {
must_get_home()
.join(NYM_DIR)
.join(DEFAULT_GATEWAYS_DIR)
.join(id)
.join(DEFAULT_CONFIG_DIR)
}
/// Derive default path to gateways's config file.
/// It should get resolved to `$HOME/.nym/gateways/<id>/config/config.toml`
pub fn default_config_filepath<P: AsRef<Path>>(id: P) -> PathBuf {
default_config_directory(id).join(DEFAULT_CONFIG_FILENAME)
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ConfigV1_1_36 {
// additional metadata holding on-disk location of this config file
#[serde(skip)]
pub(crate) save_path: Option<PathBuf>,
pub host: Host,
#[serde(default)]
pub http: Http,
pub gateway: GatewayV1_1_36,
#[serde(default)]
// currently not really used for anything useful
pub wireguard: WireguardV1_1_36,
pub storage_paths: GatewayPathsV1_1_36,
pub network_requester: NetworkRequesterV1_1_36,
#[serde(default)]
pub ip_packet_router: IpPacketRouterV1_1_36,
#[serde(default)]
pub logging: LoggingSettingsV1_1_36,
#[serde(default)]
pub debug: DebugV1_1_36,
}
impl ConfigV1_1_36 {
pub fn read_from_default_path<P: AsRef<Path>>(id: P) -> io::Result<Self> {
read_config_from_toml_file(default_config_filepath(id))
}
}
impl From<ConfigV1_1_36> for Config {
fn from(value: ConfigV1_1_36) -> Self {
Self {
save_path: value.save_path,
host: value.host,
http: value.http,
gateway: Gateway {
version: value.gateway.version,
id: value.gateway.id,
only_coconut_credentials: value.gateway.only_coconut_credentials,
listening_address: value.gateway.listening_address,
mix_port: value.gateway.mix_port,
clients_port: value.gateway.clients_port,
clients_wss_port: value.gateway.clients_wss_port,
nym_api_urls: value.gateway.nym_api_urls,
nyxd_urls: value.gateway.nyxd_urls,
cosmos_mnemonic: value.gateway.cosmos_mnemonic,
},
storage_paths: GatewayPaths {
keys: KeysPaths {
private_identity_key_file: value.storage_paths.keys.private_identity_key_file,
public_identity_key_file: value.storage_paths.keys.public_identity_key_file,
private_sphinx_key_file: value.storage_paths.keys.private_sphinx_key_file,
public_sphinx_key_file: value.storage_paths.keys.public_sphinx_key_file,
},
clients_storage: value.storage_paths.clients_storage,
network_requester_config: value.storage_paths.network_requester_config,
// \/ ADDED
ip_packet_router_config: Default::default(),
// /\ ADDED
},
network_requester: NetworkRequester {
enabled: value.network_requester.enabled,
},
// \/ ADDED
ip_packet_router: Default::default(),
// /\ ADDED
logging: LoggingSettings {
// no fields (yet)
},
debug: Debug {
packet_forwarding_initial_backoff: value.debug.packet_forwarding_initial_backoff,
packet_forwarding_maximum_backoff: value.debug.packet_forwarding_maximum_backoff,
initial_connection_timeout: value.debug.initial_connection_timeout,
maximum_connection_buffer_size: value.debug.maximum_connection_buffer_size,
presence_sending_delay: value.debug.presence_sending_delay,
stored_messages_filename_length: value.debug.stored_messages_filename_length,
message_retrieval_limit: value.debug.message_retrieval_limit,
use_legacy_framed_packet_version: value.debug.use_legacy_framed_packet_version,
..Default::default()
},
}
}
}
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize)]
pub struct GatewayV1_1_36 {
/// Version of the gateway for which this configuration was created.
pub version: String,
/// ID specifies the human readable ID of this particular gateway.
pub id: String,
/// Indicates whether this gateway is accepting only coconut credentials for accessing the
/// the mixnet, or if it also accepts non-paying clients
#[serde(default)]
pub only_coconut_credentials: bool,
/// Address to which this mixnode will bind to and will be listening for packets.
pub listening_address: IpAddr,
/// Port used for listening for all mixnet traffic.
/// (default: 1789)
pub mix_port: u16,
/// Port used for listening for all client-related traffic.
/// (default: 9000)
pub clients_port: u16,
/// If applicable, announced port for listening for secure websocket client traffic.
/// (default: None)
#[serde(deserialize_with = "de_maybe_port")]
pub clients_wss_port: Option<u16>,
/// Whether gateway collects and sends anonymized statistics
pub enabled_statistics: bool,
/// Domain address of the statistics service
pub statistics_service_url: Url,
/// Addresses to APIs from which the node gets the view of the network.
#[serde(alias = "validator_api_urls")]
pub nym_api_urls: Vec<Url>,
/// Addresses to validators which the node uses to check for double spending of ERC20 tokens.
#[serde(alias = "validator_nymd_urls")]
pub nyxd_urls: Vec<Url>,
/// Mnemonic of a cosmos wallet used in checking for double spending.
// #[deprecated(note = "move to storage")]
// TODO: I don't think this should be stored directly in the config...
pub cosmos_mnemonic: bip39::Mnemonic,
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(default)]
#[serde(deny_unknown_fields)]
pub struct WireguardV1_1_36 {
/// Specifies whether the wireguard service is enabled on this node.
pub enabled: bool,
/// Socket address this node will use for binding its wireguard interface.
/// default: `0.0.0.0:51820`
pub bind_address: SocketAddr,
/// Port announced to external clients wishing to connect to the wireguard interface.
/// Useful in the instances where the node is behind a proxy.
pub announced_port: u16,
/// The prefix denoting the maximum number of the clients that can be connected via Wireguard.
/// The maximum value for IPv4 is 32 and for IPv6 is 128
pub private_network_prefix: u8,
/// Paths for wireguard keys, client registries, etc.
pub storage_paths: WireguardPathsV1_1_36,
}
impl Default for WireguardV1_1_36 {
fn default() -> Self {
Self {
enabled: false,
bind_address: SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), WG_PORT),
announced_port: WG_PORT,
storage_paths: WireguardPathsV1_1_36 {},
private_network_prefix: 16,
}
}
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct WireguardPathsV1_1_36 {
// pub keys:
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct GatewayPathsV1_1_36 {
pub keys: KeysPathsV1_1_36,
/// Path to sqlite database containing all persistent data: messages for offline clients,
/// derived shared keys and available client bandwidths.
#[serde(alias = "persistent_storage")]
pub clients_storage: PathBuf,
/// Path to the configuration of the embedded network requester.
#[serde(deserialize_with = "de_maybe_path")]
pub network_requester_config: Option<PathBuf>,
// pub node_description: PathBuf,
// pub cosmos_bip39_mnemonic: PathBuf,
/// Path to the configuration of the embedded ip packet router.
#[serde(deserialize_with = "de_maybe_path")]
pub ip_packet_router_config: Option<PathBuf>,
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
pub struct KeysPathsV1_1_36 {
/// Path to file containing private identity key.
pub private_identity_key_file: PathBuf,
/// Path to file containing public identity key.
pub public_identity_key_file: PathBuf,
/// Path to file containing private sphinx key.
pub private_sphinx_key_file: PathBuf,
/// Path to file containing public sphinx key.
pub public_sphinx_key_file: PathBuf,
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(default)]
pub struct NetworkRequesterV1_1_36 {
/// Specifies whether network requester service is enabled in this process.
pub enabled: bool,
}
#[allow(clippy::derivable_impls)]
impl Default for NetworkRequesterV1_1_36 {
fn default() -> Self {
Self { enabled: false }
}
}
#[derive(Debug, Default, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct LoggingSettingsV1_1_36 {
// well, we need to implement something here at some point...
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(default)]
pub struct DebugV1_1_36 {
/// Initial value of an exponential backoff to reconnect to dropped TCP connection when
/// forwarding sphinx packets.
#[serde(with = "humantime_serde")]
pub packet_forwarding_initial_backoff: Duration,
/// Maximum value of an exponential backoff to reconnect to dropped TCP connection when
/// forwarding sphinx packets.
#[serde(with = "humantime_serde")]
pub packet_forwarding_maximum_backoff: Duration,
/// Timeout for establishing initial connection when trying to forward a sphinx packet.
#[serde(with = "humantime_serde")]
pub initial_connection_timeout: Duration,
/// Maximum number of packets that can be stored waiting to get sent to a particular connection.
pub maximum_connection_buffer_size: usize,
/// Delay between each subsequent presence data being sent.
#[serde(with = "humantime_serde")]
pub presence_sending_delay: Duration,
/// Length of filenames for new client messages.
pub stored_messages_filename_length: u16,
/// Number of messages from offline client that can be pulled at once from the storage.
pub message_retrieval_limit: i64,
/// Specifies whether the mixnode should be using the legacy framing for the sphinx packets.
// it's set to true by default. The reason for that decision is to preserve compatibility with the
// existing nodes whilst everyone else is upgrading and getting the code for handling the new field.
// It shall be disabled in the subsequent releases.
pub use_legacy_framed_packet_version: bool,
}
impl Default for DebugV1_1_36 {
fn default() -> Self {
Self {
packet_forwarding_initial_backoff: DEFAULT_PACKET_FORWARDING_INITIAL_BACKOFF,
packet_forwarding_maximum_backoff: DEFAULT_PACKET_FORWARDING_MAXIMUM_BACKOFF,
initial_connection_timeout: DEFAULT_INITIAL_CONNECTION_TIMEOUT,
presence_sending_delay: DEFAULT_PRESENCE_SENDING_DELAY,
maximum_connection_buffer_size: DEFAULT_MAXIMUM_CONNECTION_BUFFER_SIZE,
stored_messages_filename_length: DEFAULT_STORED_MESSAGE_FILENAME_LENGTH,
message_retrieval_limit: DEFAULT_MESSAGE_RETRIEVAL_LIMIT,
use_legacy_framed_packet_version: false,
}
}
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(default)]
pub struct IpPacketRouterV1_1_36 {
/// Specifies whether ip packet router service is enabled in this process.
pub enabled: bool,
}
#[allow(clippy::derivable_impls)]
impl Default for IpPacketRouterV1_1_36 {
fn default() -> Self {
Self { enabled: false }
}
}
+4
View File
@@ -27,6 +27,10 @@ struct Cli {
#[clap(short, long)]
pub(crate) config_env_file: Option<std::path::PathBuf>,
/// Force run the binary bypassing the deprecation in favour of nym-node
#[clap(long, hide = true)]
pub(crate) force_run: bool,
/// Flag used for disabling the printed banner in tty.
#[clap(long)]
pub(crate) no_banner: bool,
+4 -1
View File
@@ -257,6 +257,7 @@ impl<St> Gateway<St> {
.ok_or(GatewayError::UnspecifiedAuthenticatorConfig)?;
let (router_tx, mut router_rx) = oneshot::channel();
let (auth_mix_sender, auth_mix_receiver) = mpsc::unbounded();
let (peer_response_tx, peer_response_rx) = tokio::sync::mpsc::unbounded_channel();
let router_shutdown = shutdown.fork("message_router");
let transceiver = LocalGateway::new(
*self.identity_keypair.public_key(),
@@ -269,6 +270,7 @@ impl<St> Gateway<St> {
let mut authenticator_server = nym_authenticator::Authenticator::new(
opts.config.clone(),
wireguard_data.inner.clone(),
peer_response_rx,
)
.with_custom_gateway_transceiver(Box::new(transceiver))
.with_shutdown(shutdown.fork("authenticator"))
@@ -299,7 +301,8 @@ impl<St> Gateway<St> {
MessageRouter::new(auth_mix_receiver, packet_router)
.start_with_shutdown(router_shutdown);
let wg_api = nym_wireguard::start_wireguard(shutdown, wireguard_data).await?;
let wg_api =
nym_wireguard::start_wireguard(shutdown, wireguard_data, peer_response_tx).await?;
Ok(StartedAuthenticator {
wg_api,
+7 -3
View File
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-3.0-only
use crate::Cli;
use anyhow::bail;
use clap::CommandFactory;
use clap::Subcommand;
use colored::Colorize;
@@ -23,7 +24,6 @@ mod init;
mod node_details;
mod run;
mod sign;
mod upgrade_helpers;
#[derive(Subcommand)]
pub(crate) enum Commands {
@@ -67,6 +67,12 @@ struct OverrideConfig {
pub(crate) async fn execute(args: Cli) -> anyhow::Result<()> {
let bin_name = "nym-mixnode";
if !args.force_run {
let msg = "standalone mixnodes have been deprecated - please migrate to a `nym-node` via `nym-node migrate mixnode` command";
error!("{msg}");
bail!("{msg}")
}
warn!("standalone mixnodes have been deprecated - please consider migrating it to a `nym-node` via `nym-node migrate mixnode` command");
if std::io::stdout().is_terminal() {
// if user is running it in terminal session,
@@ -125,8 +131,6 @@ pub(crate) fn validate_bech32_address_or_exit(address: &str) {
}
fn try_load_current_config(id: &str) -> Result<Config, MixnodeError> {
upgrade_helpers::try_upgrade_config(id)?;
Config::read_from_default_path(id).map_err(|err| {
error!(
"Failed to load config for {id}. Are you sure you have run `init` before? (Error was: {err})",
-66
View File
@@ -1,66 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use log::info;
use nym_mixnode::config::old_config_v1_1_21::ConfigV1_1_21;
use nym_mixnode::config::old_config_v1_1_32::ConfigV1_1_32;
use nym_mixnode::config::{default_config_filepath, Config};
use nym_mixnode::error::MixnodeError;
fn try_upgrade_v1_1_21_config(id: &str) -> Result<bool, MixnodeError> {
use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
// explicitly load it as v1.1.21 (which is incompatible with the current, i.e. 1.1.22+)
let Ok(old_config) = ConfigV1_1_21::load_from_file(id) else {
// if we failed to load it, there might have been nothing to upgrade
// or maybe it was an even older file. in either way. just ignore it and carry on with our day
return Ok(false);
};
info!("It seems the mixnode is using <= v1.1.21 config template.");
info!("It is going to get updated to the current specification.");
let updated_step1: ConfigV1_1_32 = old_config.into();
let updated: Config = updated_step1.into();
updated
.save_to_default_location()
.map_err(|err| MixnodeError::ConfigSaveFailure {
path: default_config_filepath(id),
id: id.to_string(),
source: err,
})?;
Ok(true)
}
fn try_upgrade_v1_1_32_config(id: &str) -> Result<bool, MixnodeError> {
// explicitly load it as v1.1.32 (which is incompatible with the current, i.e. 1.1.22+)
let Ok(old_config) = ConfigV1_1_32::read_from_default_path(id) else {
// if we failed to load it, there might have been nothing to upgrade
// or maybe it was an even older file. in either way. just ignore it and carry on with our day
return Ok(false);
};
info!("It seems the mixnode is using <= v1.1.32 config template.");
info!("It is going to get updated to the current specification.");
let updated: Config = old_config.into();
updated
.save_to_default_location()
.map_err(|err| MixnodeError::ConfigSaveFailure {
path: default_config_filepath(id),
id: id.to_string(),
source: err,
})?;
Ok(true)
}
pub(crate) fn try_upgrade_config(id: &str) -> Result<(), MixnodeError> {
if try_upgrade_v1_1_21_config(id)? {
return Ok(());
}
if try_upgrade_v1_1_32_config(id)? {
return Ok(());
}
Ok(())
}
-2
View File
@@ -22,8 +22,6 @@ use std::str::FromStr;
use std::time::Duration;
use url::Url;
pub mod old_config_v1_1_21;
pub mod old_config_v1_1_32;
pub mod persistence;
mod template;
-251
View File
@@ -1,251 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::config::old_config_v1_1_32::{
ConfigV1_1_32, DebugV1_1_32, MixNodeV1_1_32, VerlocV1_1_32,
};
use crate::config::persistence::paths::{KeysPaths, MixNodePaths};
use nym_bin_common::logging::LoggingSettings;
use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
use nym_validator_client::nyxd;
use serde::{Deserialize, Deserializer, Serialize};
use std::net::{IpAddr, SocketAddr};
use std::path::PathBuf;
use std::str::FromStr;
use std::time::Duration;
use url::Url;
const DEFAULT_MIX_LISTENING_PORT: u16 = 1789;
const DEFAULT_VERLOC_LISTENING_PORT: u16 = 1790;
const DEFAULT_HTTP_API_LISTENING_PORT: u16 = 8000;
const NYM_API: &str = "https://validator.nymtech.net/api/";
const DESCRIPTION_FILE: &str = "description.toml";
// 'RTT MEASUREMENT'
const DEFAULT_PACKETS_PER_NODE: usize = 100;
const DEFAULT_CONNECTION_TIMEOUT: Duration = Duration::from_millis(5000);
const DEFAULT_PACKET_TIMEOUT: Duration = Duration::from_millis(1500);
const DEFAULT_DELAY_BETWEEN_PACKETS: Duration = Duration::from_millis(50);
const DEFAULT_BATCH_SIZE: usize = 50;
const DEFAULT_TESTING_INTERVAL: Duration = Duration::from_secs(60 * 60 * 12);
const DEFAULT_RETRY_TIMEOUT: Duration = Duration::from_secs(60 * 30);
// 'DEBUG'
const DEFAULT_NODE_STATS_LOGGING_DELAY: Duration = Duration::from_millis(60_000);
const DEFAULT_NODE_STATS_UPDATING_DELAY: Duration = Duration::from_millis(30_000);
const DEFAULT_PACKET_FORWARDING_INITIAL_BACKOFF: Duration = Duration::from_millis(10_000);
const DEFAULT_PACKET_FORWARDING_MAXIMUM_BACKOFF: Duration = Duration::from_millis(300_000);
const DEFAULT_INITIAL_CONNECTION_TIMEOUT: Duration = Duration::from_millis(1_500);
const DEFAULT_MAXIMUM_CONNECTION_BUFFER_SIZE: usize = 2000;
pub(super) fn de_ipaddr_from_maybe_str_socks_addr<'de, D>(
deserializer: D,
) -> Result<IpAddr, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
if let Ok(socket_addr) = SocketAddr::from_str(&s) {
Ok(socket_addr.ip())
} else {
IpAddr::from_str(&s).map_err(serde::de::Error::custom)
}
}
fn bind_all_address() -> IpAddr {
"0.0.0.0".parse().unwrap()
}
#[derive(Debug, Default, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ConfigV1_1_21 {
mixnode: MixNodeV1_1_21,
#[serde(default)]
verloc: VerlocV1_1_21,
#[serde(default)]
logging: LoggingV1_1_21,
#[serde(default)]
debug: DebugV1_1_21,
}
impl From<ConfigV1_1_21> for ConfigV1_1_32 {
fn from(value: ConfigV1_1_21) -> Self {
let node_description =
ConfigV1_1_21::default_config_directory(&value.mixnode.id).join(DESCRIPTION_FILE);
ConfigV1_1_32 {
mixnode: MixNodeV1_1_32 {
version: value.mixnode.version,
id: value.mixnode.id,
listening_address: value.mixnode.listening_address,
mix_port: value.mixnode.mix_port,
verloc_port: value.mixnode.verloc_port,
http_api_port: value.mixnode.http_api_port,
nym_api_urls: value.mixnode.nym_api_urls,
},
storage_paths: MixNodePaths {
keys: KeysPaths {
private_identity_key_file: value.mixnode.private_identity_key_file,
public_identity_key_file: value.mixnode.public_identity_key_file,
private_sphinx_key_file: value.mixnode.private_sphinx_key_file,
public_sphinx_key_file: value.mixnode.public_sphinx_key_file,
},
node_description,
},
verloc: value.verloc.into(),
logging: value.logging.into(),
debug: value.debug.into(),
}
}
}
impl MigrationNymConfig for ConfigV1_1_21 {
fn default_root_directory() -> PathBuf {
dirs::home_dir()
.expect("Failed to evaluate $HOME value")
.join(".nym")
.join("mixnodes")
}
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
struct MixNodeV1_1_21 {
version: String,
id: String,
#[serde(deserialize_with = "de_ipaddr_from_maybe_str_socks_addr")]
listening_address: IpAddr,
announce_address: String,
mix_port: u16,
verloc_port: u16,
http_api_port: u16,
private_identity_key_file: PathBuf,
public_identity_key_file: PathBuf,
private_sphinx_key_file: PathBuf,
public_sphinx_key_file: PathBuf,
nym_api_urls: Vec<Url>,
nym_root_directory: PathBuf,
wallet_address: Option<nyxd::AccountId>,
}
impl Default for MixNodeV1_1_21 {
fn default() -> Self {
MixNodeV1_1_21 {
version: env!("CARGO_PKG_VERSION").to_string(),
id: "".to_string(),
listening_address: bind_all_address(),
announce_address: "127.0.0.1".to_string(),
mix_port: DEFAULT_MIX_LISTENING_PORT,
verloc_port: DEFAULT_VERLOC_LISTENING_PORT,
http_api_port: DEFAULT_HTTP_API_LISTENING_PORT,
private_identity_key_file: Default::default(),
public_identity_key_file: Default::default(),
private_sphinx_key_file: Default::default(),
public_sphinx_key_file: Default::default(),
nym_api_urls: vec![Url::from_str(NYM_API).expect("Invalid default API URL")],
nym_root_directory: ConfigV1_1_21::default_root_directory(),
wallet_address: None,
}
}
}
#[derive(Debug, Default, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
struct LoggingV1_1_21 {}
impl From<LoggingV1_1_21> for LoggingSettings {
fn from(_value: LoggingV1_1_21) -> Self {
LoggingSettings {}
}
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
struct VerlocV1_1_21 {
packets_per_node: usize,
connection_timeout: Duration,
packet_timeout: Duration,
delay_between_packets: Duration,
tested_nodes_batch_size: usize,
testing_interval: Duration,
retry_timeout: Duration,
}
impl From<VerlocV1_1_21> for VerlocV1_1_32 {
fn from(value: VerlocV1_1_21) -> Self {
VerlocV1_1_32 {
packets_per_node: value.packets_per_node,
connection_timeout: value.connection_timeout,
packet_timeout: value.packet_timeout,
delay_between_packets: value.delay_between_packets,
tested_nodes_batch_size: value.tested_nodes_batch_size,
testing_interval: value.testing_interval,
retry_timeout: value.retry_timeout,
}
}
}
impl Default for VerlocV1_1_21 {
fn default() -> Self {
VerlocV1_1_21 {
packets_per_node: DEFAULT_PACKETS_PER_NODE,
connection_timeout: DEFAULT_CONNECTION_TIMEOUT,
packet_timeout: DEFAULT_PACKET_TIMEOUT,
delay_between_packets: DEFAULT_DELAY_BETWEEN_PACKETS,
tested_nodes_batch_size: DEFAULT_BATCH_SIZE,
testing_interval: DEFAULT_TESTING_INTERVAL,
retry_timeout: DEFAULT_RETRY_TIMEOUT,
}
}
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(default)]
struct DebugV1_1_21 {
#[serde(with = "humantime_serde")]
node_stats_logging_delay: Duration,
#[serde(with = "humantime_serde")]
node_stats_updating_delay: Duration,
#[serde(with = "humantime_serde")]
packet_forwarding_initial_backoff: Duration,
#[serde(with = "humantime_serde")]
packet_forwarding_maximum_backoff: Duration,
#[serde(with = "humantime_serde")]
initial_connection_timeout: Duration,
maximum_connection_buffer_size: usize,
use_legacy_framed_packet_version: bool,
}
impl From<DebugV1_1_21> for DebugV1_1_32 {
fn from(value: DebugV1_1_21) -> Self {
DebugV1_1_32 {
node_stats_logging_delay: value.node_stats_logging_delay,
node_stats_updating_delay: value.node_stats_updating_delay,
packet_forwarding_initial_backoff: value.packet_forwarding_initial_backoff,
packet_forwarding_maximum_backoff: value.packet_forwarding_maximum_backoff,
initial_connection_timeout: value.initial_connection_timeout,
maximum_connection_buffer_size: value.maximum_connection_buffer_size,
use_legacy_framed_packet_version: value.use_legacy_framed_packet_version,
}
}
}
impl Default for DebugV1_1_21 {
fn default() -> Self {
DebugV1_1_21 {
node_stats_logging_delay: DEFAULT_NODE_STATS_LOGGING_DELAY,
node_stats_updating_delay: DEFAULT_NODE_STATS_UPDATING_DELAY,
packet_forwarding_initial_backoff: DEFAULT_PACKET_FORWARDING_INITIAL_BACKOFF,
packet_forwarding_maximum_backoff: DEFAULT_PACKET_FORWARDING_MAXIMUM_BACKOFF,
initial_connection_timeout: DEFAULT_INITIAL_CONNECTION_TIMEOUT,
maximum_connection_buffer_size: DEFAULT_MAXIMUM_CONNECTION_BUFFER_SIZE,
use_legacy_framed_packet_version: true,
}
}
}
-265
View File
@@ -1,265 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::config;
use crate::config::persistence::paths::MixNodePaths;
use crate::config::{Config, Debug, MixNode, Verloc};
use nym_bin_common::logging::LoggingSettings;
use nym_config::{
must_get_home, read_config_from_toml_file, DEFAULT_CONFIG_DIR, DEFAULT_CONFIG_FILENAME, NYM_DIR,
};
use serde::{Deserialize, Serialize};
use std::io;
use std::net::{IpAddr, SocketAddr};
use std::path::{Path, PathBuf};
use std::time::Duration;
use url::Url;
const DEFAULT_MIXNODES_DIR: &str = "mixnodes";
// 'RTT MEASUREMENT'
const DEFAULT_PACKETS_PER_NODE: usize = 100;
const DEFAULT_CONNECTION_TIMEOUT: Duration = Duration::from_millis(5000);
const DEFAULT_PACKET_TIMEOUT: Duration = Duration::from_millis(1500);
const DEFAULT_DELAY_BETWEEN_PACKETS: Duration = Duration::from_millis(50);
const DEFAULT_BATCH_SIZE: usize = 50;
const DEFAULT_TESTING_INTERVAL: Duration = Duration::from_secs(60 * 60 * 12);
const DEFAULT_RETRY_TIMEOUT: Duration = Duration::from_secs(60 * 30);
// 'DEBUG'
const DEFAULT_NODE_STATS_LOGGING_DELAY: Duration = Duration::from_millis(60_000);
const DEFAULT_NODE_STATS_UPDATING_DELAY: Duration = Duration::from_millis(30_000);
const DEFAULT_PACKET_FORWARDING_INITIAL_BACKOFF: Duration = Duration::from_millis(10_000);
const DEFAULT_PACKET_FORWARDING_MAXIMUM_BACKOFF: Duration = Duration::from_millis(300_000);
const DEFAULT_INITIAL_CONNECTION_TIMEOUT: Duration = Duration::from_millis(1_500);
const DEFAULT_MAXIMUM_CONNECTION_BUFFER_SIZE: usize = 2000;
/// Derive default path to mixnodes's config directory.
/// It should get resolved to `$HOME/.nym/mixnodes/<id>/config`
fn default_config_directory<P: AsRef<Path>>(id: P) -> PathBuf {
must_get_home()
.join(NYM_DIR)
.join(DEFAULT_MIXNODES_DIR)
.join(id)
.join(DEFAULT_CONFIG_DIR)
}
/// Derive default path to mixnodes's config file.
/// It should get resolved to `$HOME/.nym/mixnodes/<id>/config/config.toml`
fn default_config_filepath<P: AsRef<Path>>(id: P) -> PathBuf {
default_config_directory(id).join(DEFAULT_CONFIG_FILENAME)
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ConfigV1_1_32 {
pub mixnode: MixNodeV1_1_32,
// i hope this laziness is not going to backfire...
pub storage_paths: MixNodePaths,
#[serde(default)]
pub verloc: VerlocV1_1_32,
#[serde(default)]
pub logging: LoggingSettings,
#[serde(default)]
pub debug: DebugV1_1_32,
}
impl ConfigV1_1_32 {
pub fn read_from_default_path<P: AsRef<Path>>(id: P) -> io::Result<Self> {
read_config_from_toml_file(default_config_filepath(id))
}
}
impl From<ConfigV1_1_32> for Config {
fn from(value: ConfigV1_1_32) -> Self {
Config {
// \/ ADDED
save_path: None,
// /\ ADDED
// \/ ADDED
host: config::Host {
// this is a very bad default!
public_ips: vec![value.mixnode.listening_address],
hostname: None,
},
// /\ ADDED
// \/ ADDED
http: config::Http {
bind_address: SocketAddr::new(
value.mixnode.listening_address,
value.mixnode.http_api_port,
),
landing_page_assets_path: None,
metrics_key: None,
},
// /\ ADDED
mixnode: MixNode {
version: value.mixnode.version,
id: value.mixnode.id,
listening_address: value.mixnode.listening_address,
mix_port: value.mixnode.mix_port,
verloc_port: value.mixnode.verloc_port,
nym_api_urls: value.mixnode.nym_api_urls,
},
storage_paths: value.storage_paths,
verloc: value.verloc.into(),
logging: value.logging,
debug: value.debug.into(),
}
}
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
pub struct MixNodeV1_1_32 {
/// Version of the mixnode for which this configuration was created.
pub version: String,
/// ID specifies the human readable ID of this particular mixnode.
pub id: String,
/// Address to which this mixnode will bind to and will be listening for packets.
pub listening_address: IpAddr,
/// Port used for listening for all mixnet traffic.
/// (default: 1789)
pub mix_port: u16,
/// Port used for listening for verloc traffic.
/// (default: 1790)
pub verloc_port: u16,
/// Port used for listening for http requests.
/// (default: 8000)
pub http_api_port: u16,
/// Addresses to nym APIs from which the node gets the view of the network.
pub nym_api_urls: Vec<Url>,
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct VerlocV1_1_32 {
/// Specifies number of echo packets sent to each node during a measurement run.
pub packets_per_node: usize,
/// Specifies maximum amount of time to wait for the connection to get established.
#[serde(with = "humantime_serde")]
pub connection_timeout: Duration,
/// Specifies maximum amount of time to wait for the reply packet to arrive before abandoning the test.
#[serde(with = "humantime_serde")]
pub packet_timeout: Duration,
/// Specifies delay between subsequent test packets being sent (after receiving a reply).
#[serde(with = "humantime_serde")]
pub delay_between_packets: Duration,
/// Specifies number of nodes being tested at once.
pub tested_nodes_batch_size: usize,
/// Specifies delay between subsequent test runs.
#[serde(with = "humantime_serde")]
pub testing_interval: Duration,
/// Specifies delay between attempting to run the measurement again if the previous run failed
/// due to being unable to get the list of nodes.
#[serde(with = "humantime_serde")]
pub retry_timeout: Duration,
}
impl From<VerlocV1_1_32> for Verloc {
fn from(value: VerlocV1_1_32) -> Self {
Verloc {
packets_per_node: value.packets_per_node,
connection_timeout: value.connection_timeout,
packet_timeout: value.packet_timeout,
delay_between_packets: value.delay_between_packets,
tested_nodes_batch_size: value.tested_nodes_batch_size,
testing_interval: value.testing_interval,
retry_timeout: value.retry_timeout,
}
}
}
impl Default for VerlocV1_1_32 {
fn default() -> Self {
VerlocV1_1_32 {
packets_per_node: DEFAULT_PACKETS_PER_NODE,
connection_timeout: DEFAULT_CONNECTION_TIMEOUT,
packet_timeout: DEFAULT_PACKET_TIMEOUT,
delay_between_packets: DEFAULT_DELAY_BETWEEN_PACKETS,
tested_nodes_batch_size: DEFAULT_BATCH_SIZE,
testing_interval: DEFAULT_TESTING_INTERVAL,
retry_timeout: DEFAULT_RETRY_TIMEOUT,
}
}
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(default)]
pub struct DebugV1_1_32 {
/// Delay between each subsequent node statistics being logged to the console
#[serde(with = "humantime_serde")]
pub node_stats_logging_delay: Duration,
/// Delay between each subsequent node statistics being updated
#[serde(with = "humantime_serde")]
pub node_stats_updating_delay: Duration,
/// Initial value of an exponential backoff to reconnect to dropped TCP connection when
/// forwarding sphinx packets.
#[serde(with = "humantime_serde")]
pub packet_forwarding_initial_backoff: Duration,
/// Maximum value of an exponential backoff to reconnect to dropped TCP connection when
/// forwarding sphinx packets.
#[serde(with = "humantime_serde")]
pub packet_forwarding_maximum_backoff: Duration,
/// Timeout for establishing initial connection when trying to forward a sphinx packet.
#[serde(with = "humantime_serde")]
pub initial_connection_timeout: Duration,
/// Maximum number of packets that can be stored waiting to get sent to a particular connection.
pub maximum_connection_buffer_size: usize,
/// Specifies whether the mixnode should be using the legacy framing for the sphinx packets.
// it's set to true by default. The reason for that decision is to preserve compatibility with the
// existing nodes whilst everyone else is upgrading and getting the code for handling the new field.
// It shall be disabled in the subsequent releases.
pub use_legacy_framed_packet_version: bool,
}
impl From<DebugV1_1_32> for Debug {
fn from(value: DebugV1_1_32) -> Self {
Debug {
node_stats_logging_delay: value.node_stats_logging_delay,
node_stats_updating_delay: value.node_stats_updating_delay,
packet_forwarding_initial_backoff: value.packet_forwarding_initial_backoff,
packet_forwarding_maximum_backoff: value.packet_forwarding_maximum_backoff,
initial_connection_timeout: value.initial_connection_timeout,
maximum_connection_buffer_size: value.maximum_connection_buffer_size,
use_legacy_framed_packet_version: value.use_legacy_framed_packet_version,
}
}
}
impl Default for DebugV1_1_32 {
fn default() -> Self {
DebugV1_1_32 {
node_stats_logging_delay: DEFAULT_NODE_STATS_LOGGING_DELAY,
node_stats_updating_delay: DEFAULT_NODE_STATS_UPDATING_DELAY,
packet_forwarding_initial_backoff: DEFAULT_PACKET_FORWARDING_INITIAL_BACKOFF,
packet_forwarding_maximum_backoff: DEFAULT_PACKET_FORWARDING_MAXIMUM_BACKOFF,
initial_connection_timeout: DEFAULT_INITIAL_CONNECTION_TIMEOUT,
maximum_connection_buffer_size: DEFAULT_MAXIMUM_CONNECTION_BUFFER_SIZE,
use_legacy_framed_packet_version: false,
}
}
}
+4
View File
@@ -23,6 +23,10 @@ struct Cli {
#[clap(short, long)]
pub(crate) config_env_file: Option<std::path::PathBuf>,
/// Force run the binary bypassing the deprecation in favour of nym-node
#[clap(long, hide = true)]
pub(crate) force_run: bool,
/// Flag used for disabling the printed banner in tty.
#[clap(long)]
pub(crate) no_banner: bool,
+1 -1
View File
@@ -4,7 +4,7 @@
[package]
name = "nym-api"
license = "GPL-3.0"
version = "1.1.40"
version = "1.1.42"
authors = [
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
"Jędrzej Stuczyński <andrew@nymtech.net>",
+6
View File
@@ -612,9 +612,15 @@ pub struct NymNodeDescription {
// for now we only care about their ws/wss situation, nothing more
pub mixnet_websockets: WebSockets,
#[serde(default = "default_node_role")]
pub role: NodeRole,
}
// For backwards compatibility, set a slightly artificial default
fn default_node_role() -> NodeRole {
NodeRole::Inactive
}
#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema)]
pub struct DescribedGateway {
pub bond: GatewayBond,
+1
View File
@@ -20,6 +20,7 @@ impl<R: RngCore + CryptoRng> DkgController<R> {
/// - BTE public key (alongside the proof of discrete log)
/// - ed25519 public key
/// - announce address to be used by clients for obtaining credentials
///
/// Upon successful registration, the node will receive a unique "NodeIndex"
/// which is the x-coordinate of the to be derived keys.
///
+1 -8
View File
@@ -4,7 +4,6 @@
use crate::coconut::storage::models::{EpochCredentials, IssuedCredential};
use crate::support::storage::manager::StorageManager;
use nym_coconut_dkg_common::types::EpochId;
use thiserror::Error;
#[async_trait]
pub trait CoconutStorageManagerExt {
@@ -23,6 +22,7 @@ pub trait CoconutStorageManagerExt {
/// # Arguments
///
/// * `epoch_id`: Id of the (coconut) epoch in question.
#[allow(dead_code)]
async fn create_epoch_credentials_entry(&self, epoch_id: EpochId) -> Result<(), sqlx::Error>;
/// Update the EpochCredentials by incrementing the total number of issued credentials,
@@ -361,10 +361,3 @@ impl CoconutStorageManagerExt for StorageManager {
Ok(())
}
}
#[derive(Debug, Error)]
#[error("tried to store an invalid nonce. the received value is {got} while current is {current}. expected {current} + 1")]
pub struct UnexpectedNonce {
current: u32,
got: u32,
}
+3 -3
View File
@@ -33,12 +33,12 @@ pub fn pseudorandom_account(rng: &mut ChaCha20Rng) -> AccountId {
AccountId::new("n", &dummy_account_key_hash).unwrap()
}
pub fn dealer_fixture(mut rng: &mut ChaCha20Rng, id: NodeIndex) -> DealerDetails {
pub fn dealer_fixture(rng: &mut ChaCha20Rng, id: NodeIndex) -> DealerDetails {
// we might possibly need that private key later on
let keypair = DkgKeyPair::new(dkg::params(), &mut rng);
let keypair = DkgKeyPair::new(dkg::params(), rng.clone());
let addr = pseudorandom_account(rng);
let identity_keypair = identity::KeyPair::new(&mut rng);
let identity_keypair = identity::KeyPair::new(rng);
let bte_public_key_with_proof = bs58::encode(&keypair.public_key().to_bytes()).into_string();
let port = 8080 + id;
+7 -7
View File
@@ -63,15 +63,15 @@ impl RewardedSetUpdater {
/// 2. it queries the mixnet contract to check the current `EpochState` in order to figure out whether
/// a different nym-api has already started epoch transition (not yet applicable)
/// 3. it sends a `BeginEpochTransition` message to the mixnet contract causing the following to happen:
/// - if successful, the address of the this validator is going to be saved as being responsible for progressing this epoch.
/// What it means in practice is that once we have multiple instances of nym-api running,
/// only this one will try to perform the rest of the actions. It will also allow it to
/// more easily recover in case of crashes.
/// - if successful, the address of this validator is going to be saved as being responsible for progressing this epoch.
/// What it means in practice is that once we have multiple instances of nym-api running,
/// only this one will try to perform the rest of the actions. It will also allow it to
/// more easily recover in case of crashes.
/// - the `EpochState` changes to `Rewarding`, meaning the nym-api will now be allowed to send
/// `RewardMixnode` transactions. However, it's not going to be able anything else like `ReconcileEpochEvents`
/// until that is done.
/// `RewardMixnode` transactions. However, it's not going to be able anything else like `ReconcileEpochEvents`
/// until that is done.
/// - ability to send transactions (by other users) that get resolved once given epoch/interval rolls over,
/// such as `BondMixnode` or `DelegateToMixnode` will temporarily be frozen until the entire procedure is finished.
/// such as `BondMixnode` or `DelegateToMixnode` will temporarily be frozen until the entire procedure is finished.
/// 4. it obtains the current rewarded set and for each node in there (**SORTED BY MIX_ID!!**),
/// it sends (in a single batch) `RewardMixnode` message with the measured performance.
/// Once the final message gets executed, the mixnet contract automatically transitions
+1 -1
View File
@@ -3,7 +3,7 @@
[package]
name = "nym-node"
version = "1.1.4"
version = "1.1.6"
authors.workspace = true
repository.workspace = true
homepage.workspace = true
@@ -15,7 +15,9 @@ use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct WireguardPathsV1 {
#[serde(skip)]
pub private_diffie_hellman_key_file: PathBuf,
#[serde(skip)]
pub public_diffie_hellman_key_file: PathBuf,
}
+2 -2
View File
@@ -32,7 +32,7 @@ use nym_node_http_api::{NymNodeHTTPServer, NymNodeRouter};
use nym_sphinx_acknowledgements::AckKey;
use nym_sphinx_addressing::Recipient;
use nym_task::{TaskClient, TaskManager};
use nym_wireguard::{peer_controller::PeerControlMessage, WireguardGatewayData};
use nym_wireguard::{peer_controller::PeerControlRequest, WireguardGatewayData};
use rand::rngs::OsRng;
use rand::{CryptoRng, RngCore};
use std::path::Path;
@@ -264,7 +264,7 @@ impl ExitGatewayData {
pub struct WireguardData {
inner: WireguardGatewayData,
peer_rx: UnboundedReceiver<PeerControlMessage>,
peer_rx: UnboundedReceiver<PeerControlRequest>,
}
impl WireguardData {
-2
View File
@@ -507,7 +507,6 @@ struct NetworkDetails {
stake_denom: DenomDetailsOwned,
mixnet_contract_address: String,
vesting_contract_address: String,
statistics_service_url: String,
validators: Vec<ValidatorDetails>,
}
@@ -527,7 +526,6 @@ impl From<NymNetworkDetails> for NetworkDetails {
.contracts
.vesting_contract_address
.unwrap_or_default(),
statistics_service_url: "".to_string(),
validators: details.endpoints,
}
}
+1
View File
@@ -272,6 +272,7 @@ impl WalletStateInner {
/// 1. from the configuration file
/// 2. provided remotely
/// 3. hardcoded fallback
///
/// The format is the config backend format, which is flat due to serialization preference.
pub fn get_config_validator_entries(
&self,
@@ -7,8 +7,8 @@
///
/// Wallet
/// - Login
/// -- Account
/// --- Mnemonic
/// -- Account
/// --- Mnemonic
pub(crate) use crate::wallet_storage::account_data::StoredLogin;
pub(crate) use crate::wallet_storage::password::{AccountId, LoginId, UserPassword};
+1
View File
@@ -67,6 +67,7 @@ pub use nym_credential_storage::{
ephemeral_storage::EphemeralStorage as EphemeralCredentialStorage,
models::StoredIssuedCredential, storage::Storage as CredentialStorage,
};
pub use nym_crypto::asymmetric::ed25519;
pub use nym_network_defaults::NymNetworkDetails;
pub use nym_socks5_client_core::config::Socks5;
pub use nym_sphinx::{
+2 -4
View File
@@ -610,8 +610,7 @@ where
///
/// - If the client is already registered with a gateway, use that gateway.
/// - If no gateway is registered, but there is an existing configuration and key, use that.
/// - If no gateway is registered, and there is no pre-existing configuration or key, try to
/// register a new gateway.
/// - If no gateway is registered, and there is no pre-existing configuration or key, try to register a new gateway.
///
/// # Example
///
@@ -691,8 +690,7 @@ where
///
/// - If the client is already registered with a gateway, use that gateway.
/// - If no gateway is registered, but there is an existing configuration and key, use that.
/// - If no gateway is registered, and there is no pre-existing configuration or key, try to
/// register a new gateway.
/// - If no gateway is registered, and there is no pre-existing configuration or key, try to register a new gateway.
///
/// # Example
///
@@ -8,7 +8,8 @@ use ipnetwork::IpNetwork;
use nym_client_core::{HardcodedTopologyProvider, TopologyProvider};
use nym_sdk::{mixnet::Recipient, GatewayTransceiver};
use nym_task::{TaskClient, TaskHandle};
use nym_wireguard::WireguardGatewayData;
use nym_wireguard::{peer_controller::PeerControlResponse, WireguardGatewayData};
use tokio::sync::mpsc::UnboundedReceiver;
use crate::{config::Config, error::AuthenticatorError};
@@ -30,18 +31,24 @@ pub struct Authenticator {
custom_topology_provider: Option<Box<dyn TopologyProvider + Send + Sync>>,
custom_gateway_transceiver: Option<Box<dyn GatewayTransceiver + Send + Sync>>,
wireguard_gateway_data: WireguardGatewayData,
response_rx: UnboundedReceiver<PeerControlResponse>,
shutdown: Option<TaskClient>,
on_start: Option<oneshot::Sender<OnStartData>>,
}
impl Authenticator {
pub fn new(config: Config, wireguard_gateway_data: WireguardGatewayData) -> Self {
pub fn new(
config: Config,
wireguard_gateway_data: WireguardGatewayData,
response_rx: UnboundedReceiver<PeerControlResponse>,
) -> Self {
Self {
config,
wait_for_gateway: false,
custom_topology_provider: None,
custom_gateway_transceiver: None,
wireguard_gateway_data,
response_rx,
shutdown: None,
on_start: None,
}
@@ -129,6 +136,7 @@ impl Authenticator {
self.config,
private_ip_network,
self.wireguard_gateway_data,
self.response_rx,
mixnet_client,
task_handle,
);
@@ -2,21 +2,24 @@
// SPDX-License-Identifier: Apache-2.0
use nym_sdk::TaskClient;
use nym_wireguard::peer_controller::PeerControlMessage;
use nym_wireguard::peer_controller::{PeerControlRequest, PeerControlResponse};
use tokio::sync::mpsc;
pub struct DummyHandler {
peer_rx: mpsc::UnboundedReceiver<PeerControlMessage>,
peer_rx: mpsc::UnboundedReceiver<PeerControlRequest>,
response_tx: mpsc::UnboundedSender<PeerControlResponse>,
task_client: TaskClient,
}
impl DummyHandler {
pub fn new(
peer_rx: mpsc::UnboundedReceiver<PeerControlMessage>,
peer_rx: mpsc::UnboundedReceiver<PeerControlRequest>,
response_tx: mpsc::UnboundedSender<PeerControlResponse>,
task_client: TaskClient,
) -> Self {
DummyHandler {
peer_rx,
response_tx,
task_client,
}
}
@@ -27,11 +30,17 @@ impl DummyHandler {
msg = self.peer_rx.recv() => {
if let Some(msg) = msg {
match msg {
PeerControlMessage::AddPeer(peer) => {
PeerControlRequest::AddPeer(peer) => {
log::info!("[DUMMY] Adding peer {:?}", peer);
self.response_tx.send(PeerControlResponse::AddPeer { success: true }).ok();
}
PeerControlMessage::RemovePeer(key) => {
PeerControlRequest::RemovePeer(key) => {
log::info!("[DUMMY] Removing peer {:?}", key);
self.response_tx.send(PeerControlResponse::RemovePeer { success: true }).ok();
}
PeerControlRequest::QueryBandwidth(key) => {
log::info!("[DUMMY] Querying bandwidth for peer {:?}", key);
self.response_tx.send(PeerControlResponse::QueryBandwidth { bandwidth_data: None }).ok();
}
}
} else {
@@ -14,6 +14,7 @@ use nym_crypto::asymmetric::x25519::KeyPair;
use nym_task::TaskHandle;
use nym_wireguard::WireguardGatewayData;
use rand::rngs::OsRng;
use tokio::sync::mpsc::unbounded_channel;
#[allow(clippy::struct_excessive_bools)]
#[derive(Args, Clone)]
@@ -48,11 +49,13 @@ pub(crate) async fn execute(args: &Run) -> Result<(), AuthenticatorError> {
Arc::new(KeyPair::new(&mut OsRng)),
);
let task_handler = TaskHandle::default();
let handler = DummyHandler::new(peer_rx, task_handler.fork("peer-handler"));
let (response_tx, response_rx) = unbounded_channel();
let handler = DummyHandler::new(peer_rx, response_tx, task_handler.fork("peer-handler"));
tokio::spawn(async move {
handler.run().await;
});
let mut server = nym_authenticator::Authenticator::new(config, wireguard_gateway_data);
let mut server =
nym_authenticator::Authenticator::new(config, wireguard_gateway_data, response_rx);
if let Some(custom_mixnet) = &args.common_args.custom_mixnet {
server = server.with_stored_topology(custom_mixnet)?
}
@@ -17,12 +17,13 @@ use nym_authenticator_requests::v1::{
use nym_sdk::mixnet::{InputMessage, MixnetMessageSender, Recipient, TransmissionLane};
use nym_sphinx::receiver::ReconstructedMessage;
use nym_task::TaskHandle;
use nym_wireguard::WireguardGatewayData;
use nym_wireguard::{peer_controller::PeerControlResponse, WireguardGatewayData};
use nym_wireguard_types::{
registration::{PendingRegistrations, PrivateIPs, RegistrationData},
registration::{PendingRegistrations, PrivateIPs, RegistrationData, RegistredData},
GatewayClient, InitMessage, PeerPublicKey,
};
use rand::{prelude::IteratorRandom, thread_rng};
use tokio::sync::mpsc::UnboundedReceiver;
use tokio_stream::wrappers::IntervalStream;
use crate::{config::Config, error::*};
@@ -45,6 +46,8 @@ pub(crate) struct MixnetListener {
pub(crate) wireguard_gateway_data: WireguardGatewayData,
pub(crate) response_rx: UnboundedReceiver<PeerControlResponse>,
pub(crate) free_private_network_ips: Arc<PrivateIPs>,
pub(crate) timeout_check_interval: IntervalStream,
@@ -55,6 +58,7 @@ impl MixnetListener {
config: Config,
private_ip_network: IpNetwork,
wireguard_gateway_data: WireguardGatewayData,
response_rx: UnboundedReceiver<PeerControlResponse>,
mixnet_client: nym_sdk::mixnet::MixnetClient,
task_handle: TaskHandle,
) -> Self {
@@ -66,6 +70,7 @@ impl MixnetListener {
task_handle,
registration_in_progres: Default::default(),
wireguard_gateway_data,
response_rx,
free_private_network_ips: Arc::new(
private_ip_network.iter().map(|ip| (ip, None)).collect(),
),
@@ -73,22 +78,6 @@ impl MixnetListener {
}
}
fn remove_from_registry(
&self,
remote_public: &PeerPublicKey,
gateway_client: &GatewayClient,
) -> Result<()> {
self.wireguard_gateway_data
.remove_peer(gateway_client)
.map_err(|err| {
AuthenticatorError::InternalError(format!("could not remove peer: {:?}", err))
})?;
self.wireguard_gateway_data
.client_registry()
.remove(remote_public);
Ok(())
}
fn remove_stale_registrations(&self) -> Result<()> {
for reg in self.registration_in_progres.iter().map(|reg| reg.clone()) {
let mut ip = self
@@ -121,7 +110,7 @@ impl MixnetListener {
Ok(())
}
fn on_initial_request(
async fn on_initial_request(
&mut self,
init_message: InitMessage,
request_id: u64,
@@ -136,24 +125,26 @@ impl MixnetListener {
reply_to,
));
}
let gateway_client_opt = if let Some(gateway_client) = self
if let Some(gateway_client) = self
.wireguard_gateway_data
.client_registry()
.get(&remote_public)
{
let mut private_ip_ref = self
.free_private_network_ips
.get_mut(&gateway_client.private_ip)
.ok_or(AuthenticatorError::InternalError(String::from(
"could not find private IP",
)))?;
*private_ip_ref = None;
Some(gateway_client.clone())
} else {
None
};
if let Some(gateway_client) = gateway_client_opt {
self.remove_from_registry(&remote_public, &gateway_client)?;
return Ok(AuthenticatorResponse::new_registered(
RegistredData {
pub_key: PeerPublicKey::new(
self.wireguard_gateway_data
.keypair()
.public_key()
.to_bytes()
.into(),
),
private_ip: gateway_client.private_ip,
wg_port: self.config.authenticator.announced_port,
},
reply_to,
request_id,
));
}
let mut private_ip_ref = self
.free_private_network_ips
@@ -184,7 +175,7 @@ impl MixnetListener {
))
}
fn on_final_request(
async fn on_final_request(
&mut self,
gateway_client: GatewayClient,
request_id: u64,
@@ -209,18 +200,77 @@ impl MixnetListener {
.map_err(|err| {
AuthenticatorError::InternalError(format!("could not add peer: {:?}", err))
})?;
let PeerControlResponse::AddPeer { success } =
self.response_rx
.recv()
.await
.ok_or(AuthenticatorError::InternalError(
"no response for add peer".to_string(),
))?
else {
return Err(AuthenticatorError::InternalError(
"unexpected response type".to_string(),
));
};
if !success {
return Err(AuthenticatorError::InternalError(
"adding peer could not be performed".to_string(),
));
}
self.registration_in_progres
.remove(&gateway_client.pub_key());
self.wireguard_gateway_data
.client_registry()
.insert(gateway_client.pub_key(), gateway_client);
Ok(AuthenticatorResponse::new_registered(reply_to, request_id))
Ok(AuthenticatorResponse::new_registered(
RegistredData {
pub_key: registration_data.gateway_data.pub_key,
private_ip: registration_data.gateway_data.private_ip,
wg_port: registration_data.wg_port,
},
reply_to,
request_id,
))
} else {
Err(AuthenticatorError::MacVerificationFailure)
}
}
async fn on_query_bandwidth_request(
&mut self,
peer_public_key: PeerPublicKey,
request_id: u64,
reply_to: Recipient,
) -> AuthenticatorHandleResult {
self.wireguard_gateway_data
.query_bandwidth(peer_public_key)
.map_err(|err| {
AuthenticatorError::InternalError(format!(
"could not query peer bandwidth: {:?}",
err
))
})?;
let PeerControlResponse::QueryBandwidth { bandwidth_data } = self
.response_rx
.recv()
.await
.ok_or(AuthenticatorError::InternalError(
"no response for query".to_string(),
))?
else {
return Err(AuthenticatorError::InternalError(
"unexpected response type".to_string(),
));
};
Ok(AuthenticatorResponse::new_remaining_bandwidth(
bandwidth_data,
reply_to,
request_id,
))
}
async fn on_reconstructed_message(
&mut self,
reconstructed: ReconstructedMessage,
@@ -240,9 +290,19 @@ impl MixnetListener {
match request.data {
AuthenticatorRequestData::Initial(init_msg) => {
self.on_initial_request(init_msg, request.request_id, request.reply_to)
.await
}
AuthenticatorRequestData::Final(client) => {
self.on_final_request(client, request.request_id, request.reply_to)
.await
}
AuthenticatorRequestData::QueryBandwidth(peer_public_key) => {
self.on_query_bandwidth_request(
peer_public_key,
request.request_id,
request.reply_to,
)
.await
}
}
}
@@ -2,12 +2,13 @@
// SPDX-License-Identifier: GPL-3.0-only
use bytes::Bytes;
use nym_ip_packet_requests::{codec::MultiIpPacketCodec, v6::response::IpPacketResponse};
use nym_ip_packet_requests::codec::MultiIpPacketCodec;
use nym_sdk::mixnet::{MixnetMessageSender, Recipient};
use crate::{
constants::CLIENT_HANDLER_ACTIVITY_TIMEOUT,
error::{IpPacketRouterError, Result},
mixnet_listener::SupportedClientVersion,
util::create_message::create_input_message,
};
@@ -18,13 +19,29 @@ use crate::{
// This handler is spawned as a task, and it listens to IP packets passed from the tun_listener,
// encodes it, and then sends to mixnet.
pub(crate) struct ConnectedClientHandler {
// The address of the client that this handler is connected to
nym_address: Recipient,
// The number of hops the packet should take before reaching the client
mix_hops: Option<u8>,
// Channel to receive packets from the tun_listener
forward_from_tun_rx: tokio::sync::mpsc::UnboundedReceiver<Vec<u8>>,
// Channel to send packets to the mixnet
mixnet_client_sender: nym_sdk::mixnet::MixnetClientSender,
// Channel to receive close signal
close_rx: tokio::sync::oneshot::Receiver<()>,
// Interval to check for activity timeout
activity_timeout: tokio::time::Interval,
// Encoder to bundle multiple packets into a single one
encoder: MultiIpPacketCodec,
// The version of the client
client_version: SupportedClientVersion,
}
impl ConnectedClientHandler {
@@ -32,6 +49,7 @@ impl ConnectedClientHandler {
reply_to: Recipient,
reply_to_hops: Option<u8>,
buffer_timeout: std::time::Duration,
client_version: SupportedClientVersion,
mixnet_client_sender: nym_sdk::mixnet::MixnetClientSender,
) -> (
tokio::sync::mpsc::UnboundedSender<Vec<u8>>,
@@ -55,6 +73,7 @@ impl ConnectedClientHandler {
close_rx,
activity_timeout,
encoder,
client_version,
};
let handle = tokio::spawn(async move {
@@ -67,9 +86,18 @@ impl ConnectedClientHandler {
}
async fn send_packets_to_mixnet(&mut self, packets: Bytes) -> Result<()> {
let response_packet = IpPacketResponse::new_ip_packet(packets)
.to_bytes()
.map_err(|err| IpPacketRouterError::FailedToSerializeResponsePacket { source: err })?;
let response_packet = match self.client_version {
SupportedClientVersion::V6 => {
nym_ip_packet_requests::v6::response::IpPacketResponse::new_ip_packet(packets)
.to_bytes()
}
SupportedClientVersion::V7 => {
nym_ip_packet_requests::v7::response::IpPacketResponse::new_ip_packet(packets)
.to_bytes()
}
}
.map_err(|err| IpPacketRouterError::FailedToSerializeResponsePacket { source: err })?;
let input_message = create_input_message(self.nym_address, response_packet, self.mix_hops);
self.mixnet_client_sender
@@ -96,6 +96,9 @@ pub enum IpPacketRouterError {
FailedToVerifyRequest {
source: nym_ip_packet_requests::v7::signature::SignatureError,
},
#[error("client is connected with an invalid version: {version}")]
InvalidConnectedClientVersion { version: u8 },
}
pub type Result<T> = std::result::Result<T, IpPacketRouterError>;
@@ -4,22 +4,19 @@ use std::{collections::HashMap, net::SocketAddr};
use bytes::{Bytes, BytesMut};
use futures::StreamExt;
use nym_ip_packet_requests::v7::response::{
DynamicConnectFailureReason, InfoLevel, InfoResponseReply, StaticConnectFailureReason,
};
use nym_ip_packet_requests::{
codec::MultiIpPacketCodec,
v6::{
self,
response::{
DynamicConnectFailureReason, InfoLevel, InfoResponseReply, IpPacketResponse,
StaticConnectFailureReason,
},
},
v6,
v7::{
self,
request::{
DataRequest, DisconnectRequest, DynamicConnectRequest, IpPacketRequest,
IpPacketRequestData, StaticConnectRequest,
},
signature::{SignatureError, SignedRequest},
signature::SignedRequest,
},
IpPair,
};
@@ -266,7 +263,132 @@ impl Drop for CloseTx {
}
}
type PacketHandleResult = Result<Option<v6::response::IpPacketResponse>>;
type PacketHandleResult = Result<Option<Response>>;
#[derive(Debug, Clone)]
enum Response {
V6(v6::response::IpPacketResponse),
V7(v7::response::IpPacketResponse),
}
impl Response {
fn recipient(&self) -> Option<&Recipient> {
match self {
Response::V6(response) => response.recipient(),
Response::V7(response) => response.recipient(),
}
}
fn new_static_connect_success(
request_id: u64,
reply_to: Recipient,
client_version: SupportedClientVersion,
) -> Self {
match client_version {
SupportedClientVersion::V6 => Response::V6(
v6::response::IpPacketResponse::new_static_connect_success(request_id, reply_to),
),
SupportedClientVersion::V7 => Response::V7(
v7::response::IpPacketResponse::new_static_connect_success(request_id, reply_to),
),
}
}
fn new_static_connect_failure(
request_id: u64,
reply_to: Recipient,
reason: StaticConnectFailureReason,
client_version: SupportedClientVersion,
) -> Self {
match client_version {
SupportedClientVersion::V6 => {
Response::V6(v6::response::IpPacketResponse::new_static_connect_failure(
request_id,
reply_to,
reason.into(),
))
}
SupportedClientVersion::V7 => {
Response::V7(v7::response::IpPacketResponse::new_static_connect_failure(
request_id, reply_to, reason,
))
}
}
}
fn new_dynamic_connect_success(
request_id: u64,
reply_to: Recipient,
ips: IpPair,
client_version: SupportedClientVersion,
) -> Self {
match client_version {
SupportedClientVersion::V6 => {
Response::V6(v6::response::IpPacketResponse::new_dynamic_connect_success(
request_id, reply_to, ips,
))
}
SupportedClientVersion::V7 => {
Response::V7(v7::response::IpPacketResponse::new_dynamic_connect_success(
request_id, reply_to, ips,
))
}
}
}
fn new_dynamic_connect_failure(
request_id: u64,
reply_to: Recipient,
reason: DynamicConnectFailureReason,
client_version: SupportedClientVersion,
) -> Self {
match client_version {
SupportedClientVersion::V6 => {
Response::V6(v6::response::IpPacketResponse::new_dynamic_connect_failure(
request_id,
reply_to,
reason.into(),
))
}
SupportedClientVersion::V7 => {
Response::V7(v7::response::IpPacketResponse::new_dynamic_connect_failure(
request_id, reply_to, reason,
))
}
}
}
fn new_data_info_response(
reply_to: Recipient,
reply: InfoResponseReply,
level: InfoLevel,
client_version: SupportedClientVersion,
) -> Self {
match client_version {
SupportedClientVersion::V6 => {
Response::V6(v6::response::IpPacketResponse::new_data_info_response(
reply_to,
reply.into(),
level.into(),
))
}
SupportedClientVersion::V7 => Response::V7(
v7::response::IpPacketResponse::new_data_info_response(reply_to, reply, level),
),
}
}
fn to_bytes(&self) -> Result<Vec<u8>> {
match self {
Response::V6(response) => response.to_bytes(),
Response::V7(response) => response.to_bytes(),
}
.map_err(|err| {
log::error!("Failed to serialize response packet");
IpPacketRouterError::FailedToSerializeResponsePacket { source: err }
})
}
}
#[cfg(target_os = "linux")]
pub(crate) struct MixnetListener {
@@ -297,6 +419,7 @@ impl MixnetListener {
async fn on_static_connect_request(
&mut self,
connect_request: StaticConnectRequest,
client_version: SupportedClientVersion,
) -> PacketHandleResult {
log::info!(
"Received static connect request from {sender_address}",
@@ -328,8 +451,10 @@ impl MixnetListener {
{
log::error!("Failed to update activity for client");
};
Ok(Some(IpPacketResponse::new_static_connect_success(
request_id, reply_to,
Ok(Some(Response::new_static_connect_success(
request_id,
reply_to,
client_version,
)))
}
(false, false) => {
@@ -341,6 +466,7 @@ impl MixnetListener {
reply_to,
reply_to_hops,
buffer_timeout,
client_version,
self.mixnet_client.split_sender(),
);
@@ -353,24 +479,28 @@ impl MixnetListener {
close_tx,
handle,
);
Ok(Some(IpPacketResponse::new_static_connect_success(
request_id, reply_to,
Ok(Some(Response::new_static_connect_success(
request_id,
reply_to,
client_version,
)))
}
(true, false) => {
log::info!("Requested IP is not available");
Ok(Some(IpPacketResponse::new_static_connect_failure(
Ok(Some(Response::new_static_connect_failure(
request_id,
reply_to,
StaticConnectFailureReason::RequestedIpAlreadyInUse,
client_version,
)))
}
(false, true) => {
log::info!("Nym address is already registered");
Ok(Some(IpPacketResponse::new_static_connect_failure(
Ok(Some(Response::new_static_connect_failure(
request_id,
reply_to,
StaticConnectFailureReason::RequestedNymAddressAlreadyInUse,
client_version,
)))
}
}
@@ -379,6 +509,7 @@ impl MixnetListener {
async fn on_dynamic_connect_request(
&mut self,
connect_request: DynamicConnectRequest,
client_version: SupportedClientVersion,
) -> PacketHandleResult {
log::info!(
"Received dynamic connect request from {sender_address}",
@@ -406,19 +537,21 @@ impl MixnetListener {
{
log::error!("Failed to update activity for client");
}
return Ok(Some(IpPacketResponse::new_dynamic_connect_success(
return Ok(Some(Response::new_dynamic_connect_success(
request_id,
reply_to,
existing_ips,
client_version,
)));
}
let Some(new_ips) = self.connected_clients.find_new_ip() else {
log::info!("No available IP address");
return Ok(Some(IpPacketResponse::new_dynamic_connect_failure(
return Ok(Some(Response::new_dynamic_connect_failure(
request_id,
reply_to,
DynamicConnectFailureReason::NoAvailableIp,
client_version,
)));
};
@@ -428,6 +561,7 @@ impl MixnetListener {
reply_to,
reply_to_hops,
buffer_timeout,
client_version,
self.mixnet_client.split_sender(),
);
@@ -440,17 +574,28 @@ impl MixnetListener {
close_tx,
handle,
);
Ok(Some(IpPacketResponse::new_dynamic_connect_success(
request_id, reply_to, new_ips,
Ok(Some(Response::new_dynamic_connect_success(
request_id,
reply_to,
new_ips,
client_version,
)))
}
fn on_disconnect_request(&self, _disconnect_request: DisconnectRequest) -> PacketHandleResult {
fn on_disconnect_request(
&self,
_disconnect_request: DisconnectRequest,
_client_version: SupportedClientVersion,
) -> PacketHandleResult {
log::info!("Received disconnect request: not implemented, dropping");
Ok(None)
}
async fn handle_packet(&mut self, ip_packet: &Bytes) -> PacketHandleResult {
async fn handle_packet(
&mut self,
ip_packet: &Bytes,
client_version: SupportedClientVersion,
) -> PacketHandleResult {
log::trace!("Received data request");
// We don't forward packets that we are not able to parse. BUT, there might be a good
@@ -487,12 +632,13 @@ impl MixnetListener {
Ok(None)
} else {
log::info!("Denied filter check: {dst}");
Ok(Some(IpPacketResponse::new_data_info_response(
Ok(Some(Response::new_data_info_response(
connected_client.nym_address,
InfoResponseReply::ExitPolicyFilterCheckFailed {
dst: dst.to_string(),
},
InfoLevel::Warn,
client_version,
)))
}
} else {
@@ -505,13 +651,14 @@ impl MixnetListener {
async fn on_data_request(
&mut self,
data_request: DataRequest,
client_version: SupportedClientVersion,
) -> Result<Vec<PacketHandleResult>> {
let mut responses = Vec::new();
let mut decoder = MultiIpPacketCodec::new(nym_ip_packet_requests::codec::BUFFER_TIMEOUT);
let mut bytes = BytesMut::new();
bytes.extend_from_slice(&data_request.ip_packets);
while let Ok(Some(packet)) = decoder.decode(&mut bytes) {
let result = self.handle_packet(&packet).await;
let result = self.handle_packet(&packet, client_version).await;
responses.push(result);
}
Ok(responses)
@@ -519,26 +666,13 @@ impl MixnetListener {
fn on_version_mismatch(
&self,
version: u8,
reconstructed: &ReconstructedMessage,
_version: u8,
_reconstructed: &ReconstructedMessage,
) -> PacketHandleResult {
// If it's possible to parse, do so and return back a response, otherwise just drop
let (id, recipient) =
v6::request::IpPacketRequest::from_reconstructed_message(reconstructed)
.ok()
.and_then(|request| {
request
.recipient()
.map(|recipient| (request.id().unwrap_or(0), *recipient))
})
.ok_or(IpPacketRouterError::InvalidPacketVersion(version))?;
Ok(Some(IpPacketResponse::new_version_mismatch(
id,
recipient,
version,
nym_ip_packet_requests::CURRENT_VERSION,
)))
// Just drop it. In the future we might want to return a response here, if for example
// the client is connecting with a version that is older than the currently supported
// ones.
Ok(None)
}
async fn on_reconstructed_message(
@@ -550,7 +684,7 @@ impl MixnetListener {
reconstructed.sender_tag
);
let request = match deserialize_request(&reconstructed) {
let (request, client_version) = match deserialize_request(&reconstructed) {
Err(IpPacketRouterError::InvalidPacketVersion(version)) => {
return Ok(vec![self.on_version_mismatch(version, &reconstructed)]);
}
@@ -559,21 +693,31 @@ impl MixnetListener {
match request.data {
IpPacketRequestData::StaticConnect(signed_connect_request) => {
verify_signed_request(&signed_connect_request)?;
verify_signed_request(&signed_connect_request, client_version)?;
let connect_request = signed_connect_request.request;
Ok(vec![self.on_static_connect_request(connect_request).await])
Ok(vec![
self.on_static_connect_request(connect_request, client_version)
.await,
])
}
IpPacketRequestData::DynamicConnect(signed_connect_request) => {
verify_signed_request(&signed_connect_request)?;
verify_signed_request(&signed_connect_request, client_version)?;
let connect_request = signed_connect_request.request;
Ok(vec![self.on_dynamic_connect_request(connect_request).await])
Ok(vec![
self.on_dynamic_connect_request(connect_request, client_version)
.await,
])
}
IpPacketRequestData::Disconnect(signed_disconnect_request) => {
verify_signed_request(&signed_disconnect_request)?;
verify_signed_request(&signed_disconnect_request, client_version)?;
let disconnect_request = signed_disconnect_request.request;
Ok(vec![self.on_disconnect_request(disconnect_request)])
Ok(vec![
self.on_disconnect_request(disconnect_request, client_version)
])
}
IpPacketRequestData::Data(data_request) => {
self.on_data_request(data_request, client_version).await
}
IpPacketRequestData::Data(data_request) => self.on_data_request(data_request).await,
IpPacketRequestData::Ping(_) => {
log::info!("Received ping request: not implemented, dropping");
Ok(vec![])
@@ -605,16 +749,13 @@ impl MixnetListener {
// When an incoming mixnet message triggers a response that we send back, such as during
// connect handshake.
async fn handle_response(&self, response: IpPacketResponse) -> Result<()> {
async fn handle_response(&self, response: Response) -> Result<()> {
let Some(recipient) = response.recipient() else {
log::error!("No recipient in response packet, this should NOT happen!");
return Err(IpPacketRouterError::NoRecipientInResponse);
};
let response_packet = response.to_bytes().map_err(|err| {
log::error!("Failed to serialize response packet");
IpPacketRouterError::FailedToSerializeResponsePacket { source: err }
})?;
let response_packet = response.to_bytes()?;
// We could avoid this lookup if we check this when we create the response.
let mix_hops = if let Some(c) = self
@@ -687,33 +828,65 @@ impl MixnetListener {
}
}
fn deserialize_request(reconstructed: &ReconstructedMessage) -> Result<IpPacketRequest> {
fn deserialize_request(
reconstructed: &ReconstructedMessage,
) -> Result<(IpPacketRequest, SupportedClientVersion)> {
let request_version = *reconstructed
.message
.first()
.ok_or(IpPacketRouterError::EmptyPacket)?;
// Check version of the request and convert to the latest version if necessary
match request_version {
6 => v6::request::IpPacketRequest::from_reconstructed_message(reconstructed)
.map_err(|err| IpPacketRouterError::FailedToDeserializeTaggedPacket { source: err })
.map(|r| r.into()),
7 => v7::request::IpPacketRequest::from_reconstructed_message(reconstructed)
.map_err(|err| IpPacketRouterError::FailedToDeserializeTaggedPacket { source: err }),
let request = match request_version {
6 => nym_ip_packet_requests::v6::request::IpPacketRequest::from_reconstructed_message(
reconstructed,
)
.map_err(|err| IpPacketRouterError::FailedToDeserializeTaggedPacket { source: err })
.map(|r| r.into()),
7 => nym_ip_packet_requests::v7::request::IpPacketRequest::from_reconstructed_message(
reconstructed,
)
.map_err(|err| IpPacketRouterError::FailedToDeserializeTaggedPacket { source: err }),
_ => {
log::info!("Received packet with invalid version: v{request_version}");
Err(IpPacketRouterError::InvalidPacketVersion(request_version))
}
};
let Some(request_version) = SupportedClientVersion::new(request_version) else {
return Err(IpPacketRouterError::InvalidPacketVersion(request_version));
};
// Tag the request with the version of the request
request.map(|r| (r, request_version))
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub(crate) enum SupportedClientVersion {
V6,
V7,
}
impl SupportedClientVersion {
fn new(request_version: u8) -> Option<Self> {
match request_version {
6 => Some(SupportedClientVersion::V6),
7 => Some(SupportedClientVersion::V7),
_ => None,
}
}
}
fn verify_signed_request(request: &impl SignedRequest) -> Result<()> {
fn verify_signed_request(
request: &impl SignedRequest,
client_version: SupportedClientVersion,
) -> Result<()> {
if let Err(err) = request.verify() {
// Once we start to require clients to send v7 requests, we will enfore checking
// signatures. Until then, we only check if they are present.
if !matches!(err, SignatureError::MissingSignature) {
return Err(IpPacketRouterError::FailedToVerifyRequest { source: err });
// If the client is V6, we don't care about missing signature
if client_version == SupportedClientVersion::V6 {
return Ok(());
}
return Err(IpPacketRouterError::FailedToVerifyRequest { source: err });
}
Ok(())
}
@@ -4,7 +4,7 @@
[package]
name = "nym-network-requester"
license = "GPL-3.0"
version = "1.1.38"
version = "1.1.40"
authors.workspace = true
edition.workspace = true
rust-version = "1.70"
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-cli"
version = "1.1.38"
version = "1.1.40"
authors.workspace = true
edition = "2021"
license.workspace = true
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nymvisor"
version = "0.1.3"
version = "0.1.5"
authors.workspace = true
repository.workspace = true
homepage.workspace = true