Compare commits

..

2 Commits

Author SHA1 Message Date
Jędrzej Stuczyński 4077bfa060 added env flags to 'run' 2024-02-27 08:49:35 +00:00
Jędrzej Stuczyński f60aa8a1ca proof of concept: env-configurable mixnode 2024-02-27 08:44:18 +00:00
76 changed files with 697 additions and 1026 deletions
Generated
+10 -32
View File
@@ -4280,7 +4280,7 @@ dependencies = [
"rw-stream-sink", "rw-stream-sink",
"soketto", "soketto",
"url", "url",
"webpki-roots", "webpki-roots 0.22.6",
] ]
[[package]] [[package]]
@@ -5128,7 +5128,6 @@ dependencies = [
"nym-credentials", "nym-credentials",
"nym-credentials-interface", "nym-credentials-interface",
"nym-crypto", "nym-crypto",
"nym-id",
"nym-mixnet-contract-common", "nym-mixnet-contract-common",
"nym-multisig-contract-common", "nym-multisig-contract-common",
"nym-name-service-common", "nym-name-service-common",
@@ -5169,7 +5168,6 @@ dependencies = [
"nym-credentials", "nym-credentials",
"nym-crypto", "nym-crypto",
"nym-gateway-requests", "nym-gateway-requests",
"nym-id",
"nym-network-defaults", "nym-network-defaults",
"nym-pemstore", "nym-pemstore",
"nym-sphinx", "nym-sphinx",
@@ -5628,32 +5626,6 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "nym-id"
version = "0.1.0"
dependencies = [
"nym-credential-storage",
"nym-credentials",
"thiserror",
"time",
"tracing",
"zeroize",
]
[[package]]
name = "nym-id-cli"
version = "0.1.0"
dependencies = [
"anyhow",
"bs58 0.5.0",
"clap 4.4.7",
"nym-bin-common",
"nym-credential-storage",
"nym-id",
"tokio",
"tracing",
]
[[package]] [[package]]
name = "nym-inclusion-probability" name = "nym-inclusion-probability"
version = "0.1.0" version = "0.1.0"
@@ -5892,7 +5864,6 @@ dependencies = [
"nym-credentials", "nym-credentials",
"nym-crypto", "nym-crypto",
"nym-exit-policy", "nym-exit-policy",
"nym-id",
"nym-network-defaults", "nym-network-defaults",
"nym-ordered-buffer", "nym-ordered-buffer",
"nym-sdk", "nym-sdk",
@@ -6180,7 +6151,6 @@ dependencies = [
"nym-credentials", "nym-credentials",
"nym-crypto", "nym-crypto",
"nym-gateway-requests", "nym-gateway-requests",
"nym-id",
"nym-network-defaults", "nym-network-defaults",
"nym-ordered-buffer", "nym-ordered-buffer",
"nym-pemstore", "nym-pemstore",
@@ -8044,6 +8014,7 @@ dependencies = [
"wasm-bindgen-futures", "wasm-bindgen-futures",
"wasm-streams", "wasm-streams",
"web-sys", "web-sys",
"webpki-roots 0.25.2",
"winreg", "winreg",
] ]
@@ -9206,7 +9177,7 @@ dependencies = [
"time", "time",
"tokio-stream", "tokio-stream",
"url", "url",
"webpki-roots", "webpki-roots 0.22.6",
] ]
[[package]] [[package]]
@@ -9853,6 +9824,7 @@ checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c"
dependencies = [ dependencies = [
"futures-util", "futures-util",
"log", "log",
"rustls 0.21.10",
"tokio", "tokio",
"tungstenite", "tungstenite",
] ]
@@ -10870,6 +10842,12 @@ dependencies = [
"webpki 0.22.4", "webpki 0.22.4",
] ]
[[package]]
name = "webpki-roots"
version = "0.25.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc"
[[package]] [[package]]
name = "webrtc" name = "webrtc"
version = "0.6.0" version = "0.6.0"
+3 -5
View File
@@ -56,7 +56,6 @@ members = [
"common/node-tester-utils", "common/node-tester-utils",
"common/nonexhaustive-delayqueue", "common/nonexhaustive-delayqueue",
"common/nymcoconut", "common/nymcoconut",
"common/nym-id",
"common/nymsphinx", "common/nymsphinx",
"common/nymsphinx/acknowledgements", "common/nymsphinx/acknowledgements",
"common/nymsphinx/addressing", "common/nymsphinx/addressing",
@@ -107,7 +106,6 @@ members = [
"tools/internal/ssl-inject", "tools/internal/ssl-inject",
# "tools/internal/sdk-version-bump", # "tools/internal/sdk-version-bump",
"tools/nym-cli", "tools/nym-cli",
"tools/nym-id-cli",
"tools/nym-nr-query", "tools/nym-nr-query",
"tools/nymvisor", "tools/nymvisor",
"tools/ts-rs-cli", "tools/ts-rs-cli",
@@ -161,7 +159,7 @@ log = "0.4"
once_cell = "1.7.2" once_cell = "1.7.2"
parking_lot = "0.12.1" parking_lot = "0.12.1"
rand = "0.8.5" rand = "0.8.5"
reqwest = { version = "0.11.22", default_features = false } reqwest = { version = "0.11.22", default_features = false, features = ["rustls-tls"] }
schemars = "0.8.1" schemars = "0.8.1"
serde = "1.0.152" serde = "1.0.152"
serde_json = "1.0.91" serde_json = "1.0.91"
@@ -171,9 +169,9 @@ time = "0.3.30"
thiserror = "1.0.48" thiserror = "1.0.48"
tokio = "1.33.0" tokio = "1.33.0"
tokio-util = "0.7.10" tokio-util = "0.7.10"
tokio-tungstenite = { version = "0.20.1" } tokio-tungstenite = { version = "0.20.1", features = ["rustls"] }
tracing = "0.1.37" tracing = "0.1.37"
tungstenite = { version = "0.20.1", default-features = false } tungstenite = { version = "0.20.1", default-features = false, features = ["rustls"] }
ts-rs = "7.0.0" ts-rs = "7.0.0"
utoipa = "3.5.0" utoipa = "3.5.0"
utoipa-swagger-ui = "3.1.5" utoipa-swagger-ui = "3.1.5"
-1
View File
@@ -51,6 +51,5 @@ nym-task = { path = "../../common/task" }
nym-topology = { path = "../../common/topology" } nym-topology = { path = "../../common/topology" }
nym-validator-client = { path = "../../common/client-libs/validator-client", features = ["http-client"] } nym-validator-client = { path = "../../common/client-libs/validator-client", features = ["http-client"] }
nym-client-websocket-requests = { path = "websocket-requests" } nym-client-websocket-requests = { path = "websocket-requests" }
nym-id = { path = "../../common/nym-id" }
[dev-dependencies] [dev-dependencies]
@@ -2160,9 +2160,9 @@
} }
}, },
"node_modules/ip": { "node_modules/ip": {
"version": "1.1.9", "version": "1.1.5",
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.9.tgz", "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
"integrity": "sha512-cyRxvOEpNHNtchU3Ln9KC/auJgup87llfQpQ+t5ghoC/UhL16SWzbueiCsdTnWmqAWl7LadfuwhlqmtOaqMHdQ==", "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=",
"dev": true "dev": true
}, },
"node_modules/ipaddr.js": { "node_modules/ipaddr.js": {
@@ -6157,9 +6157,9 @@
"dev": true "dev": true
}, },
"ip": { "ip": {
"version": "1.1.9", "version": "1.1.5",
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.9.tgz", "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
"integrity": "sha512-cyRxvOEpNHNtchU3Ln9KC/auJgup87llfQpQ+t5ghoC/UhL16SWzbueiCsdTnWmqAWl7LadfuwhlqmtOaqMHdQ==", "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=",
"dev": true "dev": true
}, },
"ipaddr.js": { "ipaddr.js": {
@@ -4,10 +4,14 @@
use crate::commands::try_load_current_config; use crate::commands::try_load_current_config;
use crate::error::ClientError; use crate::error::ClientError;
use clap::ArgGroup; use clap::ArgGroup;
use log::{error, info};
use nym_id::import_credential; use nym_credential_storage::models::StorableIssuedCredential;
use nym_credential_storage::storage::Storage;
use nym_credentials::coconut::bandwidth::issued::BandwidthCredentialIssuedDataVariant;
use nym_credentials::IssuedBandwidthCredential;
use std::fs; use std::fs;
use std::path::PathBuf; use std::path::PathBuf;
use zeroize::Zeroizing;
fn parse_encoded_credential_data(raw: &str) -> bs58::decode::Result<Vec<u8>> { fn parse_encoded_credential_data(raw: &str) -> bs58::decode::Result<Vec<u8>> {
bs58::decode(raw).into_vec() bs58::decode(raw).into_vec()
@@ -29,8 +33,8 @@ pub(crate) struct Args {
pub(crate) credential_path: Option<PathBuf>, pub(crate) credential_path: Option<PathBuf>,
// currently hidden as there exists only a single serialization standard // currently hidden as there exists only a single serialization standard
#[clap(long, hide = true)] #[clap(long, hide = true, default_value_t = 1)]
pub(crate) version: Option<u8>, pub(crate) version: u8,
} }
pub(crate) async fn execute(args: Args) -> Result<(), ClientError> { pub(crate) async fn execute(args: Args) -> Result<(), ClientError> {
@@ -48,7 +52,50 @@ pub(crate) async fn execute(args: Args) -> Result<(), ClientError> {
fs::read(args.credential_path.unwrap())? fs::read(args.credential_path.unwrap())?
} }
}; };
let raw_credential = Zeroizing::new(raw_credential);
import_credential(credentials_store, raw_credential, args.version).await?; // we're unpacking the data in order to make sure it's valid
// and to extract relevant metadata for storage purposes
let credential = match args.version {
1 => Zeroizing::new(
IssuedBandwidthCredential::unpack_v1(&raw_credential).map_err(|source| {
ClientError::CredentialDeserializationFailure {
storage_revision: 1,
source,
}
})?,
),
other => panic!("unknown credential serialization version {other}"),
};
info!("importing {}", credential.typ());
match credential.variant_data() {
BandwidthCredentialIssuedDataVariant::Voucher(voucher_info) => {
info!("with value of {}", voucher_info.value())
}
BandwidthCredentialIssuedDataVariant::FreePass(freepass_info) => {
info!("with expiry at {}", freepass_info.expiry_date());
if freepass_info.expired() {
error!("the free pass has already expired!");
// technically we can import it, but the gateway will just reject it so what's the point
return Err(ClientError::ExpiredCredentialImport {
expiration: freepass_info.expiry_date(),
});
}
}
}
let storable = StorableIssuedCredential {
serialization_revision: args.version,
credential_data: &raw_credential,
credential_type: credential.typ().to_string(),
epoch_id: credential
.epoch_id()
.try_into()
.expect("our epoch is has run over u32::MAX!"),
};
credentials_store.insert_issued_credential(storable).await?;
Ok(()) Ok(())
} }
+19 -4
View File
@@ -1,6 +1,6 @@
use nym_client_core::error::ClientCoreError; use nym_client_core::error::ClientCoreError;
use nym_credential_storage::error::StorageError;
use nym_id::NymIdError; use time::OffsetDateTime;
#[derive(thiserror::Error, Debug)] #[derive(thiserror::Error, Debug)]
pub enum ClientError { pub enum ClientError {
@@ -23,6 +23,21 @@ pub enum ClientError {
#[error("Attempted to start the client in invalid socket mode")] #[error("Attempted to start the client in invalid socket mode")]
InvalidSocketMode, InvalidSocketMode,
#[error(transparent)] #[error("failed to store credential: {source}")]
NymIdError(#[from] NymIdError), CredentialStorageFailure {
#[from]
source: StorageError,
},
#[error(
"failed to deserialize provided credential using revision {storage_revision}: {source}"
)]
CredentialDeserializationFailure {
storage_revision: u8,
#[source]
source: nym_credentials::error::Error,
},
#[error("attempted to import an expired credential (it expired on {expiration})")]
ExpiredCredentialImport { expiration: OffsetDateTime },
} }
-1
View File
@@ -35,7 +35,6 @@ nym-ordered-buffer = { path = "../../common/socks5/ordered-buffer" }
nym-pemstore = { path = "../../common/pemstore" } nym-pemstore = { path = "../../common/pemstore" }
nym-topology = { path = "../../common/topology" } nym-topology = { path = "../../common/topology" }
nym-socks5-client-core = { path = "../../common/socks5-client-core" } nym-socks5-client-core = { path = "../../common/socks5-client-core" }
nym-id = { path = "../../common/nym-id" }
[features] [features]
default = [] default = []
@@ -4,10 +4,14 @@
use crate::commands::try_load_current_config; use crate::commands::try_load_current_config;
use crate::error::Socks5ClientError; use crate::error::Socks5ClientError;
use clap::ArgGroup; use clap::ArgGroup;
use log::{error, info};
use nym_id::import_credential; use nym_credential_storage::models::StorableIssuedCredential;
use nym_credential_storage::storage::Storage;
use nym_credentials::coconut::bandwidth::issued::BandwidthCredentialIssuedDataVariant;
use nym_credentials::IssuedBandwidthCredential;
use std::fs; use std::fs;
use std::path::PathBuf; use std::path::PathBuf;
use zeroize::Zeroizing;
fn parse_encoded_credential_data(raw: &str) -> bs58::decode::Result<Vec<u8>> { fn parse_encoded_credential_data(raw: &str) -> bs58::decode::Result<Vec<u8>> {
bs58::decode(raw).into_vec() bs58::decode(raw).into_vec()
@@ -29,8 +33,8 @@ pub(crate) struct Args {
pub(crate) credential_path: Option<PathBuf>, pub(crate) credential_path: Option<PathBuf>,
// currently hidden as there exists only a single serialization standard // currently hidden as there exists only a single serialization standard
#[clap(long, hide = true)] #[clap(long, hide = true, default_value_t = 1)]
pub(crate) version: Option<u8>, pub(crate) version: u8,
} }
pub(crate) async fn execute(args: Args) -> Result<(), Socks5ClientError> { pub(crate) async fn execute(args: Args) -> Result<(), Socks5ClientError> {
@@ -48,7 +52,50 @@ pub(crate) async fn execute(args: Args) -> Result<(), Socks5ClientError> {
fs::read(args.credential_path.unwrap())? fs::read(args.credential_path.unwrap())?
} }
}; };
let raw_credential = Zeroizing::new(raw_credential);
import_credential(credentials_store, raw_credential, args.version).await?; // we're unpacking the data in order to make sure it's valid
// and to extract relevant metadata for storage purposes
let credential = match args.version {
1 => Zeroizing::new(
IssuedBandwidthCredential::unpack_v1(&raw_credential).map_err(|source| {
Socks5ClientError::CredentialDeserializationFailure {
storage_revision: 1,
source,
}
})?,
),
other => panic!("unknown credential serialization version {other}"),
};
info!("importing {}", credential.typ());
match credential.variant_data() {
BandwidthCredentialIssuedDataVariant::Voucher(voucher_info) => {
info!("with value of {}", voucher_info.value())
}
BandwidthCredentialIssuedDataVariant::FreePass(freepass_info) => {
info!("with expiry at {}", freepass_info.expiry_date());
if freepass_info.expired() {
error!("the free pass has already expired!");
// technically we can import it, but the gateway will just reject it so what's the point
return Err(Socks5ClientError::ExpiredCredentialImport {
expiration: freepass_info.expiry_date(),
});
}
}
}
let storable = StorableIssuedCredential {
serialization_revision: args.version,
credential_data: &raw_credential,
credential_type: credential.typ().to_string(),
epoch_id: credential
.epoch_id()
.try_into()
.expect("our epoch is has run over u32::MAX!"),
};
credentials_store.insert_issued_credential(storable).await?;
Ok(()) Ok(())
} }
+19 -4
View File
@@ -1,6 +1,6 @@
use nym_client_core::error::ClientCoreError; use nym_client_core::error::ClientCoreError;
use nym_credential_storage::error::StorageError;
use nym_id::NymIdError; use time::OffsetDateTime;
#[derive(thiserror::Error, Debug)] #[derive(thiserror::Error, Debug)]
pub enum Socks5ClientError { pub enum Socks5ClientError {
@@ -23,6 +23,21 @@ pub enum Socks5ClientError {
#[error(transparent)] #[error(transparent)]
ClientCoreError(#[from] ClientCoreError), ClientCoreError(#[from] ClientCoreError),
#[error(transparent)] #[error("failed to store credential: {source}")]
NymIdError(#[from] NymIdError), CredentialStorageFailure {
#[from]
source: StorageError,
},
#[error(
"failed to deserialize provided credential using revision {storage_revision}: {source}"
)]
CredentialDeserializationFailure {
storage_revision: u8,
#[source]
source: nym_credentials::error::Error,
},
#[error("attempted to import an expired credential (it expired on {expiration})")]
ExpiredCredentialImport { expiration: OffsetDateTime },
} }
@@ -69,35 +69,6 @@ impl BinaryBuildInformation {
} }
} }
// Varient where we want to use the metadata generated by vergen in the consuming crate.
pub const fn new_with_local_vergen(
binary_name: &'static str,
build_timestamp: &'static str,
build_version: &'static str,
commit_sha: &'static str,
commit_timestamp: &'static str,
commit_branch: &'static str,
) -> Self {
let cargo_debug = env!("VERGEN_CARGO_DEBUG");
let cargo_profile = if const_str::equal!(cargo_debug, "true") {
"debug"
} else {
"release"
};
BinaryBuildInformation {
binary_name,
build_timestamp,
build_version,
commit_sha,
commit_timestamp,
commit_branch,
rustc_version: env!("VERGEN_RUSTC_SEMVER"),
rustc_channel: env!("VERGEN_RUSTC_CHANNEL"),
cargo_profile,
}
}
pub fn to_owned(&self) -> BinaryBuildInformationOwned { pub fn to_owned(&self) -> BinaryBuildInformationOwned {
BinaryBuildInformationOwned { BinaryBuildInformationOwned {
binary_name: self.binary_name.to_owned(), binary_name: self.binary_name.to_owned(),
@@ -216,33 +187,3 @@ macro_rules! bin_info_owned {
.to_owned() .to_owned()
}; };
} }
// variant that picks up the vergen build information generated by the build.rs in the consumer
// crate.
#[macro_export]
macro_rules! bin_info_local_vergen {
() => {
$crate::build_information::BinaryBuildInformation::new_with_local_vergen(
env!("CARGO_PKG_NAME"),
env!("VERGEN_BUILD_TIMESTAMP"),
env!("CARGO_PKG_VERSION"),
env!("VERGEN_GIT_SHA"),
env!("VERGEN_GIT_COMMIT_TIMESTAMP"),
env!("VERGEN_GIT_BRANCH"),
)
};
}
#[macro_export]
macro_rules! bin_info_local_vergen_owned {
() => {
$crate::build_information::BinaryBuildInformation::new_with_local_vergen(
env!("CARGO_PKG_NAME"),
env!("VERGEN_BUILD_TIMESTAMP"),
env!("CARGO_PKG_VERSION"),
env!("VERGEN_GIT_SHA"),
env!("VERGEN_GIT_COMMIT_TIMESTAMP"),
env!("VERGEN_GIT_BRANCH"),
)
};
}
-12
View File
@@ -4,7 +4,6 @@
use crate::client::mix_traffic::transceiver::ErasedGatewayError; use crate::client::mix_traffic::transceiver::ErasedGatewayError;
use nym_crypto::asymmetric::identity::Ed25519RecoveryError; use nym_crypto::asymmetric::identity::Ed25519RecoveryError;
use nym_gateway_client::error::GatewayClientError; use nym_gateway_client::error::GatewayClientError;
use nym_task::manager::TaskStatusTrait;
use nym_topology::gateway::GatewayConversionError; use nym_topology::gateway::GatewayConversionError;
use nym_topology::NymTopologyError; use nym_topology::NymTopologyError;
use nym_validator_client::ValidatorClientError; use nym_validator_client::ValidatorClientError;
@@ -166,14 +165,3 @@ pub enum ClientCoreStatusMessage {
#[error("The connected gateway is very slow, or the connection to it is very slow")] #[error("The connected gateway is very slow, or the connection to it is very slow")]
GatewayIsVerySlow, GatewayIsVerySlow,
} }
// impl TaskStatusTrait for ClientCoreStatusMessage {}
// impl<T: std::fmt::Debug + std::fmt::Display + Send + Sync + Any + 'static> TaskStatusTrait for T {
// fn as_any(&self) -> &dyn Any {
// self
// }
//
// fn as_any_mut(&mut self) -> &mut dyn Any {
// self
// }
// }
@@ -33,14 +33,14 @@ use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use tungstenite::protocol::Message; use tungstenite::protocol::Message;
#[cfg(unix)] #[cfg(not(target_arch = "wasm32"))]
use std::os::fd::RawFd; use std::os::fd::RawFd;
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
use tokio::time::sleep; use tokio::time::sleep;
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
use tokio_tungstenite::connect_async; use tokio_tungstenite::connect_async;
#[cfg(not(unix))] #[cfg(target_arch = "wasm32")]
use std::os::raw::c_int as RawFd; use std::os::raw::c_int as RawFd;
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
use wasm_utils::websocket::JSWebsocket; use wasm_utils::websocket::JSWebsocket;
@@ -15,7 +15,7 @@ use std::os::raw::c_int as RawFd;
use std::sync::Arc; use std::sync::Arc;
use tungstenite::Message; use tungstenite::Message;
#[cfg(unix)] #[cfg(not(target_arch = "wasm32"))]
use std::os::fd::AsRawFd; use std::os::fd::AsRawFd;
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
use tokio::net::TcpStream; use tokio::net::TcpStream;
@@ -41,14 +41,14 @@ type WsConn = JSWebsocket;
type SplitStreamReceiver = oneshot::Receiver<Result<SplitStream<WsConn>, GatewayClientError>>; type SplitStreamReceiver = oneshot::Receiver<Result<SplitStream<WsConn>, GatewayClientError>>;
pub(crate) fn ws_fd(_conn: &WsConn) -> Option<RawFd> { pub(crate) fn ws_fd(_conn: &WsConn) -> Option<RawFd> {
#[cfg(unix)] #[cfg(not(target_arch = "wasm32"))]
match _conn.get_ref() { match _conn.get_ref() {
MaybeTlsStream::Plain(stream) => Some(stream.as_raw_fd()), MaybeTlsStream::Plain(stream) => Some(stream.as_raw_fd()),
&_ => unreachable!( &_ => unreachable!(
"If tls features are enabled, the inner stream needs to be unpacked into raw fd" "If tls features are enabled, the inner stream needs to be unpacked into raw fd"
), ),
} }
#[cfg(not(unix))] #[cfg(target_arch = "wasm32")]
None None
} }
-1
View File
@@ -55,7 +55,6 @@ nym-credentials = { path = "../../common/credentials" }
nym-credentials-interface = { path = "../../common/credentials-interface" } nym-credentials-interface = { path = "../../common/credentials-interface" }
nym-credential-storage = { path = "../../common/credential-storage" } nym-credential-storage = { path = "../../common/credential-storage" }
nym-credential-utils = { path = "../../common/credential-utils" } nym-credential-utils = { path = "../../common/credential-utils" }
nym-id = { path = "../nym-id" }
nym-pemstore = { path = "../../common/pemstore", version = "0.3.0" } nym-pemstore = { path = "../../common/pemstore", version = "0.3.0" }
nym-types = { path = "../../common/types" } nym-types = { path = "../../common/types" }
@@ -5,10 +5,15 @@ use crate::utils::CommonConfigsWrapper;
use anyhow::bail; use anyhow::bail;
use clap::ArgGroup; use clap::ArgGroup;
use clap::Parser; use clap::Parser;
use log::{error, info};
use nym_credential_storage::initialise_persistent_storage; use nym_credential_storage::initialise_persistent_storage;
use nym_id::import_credential; use nym_credential_storage::models::StorableIssuedCredential;
use nym_credential_storage::storage::Storage;
use nym_credentials::coconut::bandwidth::issued::BandwidthCredentialIssuedDataVariant;
use nym_credentials::IssuedBandwidthCredential;
use std::fs; use std::fs;
use std::path::PathBuf; use std::path::PathBuf;
use zeroize::Zeroizing;
fn parse_encoded_credential_data(raw: &str) -> bs58::decode::Result<Vec<u8>> { fn parse_encoded_credential_data(raw: &str) -> bs58::decode::Result<Vec<u8>> {
bs58::decode(raw).into_vec() bs58::decode(raw).into_vec()
@@ -30,8 +35,8 @@ pub struct Args {
pub(crate) credential_path: Option<PathBuf>, pub(crate) credential_path: Option<PathBuf>,
// currently hidden as there exists only a single serialization standard // currently hidden as there exists only a single serialization standard
#[clap(long, hide = true)] #[clap(long, hide = true, default_value_t = 1)]
pub(crate) version: Option<u8>, pub(crate) version: u8,
} }
pub async fn execute(args: Args) -> anyhow::Result<()> { pub async fn execute(args: Args) -> anyhow::Result<()> {
@@ -49,7 +54,6 @@ pub async fn execute(args: Args) -> anyhow::Result<()> {
"using credentials store at '{}'", "using credentials store at '{}'",
credentials_store.display() credentials_store.display()
); );
let credentials_store = initialise_persistent_storage(credentials_store).await;
let raw_credential = match args.credential_data { let raw_credential = match args.credential_data {
Some(data) => data, Some(data) => data,
@@ -58,7 +62,44 @@ pub async fn execute(args: Args) -> anyhow::Result<()> {
fs::read(args.credential_path.unwrap())? fs::read(args.credential_path.unwrap())?
} }
}; };
let raw_credential = Zeroizing::new(raw_credential);
import_credential(credentials_store, raw_credential, args.version).await?; // we're unpacking the data in order to make sure it's valid
// and to extract relevant metadata for storage purposes
let credential = match args.version {
1 => Zeroizing::new(IssuedBandwidthCredential::unpack_v1(&raw_credential)?),
other => panic!("unknown credential serialization version {other}"),
};
let persistent_storage = initialise_persistent_storage(credentials_store).await;
info!("importing {}", credential.typ());
match credential.variant_data() {
BandwidthCredentialIssuedDataVariant::Voucher(voucher_info) => {
info!("with value of {}", voucher_info.value())
}
BandwidthCredentialIssuedDataVariant::FreePass(freepass_info) => {
info!("with expiry at {}", freepass_info.expiry_date());
if freepass_info.expired() {
error!("the free pass has already expired!");
// technically we can, but the gateway will just reject it so what's the point
bail!("can't import an expired free pass")
}
}
}
let storable = StorableIssuedCredential {
serialization_revision: args.version,
credential_data: &raw_credential,
credential_type: credential.typ().to_string(),
epoch_id: credential
.epoch_id()
.try_into()
.expect("our epoch is has run over u32::MAX!"),
};
persistent_storage
.insert_issued_credential(storable)
.await?;
Ok(()) Ok(())
} }
@@ -315,12 +315,9 @@ impl IssuanceBandwidthCredential {
} }
// TODO: is that actually needed? // TODO: is that actually needed?
// idea: make it consistent with the issued credential and its vX serde
pub fn try_from_recovered_bytes(bytes: &[u8]) -> Result<Self, Error> { pub fn try_from_recovered_bytes(bytes: &[u8]) -> Result<Self, Error> {
use bincode::Options; use bincode::Options;
make_recovery_bincode_serializer() Ok(make_recovery_bincode_serializer().deserialize(bytes)?)
.deserialize(bytes)
.map_err(|source| Error::RecoveryCredentialDeserializationFailure { source })
} }
} }
@@ -330,18 +327,3 @@ fn make_recovery_bincode_serializer() -> impl bincode::Options {
.with_big_endian() .with_big_endian()
.with_varint_encoding() .with_varint_encoding()
} }
#[cfg(test)]
mod tests {
use super::*;
fn assert_zeroize_on_drop<T: ZeroizeOnDrop>() {}
fn assert_zeroize<T: Zeroize>() {}
#[test]
fn credential_is_zeroized() {
assert_zeroize::<IssuanceBandwidthCredential>();
assert_zeroize_on_drop::<IssuanceBandwidthCredential>();
}
}
@@ -116,15 +116,6 @@ impl IssuedBandwidthCredential {
} }
} }
pub fn try_unpack(bytes: &[u8], revision: impl Into<Option<u8>>) -> Result<Self, Error> {
let revision = revision.into().unwrap_or(CURRENT_SERIALIZATION_REVISION);
match revision {
1 => Self::unpack_v1(bytes),
_ => Err(Error::UnknownSerializationRevision { revision }),
}
}
pub fn epoch_id(&self) -> EpochId { pub fn epoch_id(&self) -> EpochId {
self.epoch_id self.epoch_id
} }
@@ -147,12 +138,7 @@ impl IssuedBandwidthCredential {
/// Unpack (deserialize) the credential data from the given bytes using v1 serializer. /// Unpack (deserialize) the credential data from the given bytes using v1 serializer.
pub fn unpack_v1(bytes: &[u8]) -> Result<Self, Error> { pub fn unpack_v1(bytes: &[u8]) -> Result<Self, Error> {
use bincode::Options; use bincode::Options;
make_storable_bincode_serializer() Ok(make_storable_bincode_serializer().deserialize(bytes)?)
.deserialize(bytes)
.map_err(|source| Error::SerializationFailure {
source,
revision: 1,
})
} }
pub fn randomise_signature(&mut self) { pub fn randomise_signature(&mut self) {
@@ -205,18 +191,3 @@ fn make_storable_bincode_serializer() -> impl bincode::Options {
.with_big_endian() .with_big_endian()
.with_varint_encoding() .with_varint_encoding()
} }
#[cfg(test)]
mod tests {
use super::*;
fn assert_zeroize_on_drop<T: ZeroizeOnDrop>() {}
fn assert_zeroize<T: Zeroize>() {}
#[test]
fn credential_is_zeroized() {
assert_zeroize::<IssuedBandwidthCredential>();
assert_zeroize_on_drop::<IssuedBandwidthCredential>();
}
}
+2 -13
View File
@@ -5,7 +5,6 @@ use nym_credentials_interface::CoconutError;
use nym_crypto::asymmetric::encryption::KeyRecoveryError; use nym_crypto::asymmetric::encryption::KeyRecoveryError;
use nym_validator_client::ValidatorClientError; use nym_validator_client::ValidatorClientError;
use crate::coconut::bandwidth::issued::CURRENT_SERIALIZATION_REVISION;
use thiserror::Error; use thiserror::Error;
#[derive(Debug, Error)] #[derive(Debug, Error)]
@@ -13,18 +12,8 @@ pub enum Error {
#[error("IO error")] #[error("IO error")]
IOError(#[from] std::io::Error), IOError(#[from] std::io::Error),
#[error("failed to deserialize a recovery credential: {source}")] #[error("failed to (de)serialize credential structure: {0}")]
RecoveryCredentialDeserializationFailure { source: bincode::Error }, SerializationFailure(#[from] bincode::Error),
#[error("failed to (de)serialize provided credential using revision {revision}: {source}")]
SerializationFailure {
#[source]
source: bincode::Error,
revision: u8,
},
#[error("unknown credential serializatio revision {revision}. the current (and max supported) version is {CURRENT_SERIALIZATION_REVISION}")]
UnknownSerializationRevision { revision: u8 },
#[error("The detailed description is yet to be determined")] #[error("The detailed description is yet to be determined")]
BandwidthCredentialError, BandwidthCredentialError,
-1
View File
@@ -9,4 +9,3 @@ pub use coconut::bandwidth::{
IssuedBandwidthCredential, IssuedBandwidthCredential,
}; };
pub use coconut::utils::{obtain_aggregate_signature, obtain_aggregate_verification_key}; pub use coconut::utils::{obtain_aggregate_signature, obtain_aggregate_verification_key};
pub use error::Error;
+1 -25
View File
@@ -1,32 +1,8 @@
use serde::{Deserialize, Serialize};
use std::fmt::{Display, Formatter};
use std::net::{Ipv4Addr, Ipv6Addr};
pub mod codec; pub mod codec;
pub mod request; pub mod request;
pub mod response; pub mod response;
// version 3: initial version pub const CURRENT_VERSION: u8 = 3;
// version 4: IPv6 support
pub const CURRENT_VERSION: u8 = 4;
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct IpPair {
pub ipv4: Ipv4Addr,
pub ipv6: Ipv6Addr,
}
impl IpPair {
pub fn new(ipv4: Ipv4Addr, ipv6: Ipv6Addr) -> Self {
IpPair { ipv4, ipv6 }
}
}
impl Display for IpPair {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
writeln!(f, "IPv4: {}, IPV6: {}", self.ipv4, self.ipv6)
}
}
fn make_bincode_serializer() -> impl bincode::Options { fn make_bincode_serializer() -> impl bincode::Options {
use bincode::Options; use bincode::Options;
+9 -9
View File
@@ -1,7 +1,9 @@
use std::net::IpAddr;
use nym_sphinx::addressing::clients::Recipient; use nym_sphinx::addressing::clients::Recipient;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{make_bincode_serializer, IpPair, CURRENT_VERSION}; use crate::{make_bincode_serializer, CURRENT_VERSION};
fn generate_random() -> u64 { fn generate_random() -> u64 {
use rand::RngCore; use rand::RngCore;
@@ -17,7 +19,7 @@ pub struct IpPacketRequest {
impl IpPacketRequest { impl IpPacketRequest {
pub fn new_static_connect_request( pub fn new_static_connect_request(
ips: IpPair, ip: IpAddr,
reply_to: Recipient, reply_to: Recipient,
reply_to_hops: Option<u8>, reply_to_hops: Option<u8>,
reply_to_avg_mix_delays: Option<f64>, reply_to_avg_mix_delays: Option<f64>,
@@ -29,7 +31,7 @@ impl IpPacketRequest {
version: CURRENT_VERSION, version: CURRENT_VERSION,
data: IpPacketRequestData::StaticConnect(StaticConnectRequest { data: IpPacketRequestData::StaticConnect(StaticConnectRequest {
request_id, request_id,
ips, ip,
reply_to, reply_to,
reply_to_hops, reply_to_hops,
reply_to_avg_mix_delays, reply_to_avg_mix_delays,
@@ -135,7 +137,7 @@ pub enum IpPacketRequestData {
pub struct StaticConnectRequest { pub struct StaticConnectRequest {
pub request_id: u64, pub request_id: u64,
pub ips: IpPair, pub ip: IpAddr,
// The nym-address the response should be sent back to // The nym-address the response should be sent back to
pub reply_to: Recipient, pub reply_to: Recipient,
@@ -208,8 +210,6 @@ pub struct HealthRequest {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use std::net::{Ipv4Addr, Ipv6Addr};
use std::str::FromStr;
#[test] #[test]
fn check_size_of_request() { fn check_size_of_request() {
@@ -218,15 +218,15 @@ mod tests {
data: IpPacketRequestData::StaticConnect( data: IpPacketRequestData::StaticConnect(
StaticConnectRequest { StaticConnectRequest {
request_id: 123, request_id: 123,
ips: IpPair::new(Ipv4Addr::from_str("10.0.0.1").unwrap(), Ipv6Addr::from_str("2001:db8:a160::1").unwrap()), ip: IpAddr::from([10, 0, 0, 1]),
reply_to: Recipient::try_from_base58_string("D1rrpsysCGCYXy9saP8y3kmNpGtJZUXN9SvFoUcqAsM9.9Ssso1ea5NfkbMASdiseDSjTN1fSWda5SgEVjdSN4CvV@GJqd3ZxpXWSNxTfx7B1pPtswpetH4LnJdFeLeuY5KUuN").unwrap(), reply_to: Recipient::try_from_base58_string("D1rrpsysCGCYXy9saP8y3kmNpGtJZUXN9SvFoUcqAsM9.9Ssso1ea5NfkbMASdiseDSjTN1fSWda5SgEVjdSN4CvV@GJqd3ZxpXWSNxTfx7B1pPtswpetH4LnJdFeLeuY5KUuN").unwrap(),
reply_to_hops: None, reply_to_hops: None,
reply_to_avg_mix_delays: None, reply_to_avg_mix_delays: None,
buffer_timeout: None, buffer_timeout: None,
}, },
), )
}; };
assert_eq!(connect.to_bytes().unwrap().len(), 123); assert_eq!(connect.to_bytes().unwrap().len(), 108);
} }
#[test] #[test]
+6 -4
View File
@@ -1,7 +1,9 @@
use std::net::IpAddr;
use nym_sphinx::addressing::clients::Recipient; use nym_sphinx::addressing::clients::Recipient;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{make_bincode_serializer, IpPair, CURRENT_VERSION}; use crate::{make_bincode_serializer, CURRENT_VERSION};
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct IpPacketResponse { pub struct IpPacketResponse {
@@ -36,13 +38,13 @@ impl IpPacketResponse {
} }
} }
pub fn new_dynamic_connect_success(request_id: u64, reply_to: Recipient, ips: IpPair) -> Self { pub fn new_dynamic_connect_success(request_id: u64, reply_to: Recipient, ip: IpAddr) -> Self {
Self { Self {
version: CURRENT_VERSION, version: CURRENT_VERSION,
data: IpPacketResponseData::DynamicConnect(DynamicConnectResponse { data: IpPacketResponseData::DynamicConnect(DynamicConnectResponse {
request_id, request_id,
reply_to, reply_to,
reply: DynamicConnectResponseReply::Success(DynamicConnectSuccess { ips }), reply: DynamicConnectResponseReply::Success(DynamicConnectSuccess { ip }),
}), }),
} }
} }
@@ -261,7 +263,7 @@ impl DynamicConnectResponseReply {
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct DynamicConnectSuccess { pub struct DynamicConnectSuccess {
pub ips: IpPair, pub ip: IpAddr,
} }
#[derive(Clone, Debug, Serialize, Deserialize, thiserror::Error)] #[derive(Clone, Debug, Serialize, Deserialize, thiserror::Error)]
-20
View File
@@ -1,20 +0,0 @@
[package]
name = "nym-id"
version = "0.1.0"
authors.workspace = true
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
edition.workspace = true
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
thiserror.workspace = true
time.workspace = true
tracing.workspace = true
zeroize.workspace = true
nym-credential-storage = { path = "../credential-storage" }
nym-credentials = { path = "../credentials" }
-20
View File
@@ -1,20 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::error::Error;
use thiserror::Error;
use time::OffsetDateTime;
#[derive(Debug, Error)]
pub enum NymIdError {
#[error("failed to deserialize provided credential: {source}")]
CredentialDeserializationFailure { source: nym_credentials::Error },
#[error("attempted to import an expired credential (it expired on {expiration})")]
ExpiredCredentialImport { expiration: OffsetDateTime },
#[error("failed to store credential in the provided store: {source}")]
StorageError {
source: Box<dyn Error + Send + Sync>,
},
}
-71
View File
@@ -1,71 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::NymIdError;
use nym_credential_storage::models::StorableIssuedCredential;
use nym_credential_storage::storage::Storage;
use nym_credentials::coconut::bandwidth::issued::BandwidthCredentialIssuedDataVariant;
use nym_credentials::IssuedBandwidthCredential;
use tracing::{debug, warn};
use zeroize::Zeroizing;
pub async fn import_credential<S>(
credentials_store: S,
raw_credential: Vec<u8>,
credential_version: impl Into<Option<u8>>,
) -> Result<(), NymIdError>
where
S: Storage,
<S as Storage>::StorageError: Send + Sync + 'static,
{
let raw_credential = Zeroizing::new(raw_credential);
// note: the type itself implements ZeroizeOnDrop
let credential = IssuedBandwidthCredential::try_unpack(&raw_credential, credential_version)
.map_err(|source| NymIdError::CredentialDeserializationFailure { source })?;
debug!(
"attempting to import credential of type {}",
credential.typ()
);
match credential.variant_data() {
BandwidthCredentialIssuedDataVariant::Voucher(voucher_info) => {
debug!("with value of {}", voucher_info.value())
}
BandwidthCredentialIssuedDataVariant::FreePass(freepass_info) => {
debug!("with expiry at {}", freepass_info.expiry_date());
if freepass_info.expired() {
warn!("the free pass has already expired!");
// technically we can import it, but the gateway will just reject it so what's the point
return Err(NymIdError::ExpiredCredentialImport {
expiration: freepass_info.expiry_date(),
});
}
}
}
// SAFETY:
// for the epoch to run over u32::MAX, we'd have to advance it for few centuries every block...
// the alternative is a very particularly malformed serialized data, but at that point blowing up is the right call
// because we can't rely on it anyway
#[allow(clippy::expect_used)]
let storable = StorableIssuedCredential {
serialization_revision: credential.current_serialization_revision(),
credential_data: &raw_credential,
credential_type: credential.typ().to_string(),
epoch_id: credential
.epoch_id()
.try_into()
.expect("our epoch is has run over u32::MAX!"),
};
credentials_store
.insert_issued_credential(storable)
.await
.map_err(|source| NymIdError::StorageError {
source: Box::new(source),
})?;
Ok(())
}
-11
View File
@@ -1,11 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
#![warn(clippy::expect_used)]
#![warn(clippy::unwrap_used)]
pub mod error;
pub mod import_credential;
pub use error::NymIdError;
pub use import_credential::import_credential;
-9
View File
@@ -1,7 +1,6 @@
use crate::socks::types::SocksProxyError; use crate::socks::types::SocksProxyError;
use nym_client_core::error::ClientCoreError; use nym_client_core::error::ClientCoreError;
use nym_socks5_requests::{ConnectionError, ConnectionId}; use nym_socks5_requests::{ConnectionError, ConnectionId};
use nym_task::manager::TaskStatusTrait;
#[derive(thiserror::Error, Debug)] #[derive(thiserror::Error, Debug)]
pub enum Socks5ClientCoreError { pub enum Socks5ClientCoreError {
@@ -21,14 +20,6 @@ pub enum Socks5ClientCoreError {
}, },
} }
#[derive(thiserror::Error, Debug)]
pub enum Socks5ClientCoreStatusMessage {
#[error(transparent)]
Socks5Error(#[from] Socks5ClientCoreError),
}
// impl TaskStatusTrait for Socks5ClientCoreStatusMessage {}
impl From<ConnectionError> for Socks5ClientCoreError { impl From<ConnectionError> for Socks5ClientCoreError {
fn from(value: ConnectionError) -> Self { fn from(value: ConnectionError) -> Self {
Socks5ClientCoreError::NetworkRequesterError { Socks5ClientCoreError::NetworkRequesterError {
@@ -1,7 +1,7 @@
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net> // Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::error::{Socks5ClientCoreError, Socks5ClientCoreStatusMessage}; use crate::error::Socks5ClientCoreError;
use futures::channel::mpsc; use futures::channel::mpsc;
use futures::StreamExt; use futures::StreamExt;
use log::*; use log::*;
@@ -136,8 +136,7 @@ impl MixnetResponseListener {
if let Some(received_responses) = received_responses { if let Some(received_responses) = received_responses {
for reconstructed_message in received_responses { for reconstructed_message in received_responses {
if let Err(err) = self.on_message(reconstructed_message) { if let Err(err) = self.on_message(reconstructed_message) {
let msg = Socks5ClientCoreStatusMessage::from(err); self.shutdown.send_status_msg(Box::new(err));
self.shutdown.send_status_msg(Box::new(msg));
} }
} }
} else { } else {
+1 -26
View File
@@ -3,7 +3,6 @@
use futures::{future::pending, FutureExt, SinkExt, StreamExt}; use futures::{future::pending, FutureExt, SinkExt, StreamExt};
use log::{log, Level}; use log::{log, Level};
use std::any::Any;
use std::future::Future; use std::future::Future;
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use std::{error::Error, time::Duration}; use std::{error::Error, time::Duration};
@@ -24,7 +23,7 @@ pub(crate) type SentError = Box<dyn Error + Send + Sync>;
type ErrorSender = mpsc::UnboundedSender<SentError>; type ErrorSender = mpsc::UnboundedSender<SentError>;
type ErrorReceiver = mpsc::UnboundedReceiver<SentError>; type ErrorReceiver = mpsc::UnboundedReceiver<SentError>;
pub type SentStatus = Box<dyn TaskStatusTrait + Send + Sync>; pub type SentStatus = Box<dyn Error + Send + Sync>;
pub type StatusSender = futures::channel::mpsc::Sender<SentStatus>; pub type StatusSender = futures::channel::mpsc::Sender<SentStatus>;
pub type StatusReceiver = futures::channel::mpsc::Receiver<SentStatus>; pub type StatusReceiver = futures::channel::mpsc::Receiver<SentStatus>;
@@ -49,30 +48,6 @@ pub enum TaskStatus {
Ready, Ready,
} }
pub trait TaskStatusTrait:
std::fmt::Debug + std::fmt::Display + Send + Sync + 'static + Any
{
// As Any requires 'static, it implicitly enforces the 'static lifetime here as well.
// This method tries to cast the trait object back to a reference of its concrete type.
fn as_any(&self) -> &dyn Any;
// Optionally, for downcasting to mutable references.
fn as_any_mut(&mut self) -> &mut dyn Any;
}
impl<T: std::fmt::Debug + std::fmt::Display + Send + Sync + Any + 'static> TaskStatusTrait for T {
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
// impl TaskStatusTrait for TaskStatus {}
/// Listens to status and error messages from tasks, as well as notifying them to gracefully /// Listens to status and error messages from tasks, as well as notifying them to gracefully
/// shutdown. Keeps track of if task stop unexpectedly, such as in a panic. /// shutdown. Keeps track of if task stop unexpectedly, such as in a panic.
#[derive(Debug)] #[derive(Debug)]
+14 -38
View File
@@ -1,4 +1,3 @@
use std::net::Ipv6Addr;
use std::{ use std::{
collections::HashMap, collections::HashMap,
net::{IpAddr, Ipv4Addr}, net::{IpAddr, Ipv4Addr},
@@ -20,12 +19,6 @@ const TUN_WRITE_TIMEOUT_MS: u64 = 1000;
#[derive(thiserror::Error, Debug)] #[derive(thiserror::Error, Debug)]
pub enum TunDeviceError { pub enum TunDeviceError {
#[error("{0}")]
IO(#[from] std::io::Error),
#[error("{0}")]
TokioTun(#[from] tokio_tun::Error),
#[error("timeout writing to tun device, dropping packet")] #[error("timeout writing to tun device, dropping packet")]
TunWriteTimeout, TunWriteTimeout,
@@ -51,18 +44,14 @@ pub enum TunDeviceError {
FailedToLockPeer, FailedToLockPeer,
} }
fn setup_tokio_tun_device( fn setup_tokio_tun_device(name: &str, address: Ipv4Addr, netmask: Ipv4Addr) -> tokio_tun::Tun {
name: &str,
address: Ipv4Addr,
netmask: Ipv4Addr,
) -> Result<tokio_tun::Tun, TunDeviceError> {
log::info!("Creating TUN device with: address={address}, netmask={netmask}"); log::info!("Creating TUN device with: address={address}, netmask={netmask}");
// Read MTU size from env variable NYM_MTU_SIZE, else default to 1420. // Read MTU size from env variable NYM_MTU_SIZE, else default to 1420.
let mtu = std::env::var("NYM_MTU_SIZE") let mtu = std::env::var("NYM_MTU_SIZE")
.map(|mtu| mtu.parse().expect("NYM_MTU_SIZE must be a valid integer")) .map(|mtu| mtu.parse().expect("NYM_MTU_SIZE must be a valid integer"))
.unwrap_or(1420); .unwrap_or(1420);
log::info!("Using MTU size: {mtu}"); log::info!("Using MTU size: {mtu}");
Ok(tokio_tun::Tun::builder() tokio_tun::Tun::builder()
.name(name) .name(name)
.tap(false) .tap(false)
.packet_info(false) .packet_info(false)
@@ -70,7 +59,8 @@ fn setup_tokio_tun_device(
.up() .up()
.address(address) .address(address)
.netmask(netmask) .netmask(netmask)
.try_build()?) .try_build()
.expect("Failed to setup tun device, do you have permission?")
} }
pub struct TunDevice { pub struct TunDevice {
@@ -113,18 +103,16 @@ pub struct NatInner {
pub struct TunDeviceConfig { pub struct TunDeviceConfig {
pub base_name: String, pub base_name: String,
pub ipv4: Ipv4Addr, pub ip: Ipv4Addr,
pub netmaskv4: Ipv4Addr, pub netmask: Ipv4Addr,
pub ipv6: Ipv6Addr,
pub netmaskv6: String,
} }
impl TunDevice { impl TunDevice {
pub fn new( pub fn new(
routing_mode: RoutingMode, routing_mode: RoutingMode,
config: TunDeviceConfig, config: TunDeviceConfig,
) -> Result<(Self, TunTaskTx, TunTaskResponseRx), TunDeviceError> { ) -> (Self, TunTaskTx, TunTaskResponseRx) {
let tun = Self::new_device_only(config)?; let tun = Self::new_device_only(config);
// Channels to communicate with the other tasks // Channels to communicate with the other tasks
let (tun_task_tx, tun_task_rx) = tun_task_channel(); let (tun_task_tx, tun_task_rx) = tun_task_channel();
@@ -137,32 +125,20 @@ impl TunDevice {
routing_mode, routing_mode,
}; };
Ok((tun_device, tun_task_tx, tun_task_response_rx)) (tun_device, tun_task_tx, tun_task_response_rx)
} }
pub fn new_device_only(config: TunDeviceConfig) -> Result<tokio_tun::Tun, TunDeviceError> { pub fn new_device_only(config: TunDeviceConfig) -> tokio_tun::Tun {
let TunDeviceConfig { let TunDeviceConfig {
base_name, base_name,
ipv4, ip,
netmaskv4, netmask,
ipv6,
netmaskv6,
} = config; } = config;
let name = format!("{base_name}%d"); let name = format!("{base_name}%d");
let tun = setup_tokio_tun_device(&name, ipv4, netmaskv4)?; let tun = setup_tokio_tun_device(&name, ip, netmask);
log::info!("Created TUN device: {}", tun.name()); log::info!("Created TUN device: {}", tun.name());
std::process::Command::new("ip") tun
.args([
"-6",
"addr",
"add",
&format!("{}/{}", ipv6, netmaskv6),
"dev",
&tun.name(),
])
.output()?;
Ok(tun)
} }
// Send outbound packets out on the wild internet // Send outbound packets out on the wild internet
+3 -6
View File
@@ -1,7 +1,7 @@
[book] [book]
title = "Nym Operators Guide" title = "Nym Docs"
authors = ["Max Hampshire, Serinko, Alexia Lorenza Martinel"] authors = ["Max Hampshire, Serinko, Alexia Lorenza Martinel"]
description = "Everything needed to run Nym Mixnet components" description = "Nym technical documentation"
language = "en" language = "en"
multilingual = false # for the moment - ideally work on chinese, brazillian portugese, spanish next multilingual = false # for the moment - ideally work on chinese, brazillian portugese, spanish next
src = "src" src = "src"
@@ -31,7 +31,7 @@ assets_version = "3.0.0" # do not edit: managed by `mdbook-admonish install`
minimum_rust_version = "1.66" minimum_rust_version = "1.66"
wallet_release_version = "1.2.8" wallet_release_version = "1.2.8"
# nym-vpn related variables # nym-vpn related variables
nym_vpn_latest_binary_url = "https://github.com/nymtech/nym/releases/tag/nym-vpn-alpha-0.0.4" nym_vpn_latest_binary_url = "https://github.com/nymtech/nym/releases/tag/nym-vpn-alpha-0.0.3"
nym_vpn_form_url = "https://opnform.com/forms/nymvpn-user-research-at-37c3-yccqko-2" nym_vpn_form_url = "https://opnform.com/forms/nymvpn-user-research-at-37c3-yccqko-2"
[preprocessor.last-changed] [preprocessor.last-changed]
@@ -44,9 +44,6 @@ renderer = ["html"]
# more pre-processor plugins to look into from https://github.com/rust-lang/mdBook/wiki/Third-party-plugins & https://lib.rs/keywords/mdbook-preprocessor # more pre-processor plugins to look into from https://github.com/rust-lang/mdBook/wiki/Third-party-plugins & https://lib.rs/keywords/mdbook-preprocessor
# mdbook-i18n # mdbook-i18n
# [preprocessor.api-call]
# command = "$(tokens=$(curl -L https://api.nymtech.net/cosmos/staking/v1beta1/pool | jq 'values[\"pool\"][\"bonded_tokens\"]') && echo ${tokens:0:2},${tokens:2:2})"
######### #########
# BUILD # # BUILD #
######### #########
-6
View File
@@ -23,12 +23,6 @@
- [Automatic Node Upgrade: Nymvisor Setup and Usage](nodes/nymvisor-upgrade.md) - [Automatic Node Upgrade: Nymvisor Setup and Usage](nodes/nymvisor-upgrade.md)
- [Troubleshooting](nodes/troubleshooting.md) - [Troubleshooting](nodes/troubleshooting.md)
# Token Economics
<!-- - [Fair Mixnet](tokenomics/fair-mixnet.md) -->
<!-- - [Mixnet: Nym Node Rewards](tokenomics/mixnet-rewards.md) -->
- [Nyx: Validator Rewards](tokenomics/validator-rewards.md)
# FAQ # FAQ
- [Mix Nodes](faq/mixnodes-faq.md) - [Mix Nodes](faq/mixnodes-faq.md)
@@ -23,7 +23,7 @@ systemctl start <NODE>.service
journalctl -f -u <NODE>.service # to monitor log of you node journalctl -f -u <NODE>.service # to monitor log of you node
``` ```
If these steps are too difficult and you prefer to automate the process, try to setup your flow with [Nymvisor](nymvisor-upgrade.md). If these steps are too difficult and you prefer to just run a script, you can use [ExploreNYM script](https://github.com/ExploreNYM/bash-tool) or one done by [Nym developers](https://gist.github.com/tommyv1987/4dca7cc175b70742c9ecb3d072eb8539).
> In case of a Network Requester this is all, the following step is only for Mix Nodes and Gateways. > In case of a Network Requester this is all, the following step is only for Mix Nodes and Gateways.
@@ -98,3 +98,4 @@ The most common reason for your validator being jailed is that your validator is
Running the command `df -H` will return the size of the various partitions of your VPS. Running the command `df -H` will return the size of the various partitions of your VPS.
If the `/dev/sda` partition is almost full, try pruning some of the `.gz` syslog archives and restart your validator process. If the `/dev/sda` partition is almost full, try pruning some of the `.gz` syslog archives and restart your validator process.
@@ -1,7 +0,0 @@
#!/bin/bash
stake_unyx=$(curl -s -L https://api.nymtech.net/cosmos/staking/v1beta1/pool | jq 'values["pool"]["bonded_tokens"]')
stake_unyx=$(python -c "print(int($stake_unyx))")
stake_nyx=$(python -c "print($stake_unyx / 1000000)")
voting288k_percent=$(python -c "print(288000 / $stake_nyx * 100)")
echo ${voting288k_percent:0:4}%
@@ -1,4 +0,0 @@
#!/bin/bash
stake=$(curl -s -L https://api.nymtech.net/cosmos/staking/v1beta1/pool | jq 'values["pool"]["bonded_tokens"]')
echo ${stake:1:2}.${stake:3:3}
@@ -1,55 +0,0 @@
# Nyx Validator Rewards
## Summary
* Nyx Validators are rewarded in NYM tokens from the Nym mixmining pool and increasingly from apps that run on the Nym mixnet, the first of which is the NymVPN.
* Validators are rewarded for two different types of work: signing blocks in the Nyx chain and running the NymAPI to monitor mixnet routing and sign zk-nym credentials.
* New validators can join via a NYM-to-NYX swap contract. The contract will not allow more than 1% of total stake increase per month to prevent sudden hostile takeovers. Current stake is <!-- cmdrun ../scripts/nyx-total-stake.sh --> million Nyx. Rate: 1:4.8 ~ 288k NYX for 60k NYM => <!-- cmdrun ../scripts/nyx-percent-stake.sh --> voting power
* NYX tokens serve no other purpose than self-delegation for voting power and governance. All rewards are in NYM and distributed directly to the validators self-delegation address and are not distributed to stakers.
* The contract will only allow swapping NYM to NYX and will **not** allow exchanging NYX back to NYM. A NYX holder who wishes to sell their NYX stake will have to do so via OTC trades.
## Validator Rewards
Nyx Validators perform two types of work for which they will be rewarded:
### 1. Signing blocks in the Nyx chain
A “block signing monitor" monitors blocks being produced on the Nyx chain and gathers the signatures present on every block. After an epoch ends, the monitor will assess performance of a validator and distribute tokens (to the self-delegation wallet) proportional to the voting period and uptime of the validator.
### 2. Running the NymAPI to monitor Mixnet routing and sign zk-nyms (Nyms anonymous credentials)
Validator rewards initially come from the Nym mixmining pool with additional rewards increasingly coming from paid applications running on the Nym mixnet. The first paid application is the NymVPN. Nyx validators will be rewarded for their work directly in NYM tokens to their validator self-delegation address.
1. **From mixmining pool** - at a rate of 1000 NYM per hour, of which 2/3 are distributed for signing blocks and 1/3 for zk-nyms. These are stable in NYM, and therefore will fluctuate in their fiat value depending on exchange rate.
2. **From vpn user subscriptions** - the rate is tied to the growth of NymVPN subscriptions and will be stable in fiat, fluctuating in NYM depending on exchange rate. 1/3 will be distributed for signing blocks and 2/3 for zk-nyms.
| Source | Signing blocks | Running NymAPI | Currency |
| :-- | --: | --: | :---: |
| Mixmining pool | 2/3 | 1/3 | NYM |
| NymVPN | 1/3 | 2/3 | fiat |
#### zk-nyms
The zk-nyms enable people to anonymously prove access rights to the upcoming NymVPN client without having to reveal payment details that might compromise their privacy. This is the first of what we imagine to be many possible use-cases for the zk-nym scheme.
### Allocation of Rewards from Nym mixmining pool
Rewards for validators will be distributed at an hourly rate from the mixmining pool. The amount is 1000 NYM per hour to be distributed among all validators. The fraction of mixmining rewards received by each individual validator is proportional to its contributions to the network.
Two thirds of the available rewards (670 NYM per hour) are distributed proportionally to each validators share when signing blocks in the chain, for which tx fees are also received in the same proportion; while the last third (330 NYM per hour) is allocated to validators running the NymAPI, proportionally to their contribution to signing zk-nym credentials.
The rewards are stable in NYM and fluctuate in their fiat value depending on the exchange rate of NYM tokens.
## Permissionless Nyx Chain
To allow new validators to join Nyx chain a new smart contract will be set up to release NYX in exchange for NYM. This contract allows a limited amount of NYM tokens to be deposited per month. The deposited NYM tokens are added to the mixmining pool and thus contribute to future rewards of all nodes and validators.
The smart contract will have two parameters:
1. the maximum amount of NYX available for purchase per month
2. the NYM-to-NYX exchange rate offered by the contract
### Maximum Amount of NYX Available for Purchase per Month
The contract will not allow more than 1% of total stake increase per month to prevent sudden hostile takeovers. Current stake level is <!-- cmdrun ../scripts/nyx-total-stake.sh --> million Nyx.
+1 -3
View File
@@ -6,9 +6,7 @@ use thiserror::Error;
use url::Url; use url::Url;
// Re-export request types // Re-export request types
pub use nym_explorer_api_requests::{ pub use nym_explorer_api_requests::{PrettyDetailedGatewayBond, PrettyDetailedMixNodeBond};
Location, PrettyDetailedGatewayBond, PrettyDetailedMixNodeBond,
};
// Paths // Paths
const API_VERSION: &str = "v1"; const API_VERSION: &str = "v1";
+1 -1
View File
@@ -20,7 +20,7 @@ rust-version = "1.58.1"
axum = { workspace = true } axum = { workspace = true }
anyhow = { workspace = true } anyhow = { workspace = true }
bs58 = { workspace = true } bs58 = { workspace = true }
clap = { workspace = true, features = ["cargo", "derive"] } clap = { workspace = true, features = ["cargo", "derive", "env"] }
colored = "2.0" colored = "2.0"
cupid = "0.6.1" cupid = "0.6.1"
dirs = "4.0" dirs = "4.0"
+2 -1
View File
@@ -1,13 +1,14 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net> // Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
use crate::env::vars::*;
use clap::Args; use clap::Args;
use nym_bin_common::bin_info_owned; use nym_bin_common::bin_info_owned;
use nym_bin_common::output_format::OutputFormat; use nym_bin_common::output_format::OutputFormat;
#[derive(Args)] #[derive(Args)]
pub(crate) struct BuildInfo { pub(crate) struct BuildInfo {
#[clap(short, long, default_value_t = OutputFormat::default())] #[clap(short, long, default_value_t = OutputFormat::default(), env = MIXNODE_OUTPUT_ARG)]
output: OutputFormat, output: OutputFormat,
} }
+3 -1
View File
@@ -1,7 +1,9 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
use super::DEFAULT_MIXNODE_ID;
use crate::commands::try_load_current_config; use crate::commands::try_load_current_config;
use crate::env::vars::*;
use crate::node::node_description::NodeDescription; use crate::node::node_description::NodeDescription;
use clap::Args; use clap::Args;
use colored::Colorize; use colored::Colorize;
@@ -11,7 +13,7 @@ use std::io::Write;
#[derive(Args)] #[derive(Args)]
pub(crate) struct Describe { pub(crate) struct Describe {
/// The id of the mixnode you want to describe /// The id of the mixnode you want to describe
#[clap(long)] #[clap(long, default_value = DEFAULT_MIXNODE_ID, env = MIXNODE_ID_ARG)]
id: String, id: String,
/// Human readable name of this node /// Human readable name of this node
+23 -17
View File
@@ -1,46 +1,52 @@
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net> // Copyright 2020-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
use super::OverrideConfig; use super::OverrideConfig;
use super::DEFAULT_MIXNODE_ID;
use crate::commands::override_config; use crate::commands::override_config;
use crate::config::{ use crate::config::{
default_config_directory, default_config_filepath, default_data_directory, Config, default_config_directory, default_config_filepath, default_data_directory, Config,
}; };
use crate::env::vars::*;
use crate::node::MixNode; use crate::node::MixNode;
use clap::Args; use clap::Args;
use nym_bin_common::output_format::OutputFormat; use nym_bin_common::output_format::OutputFormat;
use nym_config::defaults::{
DEFAULT_HTTP_API_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT, DEFAULT_VERLOC_LISTENING_PORT,
};
use nym_config::helpers::inaddr_any;
use nym_crypto::asymmetric::{encryption, identity}; use nym_crypto::asymmetric::{encryption, identity};
use std::net::IpAddr; use std::net::IpAddr;
use std::{fs, io}; use std::{fs, io};
#[derive(Args, Clone)] #[derive(Args, Clone, Debug)]
pub(crate) struct Init { pub(crate) struct Init {
/// Id of the mixnode we want to create config for /// Id of the mixnode we want to create config for
#[clap(long)] #[clap(long, default_value = DEFAULT_MIXNODE_ID, env = MIXNODE_ID_ARG)]
id: String, id: String,
/// The host on which the mixnode will be running /// The host on which the mixnode will be running
#[clap(long)] #[clap(long, alias = "host", default_value_t = inaddr_any(), env = MIXNODE_LISTENING_ADDRESS_ARG)]
host: IpAddr, listening_address: IpAddr,
/// The port on which the mixnode will be listening for mix packets /// The port on which the mixnode will be listening for mix packets
#[clap(long)] #[clap(long, default_value_t = DEFAULT_MIX_LISTENING_PORT, env = MIXNODE_MIX_PORT_ARG)]
mix_port: Option<u16>, mix_port: u16,
/// The port on which the mixnode will be listening for verloc packets /// The port on which the mixnode will be listening for verloc packets
#[clap(long)] #[clap(long, default_value_t = DEFAULT_VERLOC_LISTENING_PORT, env = MIXNODE_VERLOC_PORT_ARG)]
verloc_port: Option<u16>, verloc_port: u16,
/// The port on which the mixnode will be listening for http requests /// The port on which the mixnode will be listening for http requests
#[clap(long)] #[clap(long, default_value_t = DEFAULT_HTTP_API_LISTENING_PORT, env = MIXNODE_HTTP_API_PORT_ARG)]
http_api_port: Option<u16>, http_api_port: u16,
/// Comma separated list of nym-api endpoints of the validators /// Comma separated list of nym-api endpoints of the validators
// the alias here is included for backwards compatibility (1.1.4 and before) // the alias here is included for backwards compatibility (1.1.4 and before)
#[clap(long, alias = "validators", value_delimiter = ',')] #[clap(long, alias = "validators", value_delimiter = ',', env = MIXNODE_NYM_APIS_ARG)]
nym_apis: Option<Vec<url::Url>>, nym_apis: Option<Vec<url::Url>>,
#[clap(short, long, default_value_t = OutputFormat::default())] #[clap(short, long, default_value_t = OutputFormat::default(), env = MIXNODE_OUTPUT_ARG)]
output: OutputFormat, output: OutputFormat,
} }
@@ -48,10 +54,10 @@ impl From<Init> for OverrideConfig {
fn from(init_config: Init) -> Self { fn from(init_config: Init) -> Self {
OverrideConfig { OverrideConfig {
id: init_config.id, id: init_config.id,
host: Some(init_config.host), listening_address: Some(init_config.listening_address),
mix_port: init_config.mix_port, mix_port: Some(init_config.mix_port),
verloc_port: init_config.verloc_port, verloc_port: Some(init_config.verloc_port),
http_api_port: init_config.http_api_port, http_api_port: Some(init_config.http_api_port),
nym_apis: init_config.nym_apis, nym_apis: init_config.nym_apis,
} }
} }
+4 -2
View File
@@ -16,6 +16,8 @@ use nym_crypto::bech32_address_validation;
use std::net::IpAddr; use std::net::IpAddr;
use std::process; use std::process;
pub const DEFAULT_MIXNODE_ID: &str = "nym-mixnode";
mod build_info; mod build_info;
mod describe; mod describe;
mod init; mod init;
@@ -54,7 +56,7 @@ pub(crate) enum Commands {
// Configuration that can be overridden. // Configuration that can be overridden.
struct OverrideConfig { struct OverrideConfig {
id: String, id: String,
host: Option<IpAddr>, listening_address: Option<IpAddr>,
mix_port: Option<u16>, mix_port: Option<u16>,
verloc_port: Option<u16>, verloc_port: Option<u16>,
http_api_port: Option<u16>, http_api_port: Option<u16>,
@@ -79,7 +81,7 @@ pub(crate) async fn execute(args: Cli) -> anyhow::Result<()> {
fn override_config(config: Config, args: OverrideConfig) -> Config { fn override_config(config: Config, args: OverrideConfig) -> Config {
config config
.with_optional(Config::with_listening_address, args.host) .with_optional(Config::with_listening_address, args.listening_address)
.with_optional(Config::with_mix_port, args.mix_port) .with_optional(Config::with_mix_port, args.mix_port)
.with_optional(Config::with_verloc_port, args.verloc_port) .with_optional(Config::with_verloc_port, args.verloc_port)
.with_optional(Config::with_http_api_port, args.http_api_port) .with_optional(Config::with_http_api_port, args.http_api_port)
+4 -2
View File
@@ -1,7 +1,9 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
use super::DEFAULT_MIXNODE_ID;
use crate::commands::try_load_current_config; use crate::commands::try_load_current_config;
use crate::env::vars::*;
use crate::node::MixNode; use crate::node::MixNode;
use clap::Args; use clap::Args;
use nym_bin_common::output_format::OutputFormat; use nym_bin_common::output_format::OutputFormat;
@@ -9,7 +11,7 @@ use nym_bin_common::output_format::OutputFormat;
#[derive(Args)] #[derive(Args)]
pub(crate) struct NodeDetails { pub(crate) struct NodeDetails {
/// The id of the mixnode you want to show details for /// The id of the mixnode you want to show details for
#[clap(long)] #[clap(long, default_value = DEFAULT_MIXNODE_ID, env = MIXNODE_ID_ARG)]
id: String, id: String,
#[clap(short, long, default_value_t = OutputFormat::default())] #[clap(short, long, default_value_t = OutputFormat::default())]
+12 -14
View File
@@ -2,47 +2,45 @@
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
use super::OverrideConfig; use super::OverrideConfig;
use super::DEFAULT_MIXNODE_ID;
use crate::commands::{override_config, try_load_current_config, version_check}; use crate::commands::{override_config, try_load_current_config, version_check};
use crate::env::vars::*;
use crate::node::MixNode; use crate::node::MixNode;
use anyhow::bail; use anyhow::bail;
use clap::Args; use clap::Args;
use log::error; use log::error;
use nym_bin_common::output_format::OutputFormat; use nym_bin_common::output_format::OutputFormat;
use nym_config::helpers::SPECIAL_ADDRESSES; use nym_config::helpers::SPECIAL_ADDRESSES;
use nym_validator_client::nyxd;
use std::net::IpAddr; use std::net::IpAddr;
#[derive(Args, Clone)] #[derive(Args, Clone)]
pub(crate) struct Run { pub(crate) struct Run {
/// Id of the nym-mixnode we want to run /// Id of the nym-mixnode we want to run
#[clap(long)] #[clap(long, default_value = DEFAULT_MIXNODE_ID, env = MIXNODE_ID_ARG)]
id: String, id: String,
/// The custom host on which the mixnode will be running /// The custom host on which the mixnode will be running
#[clap(long)] #[clap(long, alias = "host", env = MIXNODE_LISTENING_ADDRESS_ARG)]
host: Option<IpAddr>, listening_address: Option<IpAddr>,
/// The wallet address you will use to bond this mixnode, e.g. nymt1z9egw0knv47nmur0p8vk4rcx59h9gg4zuxrrr9
#[clap(long)]
wallet_address: Option<nyxd::AccountId>,
/// The port on which the mixnode will be listening for mix packets /// The port on which the mixnode will be listening for mix packets
#[clap(long)] #[clap(long, env = MIXNODE_MIX_PORT_ARG)]
mix_port: Option<u16>, mix_port: Option<u16>,
/// The port on which the mixnode will be listening for verloc packets /// The port on which the mixnode will be listening for verloc packets
#[clap(long)] #[clap(long, env = MIXNODE_VERLOC_PORT_ARG)]
verloc_port: Option<u16>, verloc_port: Option<u16>,
/// The port on which the mixnode will be listening for http requests /// The port on which the mixnode will be listening for http requests
#[clap(long)] #[clap(long, env = MIXNODE_HTTP_API_PORT_ARG)]
http_api_port: Option<u16>, http_api_port: Option<u16>,
/// Comma separated list of nym-api endpoints of the validators /// Comma separated list of nym-api endpoints of the validators
// the alias here is included for backwards compatibility (1.1.4 and before) // the alias here is included for backwards compatibility (1.1.4 and before)
#[clap(long, alias = "validators", value_delimiter = ',')] #[clap(long, alias = "validators", value_delimiter = ',', env = MIXNODE_NYM_APIS_ARG)]
nym_apis: Option<Vec<url::Url>>, nym_apis: Option<Vec<url::Url>>,
#[clap(short, long, default_value_t = OutputFormat::default())] #[clap(short, long, default_value_t = OutputFormat::default(), env = MIXNODE_OUTPUT_ARG)]
output: OutputFormat, output: OutputFormat,
} }
@@ -50,7 +48,7 @@ impl From<Run> for OverrideConfig {
fn from(run_config: Run) -> Self { fn from(run_config: Run) -> Self {
OverrideConfig { OverrideConfig {
id: run_config.id, id: run_config.id,
host: run_config.host, listening_address: run_config.listening_address,
mix_port: run_config.mix_port, mix_port: run_config.mix_port,
verloc_port: run_config.verloc_port, verloc_port: run_config.verloc_port,
http_api_port: run_config.http_api_port, http_api_port: run_config.http_api_port,
+5 -4
View File
@@ -1,7 +1,10 @@
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net> // Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
use super::version_check;
use super::DEFAULT_MIXNODE_ID;
use crate::commands::{try_load_current_config, validate_bech32_address_or_exit}; use crate::commands::{try_load_current_config, validate_bech32_address_or_exit};
use crate::env::vars::*;
use crate::node::helpers::load_identity_keys; use crate::node::helpers::load_identity_keys;
use anyhow::{bail, Result}; use anyhow::{bail, Result};
use clap::{ArgGroup, Args}; use clap::{ArgGroup, Args};
@@ -12,13 +15,11 @@ use nym_types::helpers::ConsoleSigningOutput;
use nym_validator_client::nyxd; use nym_validator_client::nyxd;
use std::convert::TryFrom; use std::convert::TryFrom;
use super::version_check;
#[derive(Args, Clone)] #[derive(Args, Clone)]
#[clap(group(ArgGroup::new("sign").required(true).args(&["wallet_address", "text", "contract_msg"])))] #[clap(group(ArgGroup::new("sign").required(true).args(&["wallet_address", "text", "contract_msg"])))]
pub(crate) struct Sign { pub(crate) struct Sign {
/// The id of the mixnode you want to sign with /// The id of the mixnode you want to sign with
#[clap(long)] #[clap(long, default_value = DEFAULT_MIXNODE_ID, env = MIXNODE_ID_ARG)]
id: String, id: String,
/// Signs your blockchain address with your identity key /// Signs your blockchain address with your identity key
@@ -34,7 +35,7 @@ pub(crate) struct Sign {
#[clap(long)] #[clap(long)]
contract_msg: Option<String>, contract_msg: Option<String>,
#[clap(short, long, default_value_t = OutputFormat::default())] #[clap(short, long, default_value_t = OutputFormat::default(), env = MIXNODE_OUTPUT_ARG)]
output: OutputFormat, output: OutputFormat,
} }
+12
View File
@@ -0,0 +1,12 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
pub mod vars {
pub const MIXNODE_ID_ARG: &str = "NYM_MIXNODE_ID";
pub const MIXNODE_LISTENING_ADDRESS_ARG: &str = "NYM_MIXNODE_LISTENING_ADDRESS";
pub const MIXNODE_MIX_PORT_ARG: &str = "NYM_MIXNODE_MIX_PORT";
pub const MIXNODE_VERLOC_PORT_ARG: &str = "NYM_MIXNODE_VERLOC_PORT";
pub const MIXNODE_HTTP_API_PORT_ARG: &str = "NYM_MIXNODE_HTTP_API_PORT";
pub const MIXNODE_NYM_APIS_ARG: &str = "NYM_MIXNODE_NYM_APIS";
pub const MIXNODE_OUTPUT_ARG: &str = "NYM_MIXNODE_OUTPUT";
}
+1
View File
@@ -18,6 +18,7 @@ use tracing::instrument;
mod commands; mod commands;
mod config; mod config;
mod env;
pub(crate) mod error; pub(crate) mod error;
mod node; mod node;
+10 -1
View File
@@ -5516,6 +5516,7 @@ dependencies = [
"wasm-bindgen-futures", "wasm-bindgen-futures",
"wasm-streams", "wasm-streams",
"web-sys", "web-sys",
"webpki-roots 0.25.4",
"winreg 0.50.0", "winreg 0.50.0",
] ]
@@ -6482,7 +6483,7 @@ dependencies = [
"thiserror", "thiserror",
"tokio-stream", "tokio-stream",
"url", "url",
"webpki-roots", "webpki-roots 0.22.6",
] ]
[[package]] [[package]]
@@ -7271,6 +7272,7 @@ checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c"
dependencies = [ dependencies = [
"futures-util", "futures-util",
"log", "log",
"rustls 0.21.7",
"tokio", "tokio",
"tungstenite", "tungstenite",
] ]
@@ -7452,6 +7454,7 @@ dependencies = [
"httparse", "httparse",
"log", "log",
"rand 0.8.5", "rand 0.8.5",
"rustls 0.21.7",
"sha1", "sha1",
"thiserror", "thiserror",
"url", "url",
@@ -7895,6 +7898,12 @@ dependencies = [
"webpki", "webpki",
] ]
[[package]]
name = "webpki-roots"
version = "0.25.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1"
[[package]] [[package]]
name = "webview2-com" name = "webview2-com"
version = "0.19.1" version = "0.19.1"
+7
View File
@@ -4444,6 +4444,7 @@ dependencies = [
"wasm-bindgen", "wasm-bindgen",
"wasm-bindgen-futures", "wasm-bindgen-futures",
"web-sys", "web-sys",
"webpki-roots",
"winreg", "winreg",
] ]
@@ -6271,6 +6272,12 @@ dependencies = [
"system-deps 6.1.1", "system-deps 6.1.1",
] ]
[[package]]
name = "webpki-roots"
version = "0.25.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1"
[[package]] [[package]]
name = "webview2-com" name = "webview2-com"
version = "0.19.1" version = "0.19.1"
+2 -2
View File
@@ -2300,9 +2300,9 @@ dependencies = [
[[package]] [[package]]
name = "mio" name = "mio"
version = "0.8.11" version = "0.8.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09"
dependencies = [ dependencies = [
"libc", "libc",
"wasi 0.11.0+wasi-snapshot-preview1", "wasi 0.11.0+wasi-snapshot-preview1",
-1
View File
@@ -655,7 +655,6 @@ where
.next() .next()
.await .await
.ok_or(Error::Socks5NotStarted)? .ok_or(Error::Socks5NotStarted)?
.as_any()
.downcast_ref::<TaskStatus>() .downcast_ref::<TaskStatus>()
.ok_or(Error::Socks5NotStarted)? .ok_or(Error::Socks5NotStarted)?
{ {
@@ -1,7 +1,7 @@
import React, { FC } from 'react'; import React, { FC } from 'react';
import { ChainProvider, useChain } from '@cosmos-kit/react'; import { ChainProvider, useChain } from '@cosmos-kit/react';
import { assets, chains } from 'chain-registry'; import { assets, chains } from 'chain-registry';
import { wallets as keplr } from '@cosmos-kit/keplr-extension'; import { wallets as keplr } from '@cosmos-kit/keplr';
import { wallets as ledger } from '@cosmos-kit/ledger'; import { wallets as ledger } from '@cosmos-kit/ledger';
import Button from '@mui/material/Button'; import Button from '@mui/material/Button';
import CircularProgress from '@mui/material/CircularProgress'; import CircularProgress from '@mui/material/CircularProgress';
+10 -11
View File
@@ -12,17 +12,16 @@
"start": "next start" "start": "next start"
}, },
"dependencies": { "dependencies": {
"@cosmjs/amino": "^0.32.2", "@cosmjs/amino": "^0.31.1",
"@cosmjs/cosmwasm-launchpad": "^0.25.6", "@cosmjs/cosmwasm-launchpad": "^0.25.6",
"@cosmjs/cosmwasm-stargate": "^0.32.2", "@cosmjs/cosmwasm-stargate": "^0.31.1",
"@cosmjs/encoding": "^0.32.2", "@cosmjs/encoding": "^0.31.1",
"@cosmjs/proto-signing": "^0.32.2", "@cosmjs/proto-signing": "^0.31.1",
"@cosmjs/stargate": "^0.32.2", "@cosmjs/stargate": "^0.31.0",
"@cosmos-kit/core": "^2.8.9", "@cosmos-kit/core": "^2.6.2",
"@cosmos-kit/keplr": "^2.6.9", "@cosmos-kit/keplr": "^2.3.9",
"@cosmos-kit/keplr-extension": "^2.7.9", "@cosmos-kit/ledger": "^2.4.3",
"@cosmos-kit/ledger": "^2.6.9", "@cosmos-kit/react": "^2.8.0",
"@cosmos-kit/react": "^2.10.11",
"@emotion/react": "^11.11.1", "@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0", "@emotion/styled": "^11.11.0",
"@interchain-ui/react": "^1.8.0", "@interchain-ui/react": "^1.8.0",
@@ -33,7 +32,7 @@
"@nymproject/mix-fetch-full-fat": ">=1.2.4-rc.2 || ^1", "@nymproject/mix-fetch-full-fat": ">=1.2.4-rc.2 || ^1",
"@nymproject/sdk-full-fat": ">=1.2.4-rc.2 || ^1", "@nymproject/sdk-full-fat": ">=1.2.4-rc.2 || ^1",
"chain-registry": "^1.19.0", "chain-registry": "^1.19.0",
"cosmjs-types": "^0.9.0", "cosmjs-types": "^0.8.0",
"next": "^13.4.19", "next": "^13.4.19",
"nextra": "latest", "nextra": "latest",
"nextra-theme-docs": "latest", "nextra-theme-docs": "latest",
@@ -32,6 +32,7 @@ If you're unsure where to start, the following set of questions should help you
### Use one of our standalone Nym clients ### Use one of our standalone Nym clients
If you've answered 'No' to all of the above, you may need to use one of our [standalone clients](https://nymtech.net/docs/clients/overview.html). All Nym client packages present basically the same capabilities to the privacy application developer. They need to run as a persistent process in order to stay connected and ready to receive any incoming messages from their gateway nodes. They register and authenticate to gateways, and construct Sphinx packets. While setting up those, and depending on your usecase, you may need to set-up and run a Service Provider. Read below the "How to deal with Service Providers" section for more details. If you've answered 'No' to all of the above, you may need to use one of our [standalone clients](https://nymtech.net/docs/clients/overview.html). All Nym client packages present basically the same capabilities to the privacy application developer. They need to run as a persistent process in order to stay connected and ready to receive any incoming messages from their gateway nodes. They register and authenticate to gateways, and construct Sphinx packets. While setting up those, and depending on your usecase, you may need to set-up and run a Service Provider. Read below the "How to deal with Service Providers" section for more details.
You can find more information about the different standalone clients and the ways to interact with them [in this page](https://nymtech.net/developers/integrations/mixnet-integration.html).
@@ -621,10 +621,10 @@
"@nodelib/fs.scandir" "2.1.5" "@nodelib/fs.scandir" "2.1.5"
fastq "^1.6.0" fastq "^1.6.0"
"@nymproject/mix-fetch-wasm-node@>=1.2.4-rc.2 || ^1": "@nymproject/mix-fetch-wasm-node@>=1.2.0-rc.10 || ^1":
version "1.2.3" version "1.2.0"
resolved "https://registry.yarnpkg.com/@nymproject/mix-fetch-wasm-node/-/mix-fetch-wasm-node-1.2.3.tgz#f600df714782e6eb691faa14683e44c2507a8e53" resolved "https://registry.yarnpkg.com/@nymproject/mix-fetch-wasm-node/-/mix-fetch-wasm-node-1.2.0.tgz#76439db4eb5fd4b95dcf6b6883cb5a059aeb5ad2"
integrity sha512-J9mj52WSpsGpuCeW65zEj8RWJ3GvWG0VqVWIDD6w1RK4SesXiGb7ghD7F1rChRMlSbl9rP9reYkmAHz63Sb6Cw== integrity sha512-vdEO4WfY1ql+DXIMR4nHvIlTB9tzhALiVjzbbf7UBgrQLxPSFTD2oGwGOVfgpNvXv0F92rDj3AHRommKKGa5pw==
"@rollup/plugin-commonjs@^24.0.1": "@rollup/plugin-commonjs@^24.0.1":
version "24.1.0" version "24.1.0"
@@ -1648,14 +1648,13 @@ es-to-primitive@^1.2.1:
is-date-object "^1.0.1" is-date-object "^1.0.1"
is-symbol "^1.0.2" is-symbol "^1.0.2"
es5-ext@^0.10.35, es5-ext@^0.10.46, es5-ext@^0.10.50, es5-ext@^0.10.53, es5-ext@^0.10.61, es5-ext@^0.10.62, es5-ext@~0.10.14, es5-ext@~0.10.2, es5-ext@~0.10.46: es5-ext@^0.10.35, es5-ext@^0.10.46, es5-ext@^0.10.50, es5-ext@^0.10.53, es5-ext@^0.10.61, es5-ext@~0.10.14, es5-ext@~0.10.2, es5-ext@~0.10.46:
version "0.10.64" version "0.10.62"
resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.64.tgz#12e4ffb48f1ba2ea777f1fcdd1918ef73ea21714" resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.62.tgz#5e6adc19a6da524bf3d1e02bbc8960e5eb49a9a5"
integrity sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg== integrity sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==
dependencies: dependencies:
es6-iterator "^2.0.3" es6-iterator "^2.0.3"
es6-symbol "^3.1.3" es6-symbol "^3.1.3"
esniff "^2.0.1"
next-tick "^1.1.0" next-tick "^1.1.0"
es6-iterator@^2.0.3: es6-iterator@^2.0.3:
@@ -1888,16 +1887,6 @@ eslint@^8.10.0:
strip-ansi "^6.0.1" strip-ansi "^6.0.1"
text-table "^0.2.0" text-table "^0.2.0"
esniff@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/esniff/-/esniff-2.0.1.tgz#a4d4b43a5c71c7ec51c51098c1d8a29081f9b308"
integrity sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==
dependencies:
d "^1.0.1"
es5-ext "^0.10.62"
event-emitter "^0.3.5"
type "^2.7.2"
espree@^9.6.0, espree@^9.6.1: espree@^9.6.0, espree@^9.6.1:
version "9.6.1" version "9.6.1"
resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f"
+118 -35
View File
@@ -65,11 +65,46 @@
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45"
integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==
"@jridgewell/sourcemap-codec@^1.4.13", "@jridgewell/sourcemap-codec@^1.4.15": "@jridgewell/gen-mapping@^0.3.0":
version "0.3.3"
resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098"
integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==
dependencies:
"@jridgewell/set-array" "^1.0.1"
"@jridgewell/sourcemap-codec" "^1.4.10"
"@jridgewell/trace-mapping" "^0.3.9"
"@jridgewell/resolve-uri@^3.1.0":
version "3.1.1"
resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721"
integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==
"@jridgewell/set-array@^1.0.1":
version "1.1.2"
resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72"
integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==
"@jridgewell/source-map@^0.3.3":
version "0.3.5"
resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.5.tgz#a3bb4d5c6825aab0d281268f47f6ad5853431e91"
integrity sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==
dependencies:
"@jridgewell/gen-mapping" "^0.3.0"
"@jridgewell/trace-mapping" "^0.3.9"
"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.13", "@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.4.15":
version "1.4.15" version "1.4.15"
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32"
integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==
"@jridgewell/trace-mapping@^0.3.9":
version "0.3.19"
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz#f8a3249862f91be48d3127c3cfe992f79b4b8811"
integrity sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==
dependencies:
"@jridgewell/resolve-uri" "^3.1.0"
"@jridgewell/sourcemap-codec" "^1.4.14"
"@nodelib/fs.scandir@2.1.5": "@nodelib/fs.scandir@2.1.5":
version "2.1.5" version "2.1.5"
resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
@@ -91,10 +126,10 @@
"@nodelib/fs.scandir" "2.1.5" "@nodelib/fs.scandir" "2.1.5"
fastq "^1.6.0" fastq "^1.6.0"
"@nymproject/nym-client-wasm-node@>=1.2.4-rc.2 || ^1": "@nymproject/nym-client-wasm-node@^1.2.0":
version "1.2.3" version "1.2.0"
resolved "https://registry.yarnpkg.com/@nymproject/nym-client-wasm-node/-/nym-client-wasm-node-1.2.3.tgz#e75e234714494fafffb86115cdee6759dcede366" resolved "https://registry.yarnpkg.com/@nymproject/nym-client-wasm-node/-/nym-client-wasm-node-1.2.0.tgz#46abe6b7535e80107d837bc6cc47edc1734aa773"
integrity sha512-fUGld4MJOgnvyqk5/KIpFePIXn8Nsl/7T/jh9a9WdTWntECnnJ/DBqoO+6NzDkyXaLYhByqR7reO8ZApNR0YCw== integrity sha512-L2hvMuudTQJgS/ZGGCSCD2oroccf7jWYXrq/E0Qqu0IxLlyjnTJ1xWWwXzUuwzBs+V5YZb0VIdB0kAovGLA+VA==
"@rollup/plugin-commonjs@^24.0.1": "@rollup/plugin-commonjs@^24.0.1":
version "24.1.0" version "24.1.0"
@@ -108,12 +143,14 @@
is-reference "1.2.1" is-reference "1.2.1"
magic-string "^0.27.0" magic-string "^0.27.0"
"@rollup/plugin-json@^6.0.0": "@rollup/plugin-inject@^5.0.3":
version "6.1.0" version "5.0.5"
resolved "https://registry.yarnpkg.com/@rollup/plugin-json/-/plugin-json-6.1.0.tgz#fbe784e29682e9bb6dee28ea75a1a83702e7b805" resolved "https://registry.yarnpkg.com/@rollup/plugin-inject/-/plugin-inject-5.0.5.tgz#616f3a73fe075765f91c5bec90176608bed277a3"
integrity sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA== integrity sha512-2+DEJbNBoPROPkgTDNe8/1YXWcqxbN5DTjASVIOx8HS+pITXushyNiBV56RB08zuptzz8gT3YfkqriTBVycepg==
dependencies: dependencies:
"@rollup/pluginutils" "^5.1.0" "@rollup/pluginutils" "^5.0.1"
estree-walker "^2.0.2"
magic-string "^0.30.3"
"@rollup/plugin-node-resolve@^15.0.1": "@rollup/plugin-node-resolve@^15.0.1":
version "15.2.3" version "15.2.3"
@@ -135,6 +172,15 @@
"@rollup/pluginutils" "^5.0.1" "@rollup/pluginutils" "^5.0.1"
magic-string "^0.30.3" magic-string "^0.30.3"
"@rollup/plugin-terser@^0.2.1":
version "0.2.1"
resolved "https://registry.yarnpkg.com/@rollup/plugin-terser/-/plugin-terser-0.2.1.tgz#dcf0b163216dafb64611b170a7667e76a7f03d2b"
integrity sha512-hV52c8Oo6/cXZZxVVoRNBb4zh+EKSHS4I1sedWV5pf0O+hTLSkrf6w86/V0AZutYtwBguB6HLKwz89WDBfwGOA==
dependencies:
serialize-javascript "^6.0.0"
smob "^0.0.6"
terser "^5.15.1"
"@rollup/plugin-typescript@^10.0.1": "@rollup/plugin-typescript@^10.0.1":
version "10.0.1" version "10.0.1"
resolved "https://registry.yarnpkg.com/@rollup/plugin-typescript/-/plugin-typescript-10.0.1.tgz#270b515b116ea28320e6bb62451c4767d49072d6" resolved "https://registry.yarnpkg.com/@rollup/plugin-typescript/-/plugin-typescript-10.0.1.tgz#270b515b116ea28320e6bb62451c4767d49072d6"
@@ -177,15 +223,6 @@
estree-walker "^2.0.2" estree-walker "^2.0.2"
picomatch "^2.3.1" picomatch "^2.3.1"
"@rollup/pluginutils@^5.1.0":
version "5.1.0"
resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-5.1.0.tgz#7e53eddc8c7f483a4ad0b94afb1f7f5fd3c771e0"
integrity sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==
dependencies:
"@types/estree" "^1.0.0"
estree-walker "^2.0.2"
picomatch "^2.3.1"
"@types/estree@*", "@types/estree@^1.0.0": "@types/estree@*", "@types/estree@^1.0.0":
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.2.tgz#ff02bc3dc8317cd668dfec247b750ba1f1d62453" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.2.tgz#ff02bc3dc8317cd668dfec247b750ba1f1d62453"
@@ -315,7 +352,7 @@ acorn-jsx@^5.3.2:
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
acorn@^8.9.0: acorn@^8.8.2, acorn@^8.9.0:
version "8.10.0" version "8.10.0"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5"
integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw== integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==
@@ -512,6 +549,11 @@ braces@^3.0.2, braces@~3.0.2:
dependencies: dependencies:
fill-range "^7.0.1" fill-range "^7.0.1"
buffer-from@^1.0.0:
version "1.1.2"
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
builtin-modules@^3.3.0: builtin-modules@^3.3.0:
version "3.3.0" version "3.3.0"
resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.3.0.tgz#cae62812b89801e9656336e46223e030386be7b6" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.3.0.tgz#cae62812b89801e9656336e46223e030386be7b6"
@@ -581,6 +623,11 @@ comlink@^4.3.1:
resolved "https://registry.yarnpkg.com/comlink/-/comlink-4.4.1.tgz#e568b8e86410b809e8600eb2cf40c189371ef981" resolved "https://registry.yarnpkg.com/comlink/-/comlink-4.4.1.tgz#e568b8e86410b809e8600eb2cf40c189371ef981"
integrity sha512-+1dlx0aY5Jo1vHy/tSsIGpSkN4tS9rZSW8FIhG0JH/crs9wwweswIo/POr451r7bZww3hFbPAKnTpimzL/mm4Q== integrity sha512-+1dlx0aY5Jo1vHy/tSsIGpSkN4tS9rZSW8FIhG0JH/crs9wwweswIo/POr451r7bZww3hFbPAKnTpimzL/mm4Q==
commander@^2.20.0:
version "2.20.3"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
commander@~9.4.0: commander@~9.4.0:
version "9.4.1" version "9.4.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-9.4.1.tgz#d1dd8f2ce6faf93147295c0df13c7c21141cfbdd" resolved "https://registry.yarnpkg.com/commander/-/commander-9.4.1.tgz#d1dd8f2ce6faf93147295c0df13c7c21141cfbdd"
@@ -833,14 +880,13 @@ es-to-primitive@^1.2.1:
is-date-object "^1.0.1" is-date-object "^1.0.1"
is-symbol "^1.0.2" is-symbol "^1.0.2"
es5-ext@^0.10.35, es5-ext@^0.10.46, es5-ext@^0.10.50, es5-ext@^0.10.53, es5-ext@^0.10.61, es5-ext@^0.10.62, es5-ext@~0.10.14, es5-ext@~0.10.2, es5-ext@~0.10.46: es5-ext@^0.10.35, es5-ext@^0.10.46, es5-ext@^0.10.50, es5-ext@^0.10.53, es5-ext@^0.10.61, es5-ext@~0.10.14, es5-ext@~0.10.2, es5-ext@~0.10.46:
version "0.10.64" version "0.10.62"
resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.64.tgz#12e4ffb48f1ba2ea777f1fcdd1918ef73ea21714" resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.62.tgz#5e6adc19a6da524bf3d1e02bbc8960e5eb49a9a5"
integrity sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg== integrity sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==
dependencies: dependencies:
es6-iterator "^2.0.3" es6-iterator "^2.0.3"
es6-symbol "^3.1.3" es6-symbol "^3.1.3"
esniff "^2.0.1"
next-tick "^1.1.0" next-tick "^1.1.0"
es6-iterator@^2.0.3: es6-iterator@^2.0.3:
@@ -1085,16 +1131,6 @@ eslint@^8.10.0:
strip-ansi "^6.0.1" strip-ansi "^6.0.1"
text-table "^0.2.0" text-table "^0.2.0"
esniff@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/esniff/-/esniff-2.0.1.tgz#a4d4b43a5c71c7ec51c51098c1d8a29081f9b308"
integrity sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==
dependencies:
d "^1.0.1"
es5-ext "^0.10.62"
event-emitter "^0.3.5"
type "^2.7.2"
espree@^9.6.0, espree@^9.6.1: espree@^9.6.0, espree@^9.6.1:
version "9.6.1" version "9.6.1"
resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f"
@@ -2207,6 +2243,13 @@ queue-microtask@^1.2.2:
resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
randombytes@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a"
integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==
dependencies:
safe-buffer "^5.1.0"
range-parser@~1.2.1: range-parser@~1.2.1:
version "1.2.1" version "1.2.1"
resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
@@ -2357,6 +2400,11 @@ safe-array-concat@^1.0.1:
has-symbols "^1.0.3" has-symbols "^1.0.3"
isarray "^2.0.5" isarray "^2.0.5"
safe-buffer@^5.1.0:
version "5.2.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
safe-regex-test@^1.0.0: safe-regex-test@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.0.tgz#793b874d524eb3640d1873aad03596db2d4f2295" resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.0.tgz#793b874d524eb3640d1873aad03596db2d4f2295"
@@ -2397,6 +2445,13 @@ send@0.18.0:
range-parser "~1.2.1" range-parser "~1.2.1"
statuses "2.0.1" statuses "2.0.1"
serialize-javascript@^6.0.0:
version "6.0.1"
resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.1.tgz#b206efb27c3da0b0ab6b52f48d170b7996458e5c"
integrity sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==
dependencies:
randombytes "^2.1.0"
serve-static@~1.15.0: serve-static@~1.15.0:
version "1.15.0" version "1.15.0"
resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540" resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540"
@@ -2464,6 +2519,24 @@ slash@^3.0.0:
resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
smob@^0.0.6:
version "0.0.6"
resolved "https://registry.yarnpkg.com/smob/-/smob-0.0.6.tgz#09b268fea916158a2781c152044c6155adbb8aa1"
integrity sha512-V21+XeNni+tTyiST1MHsa84AQhT1aFZipzPpOFAVB8DkHzwJyjjAmt9bgwnuZiZWnIbMo2duE29wybxv/7HWUw==
source-map-support@~0.5.20:
version "0.5.21"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f"
integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==
dependencies:
buffer-from "^1.0.0"
source-map "^0.6.0"
source-map@^0.6.0:
version "0.6.1"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
source-map@^0.7.4: source-map@^0.7.4:
version "0.7.4" version "0.7.4"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656"
@@ -2567,6 +2640,16 @@ tapable@^2.2.0:
resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0"
integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==
terser@^5.15.1:
version "5.22.0"
resolved "https://registry.yarnpkg.com/terser/-/terser-5.22.0.tgz#4f18103f84c5c9437aafb7a14918273310a8a49d"
integrity sha512-hHZVLgRA2z4NWcN6aS5rQDc+7Dcy58HOf2zbYwmFcQ+ua3h6eEFf5lIDKTzbWwlazPyOZsFQO8V80/IjVNExEw==
dependencies:
"@jridgewell/source-map" "^0.3.3"
acorn "^8.8.2"
commander "^2.20.0"
source-map-support "~0.5.20"
text-table@^0.2.0: text-table@^0.2.0:
version "0.2.0" version "0.2.0"
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
@@ -600,9 +600,9 @@
} }
}, },
"node_modules/ip": { "node_modules/ip": {
"version": "1.1.9", "version": "1.1.8",
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.9.tgz", "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz",
"integrity": "sha512-cyRxvOEpNHNtchU3Ln9KC/auJgup87llfQpQ+t5ghoC/UhL16SWzbueiCsdTnWmqAWl7LadfuwhlqmtOaqMHdQ==" "integrity": "sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg=="
}, },
"node_modules/is-arrayish": { "node_modules/is-arrayish": {
"version": "0.2.1", "version": "0.2.1",
@@ -976,9 +976,9 @@
} }
}, },
"node_modules/socks/node_modules/ip": { "node_modules/socks/node_modules/ip": {
"version": "2.0.1", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/ip/-/ip-2.0.1.tgz", "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz",
"integrity": "sha512-lJUL9imLTNi1ZfXT+DU6rBBdbiKGBuay9B6xGSPVjUeQwaH1RIGqef8RZkUtHioLmSNpPR5M4HVKJGm1j8FWVQ==" "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ=="
}, },
"node_modules/source-map": { "node_modules/source-map": {
"version": "0.6.1", "version": "0.6.1",
@@ -1,13 +1,9 @@
use std::net::{Ipv4Addr, Ipv6Addr};
use std::time::Duration; use std::time::Duration;
// The interface used to route traffic // The interface used to route traffic
pub const TUN_BASE_NAME: &str = "nymtun"; pub const TUN_BASE_NAME: &str = "nymtun";
pub const TUN_DEVICE_ADDRESS_V4: Ipv4Addr = Ipv4Addr::new(10, 0, 0, 1); pub const TUN_DEVICE_ADDRESS: &str = "10.0.0.1";
pub const TUN_DEVICE_NETMASK_V4: Ipv4Addr = Ipv4Addr::new(255, 255, 255, 0); pub const TUN_DEVICE_NETMASK: &str = "255.255.255.0";
pub const TUN_DEVICE_ADDRESS_V6: Ipv6Addr = Ipv6Addr::new(0x2001, 0xdb8, 0xa160, 0, 0, 0, 0, 0x1); // 2001:db8:a160::1
pub const TUN_DEVICE_NETMASK_V6: &str = "120";
// We routinely check if any clients needs to be disconnected at this interval // We routinely check if any clients needs to be disconnected at this interval
pub(crate) const DISCONNECT_TIMER_INTERVAL: Duration = Duration::from_secs(10); pub(crate) const DISCONNECT_TIMER_INTERVAL: Duration = Duration::from_secs(10);
@@ -11,10 +11,6 @@ pub enum IpPacketRouterError {
#[error("client-core error: {0}")] #[error("client-core error: {0}")]
ClientCoreError(#[from] ClientCoreError), ClientCoreError(#[from] ClientCoreError),
#[cfg(target_os = "linux")]
#[error("tun device error: {0}")]
TunDeviceError(#[from] nym_tun::tun_device::TunDeviceError),
#[error("failed to load configuration file: {0}")] #[error("failed to load configuration file: {0}")]
FailedToLoadConfig(String), FailedToLoadConfig(String),
@@ -133,13 +133,11 @@ impl IpPacketRouter {
// Create the TUN device that we interact with the rest of the world with // Create the TUN device that we interact with the rest of the world with
let config = nym_tun::tun_device::TunDeviceConfig { let config = nym_tun::tun_device::TunDeviceConfig {
base_name: crate::constants::TUN_BASE_NAME.to_string(), base_name: crate::constants::TUN_BASE_NAME.to_string(),
ipv4: crate::constants::TUN_DEVICE_ADDRESS_V4, ip: crate::constants::TUN_DEVICE_ADDRESS.parse().unwrap(),
netmaskv4: crate::constants::TUN_DEVICE_NETMASK_V4, netmask: crate::constants::TUN_DEVICE_NETMASK.parse().unwrap(),
ipv6: crate::constants::TUN_DEVICE_ADDRESS_V6,
netmaskv6: crate::constants::TUN_DEVICE_NETMASK_V6.to_string(),
}; };
let (tun_reader, tun_writer) = let (tun_reader, tun_writer) =
tokio::io::split(nym_tun::tun_device::TunDevice::new_device_only(config)?); tokio::io::split(nym_tun::tun_device::TunDevice::new_device_only(config));
// Channel used by the IpPacketRouter to signal connected and disconnected clients to the // Channel used by the IpPacketRouter to signal connected and disconnected clients to the
// TunListener // TunListener
@@ -1,6 +1,7 @@
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; use std::{
use std::sync::Arc; collections::HashMap,
use std::{collections::HashMap, net::SocketAddr}; net::{IpAddr, SocketAddr},
};
use bytes::{Bytes, BytesMut}; use bytes::{Bytes, BytesMut};
use futures::StreamExt; use futures::StreamExt;
@@ -11,7 +12,6 @@ use nym_ip_packet_requests::{
DynamicConnectFailureReason, ErrorResponseReply, IpPacketResponse, DynamicConnectFailureReason, ErrorResponseReply, IpPacketResponse,
StaticConnectFailureReason, StaticConnectFailureReason,
}, },
IpPair,
}; };
use nym_sdk::mixnet::{MixnetMessageSender, Recipient}; use nym_sdk::mixnet::{MixnetMessageSender, Recipient};
use nym_sphinx::receiver::ReconstructedMessage; use nym_sphinx::receiver::ReconstructedMessage;
@@ -19,7 +19,6 @@ use nym_task::TaskHandle;
use tap::TapFallible; use tap::TapFallible;
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
use tokio::io::AsyncWriteExt; use tokio::io::AsyncWriteExt;
use tokio::sync::RwLock;
use tokio_util::codec::Decoder; use tokio_util::codec::Decoder;
use crate::{ use crate::{
@@ -38,8 +37,7 @@ use crate::{
pub(crate) struct ConnectedClients { pub(crate) struct ConnectedClients {
// The set of connected clients // The set of connected clients
clients_ipv4_mapping: HashMap<Ipv4Addr, ConnectedClient>, clients: HashMap<IpAddr, ConnectedClient>,
clients_ipv6_mapping: HashMap<Ipv6Addr, ConnectedClient>,
// Notify the tun listener when a new client connects or disconnects // Notify the tun listener when a new client connects or disconnects
tun_listener_connected_client_tx: tokio::sync::mpsc::UnboundedSender<ConnectedClientEvent>, tun_listener_connected_client_tx: tokio::sync::mpsc::UnboundedSender<ConnectedClientEvent>,
@@ -50,59 +48,46 @@ impl ConnectedClients {
let (connected_client_tx, connected_client_rx) = tokio::sync::mpsc::unbounded_channel(); let (connected_client_tx, connected_client_rx) = tokio::sync::mpsc::unbounded_channel();
( (
Self { Self {
clients_ipv4_mapping: Default::default(), clients: Default::default(),
clients_ipv6_mapping: Default::default(),
tun_listener_connected_client_tx: connected_client_tx, tun_listener_connected_client_tx: connected_client_tx,
}, },
tun_listener::ConnectedClientsListener::new(connected_client_rx), tun_listener::ConnectedClientsListener::new(connected_client_rx),
) )
} }
fn is_ip_connected(&self, ips: &IpPair) -> bool { fn is_ip_connected(&self, ip: &IpAddr) -> bool {
self.clients_ipv4_mapping.contains_key(&ips.ipv4) self.clients.contains_key(ip)
|| self.clients_ipv6_mapping.contains_key(&ips.ipv6)
} }
fn get_client_from_ip_mut(&mut self, ip: &IpAddr) -> Option<&mut ConnectedClient> { fn get_client_from_ip_mut(&mut self, ip: &IpAddr) -> Option<&mut ConnectedClient> {
match ip { self.clients.get_mut(ip)
IpAddr::V4(ip) => self.clients_ipv4_mapping.get_mut(ip),
IpAddr::V6(ip) => self.clients_ipv6_mapping.get_mut(ip),
}
} }
fn is_nym_address_connected(&self, nym_address: &Recipient) -> bool { fn is_nym_address_connected(&self, nym_address: &Recipient) -> bool {
self.clients_ipv4_mapping self.clients
.values() .values()
.any(|client| client.nym_address == *nym_address) .any(|client| client.nym_address == *nym_address)
} }
fn lookup_ip_from_nym_address(&self, nym_address: &Recipient) -> Option<IpPair> { fn lookup_ip_from_nym_address(&self, nym_address: &Recipient) -> Option<IpAddr> {
self.clients_ipv4_mapping self.clients.iter().find_map(|(ip, client)| {
.iter() if client.nym_address == *nym_address {
.find_map(|(ipv4, connected_client)| { Some(*ip)
if connected_client.nym_address == *nym_address { } else {
Some(IpPair::new(*ipv4, connected_client.ipv6)) None
} else { }
None })
}
})
} }
fn lookup_client_from_nym_address(&self, nym_address: &Recipient) -> Option<&ConnectedClient> { fn lookup_client_from_nym_address(&self, nym_address: &Recipient) -> Option<&ConnectedClient> {
self.clients_ipv4_mapping self.clients
.iter() .values()
.find_map(|(_, connected_client)| { .find(|client| client.nym_address == *nym_address)
if connected_client.nym_address == *nym_address {
Some(connected_client)
} else {
None
}
})
} }
fn connect( fn connect(
&mut self, &mut self,
ips: IpPair, ip: IpAddr,
nym_address: Recipient, nym_address: Recipient,
mix_hops: Option<u8>, mix_hops: Option<u8>,
forward_from_tun_tx: tokio::sync::mpsc::UnboundedSender<Vec<u8>>, forward_from_tun_tx: tokio::sync::mpsc::UnboundedSender<Vec<u8>>,
@@ -111,25 +96,21 @@ impl ConnectedClients {
) { ) {
// The map of connected clients that the mixnet listener keeps track of. It monitors // The map of connected clients that the mixnet listener keeps track of. It monitors
// activity and disconnects clients that have been inactive for too long. // activity and disconnects clients that have been inactive for too long.
let client = ConnectedClient { self.clients.insert(
nym_address, ip,
ipv6: ips.ipv6, ConnectedClient {
mix_hops,
last_activity: Arc::new(RwLock::new(std::time::Instant::now())),
_close_tx: Arc::new(CloseTx {
nym_address, nym_address,
inner: Some(close_tx), mix_hops,
}), last_activity: std::time::Instant::now(),
handle: Arc::new(handle), close_tx: Some(close_tx),
}; handle,
log::info!("Inserting {} and {}", ips.ipv4, ips.ipv6); },
self.clients_ipv4_mapping.insert(ips.ipv4, client.clone()); );
self.clients_ipv6_mapping.insert(ips.ipv6, client);
// Send the connected client info to the tun listener, which will use it to forward packets // Send the connected client info to the tun listener, which will use it to forward packets
// to the connected client handler. // to the connected client handler.
self.tun_listener_connected_client_tx self.tun_listener_connected_client_tx
.send(ConnectedClientEvent::Connect(Box::new(ConnectEvent { .send(ConnectedClientEvent::Connect(Box::new(ConnectEvent {
ips, ip,
forward_from_tun_tx, forward_from_tun_tx,
}))) })))
.tap_err(|err| { .tap_err(|err| {
@@ -138,9 +119,9 @@ impl ConnectedClients {
.ok(); .ok();
} }
async fn update_activity(&mut self, ips: &IpPair) -> Result<()> { fn update_activity(&mut self, ip: &IpAddr) -> Result<()> {
if let Some(client) = self.clients_ipv4_mapping.get(&ips.ipv4) { if let Some(client) = self.clients.get_mut(ip) {
*client.last_activity.write().await = std::time::Instant::now(); client.last_activity = std::time::Instant::now();
Ok(()) Ok(())
} else { } else {
Err(IpPacketRouterError::FailedToUpdateClientActivity) Err(IpPacketRouterError::FailedToUpdateClientActivity)
@@ -148,15 +129,12 @@ impl ConnectedClients {
} }
// Identify connected client handlers that have stopped without being told to stop // Identify connected client handlers that have stopped without being told to stop
fn get_finished_client_handlers(&mut self) -> Vec<(IpPair, Recipient)> { fn get_finished_client_handlers(&mut self) -> Vec<(IpAddr, Recipient)> {
self.clients_ipv4_mapping self.clients
.iter_mut() .iter_mut()
.filter_map(|(ip, connected_client)| { .filter_map(|(ip, client)| {
if connected_client.handle.is_finished() { if client.handle.is_finished() {
Some(( Some((*ip, client.nym_address))
IpPair::new(*ip, connected_client.ipv6),
connected_client.nym_address,
))
} else { } else {
None None
} }
@@ -164,29 +142,26 @@ impl ConnectedClients {
.collect() .collect()
} }
async fn get_inactive_clients(&mut self) -> Vec<(IpPair, Recipient)> { fn get_inactive_clients(&mut self) -> Vec<(IpAddr, Recipient)> {
let now = std::time::Instant::now(); let now = std::time::Instant::now();
let mut ret = vec![]; self.clients
for (ip, connected_client) in self.clients_ipv4_mapping.iter() { .iter()
if now.duration_since(*connected_client.last_activity.read().await) .filter_map(|(ip, client)| {
> CLIENT_MIXNET_INACTIVITY_TIMEOUT if now.duration_since(client.last_activity) > CLIENT_MIXNET_INACTIVITY_TIMEOUT {
{ Some((*ip, client.nym_address))
ret.push(( } else {
IpPair::new(*ip, connected_client.ipv6), None
connected_client.nym_address, }
)) })
} .collect()
}
ret
} }
fn disconnect_stopped_client_handlers(&mut self, stopped_clients: Vec<(IpPair, Recipient)>) { fn disconnect_stopped_client_handlers(&mut self, stopped_clients: Vec<(IpAddr, Recipient)>) {
for (ips, _) in &stopped_clients { for (ip, _) in &stopped_clients {
log::info!("Disconnect stopped client: {ips}"); log::info!("Disconnect stopped client: {ip}");
self.clients_ipv4_mapping.remove(&ips.ipv4); self.clients.remove(ip);
self.clients_ipv6_mapping.remove(&ips.ipv6);
self.tun_listener_connected_client_tx self.tun_listener_connected_client_tx
.send(ConnectedClientEvent::Disconnect(DisconnectEvent(*ips))) .send(ConnectedClientEvent::Disconnect(DisconnectEvent(*ip)))
.tap_err(|err| { .tap_err(|err| {
log::error!("Failed to send disconnect event: {err}"); log::error!("Failed to send disconnect event: {err}");
}) })
@@ -194,13 +169,12 @@ impl ConnectedClients {
} }
} }
fn disconnect_inactive_clients(&mut self, inactive_clients: Vec<(IpPair, Recipient)>) { fn disconnect_inactive_clients(&mut self, inactive_clients: Vec<(IpAddr, Recipient)>) {
for (ips, _) in &inactive_clients { for (ip, _) in &inactive_clients {
log::info!("Disconnect inactive client: {ips}"); log::info!("Disconnect inactive client: {ip}");
self.clients_ipv4_mapping.remove(&ips.ipv4); self.clients.remove(ip);
self.clients_ipv6_mapping.remove(&ips.ipv6);
self.tun_listener_connected_client_tx self.tun_listener_connected_client_tx
.send(ConnectedClientEvent::Disconnect(DisconnectEvent(*ips))) .send(ConnectedClientEvent::Disconnect(DisconnectEvent(*ip)))
.tap_err(|err| { .tap_err(|err| {
log::error!("Failed to send disconnect event: {err}"); log::error!("Failed to send disconnect event: {err}");
}) })
@@ -208,49 +182,40 @@ impl ConnectedClients {
} }
} }
fn find_new_ip(&self) -> Option<IpPair> { fn find_new_ip(&self) -> Option<IpAddr> {
generate_new_ip::find_new_ips(&self.clients_ipv4_mapping, &self.clients_ipv6_mapping) generate_new_ip::find_new_ip(&self.clients)
} }
} }
pub(crate) struct CloseTx {
pub(crate) nym_address: Recipient,
// Send to connected clients listener to stop. This is option only because we need to take
// ownership of it when the client is dropped.
pub(crate) inner: Option<tokio::sync::oneshot::Sender<()>>,
}
#[derive(Clone)]
pub(crate) struct ConnectedClient { pub(crate) struct ConnectedClient {
// The nym address of the connected client that we are communicating with on the other side of // The nym address of the connected client that we are communicating with on the other side of
// the mixnet // the mixnet
pub(crate) nym_address: Recipient, pub(crate) nym_address: Recipient,
// The assigned IPv6 address of this client
pub(crate) ipv6: Ipv6Addr,
// Number of mix node hops that the client has requested to use // Number of mix node hops that the client has requested to use
pub(crate) mix_hops: Option<u8>, pub(crate) mix_hops: Option<u8>,
// Keep track of last activity so we can disconnect inactive clients // Keep track of last activity so we can disconnect inactive clients
pub(crate) last_activity: Arc<RwLock<std::time::Instant>>, pub(crate) last_activity: std::time::Instant,
pub(crate) _close_tx: Arc<CloseTx>, // Send to connected clients listener to stop. This is option only because we need to take
// ownership of it when the client is dropped.
pub(crate) close_tx: Option<tokio::sync::oneshot::Sender<()>>,
// Handle for the connected client handler // Handle for the connected client handler
pub(crate) handle: Arc<tokio::task::JoinHandle<()>>, pub(crate) handle: tokio::task::JoinHandle<()>,
} }
impl ConnectedClient { impl ConnectedClient {
async fn update_activity(&self) { fn update_activity(&mut self) {
*self.last_activity.write().await = std::time::Instant::now(); self.last_activity = std::time::Instant::now();
} }
} }
impl Drop for CloseTx { impl Drop for ConnectedClient {
fn drop(&mut self) { fn drop(&mut self) {
log::debug!("signal to close client: {}", self.nym_address); log::debug!("signal to close client: {}", self.nym_address);
if let Some(close_tx) = self.inner.take() { if let Some(close_tx) = self.close_tx.take() {
close_tx.send(()).ok(); close_tx.send(()).ok();
} }
} }
@@ -294,7 +259,7 @@ impl MixnetListener {
); );
let request_id = connect_request.request_id; let request_id = connect_request.request_id;
let requested_ips = connect_request.ips; let requested_ip = connect_request.ip;
let reply_to = connect_request.reply_to; let reply_to = connect_request.reply_to;
let reply_to_hops = connect_request.reply_to_hops; let reply_to_hops = connect_request.reply_to_hops;
// TODO: add to connect request // TODO: add to connect request
@@ -302,7 +267,7 @@ impl MixnetListener {
// TODO: ignoring reply_to_avg_mix_delays for now // TODO: ignoring reply_to_avg_mix_delays for now
// Check that the IP is available in the set of connected clients // Check that the IP is available in the set of connected clients
let is_ip_taken = self.connected_clients.is_ip_connected(&requested_ips); let is_ip_taken = self.connected_clients.is_ip_connected(&requested_ip);
// Check that the nym address isn't already registered // Check that the nym address isn't already registered
let is_nym_address_taken = self.connected_clients.is_nym_address_connected(&reply_to); let is_nym_address_taken = self.connected_clients.is_nym_address_connected(&reply_to);
@@ -312,8 +277,7 @@ impl MixnetListener {
log::info!("Connecting an already connected client"); log::info!("Connecting an already connected client");
if self if self
.connected_clients .connected_clients
.update_activity(&requested_ips) .update_activity(&requested_ip)
.await
.is_err() .is_err()
{ {
log::error!("Failed to update activity for client"); log::error!("Failed to update activity for client");
@@ -336,7 +300,7 @@ impl MixnetListener {
// Register the new client in the set of connected clients // Register the new client in the set of connected clients
self.connected_clients.connect( self.connected_clients.connect(
requested_ips, requested_ip,
reply_to, reply_to,
reply_to_hops, reply_to_hops,
forward_from_tun_tx, forward_from_tun_tx,
@@ -386,12 +350,11 @@ impl MixnetListener {
// TODO: this is problematic. Until we sign connect requests this means you can spam people // TODO: this is problematic. Until we sign connect requests this means you can spam people
// with return traffic // with return traffic
if let Some(existing_ips) = self.connected_clients.lookup_ip_from_nym_address(&reply_to) { if let Some(existing_ip) = self.connected_clients.lookup_ip_from_nym_address(&reply_to) {
log::info!("Found existing client for nym address"); log::info!("Found existing client for nym address");
if self if self
.connected_clients .connected_clients
.update_activity(&existing_ips) .update_activity(&existing_ip)
.await
.is_err() .is_err()
{ {
log::error!("Failed to update activity for client"); log::error!("Failed to update activity for client");
@@ -399,11 +362,11 @@ impl MixnetListener {
return Ok(Some(IpPacketResponse::new_dynamic_connect_success( return Ok(Some(IpPacketResponse::new_dynamic_connect_success(
request_id, request_id,
reply_to, reply_to,
existing_ips, existing_ip,
))); )));
} }
let Some(new_ips) = self.connected_clients.find_new_ip() else { let Some(new_ip) = self.connected_clients.find_new_ip() else {
log::info!("No available IP address"); log::info!("No available IP address");
return Ok(Some(IpPacketResponse::new_dynamic_connect_failure( return Ok(Some(IpPacketResponse::new_dynamic_connect_failure(
request_id, request_id,
@@ -423,7 +386,7 @@ impl MixnetListener {
// Register the new client in the set of connected clients // Register the new client in the set of connected clients
self.connected_clients.connect( self.connected_clients.connect(
new_ips, new_ip,
reply_to, reply_to,
reply_to_hops, reply_to_hops,
forward_from_tun_tx, forward_from_tun_tx,
@@ -431,7 +394,7 @@ impl MixnetListener {
handle, handle,
); );
Ok(Some(IpPacketResponse::new_dynamic_connect_success( Ok(Some(IpPacketResponse::new_dynamic_connect_success(
request_id, reply_to, new_ips, request_id, reply_to, new_ip,
))) )))
} }
@@ -465,7 +428,7 @@ impl MixnetListener {
if let Some(connected_client) = self.connected_clients.get_client_from_ip_mut(&src_addr) { if let Some(connected_client) = self.connected_clients.get_client_from_ip_mut(&src_addr) {
// Keep track of activity so we can disconnect inactive clients // Keep track of activity so we can disconnect inactive clients
connected_client.update_activity().await; connected_client.update_activity();
// For packets without a port, use 0. // For packets without a port, use 0.
let dst = dst.unwrap_or_else(|| SocketAddr::new(dst_addr, 0)); let dst = dst.unwrap_or_else(|| SocketAddr::new(dst_addr, 0));
@@ -576,9 +539,9 @@ impl MixnetListener {
} }
} }
async fn handle_disconnect_timer(&mut self) { fn handle_disconnect_timer(&mut self) {
let stopped_clients = self.connected_clients.get_finished_client_handlers(); let stopped_clients = self.connected_clients.get_finished_client_handlers();
let inactive_clients = self.connected_clients.get_inactive_clients().await; let inactive_clients = self.connected_clients.get_inactive_clients();
// TODO: Send disconnect responses to all disconnected clients // TODO: Send disconnect responses to all disconnected clients
//for (ip, nym_address) in stopped_clients.iter().chain(disconnected_clients.iter()) { //for (ip, nym_address) in stopped_clients.iter().chain(disconnected_clients.iter()) {
@@ -608,14 +571,10 @@ impl MixnetListener {
})?; })?;
// We could avoid this lookup if we check this when we create the response. // We could avoid this lookup if we check this when we create the response.
let mix_hops = if let Some(c) = self let mix_hops = self
.connected_clients .connected_clients
.lookup_client_from_nym_address(recipient) .lookup_client_from_nym_address(recipient)
{ .and_then(|c| c.mix_hops);
c.mix_hops
} else {
None
};
let input_message = create_input_message(*recipient, response_packet, mix_hops); let input_message = create_input_message(*recipient, response_packet, mix_hops);
self.mixnet_client self.mixnet_client
@@ -654,7 +613,7 @@ impl MixnetListener {
log::debug!("IpPacketRouter [main loop]: received shutdown"); log::debug!("IpPacketRouter [main loop]: received shutdown");
}, },
_ = disconnect_timer.tick() => { _ = disconnect_timer.tick() => {
self.handle_disconnect_timer().await; self.handle_disconnect_timer();
}, },
msg = self.mixnet_client.next() => { msg = self.mixnet_client.next() => {
if let Some(msg) = msg { if let Some(msg) = msg {
@@ -683,9 +642,9 @@ pub(crate) enum ConnectedClientEvent {
Connect(Box<ConnectEvent>), Connect(Box<ConnectEvent>),
} }
pub(crate) struct DisconnectEvent(pub(crate) IpPair); pub(crate) struct DisconnectEvent(pub(crate) IpAddr);
pub(crate) struct ConnectEvent { pub(crate) struct ConnectEvent {
pub(crate) ips: IpPair, pub(crate) ip: IpAddr,
pub(crate) forward_from_tun_tx: tokio::sync::mpsc::UnboundedSender<Vec<u8>>, pub(crate) forward_from_tun_tx: tokio::sync::mpsc::UnboundedSender<Vec<u8>>,
} }
@@ -1,7 +1,5 @@
use std::collections::HashMap; use std::{collections::HashMap, net::IpAddr};
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use nym_ip_packet_requests::IpPair;
use nym_task::TaskClient; use nym_task::TaskClient;
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
use tokio::io::AsyncReadExt; use tokio::io::AsyncReadExt;
@@ -17,12 +15,10 @@ use crate::{
// It's even ok if this is slightly out of date // It's even ok if this is slightly out of date
pub(crate) struct ConnectedClientMirror { pub(crate) struct ConnectedClientMirror {
pub(crate) forward_from_tun_tx: tokio::sync::mpsc::UnboundedSender<Vec<u8>>, pub(crate) forward_from_tun_tx: tokio::sync::mpsc::UnboundedSender<Vec<u8>>,
pub(crate) ips: IpPair,
} }
pub(crate) struct ConnectedClientsListener { pub(crate) struct ConnectedClientsListener {
clients_ipv4: HashMap<Ipv4Addr, ConnectedClientMirror>, clients: HashMap<IpAddr, ConnectedClientMirror>,
clients_ipv6: HashMap<Ipv6Addr, ConnectedClientMirror>,
connected_client_rx: connected_client_rx:
tokio::sync::mpsc::UnboundedReceiver<mixnet_listener::ConnectedClientEvent>, tokio::sync::mpsc::UnboundedReceiver<mixnet_listener::ConnectedClientEvent>,
} }
@@ -34,48 +30,35 @@ impl ConnectedClientsListener {
>, >,
) -> Self { ) -> Self {
ConnectedClientsListener { ConnectedClientsListener {
clients_ipv4: HashMap::new(), clients: HashMap::new(),
clients_ipv6: HashMap::new(),
connected_client_rx, connected_client_rx,
} }
} }
pub(crate) fn get(&self, ip: &IpAddr) -> Option<&ConnectedClientMirror> { pub(crate) fn get(&self, ip: &IpAddr) -> Option<&ConnectedClientMirror> {
match ip { self.clients.get(ip)
IpAddr::V4(ip) => self.clients_ipv4.get(ip),
IpAddr::V6(ip) => self.clients_ipv6.get(ip),
}
} }
pub(crate) fn update(&mut self, event: mixnet_listener::ConnectedClientEvent) { pub(crate) fn update(&mut self, event: mixnet_listener::ConnectedClientEvent) {
match event { match event {
mixnet_listener::ConnectedClientEvent::Connect(connected_event) => { mixnet_listener::ConnectedClientEvent::Connect(connected_event) => {
let mixnet_listener::ConnectEvent { let mixnet_listener::ConnectEvent {
ips, ip,
forward_from_tun_tx, forward_from_tun_tx,
} = *connected_event; } = *connected_event;
log::trace!("Connect client: {ips}"); log::trace!("Connect client: {ip}");
self.clients_ipv4.insert( self.clients.insert(
ips.ipv4, ip,
ConnectedClientMirror {
forward_from_tun_tx: forward_from_tun_tx.clone(),
ips,
},
);
self.clients_ipv6.insert(
ips.ipv6,
ConnectedClientMirror { ConnectedClientMirror {
forward_from_tun_tx, forward_from_tun_tx,
ips,
}, },
); );
} }
mixnet_listener::ConnectedClientEvent::Disconnect( mixnet_listener::ConnectedClientEvent::Disconnect(
mixnet_listener::DisconnectEvent(ips), mixnet_listener::DisconnectEvent(ip),
) => { ) => {
log::trace!("Disconnect client: {ips}"); log::trace!("Disconnect client: {ip}");
self.clients_ipv4.remove(&ips.ipv4); self.clients.remove(&ip);
self.clients_ipv6.remove(&ips.ipv6);
} }
} }
} }
@@ -99,7 +82,6 @@ impl TunListener {
if let Some(ConnectedClientMirror { if let Some(ConnectedClientMirror {
forward_from_tun_tx, forward_from_tun_tx,
ips,
}) = self.connected_clients.get(&dst_addr) }) = self.connected_clients.get(&dst_addr)
{ {
let packet = buf[..len].to_vec(); let packet = buf[..len].to_vec();
@@ -107,7 +89,7 @@ impl TunListener {
log::warn!("Failed to forward packet to connected client {dst_addr}: disconnecting it from tun listener"); log::warn!("Failed to forward packet to connected client {dst_addr}: disconnecting it from tun listener");
self.connected_clients self.connected_clients
.update(mixnet_listener::ConnectedClientEvent::Disconnect( .update(mixnet_listener::ConnectedClientEvent::Disconnect(
mixnet_listener::DisconnectEvent(*ips), mixnet_listener::DisconnectEvent(dst_addr),
)); ));
} }
} else { } else {
@@ -1,52 +1,38 @@
use nym_ip_packet_requests::IpPair; use std::{
use std::net::Ipv6Addr; collections::HashMap,
use std::{collections::HashMap, net::Ipv4Addr}; net::{IpAddr, Ipv4Addr},
};
use crate::constants::{TUN_DEVICE_ADDRESS_V4, TUN_DEVICE_ADDRESS_V6}; use crate::{constants::TUN_DEVICE_ADDRESS, mixnet_listener::ConnectedClient};
// Find an available IP address in self.connected_clients // Find an available IP address in self.connected_clients
// TODO: make this nicer // TODO: make this nicer
fn generate_random_ips_within_subnet() -> IpPair { fn generate_random_ip_within_subnet() -> Ipv4Addr {
let mut rng = rand::thread_rng(); let mut rng = rand::thread_rng();
// Generate a random number in the range 1-254 // Generate a random number in the range 1-254
let last_octet = rand::Rng::gen_range(&mut rng, 1..=254); let last_octet = rand::Rng::gen_range(&mut rng, 1..=254);
let ipv4 = Ipv4Addr::new(10, 0, 0, last_octet); Ipv4Addr::new(10, 0, 0, last_octet)
let ipv6 = Ipv6Addr::new(0x2001, 0x0db8, 0xa160, 0, 0, 0, 0, last_octet as u16);
IpPair::new(ipv4, ipv6)
} }
fn is_ip_taken<T>( fn is_ip_taken(
connected_clients_ipv4: &HashMap<Ipv4Addr, T>, connected_clients: &HashMap<IpAddr, ConnectedClient>,
connected_clients_ipv6: &HashMap<Ipv6Addr, T>, tun_ip: Ipv4Addr,
tun_ips: IpPair, ip: Ipv4Addr,
ips: IpPair,
) -> bool { ) -> bool {
connected_clients_ipv4.contains_key(&ips.ipv4) connected_clients.contains_key(&ip.into()) || ip == tun_ip
|| connected_clients_ipv6.contains_key(&ips.ipv6)
|| ips.ipv4 == tun_ips.ipv4
|| ips.ipv6 == tun_ips.ipv6
} }
// TODO: brute force approach. We could consider using a more efficient algorithm // TODO: brute force approach. We could consider using a more efficient algorithm
pub(crate) fn find_new_ips<T>( pub(crate) fn find_new_ip(connected_clients: &HashMap<IpAddr, ConnectedClient>) -> Option<IpAddr> {
connected_clients_ipv4: &HashMap<Ipv4Addr, T>, let mut new_ip = generate_random_ip_within_subnet();
connected_clients_ipv6: &HashMap<Ipv6Addr, T>,
) -> Option<IpPair> {
let mut new_ips = generate_random_ips_within_subnet();
let mut tries = 0; let mut tries = 0;
let tun_ips = IpPair::new(TUN_DEVICE_ADDRESS_V4, TUN_DEVICE_ADDRESS_V6); let tun_ip = TUN_DEVICE_ADDRESS.parse::<Ipv4Addr>().unwrap();
while is_ip_taken(connected_clients, tun_ip, new_ip) {
while is_ip_taken( new_ip = generate_random_ip_within_subnet();
connected_clients_ipv4,
connected_clients_ipv6,
tun_ips,
new_ips,
) {
new_ips = generate_random_ips_within_subnet();
tries += 1; tries += 1;
if tries > 100 { if tries > 100 {
return None; return None;
} }
} }
Some(new_ips) Some(new_ip.into())
} }
@@ -61,7 +61,7 @@ nym-statistics-common = { path = "../../common/statistics" }
nym-task = { path = "../../common/task" } nym-task = { path = "../../common/task" }
nym-types = { path = "../../common/types" } nym-types = { path = "../../common/types" }
nym-exit-policy = { path = "../../common/exit-policy", features = ["client"] } nym-exit-policy = { path = "../../common/exit-policy", features = ["client"] }
nym-id = { path = "../../common/nym-id" }
[dev-dependencies] [dev-dependencies]
tempfile = "3.5.0" tempfile = "3.5.0"
@@ -4,10 +4,14 @@
use crate::cli::try_load_current_config; use crate::cli::try_load_current_config;
use crate::error::NetworkRequesterError; use crate::error::NetworkRequesterError;
use clap::ArgGroup; use clap::ArgGroup;
use log::{error, info};
use nym_id::import_credential; use nym_credential_storage::models::StorableIssuedCredential;
use nym_credential_storage::storage::Storage;
use nym_credentials::coconut::bandwidth::issued::BandwidthCredentialIssuedDataVariant;
use nym_credentials::IssuedBandwidthCredential;
use std::fs; use std::fs;
use std::path::PathBuf; use std::path::PathBuf;
use zeroize::Zeroizing;
fn parse_encoded_credential_data(raw: &str) -> bs58::decode::Result<Vec<u8>> { fn parse_encoded_credential_data(raw: &str) -> bs58::decode::Result<Vec<u8>> {
bs58::decode(raw).into_vec() bs58::decode(raw).into_vec()
@@ -29,8 +33,8 @@ pub(crate) struct Args {
pub(crate) credential_path: Option<PathBuf>, pub(crate) credential_path: Option<PathBuf>,
// currently hidden as there exists only a single serialization standard // currently hidden as there exists only a single serialization standard
#[clap(long, hide = true)] #[clap(long, hide = true, default_value_t = 1)]
pub(crate) version: Option<u8>, pub(crate) version: u8,
} }
pub(crate) async fn execute(args: Args) -> Result<(), NetworkRequesterError> { pub(crate) async fn execute(args: Args) -> Result<(), NetworkRequesterError> {
@@ -48,7 +52,50 @@ pub(crate) async fn execute(args: Args) -> Result<(), NetworkRequesterError> {
fs::read(args.credential_path.unwrap())? fs::read(args.credential_path.unwrap())?
} }
}; };
let raw_credential = Zeroizing::new(raw_credential);
import_credential(credentials_store, raw_credential, args.version).await?; // we're unpacking the data in order to make sure it's valid
// and to extract relevant metadata for storage purposes
let credential = match args.version {
1 => Zeroizing::new(
IssuedBandwidthCredential::unpack_v1(&raw_credential).map_err(|source| {
NetworkRequesterError::CredentialDeserializationFailure {
storage_revision: 1,
source,
}
})?,
),
other => panic!("unknown credential serialization version {other}"),
};
info!("importing {}", credential.typ());
match credential.variant_data() {
BandwidthCredentialIssuedDataVariant::Voucher(voucher_info) => {
info!("with value of {}", voucher_info.value())
}
BandwidthCredentialIssuedDataVariant::FreePass(freepass_info) => {
info!("with expiry at {}", freepass_info.expiry_date());
if freepass_info.expired() {
error!("the free pass has already expired!");
// technically we can import it, but the gateway will just reject it so what's the point
return Err(NetworkRequesterError::ExpiredCredentialImport {
expiration: freepass_info.expiry_date(),
});
}
}
}
let storable = StorableIssuedCredential {
serialization_revision: args.version,
credential_data: &raw_credential,
credential_type: credential.typ().to_string(),
epoch_id: credential
.epoch_id()
.try_into()
.expect("our epoch is has run over u32::MAX!"),
};
credentials_store.insert_issued_credential(storable).await?;
Ok(()) Ok(())
} }
@@ -2,11 +2,11 @@
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
pub use nym_client_core::error::ClientCoreError; pub use nym_client_core::error::ClientCoreError;
use nym_credential_storage::error::StorageError;
use nym_exit_policy::policy::PolicyError; use nym_exit_policy::policy::PolicyError;
use nym_id::NymIdError;
use nym_socks5_requests::{RemoteAddress, Socks5RequestError}; use nym_socks5_requests::{RemoteAddress, Socks5RequestError};
use std::net::SocketAddr; use std::net::SocketAddr;
use time::OffsetDateTime;
#[derive(thiserror::Error, Debug)] #[derive(thiserror::Error, Debug)]
pub enum NetworkRequesterError { pub enum NetworkRequesterError {
@@ -70,6 +70,21 @@ pub enum NetworkRequesterError {
#[error("can't setup an exit policy without any upstream urls")] #[error("can't setup an exit policy without any upstream urls")]
NoUpstreamExitPolicy, NoUpstreamExitPolicy,
#[error(transparent)] #[error("failed to store credential: {source}")]
NymIdError(#[from] NymIdError), CredentialStorageFailure {
#[from]
source: StorageError,
},
#[error(
"failed to deserialize provided credential using revision {storage_revision}: {source}"
)]
CredentialDeserializationFailure {
storage_revision: u8,
#[source]
source: nym_credentials::error::Error,
},
#[error("attempted to import an expired credential (it expired on {expiration})")]
ExpiredCredentialImport { expiration: OffsetDateTime },
} }
-22
View File
@@ -1,22 +0,0 @@
[package]
name = "nym-id-cli"
version = "0.1.0"
authors.workspace = true
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
edition.workspace = true
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow.workspace = true
bs58.workspace = true
clap = { workspace = true, features = ["derive"] }
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
tracing.workspace = true
nym-bin-common = { path = "../../common/bin-common", features = ["output_format", "basic_tracing"] }
nym-credential-storage = { path = "../../common/credential-storage" }
nym-id = { path = "../../common/nym-id" }
@@ -1,15 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use nym_bin_common::bin_info_owned;
use nym_bin_common::output_format::OutputFormat;
#[derive(clap::Args)]
pub(crate) struct Args {
#[clap(short, long, default_value_t = OutputFormat::default())]
output: OutputFormat,
}
pub(crate) fn execute(args: Args) {
println!("{}", args.output.format(&bin_info_owned!()))
}
@@ -1,48 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::ArgGroup;
use nym_id::import_credential;
use std::fs;
use std::path::PathBuf;
fn parse_encoded_credential_data(raw: &str) -> bs58::decode::Result<Vec<u8>> {
bs58::decode(raw).into_vec()
}
#[derive(clap::Args)]
#[clap(group(ArgGroup::new("cred_data").required(true)))]
pub(crate) struct Args {
/// Explicitly provide the encoded credential data (as base58)
#[clap(long, group = "cred_data", value_parser = parse_encoded_credential_data)]
pub(crate) credential_data: Option<Vec<u8>>,
/// Specifies the path to file containing binary credential data
#[clap(long, group = "cred_data")]
pub(crate) credential_path: Option<PathBuf>,
/// Specifies path to the credentials storage
#[clap(long)]
pub credentials_store_path: PathBuf,
// currently hidden as there exists only a single serialization standard
#[clap(long, hide = true)]
pub(crate) version: Option<u8>,
}
pub(crate) async fn execute(args: Args) -> anyhow::Result<()> {
let credentials_store =
nym_credential_storage::initialise_persistent_storage(args.credentials_store_path).await;
let raw_credential = match args.credential_data {
Some(data) => data,
None => {
// SAFETY: one of those arguments must have been set
#[allow(clippy::unwrap_used)]
fs::read(args.credential_path.unwrap())?
}
};
import_credential(credentials_store, raw_credential, args.version).await?;
Ok(())
}
-45
View File
@@ -1,45 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
mod build_info;
mod import_credential;
mod setup;
use clap::{Parser, Subcommand};
use nym_bin_common::bin_info;
use std::sync::OnceLock;
fn pretty_build_info_static() -> &'static str {
static PRETTY_BUILD_INFORMATION: OnceLock<String> = OnceLock::new();
PRETTY_BUILD_INFORMATION.get_or_init(|| bin_info!().pretty_print())
}
#[derive(Parser)]
#[clap(author = "Nymtech", version, long_version = pretty_build_info_static(), about)]
pub(crate) struct Cli {
#[clap(subcommand)]
command: Commands,
}
impl Cli {
pub async fn execute(self) -> anyhow::Result<()> {
match self.command {
Commands::ImportCredential(args) => import_credential::execute(args).await?,
Commands::BuildInfo(args) => build_info::execute(args),
}
Ok(())
}
}
#[derive(Subcommand)]
pub(crate) enum Commands {
// TODO: to be determined how it's going to work in nymvpn et al.
// ///
// Setup,
/// Attempt to import a bandwidth credential into the provided storage.
ImportCredential(import_credential::Args),
/// Show build information of this binary
BuildInfo(build_info::Args),
}
-2
View File
@@ -1,2 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
-19
View File
@@ -1,19 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
#![warn(clippy::expect_used)]
#![warn(clippy::unwrap_used)]
use crate::commands::Cli;
use clap::Parser;
use nym_bin_common::logging::setup_tracing_logger;
mod commands;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
setup_tracing_logger();
let cli = Cli::parse();
cli.execute().await
}
+3 -3
View File
@@ -376,9 +376,9 @@ inherits@2, inherits@^2.0.3:
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
ip@^2.0.0: ip@^2.0.0:
version "2.0.1" version "2.0.0"
resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.1.tgz#e8f3595d33a3ea66490204234b77636965307105" resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.0.tgz#4cf4ab182fee2314c75ede1276f8c80b479936da"
integrity sha512-lJUL9imLTNi1ZfXT+DU6rBBdbiKGBuay9B6xGSPVjUeQwaH1RIGqef8RZkUtHioLmSNpPR5M4HVKJGm1j8FWVQ== integrity sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==
is-fullwidth-code-point@^3.0.0: is-fullwidth-code-point@^3.0.0:
version "3.0.0" version "3.0.0"
+6 -4
View File
@@ -17,7 +17,8 @@
}, },
"../../../dist/node/wasm/mix-fetch": { "../../../dist/node/wasm/mix-fetch": {
"name": "@nymproject/mix-fetch-wasm", "name": "@nymproject/mix-fetch-wasm",
"version": "1.2.0-rc.2" "version": "1.2.0-rc.2",
"license": "Apache-2.0"
}, },
"node_modules/@gar/promisify": { "node_modules/@gar/promisify": {
"version": "1.1.3", "version": "1.1.3",
@@ -642,9 +643,10 @@
"license": "ISC" "license": "ISC"
}, },
"node_modules/ip": { "node_modules/ip": {
"version": "2.0.1", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/ip/-/ip-2.0.1.tgz", "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz",
"integrity": "sha512-lJUL9imLTNi1ZfXT+DU6rBBdbiKGBuay9B6xGSPVjUeQwaH1RIGqef8RZkUtHioLmSNpPR5M4HVKJGm1j8FWVQ==", "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==",
"license": "MIT",
"optional": true "optional": true
}, },
"node_modules/is-fullwidth-code-point": { "node_modules/is-fullwidth-code-point": {
+3 -3
View File
@@ -396,9 +396,9 @@ inherits@2, inherits@^2.0.3:
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
ip@^2.0.0: ip@^2.0.0:
version "2.0.1" version "2.0.0"
resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.1.tgz#e8f3595d33a3ea66490204234b77636965307105" resolved "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz"
integrity sha512-lJUL9imLTNi1ZfXT+DU6rBBdbiKGBuay9B6xGSPVjUeQwaH1RIGqef8RZkUtHioLmSNpPR5M4HVKJGm1j8FWVQ== integrity sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==
is-fullwidth-code-point@^3.0.0: is-fullwidth-code-point@^3.0.0:
version "3.0.0" version "3.0.0"
+3 -3
View File
@@ -11174,9 +11174,9 @@ interpret@^2.2.0:
integrity sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw== integrity sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==
ip@^2.0.0: ip@^2.0.0:
version "2.0.1" version "2.0.0"
resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.1.tgz#e8f3595d33a3ea66490204234b77636965307105" resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.0.tgz#4cf4ab182fee2314c75ede1276f8c80b479936da"
integrity sha512-lJUL9imLTNi1ZfXT+DU6rBBdbiKGBuay9B6xGSPVjUeQwaH1RIGqef8RZkUtHioLmSNpPR5M4HVKJGm1j8FWVQ== integrity sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==
ipaddr.js@1.9.1: ipaddr.js@1.9.1:
version "1.9.1" version "1.9.1"