Compare commits
31 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b45eb16783 | |||
| f8523dc7d1 | |||
| 3d5ac0b883 | |||
| de0fb7459d | |||
| e2ead6dbe1 | |||
| 7b10d92ca4 | |||
| 2c6e5eb673 | |||
| 02fde4e530 | |||
| cc25fc1f32 | |||
| c971e486b5 | |||
| 96a9eb6f6a | |||
| 9eeb61ea0a | |||
| 08042c61ad | |||
| 36c74f30e5 | |||
| fd1d437211 | |||
| 4956d13bdc | |||
| 6478736654 | |||
| d9f6c0723e | |||
| f86050d916 | |||
| 52f5656190 | |||
| 21cd90f238 | |||
| 4e51188d35 | |||
| 22eb199936 | |||
| a2fc1bbc96 | |||
| 621599692f | |||
| 72e243042e | |||
| c253b22f69 | |||
| 66f3a3e9a8 | |||
| c2ad4e5bb4 | |||
| 5f7f5ef92d | |||
| 008afe7a85 |
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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,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,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,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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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:
|
||||
|
||||

|
||||
|
||||
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.
|
||||
|
||||
@@ -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](https://nymtech.net/operators/nodes/nymvisor-upgrade.html): `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.
|
||||
|
||||
@@ -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.
|
||||
```
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@@ -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;
|
||||
|
||||
@@ -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,6 +1,6 @@
|
||||
[package]
|
||||
name = "explorer-api"
|
||||
version = "1.1.36"
|
||||
version = "1.1.38"
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
|
||||
|
||||
@@ -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})",
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 }
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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})",
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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>",
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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.
|
||||
///
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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,
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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};
|
||||
|
||||
|
||||
@@ -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::{
|
||||
|
||||
@@ -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,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-cli"
|
||||
version = "1.1.38"
|
||||
version = "1.1.40"
|
||||
authors.workspace = true
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nymvisor"
|
||||
version = "0.1.3"
|
||||
version = "0.1.5"
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
|
||||
Reference in New Issue
Block a user