Compare commits

..

3 Commits

Author SHA1 Message Date
Jon Häggblad b7f445a330 Delete wireguard feature flag altogether 2023-11-17 09:41:02 +01:00
Jon Häggblad 360c7fda57 Update ci-build 2023-11-17 09:37:21 +01:00
Jon Häggblad 2cbb2d8327 gateway: enable wireguard at runtime 2023-11-17 09:35:23 +01:00
116 changed files with 1251 additions and 2838 deletions
+6 -7
View File
@@ -75,29 +75,28 @@ jobs:
uses: actions-rs/cargo@v1
with:
command: build
# Enable wireguard by default on linux only
args: --workspace --features wireguard
args: --workspace
- name: Build all examples
if: matrix.os == 'custom-linux'
uses: actions-rs/cargo@v1
with:
command: build
args: --workspace --examples --features wireguard
args: --workspace --examples
- name: Run all tests
if: matrix.os == 'custom-linux'
uses: actions-rs/cargo@v1
with:
command: test
args: --workspace --features wireguard
args: --workspace
- name: Run expensive tests
if: (github.ref == 'refs/heads/develop' || github.event.pull_request.base.ref == 'develop' || github.event.pull_request.base.ref == 'master') && matrix.os == 'custom-linux'
uses: actions-rs/cargo@v1
with:
command: test
args: --workspace --features wireguard -- --ignored
args: --workspace -- --ignored
- name: Annotate with clippy checks
if: matrix.os == 'custom-linux'
@@ -105,10 +104,10 @@ jobs:
continue-on-error: true
with:
token: ${{ secrets.GITHUB_TOKEN }}
args: --workspace --features wireguard
args: --workspace
- name: Clippy
uses: actions-rs/cargo@v1
with:
command: clippy
args: --workspace --all-targets --features wireguard -- -D warnings
args: --workspace --all-targets -- -D warnings
+7 -11
View File
@@ -9,11 +9,9 @@ on:
jobs:
build:
runs-on: ubuntu-20.04-16-core
runs-on: custom-linux
steps:
- uses: actions/checkout@v3
- name: Install Dependencies (Linux)
run: sudo apt-get update && sudo apt-get install -y build-essential curl wget libssl-dev libudev-dev squashfs-tools protobuf-compiler
- name: Install rsync
run: sudo apt-get install rsync
- uses: rlespinasse/github-slug-action@v3.x
@@ -30,22 +28,20 @@ jobs:
command: build
args: --workspace --release
- name: Install mdbook
run: cargo install mdbook
# run: (test -x $HOME/.cargo/bin/mdbook || cargo install --vers "^0.4.35" mdbook)
run: (test -x $HOME/.cargo/bin/mdbook || cargo install --vers "^0.4.35" mdbook)
- name: Install mdbook plugins
run: |
cargo install mdbook-variables
cargo install mdbook-admonish
cargo install mdbook-last-changed
cargo install mdbook-theme
cargo install mdbook-linkcheck
cargo install --vers "=0.2.2" mdbook-variables && cargo install \
--vers "^1.8.0" mdbook-admonish --force && cargo install --vers \
"^0.1.2" mdbook-last-changed && cargo install --vers "^0.1.2" mdbook-theme \
&& cargo install --vers "^0.7.7" mdbook-linkcheck \
# && cd documentation \
# && mdbook-admonish install dev-portal \
# && mdbook-admonish install docs \
# && mdbook-admonish install operators
- name: Build all projects in documentation/ & move to ~/dist/docs/
run: cd documentation && ./build_all_to_dist.sh
run: cd documentation && ./build_all_to_dist.sh
continue-on-error: false
- name: Deploy branch to CI www
continue-on-error: true
+1 -1
View File
@@ -35,7 +35,7 @@ jobs:
uses: actions-rs/cargo@v1
with:
command: build
args: --manifest-path ${{ env.CARGOTOML_PATH }} --features custom-protocol
args: --manifest-path ${{ env.CARGOTOML_PATH }} --lib --features custom-protocol
# - name: Run all tests
# uses: actions-rs/cargo@v1
-43
View File
@@ -1,43 +0,0 @@
# Simple workflow for deploying static content to GitHub Pages
name: Deploy static content to Pages
on:
# Runs on pushes targeting the default branch
push:
branches: ["feature/ppa-repo"]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
contents: read
pages: write
id-token: write
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
group: "pages"
cancel-in-progress: false
jobs:
# Single deploy job since we're just deploying
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup Pages
uses: actions/configure-pages@v3
- name: Upload artifact
uses: actions/upload-pages-artifact@v2
with:
# Upload entire repository
path: './ppa'
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v2
+1 -3
View File
@@ -45,6 +45,4 @@ envs/qwerty.env
cpu-cycles/libcpucycles/build
foxyfox.env
.next
ppa-private-key.b64
ppa-private-key.asc
.next
-2
View File
@@ -10,13 +10,11 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
- [mixnode] replace rocket with axum ([#4071])
- incorporate the nym node HTTP api into the mixnode ([#4070])
- replaced '--disable-sign-ext' with '--signext-lowering' when running wasm-opt ([#3896])
- Added PPA repo hosting support and nym-mixnode package with tooling for publishing ([#4165])
[#4077]: https://github.com/nymtech/nym/pull/4077
[#4071]: https://github.com/nymtech/nym/pull/4071
[#4070]: https://github.com/nymtech/nym/issues/4070
[#3896]: https://github.com/nymtech/nym/pull/3896
[#4165]: https://github.com/nymtech/nym/pull/4165
## [2023.3-kinder] (2023-10-31)
Generated
-35
View File
@@ -6657,22 +6657,10 @@ dependencies = [
"thiserror",
]
[[package]]
name = "nym-ip-packet-requests"
version = "0.1.0"
dependencies = [
"bincode",
"bytes",
"nym-sphinx",
"serde",
]
[[package]]
name = "nym-ip-packet-router"
version = "0.1.0"
dependencies = [
"bincode",
"bytes",
"etherparse",
"futures",
"log",
@@ -6680,13 +6668,11 @@ dependencies = [
"nym-client-core",
"nym-config",
"nym-exit-policy",
"nym-ip-packet-requests",
"nym-network-requester",
"nym-sdk",
"nym-service-providers-common",
"nym-sphinx",
"nym-task",
"nym-tun",
"nym-wireguard",
"nym-wireguard-types",
"reqwest",
@@ -6934,7 +6920,6 @@ dependencies = [
"nym-crypto",
"nym-node-requests",
"nym-task",
"nym-wireguard",
"nym-wireguard-types",
"rand 0.7.3",
"serde",
@@ -7477,18 +7462,6 @@ dependencies = [
"wasm-utils",
]
[[package]]
name = "nym-tun"
version = "0.1.0"
dependencies = [
"etherparse",
"log",
"nym-wireguard-types",
"thiserror",
"tokio",
"tokio-tun",
]
[[package]]
name = "nym-types"
version = "1.0.0"
@@ -7608,7 +7581,6 @@ version = "0.1.0"
dependencies = [
"async-recursion",
"base64 0.21.4",
"bincode",
"boringtun",
"bytes",
"dashmap",
@@ -7617,9 +7589,7 @@ dependencies = [
"ip_network",
"ip_network_table",
"log",
"nym-sphinx",
"nym-task",
"nym-tun",
"nym-wireguard-types",
"rand 0.8.5",
"serde",
@@ -7635,19 +7605,14 @@ version = "0.1.0"
dependencies = [
"base64 0.21.4",
"boringtun",
"bytes",
"dashmap",
"hmac 0.12.1",
"ip_network",
"ip_network_table",
"log",
"nym-crypto",
"rand 0.7.3",
"serde",
"serde_json",
"sha2 0.10.8",
"thiserror",
"tokio",
"utoipa",
"x25519-dalek 2.0.0",
]
-2
View File
@@ -49,7 +49,6 @@ members = [
"common/exit-policy",
"common/http-api-client",
"common/inclusion-probability",
"common/ip-packet-requests",
"common/ledger",
"common/mixnode-common",
"common/network-defaults",
@@ -75,7 +74,6 @@ members = [
"common/store-cipher",
"common/task",
"common/topology",
"common/tun",
"common/types",
"common/wasm/client-core",
"common/wasm/storage",
-4
View File
@@ -168,7 +168,3 @@ generate-typescript:
run-api-tests:
cd nym-api/tests/functional_test && yarn test:qa
# Build debian package, and update PPA
# Requires base64 encode GPG key to be set up in environment PPA_SIGNING_KEY
deb:
scripts/ppa.sh
+1 -6
View File
@@ -22,10 +22,5 @@ async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
}
setup_logging();
if let Err(err) = commands::execute(args).await {
log::error!("{err}");
println!("An error occurred: {err}");
std::process::exit(1);
}
Ok(())
commands::execute(args).await
}
+1 -6
View File
@@ -21,10 +21,5 @@ async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
}
setup_logging();
if let Err(err) = commands::execute(args).await {
log::error!("{err}");
println!("An error occurred: {err}");
std::process::exit(1);
}
Ok(())
commands::execute(args).await
}
@@ -263,7 +263,7 @@ impl ActionController {
pub(super) async fn run_with_shutdown(&mut self, mut shutdown: nym_task::TaskClient) {
debug!("Started ActionController with graceful shutdown support");
loop {
while !shutdown.is_shutdown() {
tokio::select! {
action = self.incoming_actions.next() => match action {
Some(action) => self.process_action(action),
@@ -283,7 +283,6 @@ impl ActionController {
},
_ = shutdown.recv_with_delay() => {
log::trace!("ActionController: Received shutdown");
break;
}
}
}
@@ -40,7 +40,7 @@ impl SentNotificationListener {
pub(super) async fn run_with_shutdown(&mut self, mut shutdown: nym_task::TaskClient) {
debug!("Started SentNotificationListener with graceful shutdown support");
loop {
while !shutdown.is_shutdown() {
tokio::select! {
frag_id = self.sent_notifier.next() => match frag_id {
Some(frag_id) => {
@@ -53,7 +53,6 @@ impl SentNotificationListener {
},
_ = shutdown.recv_with_delay() => {
log::trace!("SentNotificationListener: Received shutdown");
break;
}
}
}
@@ -500,12 +500,11 @@ where
{
let mut status_timer = tokio::time::interval(Duration::from_secs(5));
loop {
while !shutdown.is_shutdown() {
tokio::select! {
biased;
_ = shutdown.recv_with_delay() => {
log::trace!("OutQueueControl: Received shutdown");
break;
}
_ = status_timer.tick() => {
self.log_status(&mut shutdown);
@@ -39,7 +39,7 @@ where
mem_state: CombinedReplyStorage,
mut shutdown: nym_task::TaskClient,
) {
use log::{debug, error, info};
use log::{debug, error, info, warn};
debug!("Started PersistentReplyStorage");
if let Err(err) = self.backend.start_storage_session().await {
@@ -50,7 +50,7 @@ where
shutdown.recv().await;
info!("PersistentReplyStorage is flushing all reply-related data to underlying storage");
info!("you MUST NOT forcefully shutdown now or you risk data corruption!");
warn!("you MUST NOT forcefully shutdown now or you risk data corruption!");
if let Err(err) = self.backend.flush_surb_storage(&mem_state).await {
error!("failed to flush our reply-related data to the persistent storage: {err}")
} else {
+14 -14
View File
@@ -15,34 +15,34 @@ pub enum ClientCoreError {
#[error("I/O error: {0}")]
IoError(#[from] std::io::Error),
#[error("gateway client error ({gateway_id}): {source}")]
#[error("Gateway client error ({gateway_id}): {source}")]
GatewayClientError {
gateway_id: String,
source: GatewayClientError,
},
#[error("custom gateway client error: {source}")]
#[error("Custom gateway client error: {source}")]
ErasedGatewayClientError {
#[from]
source: ErasedGatewayError,
},
#[error("ed25519 error: {0}")]
#[error("Ed25519 error: {0}")]
Ed25519RecoveryError(#[from] Ed25519RecoveryError),
#[error("validator client error: {0}")]
#[error("Validator client error: {0}")]
ValidatorClientError(#[from] ValidatorClientError),
#[error("no gateway with id: {0}")]
#[error("No gateway with id: {0}")]
NoGatewayWithId(String),
#[error("no gateways on network")]
#[error("No gateways on network")]
NoGatewaysOnNetwork,
#[error("list of nym apis is empty")]
#[error("List of nym apis is empty")]
ListOfNymApisIsEmpty,
#[error("the current network topology seem to be insufficient to route any packets through")]
#[error("The current network topology seem to be insufficient to route any packets through")]
InsufficientNetworkTopology(#[from] NymTopologyError),
#[error("experienced a failure with our reply surb persistent storage: {source}")]
@@ -60,7 +60,7 @@ pub enum ClientCoreError {
source: Box<dyn Error + Send + Sync>,
},
#[error("the gateway id is invalid - {0}")]
#[error("The gateway id is invalid - {0}")]
UnableToCreatePublicKeyFromGatewayId(Ed25519RecoveryError),
#[error("The gateway is malformed: {source}")]
@@ -79,23 +79,23 @@ pub enum ClientCoreError {
#[error("failed to establish gateway connection (wasm)")]
GatewayJsConnectionFailure,
#[error("gateway connection was abruptly closed")]
#[error("Gateway connection was abruptly closed")]
GatewayConnectionAbruptlyClosed,
#[error("timed out while trying to establish gateway connection")]
#[error("Timed out while trying to establish gateway connection")]
GatewayConnectionTimeout,
#[error("no ping measurements for the gateway ({identity}) performed")]
#[error("No ping measurements for the gateway ({identity}) performed")]
NoGatewayMeasurements { identity: String },
#[error("failed to register receiver for reconstructed mixnet messages")]
FailedToRegisterReceiver,
#[error("unexpected exit")]
#[error("Unexpected exit")]
UnexpectedExit,
#[error(
"this operation would have resulted in clients keys being overwritten without permission"
"This operation would have resulted in clients keys being overwritten without permission"
)]
ForbiddenKeyOverwrite,
-10
View File
@@ -68,23 +68,13 @@ pub async fn current_gateways<R: Rng>(
log::trace!("Fetching list of gateways from: {nym_api}");
let gateways = client.get_cached_described_gateways().await?;
log::debug!("Found {} gateways", gateways.len());
log::trace!("Gateways: {:#?}", gateways);
let valid_gateways = gateways
.into_iter()
.filter_map(|gateway| gateway.try_into().ok())
.collect::<Vec<gateway::Node>>();
log::debug!("Ater checking validity: {}", valid_gateways.len());
log::trace!("Valid gateways: {:#?}", valid_gateways);
// we were always filtering by version so I'm not removing that 'feature'
let filtered_gateways = valid_gateways.filter_by_version(env!("CARGO_PKG_VERSION"));
log::debug!("After filtering for version: {}", filtered_gateways.len());
log::trace!("Filtered gateways: {:#?}", filtered_gateways);
log::info!("nym-api reports {} valid gateways", filtered_gateways.len());
Ok(filtered_gateways)
}
-3
View File
@@ -94,8 +94,6 @@ where
D::StorageError: Send + Sync + 'static,
T: DeserializeOwned + Serialize + Send + Sync,
{
log::trace!("Setting up new gateway");
// if we're setting up new gateway, failing to load existing information is fine.
// as a matter of fact, it's only potentially a problem if we DO succeed
if _load_gateway_details(details_store).await.is_ok() && !overwrite_data {
@@ -212,7 +210,6 @@ where
D::StorageError: Send + Sync + 'static,
T: DeserializeOwned + Serialize + Send + Sync,
{
log::trace!("Setting up gateway");
match setup {
GatewaySetup::MustLoad => use_loaded_gateway_details(key_store, details_store).await,
GatewaySetup::New {
@@ -142,7 +142,7 @@ pub fn funds_from_cosmos_msgs(msgs: Vec<CosmosMsg>) -> Option<Coin> {
contract_addr: _,
msg,
funds: _,
})) = msgs.first()
})) = msgs.get(0)
{
if let Ok(ExecuteMsg::ReleaseFunds { funds }) = from_binary::<ExecuteMsg>(msg) {
return Some(funds);
@@ -62,7 +62,7 @@ pub fn owner_from_cosmos_msgs(msgs: &[CosmosMsg]) -> Option<Addr> {
contract_addr: _,
msg,
funds: _,
})) = msgs.first()
})) = msgs.get(0)
{
if let Ok(ExecuteMsg::VerifyVerificationKeyShare { owner, .. }) =
from_binary::<ExecuteMsg>(msg)
@@ -49,7 +49,7 @@ impl Account {
pub fn period_duration(&self) -> Result<u64, VestingContractError> {
self.periods
.first()
.get(0)
.ok_or(VestingContractError::UnpopulatedVestingPeriods {
owner: self.owner_address.clone(),
})
+5 -1
View File
@@ -115,7 +115,11 @@ impl Dealing {
.map(|&node_index| polynomial.evaluate_at(&Scalar::from(node_index)).into())
.collect::<Vec<_>>();
let remote_share_key_pairs = shares.iter().zip(receivers.values()).collect::<Vec<_>>();
let remote_share_key_pairs = shares
.iter()
.zip(receivers.values())
.map(|(share, key)| (share, key))
.collect::<Vec<_>>();
let ordered_public_keys = receivers.values().copied().collect::<Vec<_>>();
let (ciphertexts, hazmat) = encrypt_shares(&remote_share_key_pairs, params, &mut rng);
-17
View File
@@ -1,17 +0,0 @@
[package]
name = "nym-ip-packet-requests"
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]
bincode = "1.3.3"
bytes = "1.5.0"
nym-sphinx = { path = "../nymsphinx" }
serde = { workspace = true, features = ["derive"] }
-40
View File
@@ -1,40 +0,0 @@
#[derive(serde::Serialize, serde::Deserialize)]
pub struct TaggedIpPacket {
pub packet: bytes::Bytes,
pub return_address: nym_sphinx::addressing::clients::Recipient,
pub return_mix_hops: Option<u8>,
// pub return_mix_delays: Option<f64>,
}
impl TaggedIpPacket {
pub fn new(
packet: bytes::Bytes,
return_address: nym_sphinx::addressing::clients::Recipient,
return_mix_hops: Option<u8>,
) -> Self {
TaggedIpPacket {
packet,
return_address,
return_mix_hops,
}
}
pub fn from_reconstructed_message(
message: &nym_sphinx::receiver::ReconstructedMessage,
) -> Result<Self, bincode::Error> {
use bincode::Options;
make_bincode_serializer().deserialize(&message.message)
}
pub fn to_bytes(&self) -> Result<Vec<u8>, bincode::Error> {
use bincode::Options;
make_bincode_serializer().serialize(self)
}
}
fn make_bincode_serializer() -> impl bincode::Options {
use bincode::Options;
bincode::DefaultOptions::new()
.with_big_endian()
.with_varint_encoding()
}
+1 -1
View File
@@ -50,7 +50,7 @@ where
impl Aggregatable for PartialSignature {
fn aggregate(sigs: &[PartialSignature], indices: Option<&[u64]>) -> Result<Signature> {
let h = sigs
.first()
.get(0)
.ok_or_else(|| CoconutError::Aggregation("Empty set of signatures".to_string()))?
.sig1();
+2 -5
View File
@@ -493,13 +493,10 @@ impl TaskClient {
impl Drop for TaskClient {
fn drop(&mut self) {
if !self.mode.should_signal_on_drop() {
self.log(
Level::Trace,
"the task client is getting dropped (but instructed to not signal)",
);
self.log(Level::Debug, "the task client is getting dropped");
return;
} else {
self.log(Level::Debug, "the task client is getting dropped");
self.log(Level::Info, "the task client is getting dropped");
}
if !self.is_shutdown_poll() {
-24
View File
@@ -1,24 +0,0 @@
[package]
name = "nym-tun"
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
tokio = { workspace = true, features = ["rt-multi-thread", "net", "io-util", "time", "sync", "macros"] }
etherparse = "0.13.0"
log.workspace = true
nym-wireguard-types = { path = "../wireguard-types", optional = true }
[target.'cfg(target_os = "linux")'.dependencies]
tokio-tun = "0.9.0"
[features]
wireguard = ["nym-wireguard-types"]
-7
View File
@@ -1,7 +0,0 @@
#[cfg(target_os = "linux")]
mod linux;
pub mod tun_task_channel;
#[cfg(target_os = "linux")]
pub use linux::tun_device;
-84
View File
@@ -1,84 +0,0 @@
#![cfg_attr(not(target_os = "linux"), allow(dead_code))]
use std::time::Duration;
use tokio::sync::mpsc::{
self,
error::{SendError, SendTimeoutError, TrySendError},
};
pub(crate) type TunTaskPayload = (u64, Vec<u8>);
#[derive(Clone)]
pub struct TunTaskTx(mpsc::Sender<TunTaskPayload>);
pub(crate) struct TunTaskRx(mpsc::Receiver<TunTaskPayload>);
impl TunTaskTx {
pub async fn send(&self, data: TunTaskPayload) -> Result<(), SendError<TunTaskPayload>> {
self.0.send(data).await
}
pub fn try_send(&self, data: TunTaskPayload) -> Result<(), TrySendError<TunTaskPayload>> {
self.0.try_send(data)
}
}
impl TunTaskRx {
pub(crate) async fn recv(&mut self) -> Option<TunTaskPayload> {
self.0.recv().await
}
}
pub(crate) fn tun_task_channel() -> (TunTaskTx, TunTaskRx) {
let (tun_task_tx, tun_task_rx) = tokio::sync::mpsc::channel(128);
(TunTaskTx(tun_task_tx), TunTaskRx(tun_task_rx))
}
const TUN_TASK_RESPONSE_SEND_TIMEOUT_MS: u64 = 1_000;
// Send responses back from the tun device back to the PacketRelayer
pub(crate) struct TunTaskResponseTx(mpsc::Sender<TunTaskPayload>);
pub struct TunTaskResponseRx(mpsc::Receiver<TunTaskPayload>);
#[derive(thiserror::Error, Debug)]
pub enum TunTaskResponseSendError {
#[error("failed to send tun response: {0}")]
SendTimeoutError(#[from] SendTimeoutError<TunTaskPayload>),
#[error("failed to send tun response: {0}")]
SendError(#[from] SendError<TunTaskPayload>),
#[error("failed to send tun response: {0}")]
TrySendError(#[from] TrySendError<TunTaskPayload>),
}
impl TunTaskResponseTx {
#[allow(unused)]
pub(crate) async fn send(&self, data: TunTaskPayload) -> Result<(), TunTaskResponseSendError> {
Ok(self
.0
.send_timeout(
data,
Duration::from_millis(TUN_TASK_RESPONSE_SEND_TIMEOUT_MS),
)
.await?)
}
pub(crate) fn try_send(&self, data: TunTaskPayload) -> Result<(), TunTaskResponseSendError> {
Ok(self.0.try_send(data)?)
}
}
impl TunTaskResponseRx {
pub async fn recv(&mut self) -> Option<TunTaskPayload> {
self.0.recv().await
}
}
pub(crate) fn tun_task_response_channel() -> (TunTaskResponseTx, TunTaskResponseRx) {
let (tun_task_tx, tun_task_rx) = tokio::sync::mpsc::channel(128);
(
TunTaskResponseTx(tun_task_tx),
TunTaskResponseRx(tun_task_rx),
)
}
+1 -6
View File
@@ -12,14 +12,9 @@ license.workspace = true
[dependencies]
base64 = { workspace = true }
bytes = "1.5.0"
dashmap = { workspace = true }
ip_network = "0.4.1"
ip_network_table = "0.2.0"
log = { workspace = true }
serde = { workspace = true, features = ["derive"] }
thiserror = { workspace = true }
tokio = { workspace = true, features = ["sync", "time"] }
nym-crypto = { path = "../crypto", features = ["asymmetric"] }
@@ -50,4 +45,4 @@ nym-crypto = { path = "../crypto", features = ["rand"]}
default = ["verify"]
openapi = ["utoipa", "serde_json"]
# this is moved to a separate feature as we really need clients to import it (especially, *cough*, wasm)
verify = ["hmac", "sha2"]
verify = ["hmac", "sha2"]
-2
View File
@@ -4,8 +4,6 @@
pub mod error;
pub mod public_key;
pub mod registration;
#[cfg(not(target_arch = "wasm32"))]
pub mod tun_common;
pub use error::Error;
pub use public_key::PeerPublicKey;
+4 -2
View File
@@ -93,7 +93,8 @@ impl GatewayClient {
) -> Self {
// convert from 1.0 x25519-dalek private key into 2.0 x25519-dalek
#[allow(clippy::expect_used)]
let static_secret = boringtun::x25519::StaticSecret::from(local_secret.to_bytes());
let static_secret = boringtun::x25519::StaticSecret::try_from(local_secret.to_bytes())
.expect("conversion between x25519 private keys is infallible");
let local_public: boringtun::x25519::PublicKey = (&static_secret).into();
let remote_public = boringtun::x25519::PublicKey::from(remote_public.to_bytes());
@@ -122,7 +123,8 @@ impl GatewayClient {
pub fn verify(&self, gateway_key: &PrivateKey, nonce: u64) -> Result<(), Error> {
// convert from 1.0 x25519-dalek private key into 2.0 x25519-dalek
#[allow(clippy::expect_used)]
let static_secret = boringtun::x25519::StaticSecret::from(gateway_key.to_bytes());
let static_secret = boringtun::x25519::StaticSecret::try_from(gateway_key.to_bytes())
.expect("conversion between x25519 private keys is infallible");
let dh = static_secret.diffie_hellman(&self.pub_key);
@@ -1,3 +0,0 @@
pub mod active_peers;
pub mod event;
pub mod network_table;
-3
View File
@@ -13,7 +13,6 @@ license.workspace = true
[dependencies]
async-recursion = "1.0.4"
base64 = "0.21.3"
bincode = "1.3.3"
# The latest version on crates.io at the time of writing this (6.0.0) has a
# version mismatch with x25519-dalek/curve25519-dalek that is resolved in the
# latest commit. So pick that for now.
@@ -28,8 +27,6 @@ ip_network_table = "0.2.0"
log.workspace = true
nym-task = { path = "../task" }
nym-wireguard-types = { path = "../wireguard-types" }
nym-sphinx = { path = "../nymsphinx" }
nym-tun = { path = "../tun" , features = ["wireguard"] }
rand.workspace = true
serde = { workspace = true, features = ["derive"] }
tap.workspace = true
@@ -1,4 +1,4 @@
use std::{net::SocketAddr, time::Duration};
use std::net::SocketAddr;
use boringtun::x25519;
use dashmap::{
@@ -7,47 +7,26 @@ use dashmap::{
};
use tokio::sync::mpsc::{self};
use crate::tun_common::{event::Event, network_table::NetworkTable};
// Registered peers
pub type PeersByIp = NetworkTable<PeerEventSender>;
use crate::event::Event;
// Channels that are used to communicate with the various tunnels
#[derive(Clone)]
pub struct PeerEventSender(mpsc::Sender<Event>);
pub struct PeerEventReceiver(mpsc::Receiver<Event>);
#[derive(thiserror::Error, Debug)]
pub enum PeerEventSenderError {
#[error("send failed: timeout: {source}")]
SendTimeoutError {
#[from]
source: mpsc::error::SendTimeoutError<Event>,
},
#[error("send failed: {source}")]
SendError {
#[from]
source: mpsc::error::SendError<Event>,
},
}
pub(crate) struct PeerEventReceiver(mpsc::Receiver<Event>);
impl PeerEventSender {
pub async fn send(&self, event: Event) -> Result<(), PeerEventSenderError> {
Ok(self
.0
.send_timeout(event, Duration::from_millis(1000))
.await?)
pub(crate) async fn send(&self, event: Event) -> Result<(), mpsc::error::SendError<Event>> {
self.0.send(event).await
}
}
impl PeerEventReceiver {
pub async fn recv(&mut self) -> Option<Event> {
pub(crate) async fn recv(&mut self) -> Option<Event> {
self.0.recv().await
}
}
pub fn peer_event_channel() -> (PeerEventSender, PeerEventReceiver) {
pub(crate) fn peer_event_channel() -> (PeerEventSender, PeerEventReceiver) {
let (tx, rx) = mpsc::channel(16);
(PeerEventSender(tx), PeerEventReceiver(rx))
}
@@ -56,20 +35,20 @@ pub(crate) type PeersByKey = DashMap<x25519::PublicKey, PeerEventSender>;
pub(crate) type PeersByAddr = DashMap<SocketAddr, PeerEventSender>;
#[derive(Default)]
pub struct ActivePeers {
pub(crate) struct ActivePeers {
active_peers: PeersByKey,
active_peers_by_addr: PeersByAddr,
}
impl ActivePeers {
pub fn remove(&self, public_key: &x25519::PublicKey) {
pub(crate) fn remove(&self, public_key: &x25519::PublicKey) {
log::info!("Removing peer: {public_key:?}");
self.active_peers.remove(public_key);
log::warn!("TODO: remove from peers_by_ip?");
log::warn!("TODO: remove from peers_by_addr");
}
pub fn insert(
pub(crate) fn insert(
&self,
public_key: x25519::PublicKey,
addr: SocketAddr,
@@ -79,14 +58,17 @@ impl ActivePeers {
self.active_peers_by_addr.insert(addr, peer_tx);
}
pub fn get_by_key_mut(
pub(crate) fn get_by_key_mut(
&self,
public_key: &x25519::PublicKey,
) -> Option<RefMut<'_, x25519::PublicKey, PeerEventSender>> {
self.active_peers.get_mut(public_key)
}
pub fn get_by_addr(&self, addr: &SocketAddr) -> Option<Ref<'_, SocketAddr, PeerEventSender>> {
pub(crate) fn get_by_addr(
&self,
addr: &SocketAddr,
) -> Option<Ref<'_, SocketAddr, PeerEventSender>> {
self.active_peers_by_addr.get(addr)
}
}
+8 -7
View File
@@ -3,10 +3,15 @@
// #![warn(clippy::expect_used)]
// #![warn(clippy::unwrap_used)]
mod active_peers;
mod error;
mod event;
mod network_table;
mod packet_relayer;
mod platform;
mod registered_peers;
pub mod setup;
mod setup;
pub mod tun_task_channel;
mod udp_listener;
mod wg_tunnel;
@@ -15,9 +20,7 @@ use std::sync::Arc;
// Currently the module related to setting up the virtual network device is platform specific.
#[cfg(target_os = "linux")]
use nym_tun::tun_device;
use nym_tun::tun_task_channel;
pub use platform::linux::tun_device;
/// Start wireguard UDP listener and TUN device
///
@@ -33,9 +36,7 @@ pub async fn start_wireguard(
// We can optionally index peers by their IP like standard wireguard. If we don't then we do
// plain NAT where we match incoming destination IP with outgoing source IP.
use nym_wireguard_types::tun_common::network_table::NetworkTable;
let peers_by_ip = Arc::new(tokio::sync::Mutex::new(NetworkTable::new()));
let peers_by_ip = Arc::new(tokio::sync::Mutex::new(network_table::NetworkTable::new()));
// Alternative 1:
let routing_mode = tun_device::RoutingMode::new_allowed_ips(peers_by_ip.clone());
@@ -9,7 +9,7 @@ pub struct NetworkTable<T> {
}
impl<T> NetworkTable<T> {
pub fn new() -> Self {
pub(crate) fn new() -> Self {
Self {
ips: IpNetworkTable::new(),
}
+5 -3
View File
@@ -3,9 +3,11 @@ use std::{collections::HashMap, sync::Arc};
use tap::TapFallible;
use tokio::sync::mpsc::{self};
use crate::tun_task_channel::{TunTaskResponseRx, TunTaskTx};
use nym_wireguard_types::tun_common::{active_peers::PeerEventSender, event::Event};
use crate::{
active_peers::PeerEventSender,
event::Event,
tun_task_channel::{TunTaskResponseRx, TunTaskTx},
};
#[derive(Clone)]
pub struct PacketRelaySender(pub(crate) mpsc::Sender<(u64, Vec<u8>)>);
@@ -1,64 +1,22 @@
use std::{
collections::HashMap,
net::{IpAddr, Ipv4Addr},
time::Duration,
sync::Arc,
};
use etherparse::{InternetSlice, SlicedPacket};
use tokio::{
io::{AsyncReadExt, AsyncWriteExt},
time::timeout,
};
use tap::TapFallible;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use crate::tun_task_channel::{
tun_task_channel, tun_task_response_channel, TunTaskPayload, TunTaskResponseRx,
TunTaskResponseSendError, TunTaskResponseTx, TunTaskRx, TunTaskTx,
};
#[cfg(feature = "wireguard")]
use nym_wireguard_types::tun_common::{
active_peers::{PeerEventSenderError, PeersByIp},
use crate::{
event::Event,
tun_task_channel::{
tun_task_channel, tun_task_response_channel, TunTaskPayload, TunTaskResponseRx,
TunTaskResponseTx, TunTaskRx, TunTaskTx,
},
udp_listener::PeersByIp,
};
#[cfg(feature = "wireguard")]
const MUTEX_LOCK_TIMEOUT_MS: u64 = 200;
const TUN_WRITE_TIMEOUT_MS: u64 = 1000;
#[derive(thiserror::Error, Debug)]
pub enum TunDeviceError {
#[error("timeout writing to tun device, dropping packet")]
TunWriteTimeout,
#[error("error writing to tun device: {source}")]
TunWriteError { source: std::io::Error },
#[cfg(feature = "wireguard")]
#[error("failed forwarding packet to peer: {source}")]
ForwardToPeerFailed {
#[from]
source: PeerEventSenderError,
},
#[error("failed to forward responding packet with tag: {source}")]
ForwardNatResponseFailed {
#[from]
source: TunTaskResponseSendError,
},
#[error("unable to parse headers in packet")]
UnableToParseHeaders {
#[from]
source: etherparse::ReadError,
},
#[error("unable to parse src and dst address from packet: ip header missing")]
UnableToParseAddressIpHeaderMissing,
#[error("unable to lock peer mutex")]
FailedToLockPeer,
}
fn setup_tokio_tun_device(name: &str, address: Ipv4Addr, netmask: Ipv4Addr) -> tokio_tun::Tun {
log::info!("Creating TUN device with: address={address}, netmask={netmask}");
// Read MTU size from env variable NYM_MTU_SIZE, else default to 1420.
@@ -93,7 +51,6 @@ pub struct TunDevice {
pub enum RoutingMode {
// The routing table, as how wireguard does it
#[cfg(feature = "wireguard")]
AllowedIps(AllowedIpsInner),
// This is an alternative to the routing table, where we just match outgoing source IP with
@@ -108,27 +65,13 @@ impl RoutingMode {
})
}
#[cfg(feature = "wireguard")]
pub fn new_allowed_ips(peers_by_ip: std::sync::Arc<tokio::sync::Mutex<PeersByIp>>) -> Self {
pub fn new_allowed_ips(peers_by_ip: Arc<tokio::sync::Mutex<PeersByIp>>) -> Self {
RoutingMode::AllowedIps(AllowedIpsInner { peers_by_ip })
}
}
#[cfg(feature = "wireguard")]
pub struct AllowedIpsInner {
peers_by_ip: std::sync::Arc<tokio::sync::Mutex<PeersByIp>>,
}
#[cfg(feature = "wireguard")]
impl AllowedIpsInner {
async fn lock(&self) -> Result<tokio::sync::MutexGuard<PeersByIp>, TunDeviceError> {
timeout(
Duration::from_millis(MUTEX_LOCK_TIMEOUT_MS),
self.peers_by_ip.as_ref().lock(),
)
.await
.map_err(|_| TunDeviceError::FailedToLockPeer)
}
peers_by_ip: Arc<tokio::sync::Mutex<PeersByIp>>,
}
pub struct NatInner {
@@ -171,33 +114,48 @@ impl TunDevice {
}
// Send outbound packets out on the wild internet
async fn handle_tun_write(&mut self, data: TunTaskPayload) -> Result<(), TunDeviceError> {
async fn handle_tun_write(&mut self, data: TunTaskPayload) {
let (tag, packet) = data;
let ParsedAddresses { src_addr, dst_addr } = parse_src_dst_address(&packet)?;
log::debug!(
let Some(dst_addr) = boringtun::noise::Tunn::dst_address(&packet) else {
log::error!("Unable to parse dst_address in packet that was supposed to be written to tun device");
return;
};
let Some(src_addr) = parse_src_address(&packet) else {
log::error!("Unable to parse src_address in packet that was supposed to be written to tun device");
return;
};
log::info!(
"iface: write Packet({src_addr} -> {dst_addr}, {} bytes)",
packet.len()
);
// TODO: expire old entries
#[allow(irrefutable_let_patterns)]
if let RoutingMode::Nat(nat_table) = &mut self.routing_mode {
nat_table.nat_table.insert(src_addr, tag);
}
timeout(
Duration::from_millis(TUN_WRITE_TIMEOUT_MS),
tokio::time::timeout(
std::time::Duration::from_millis(1000),
self.tun.write_all(&packet),
)
.await
.map_err(|_| TunDeviceError::TunWriteTimeout)?
.map_err(|err| TunDeviceError::TunWriteError { source: err })
.tap_err(|err| {
log::error!("iface: write error: {err}");
})
.ok();
}
// Receive reponse packets from the wild internet
async fn handle_tun_read(&self, packet: &[u8]) -> Result<(), TunDeviceError> {
let ParsedAddresses { src_addr, dst_addr } = parse_src_dst_address(packet)?;
log::debug!(
async fn handle_tun_read(&self, packet: &[u8]) {
let Some(dst_addr) = boringtun::noise::Tunn::dst_address(packet) else {
log::error!("Unable to parse dst_address in packet that was read from tun device");
return;
};
let Some(src_addr) = parse_src_address(packet) else {
log::error!("Unable to parse src_address in packet that was read from tun device");
return;
};
log::info!(
"iface: read Packet({src_addr} -> {dst_addr}, {} bytes)",
packet.len(),
);
@@ -206,32 +164,47 @@ impl TunDevice {
match self.routing_mode {
// This is how wireguard does it, by consulting the AllowedIPs table.
#[cfg(feature = "wireguard")]
RoutingMode::AllowedIps(ref peers_by_ip) => {
let peers = peers_by_ip.lock().await?;
let Ok(peers) = tokio::time::timeout(
std::time::Duration::from_millis(1000),
peers_by_ip.peers_by_ip.as_ref().lock(),
)
.await
else {
log::error!("Failed to lock peer");
return;
};
if let Some(peer_tx) = peers.longest_match(dst_addr).map(|(_, tx)| tx) {
log::debug!("Forward packet to wg tunnel");
return peer_tx
.send(Event::Ip(packet.to_vec().into()))
.await
.map_err(|err| err.into());
log::info!("Forward packet to wg tunnel");
tokio::time::timeout(
std::time::Duration::from_millis(1000),
peer_tx.send(Event::Ip(packet.to_vec().into())),
)
.await
.tap_err(|err| log::error!("Failed to forward packet to wg tunnel: {err}"))
.ok();
return;
}
}
// But we can also do it by consulting the NAT table.
RoutingMode::Nat(ref nat_table) => {
if let Some(tag) = nat_table.nat_table.get(&dst_addr) {
log::debug!("Forward packet with NAT tag: {tag}");
return self
.tun_task_response_tx
.try_send((*tag, packet.to_vec()))
.map_err(|err| err.into());
log::info!("Forward packet with tag: {tag}");
tokio::time::timeout(
std::time::Duration::from_millis(1000),
self.tun_task_response_tx.send((*tag, packet.to_vec())),
)
.await
.tap_err(|err| log::error!("Failed to foward packet with tag: {err}"))
.ok();
return;
}
}
}
log::info!("No peer found, packet dropped");
Ok(())
}
pub async fn run(mut self) {
@@ -243,9 +216,13 @@ impl TunDevice {
len = self.tun.read(&mut buf) => match len {
Ok(len) => {
let packet = &buf[..len];
if let Err(err) = self.handle_tun_read(packet).await {
log::error!("iface: handle_tun_read failed: {err}")
}
tokio::time::timeout(
std::time::Duration::from_millis(1000),
self.handle_tun_read(packet)
)
.await
.tap_err(|_err| log::error!("Failed: handle_tun_read timeout"))
.ok();
},
Err(err) => {
log::info!("iface: read error: {err}");
@@ -254,9 +231,13 @@ impl TunDevice {
},
// Writing to the TUN device
Some(data) = self.tun_task_rx.recv() => {
if let Err(err) = self.handle_tun_write(data).await {
log::error!("ifcae: handle_tun_write failed: {err}");
}
tokio::time::timeout(
std::time::Duration::from_millis(1000),
self.handle_tun_write(data)
)
.await
.tap_err(|_err| log::error!("Failed: handle_tun_write timeout"))
.ok();
}
}
}
@@ -268,22 +249,12 @@ impl TunDevice {
}
}
struct ParsedAddresses {
src_addr: IpAddr,
dst_addr: IpAddr,
}
fn parse_src_dst_address(packet: &[u8]) -> Result<ParsedAddresses, TunDeviceError> {
let headers = SlicedPacket::from_ip(packet)?;
match headers.ip {
Some(InternetSlice::Ipv4(ip, _)) => Ok(ParsedAddresses {
src_addr: ip.source_addr().into(),
dst_addr: ip.destination_addr().into(),
}),
Some(InternetSlice::Ipv6(ip, _)) => Ok(ParsedAddresses {
src_addr: ip.source_addr().into(),
dst_addr: ip.destination_addr().into(),
}),
None => Err(TunDeviceError::UnableToParseAddressIpHeaderMissing),
}
fn parse_src_address(packet: &[u8]) -> Option<IpAddr> {
let headers = SlicedPacket::from_ip(packet)
.tap_err(|err| log::error!("Unable to parse IP packet: {err:?}"))
.ok()?;
Some(match headers.ip? {
InternetSlice::Ipv4(ip, _) => ip.source_addr().into(),
InternetSlice::Ipv6(ip, _) => ip.source_addr().into(),
})
}
+2
View File
@@ -0,0 +1,2 @@
#[cfg(target_os = "linux")]
pub(crate) mod linux;
+2 -2
View File
@@ -31,7 +31,7 @@ fn decode_base64_key(base64_key: &str) -> [u8; 32] {
pub fn server_static_private_key() -> x25519::StaticSecret {
// TODO: this is a temporary solution for development
let static_private_bytes: [u8; 32] = decode_base64_key(PRIVATE_KEY);
let static_private = x25519::StaticSecret::from(static_private_bytes);
let static_private = x25519::StaticSecret::try_from(static_private_bytes).unwrap();
let static_public = x25519::PublicKey::from(&static_private);
info!(
"wg public key: {}",
@@ -47,7 +47,7 @@ pub fn peer_static_public_key() -> x25519::PublicKey {
let peer = std::env::var("NYM_PEER_PUBLIC_KEY").expect("NYM_PEER_PUBLIC_KEY must be set");
let peer_static_public_bytes: [u8; 32] = decode_base64_key(&peer);
let peer_static_public = x25519::PublicKey::from(peer_static_public_bytes);
let peer_static_public = x25519::PublicKey::try_from(peer_static_public_bytes).unwrap();
info!(
"Adding wg peer public key: {}",
general_purpose::STANDARD.encode(peer_static_public)
+54
View File
@@ -0,0 +1,54 @@
use tokio::sync::mpsc;
pub(crate) type TunTaskPayload = (u64, Vec<u8>);
#[derive(Clone)]
pub struct TunTaskTx(mpsc::Sender<TunTaskPayload>);
pub(crate) struct TunTaskRx(mpsc::Receiver<TunTaskPayload>);
impl TunTaskTx {
pub async fn send(
&self,
data: TunTaskPayload,
) -> Result<(), tokio::sync::mpsc::error::SendError<TunTaskPayload>> {
self.0.send(data).await
}
}
impl TunTaskRx {
pub(crate) async fn recv(&mut self) -> Option<TunTaskPayload> {
self.0.recv().await
}
}
pub(crate) fn tun_task_channel() -> (TunTaskTx, TunTaskRx) {
let (tun_task_tx, tun_task_rx) = tokio::sync::mpsc::channel(16);
(TunTaskTx(tun_task_tx), TunTaskRx(tun_task_rx))
}
// Send responses back from the tun device back to the PacketRelayer
pub(crate) struct TunTaskResponseTx(mpsc::Sender<TunTaskPayload>);
pub struct TunTaskResponseRx(mpsc::Receiver<TunTaskPayload>);
impl TunTaskResponseTx {
pub(crate) async fn send(
&self,
data: TunTaskPayload,
) -> Result<(), tokio::sync::mpsc::error::SendError<TunTaskPayload>> {
self.0.send(data).await
}
}
impl TunTaskResponseRx {
pub async fn recv(&mut self) -> Option<TunTaskPayload> {
self.0.recv().await
}
}
pub(crate) fn tun_task_response_channel() -> (TunTaskResponseTx, TunTaskResponseRx) {
let (tun_task_tx, tun_task_rx) = tokio::sync::mpsc::channel(16);
(
TunTaskResponseTx(tun_task_tx),
TunTaskResponseRx(tun_task_rx),
)
}
+7 -8
View File
@@ -7,19 +7,15 @@ use boringtun::{
use futures::StreamExt;
use log::error;
use nym_task::TaskClient;
use nym_wireguard_types::{
registration::GatewayClientRegistry,
tun_common::{
active_peers::{ActivePeers, PeersByIp},
event::Event,
},
PeerPublicKey, WG_PORT,
};
use nym_wireguard_types::{registration::GatewayClientRegistry, PeerPublicKey, WG_PORT};
use tap::TapFallible;
use tokio::{net::UdpSocket, sync::Mutex};
use crate::{
active_peers::{ActivePeers, PeerEventSender},
error::WgError,
event::Event,
network_table::NetworkTable,
packet_relayer::PacketRelaySender,
registered_peers::{RegisteredPeer, RegisteredPeers},
setup::{self, WG_ADDRESS},
@@ -28,6 +24,9 @@ use crate::{
const MAX_PACKET: usize = 65535;
// Registered peers
pub(crate) type PeersByIp = NetworkTable<PeerEventSender>;
async fn add_test_peer(registered_peers: &mut RegisteredPeers) {
let peer_static_public = PeerPublicKey::new(setup::peer_static_public_key());
let peer_index = 0;
+8 -6
View File
@@ -7,16 +7,18 @@ use boringtun::{
};
use bytes::Bytes;
use log::{debug, error, info, warn};
use nym_wireguard_types::tun_common::{
active_peers::{peer_event_channel, PeerEventReceiver, PeerEventSender},
event::Event,
network_table::NetworkTable,
};
use rand::RngCore;
use tap::TapFallible;
use tokio::{net::UdpSocket, sync::broadcast, time::timeout};
use crate::{error::WgError, packet_relayer::PacketRelaySender, registered_peers::PeerIdx};
use crate::{
active_peers::{peer_event_channel, PeerEventReceiver, PeerEventSender},
error::WgError,
event::Event,
network_table::NetworkTable,
packet_relayer::PacketRelaySender,
registered_peers::PeerIdx,
};
const HANDSHAKE_MAX_RATE: u64 = 10;
+1 -1
View File
@@ -6,4 +6,4 @@ For more in-depth information about nodes, network traffic flows, clients, cocon
If you are looking for information and setup guides for the various pieces of Nym mixnet infrastructure (mix nodes, gateways and network requesters) and Nyx blockchain validators see the **new [Operators Guides](https://nymtech.net/operators)** book.
If you're looking for TypeScript/JavaScript related information such as SDKs to build your own tools, step-by-step tutorials, live playgrounds and more, make sure to check out the **new [TS SDK Handbook](https://sdk.nymtech.net/)** !
If you're looking for TypeScript/JavaScript related information such as SDKs to build your own tools, step-by-step tutorials, live playgrounds and more, make sure to check out the **new [TS SDK Handbook](https://sdk.nymtech.net/)** !
@@ -89,7 +89,7 @@ You can check the necessary parameters for the available commands by running:
Before you can use the client, you need to initalise a new instance of it, which can be done with the following command:
```
./nym-socks5-client init --id docs-example --use-reply-surbs true --provider Entztfv6Uaz2hpYHQJ6JKoaCTpDL5dja18SuQWVJAmmx.Cvhn9rBJw5Ay9wgHcbgCnVg89MPSV5s2muPV2YF1BXYu@Fo4f4SQLdoyoGkFae5TpVhRVoXCF8UiypLVGtGjujVPf
./nym-socks5-client init --id docs-example --provider Entztfv6Uaz2hpYHQJ6JKoaCTpDL5dja18SuQWVJAmmx.Cvhn9rBJw5Ay9wgHcbgCnVg89MPSV5s2muPV2YF1BXYu@Fo4f4SQLdoyoGkFae5TpVhRVoXCF8UiypLVGtGjujVPf
```
~~~admonish example collapsible=true title="Console output"
@@ -100,8 +100,6 @@ Before you can use the client, you need to initalise a new instance of it, which
The `--id` in the example above is a local identifier so that you can name your clients and keep track of them on your local system; it is **never** transmitted over the network.
The `--use-reply-surbs` field denotes whether you wish to send [SURBs](../architecture/traffic-flow.md#private-replies-using-surbs) along with your request. It defaults to `false`, we are explicitly setting it as `true`. It defaults to `false` for compatibility with older versions of the [Network Requester](../nodes/network-requester.md).
The `--provider` field needs to be filled with the Nym address of a Network Requester that can make network requests on your behalf. If you don't want to [run your own](../nodes/network-requester.md) you can select one from the [mixnet explorer](https://explorer.nymtech.net/network-components/service-providers) by copying its `Client ID` and using this as the value of the `--provider` flag. Alternatively, you could use [this list](https://harbourmaster.nymtech.net/).
Since the nodes on this list are the infrastructure for [Nymconnect](https://nymtech.net/developers/quickstart/nymconnect-gui.html) they will support all apps on the [default whitelist](../nodes/network-requester.md#network-requester-whitelist): Keybase, Telegram, Electrum, Blockstream Green, and Helios.
@@ -150,7 +148,7 @@ Alternatively, a custom host can be set in the `config.toml` file under the `soc
### Running the socks5 client
You can run the initialised client by doing this:
You can run the initalised client by doing this:
```
./nym-socks5-client run --id docs-example
@@ -12,5 +12,5 @@ You can read more about how SURBs function under the hood [here](../../../archit
In order to reply to an incoming message using SURBs, you can construct a `recipient` from the `sender_tag` sent along with the message you wish to reply to:
```rust,noplayground
{{#include ../../../../../../sdk/rust/nym-sdk/examples/surb_reply.rs}}
{{#include ../../../../../../sdk/rust/nym-sdk/examples/surb-reply.rs}}
```
+1 -1
View File
@@ -136,7 +136,7 @@ pub enum Error {
NotFound(String),
#[error("Configuration file does not exist")]
/// This is returned if IoError happens during configuration file read/write.
Io(#[from] std::io::Error),
IoError(#[from] std::io::Error),
/// This is returned if configuration file is invalid.
#[error("Configuration file is invalid: '{0}'")]
InvalidFormat(String),
+1 -4
View File
@@ -76,7 +76,7 @@ nym-statistics-common = { path = "../common/statistics" }
nym-task = { path = "../common/task" }
nym-types = { path = "../common/types" }
nym-validator-client = { path = "../common/client-libs/validator-client" }
nym-wireguard = { path = "../common/wireguard", optional = true }
nym-wireguard = { path = "../common/wireguard" }
nym-ip-packet-router = { path = "../service-providers/ip-packet-router" }
[dev-dependencies]
@@ -92,6 +92,3 @@ sqlx = { version = "0.5", features = [
"macros",
"migrate",
] }
[features]
wireguard = ["nym-wireguard"]
+2 -2
View File
@@ -229,14 +229,14 @@ impl<'a> HttpApiBuilder<'a> {
IpAddr::from(Ipv4Addr::new(10, 1, 0, 0)),
self.gateway_config.wireguard.private_network_prefix,
)?;
let wg_state = self.client_registry.and_then(|client_registry| {
let wg_state = self.client_registry.map(|client_registry| {
WireguardAppState::new(
self.sphinx_keypair,
client_registry,
Default::default(),
self.gateway_config.wireguard.bind_address.port(),
wireguard_private_network,
)
.ok()
});
let router = nym_node::http::NymNodeRouter::new(config, wg_state);
+12 -10
View File
@@ -200,7 +200,6 @@ impl<St> Gateway<St> {
mixnet_handling::Listener::new(listening_address, shutdown).start(connection_handler);
}
#[cfg(feature = "wireguard")]
async fn start_wireguard(
&self,
shutdown: TaskClient,
@@ -520,15 +519,18 @@ impl<St> Gateway<St> {
Arc::new(coconut_verifier),
);
// Once this is a bit more mature, make this a commandline flag instead of a compile time
// flag
#[cfg(feature = "wireguard")]
if let Err(err) = self
.start_wireguard(shutdown.subscribe().named("wireguard"))
.await
{
// that's a nasty workaround, but anyhow errors are generally nicer, especially on exit
bail!("{err}")
// TODO: later we'll make this a commandline flag
let wireguard_enabled = std::env::var("NYM_ENABLE_WIREGUARD")
.map(|v| v == "1")
.unwrap_or(false);
if wireguard_enabled {
if let Err(err) = self
.start_wireguard(shutdown.subscribe().named("wireguard"))
.await
{
// that's a nasty workaround, but anyhow errors are generally nicer, especially on exit
bail!("{err}")
}
}
info!("Finished nym gateway startup procedure - it should now be able to receive mix and client traffic!");
-42
View File
@@ -14,45 +14,3 @@ A Rust mixnode implementation.
* `nym-mixnode run --layer 1 --host x.x.x.x` will start the mixnode in layer 1 and bind to the specified host IP address. Coordinate with other people in your network to find out which layer needs coverage.
By default, the Nym Mixnode will start on port 1789. If desired, you can change the port using the `--port` option.
## Install debian
```bash
sudo curl -s --compressed "https://nymtech.github.io/nym/nymtech.gpg" | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/nymtech.gpg > /dev/null
sudo echo "deb [signed-by=/etc/apt/trusted.gpg.d/nymtech.gpg] https://nymtech.github.io/nym/ /" > nymtech.list
sudo apt-get update
sudo apt-get install nym-mixnode
# See below for starting and managing the node
```
## Systemd support
```bash
sudo systemctl enable nym-mixnode
# Run
sudo systemctl start nym-mixnode
# Check status
sudo systemctl status nym-mixnode
# Logs
journalctl -f -u nym-mixnode
```
## Build debian package
```bash
# cargo install cargo-deb
# Build package
cargo deb -p nym-mixnode
# Install
# This will init the mixnode to `/etc/nym` as `nym` user, and create a systemd service
sudo dpkg -i target/debian/<PACKAGE>
```
-1
View File
@@ -164,7 +164,6 @@ impl Config {
save_formatted_config_to_file(self, config_save_location)
}
#[allow(unused)]
pub fn try_save(&self) -> io::Result<()> {
if let Some(save_location) = &self.save_path {
save_formatted_config_to_file(self, save_location)
+1 -1
View File
@@ -103,7 +103,7 @@ nym-task = { path = "../common/task" }
nym-topology = { path = "../common/topology" }
nym-api-requests = { path = "nym-api-requests" }
nym-validator-client = { path = "../common/client-libs/validator-client" }
nym-bin-common = { path = "../common/bin-common", features = ["output_format"] }
nym-bin-common = { path = "../common/bin-common" }
nym-node-tester-utils = { path = "../common/node-tester-utils" }
nym-node-requests = { path = "../nym-node/nym-node-requests" }
+1 -13
View File
@@ -363,14 +363,11 @@ pub struct NymNodeDescription {
// TODO: do we really care about ALL build info or just the version?
pub build_information: BinaryBuildInformationOwned,
#[serde(default)]
pub network_requester: Option<NetworkRequesterDetails>,
// for now we only care about their ws/wss situation, nothing more
pub mixnet_websockets: WebSockets,
}
#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema)]
#[derive(Clone, Serialize, Deserialize, schemars::JsonSchema)]
pub struct DescribedGateway {
pub bond: GatewayBond,
pub self_described: Option<NymNodeDescription>,
@@ -384,12 +381,3 @@ impl From<GatewayBond> for DescribedGateway {
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema)]
pub struct NetworkRequesterDetails {
/// address of the embedded network requester
pub address: String,
/// flag indicating whether this network requester uses the exit policy rather than the deprecated allow list
pub uses_exit_policy: bool,
}
+1 -1
View File
@@ -46,7 +46,7 @@ pub async fn extract_encryption_key(
.ok_or(CoconutError::DepositValueNotFound)?
.value
.as_ref();
let deposit_value_plain = public_attributes_plain.first().cloned().unwrap_or_default();
let deposit_value_plain = public_attributes_plain.get(0).cloned().unwrap_or_default();
if deposit_value != deposit_value_plain {
return Err(CoconutError::DifferentPublicAttributes(
deposit_value.to_string(),
+33 -14
View File
@@ -9,7 +9,8 @@ use crate::network::models::NetworkDetails;
use crate::node_describe_cache::DescribedNodes;
use crate::node_status_api::uptime_updater::HistoricalUptimeUpdater;
use crate::support::caching::cache::SharedCache;
use crate::support::cli::{self, Commands};
use crate::support::cli;
use crate::support::cli::CliArgs;
use crate::support::config::Config;
use crate::support::storage;
use crate::support::storage::NymApiStorage;
@@ -19,6 +20,7 @@ use anyhow::Result;
use circulating_supply_api::cache::CirculatingSupplyCache;
use clap::Parser;
use coconut::dkg::controller::DkgController;
use log::info;
use node_status_api::NodeStatusCache;
use nym_bin_common::logging::setup_logging;
use nym_contract_cache::cache::NymContractCache;
@@ -55,20 +57,9 @@ async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
}}
setup_logging();
let args = cli::Cli::parse();
trace!("{:#?}", args);
let args = cli::CliArgs::parse();
setup_env(args.config_env_file.as_ref());
let command = args.command.unwrap_or(Commands::Run(Box::new(args.run)));
match command {
Commands::BuildInfo(m) => {
cli::build_info::execute(m);
Ok(())
}
Commands::Run(m) => cli::run::execute(*m).await,
}
run_nym_api(args).await
}
async fn start_nym_api_tasks(
@@ -217,3 +208,31 @@ async fn start_nym_api_tasks(
rocket_handle: rocket_shutdown_handle,
})
}
async fn run_nym_api(cli_args: CliArgs) -> Result<(), Box<dyn Error + Send + Sync>> {
let save_to_file = cli_args.save_config;
let config = cli::build_config(cli_args)?;
// if we just wanted to write data to the config, exit, don't start any tasks
if save_to_file {
info!("Saving the configuration to a file");
config.save_to_default_location()?;
config
.get_ephemera_args()
.cmd
.clone()
.execute(Some(&config.get_id()));
return Ok(());
}
let shutdown_handlers = start_nym_api_tasks(config).await?;
let res = shutdown_handlers
.task_manager_handle
.catch_interrupt()
.await;
log::info!("Stopping nym API");
shutdown_handlers.rocket_handle.notify();
res
}
+2 -21
View File
@@ -7,8 +7,8 @@ use crate::support::caching::refresher::{CacheItemProvider, CacheRefresher};
use crate::support::config;
use crate::support::config::DEFAULT_NODE_DESCRIBE_BATCH_SIZE;
use futures_util::{stream, StreamExt};
use nym_api_requests::models::{NetworkRequesterDetails, NymNodeDescription};
use nym_config::defaults::{mainnet, DEFAULT_NYM_NODE_HTTP_PORT};
use nym_api_requests::models::NymNodeDescription;
use nym_config::defaults::DEFAULT_NYM_NODE_HTTP_PORT;
use nym_contracts_common::IdentityKey;
use nym_mixnet_contract_common::Gateway;
use nym_node_requests::api::client::{NymNodeApiClientError, NymNodeApiClientExt};
@@ -152,28 +152,9 @@ async fn get_gateway_description(
source: err,
})?;
let network_requester =
if let Ok(nr) = client.get_network_requester().await {
let exit_policy = client.get_exit_policy().await.map_err(|err| {
NodeDescribeCacheError::ApiFailure {
gateway: gateway.identity_key.clone(),
source: err,
}
})?;
let uses_nym_exit_policy = exit_policy.upstream_source == mainnet::EXIT_POLICY_URL;
Some(NetworkRequesterDetails {
address: nr.address,
uses_exit_policy: exit_policy.enabled && uses_nym_exit_policy,
})
} else {
None
};
let description = NymNodeDescription {
host_information: host_info.data,
build_information: build_info,
network_requester,
mixnet_websockets: websockets,
};
-15
View File
@@ -1,15 +0,0 @@
// Copyright 2023 - 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, Debug)]
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!()))
}
+91 -31
View File
@@ -5,15 +5,13 @@ use super::config::Config;
use crate::support::config::default_config_filepath;
use crate::support::config::helpers::{initialise_new, try_load_current_config};
use ::nym_config::defaults::var_names::{MIXNET_CONTRACT_ADDRESS, VESTING_CONTRACT_ADDRESS};
use anyhow::{bail, Result};
use clap::{Parser, Subcommand};
use anyhow::Result;
use clap::Parser;
use lazy_static::lazy_static;
use nym_bin_common::bin_info;
use nym_config::defaults::var_names::NYXD;
use nym_config::OptionalSet;
pub(crate) mod build_info;
pub(crate) mod run;
use nym_validator_client::nyxd;
lazy_static! {
pub static ref PRETTY_BUILD_INFORMATION: String = bin_info!().pretty_print();
@@ -24,32 +22,100 @@ fn pretty_build_info_static() -> &'static str {
&PRETTY_BUILD_INFORMATION
}
#[derive(Parser, Debug)]
#[command(args_conflicts_with_subcommands = true)]
// explicitly defined custom parser (as opposed to just using
// #[arg(value_parser = clap::value_parser!(u8).range(0..100))]
// for better error message
fn threshold_in_range(s: &str) -> Result<u8, String> {
let threshold: usize = s
.parse()
.map_err(|_| format!("`{s}` isn't a valid threshold number"))?;
if threshold > 100 {
Err(format!("{threshold} is not within the range 0-100"))
} else {
Ok(threshold as u8)
}
}
#[derive(Parser)]
#[clap(author = "Nymtech", version, long_version = pretty_build_info_static(), about)]
pub(crate) struct Cli {
pub(crate) struct CliArgs {
/// Path pointing to an env file that configures the Nym API.
#[clap(short, long)]
pub(crate) config_env_file: Option<std::path::PathBuf>,
#[clap(subcommand)]
pub(crate) command: Option<Commands>,
/// Id of the nym-api we want to run
#[clap(long)]
pub(crate) id: String,
// this shouldn't really be here, but we don't want to break backwards compat
#[clap(flatten)]
pub(crate) run: run::Args,
/// Specifies whether network monitoring is enabled on this API
#[clap(short = 'm', long)]
pub(crate) enable_monitor: Option<bool>,
/// Specifies whether network rewarding is enabled on this API
#[clap(short = 'r', long, requires = "enable_monitor", requires = "mnemonic")]
pub(crate) enable_rewarding: Option<bool>,
/// Specifies whether ephemera is used to aggregate monitor data on this API
#[clap(short = 'e', long, requires = "enable_monitor")]
pub(crate) enable_ephemera: Option<bool>,
/// Endpoint to nyxd instance from which the monitor will grab nodes to test
#[clap(long)]
pub(crate) nyxd_validator: Option<url::Url>,
/// Address of the mixnet contract managing the network
#[clap(long)]
pub(crate) mixnet_contract: Option<nyxd::AccountId>,
/// Address of the vesting contract holding locked tokens
#[clap(long)]
pub(crate) vesting_contract: Option<nyxd::AccountId>,
/// Mnemonic of the network monitor used for rewarding operators
// even though we're currently converting the mnemonic to string (and then back to the concrete type)
// at least we're getting immediate validation when passing the arguments
#[clap(long)]
pub(crate) mnemonic: Option<bip39::Mnemonic>,
/// Specifies whether a config file based on provided arguments should be saved to a file
#[clap(short = 'w', long)]
pub(crate) save_config: bool,
/// Specifies the minimum percentage of monitor test run data present in order to distribute rewards for given interval.
#[clap(long, value_parser = threshold_in_range)]
pub(crate) monitor_threshold: Option<u8>,
/// Mixnodes with reliability lower the this get blacklisted by network monitor, get no traffic and cannot be selected into a rewarded set.
#[clap(long, value_parser = threshold_in_range)]
pub(crate) min_mixnode_reliability: Option<u8>,
/// Gateways with reliability lower the this get blacklisted by network monitor, get no traffic and cannot be selected into a rewarded set.
#[clap(long, value_parser = threshold_in_range)]
pub(crate) min_gateway_reliability: Option<u8>,
/// Set this nym api to work in a enabled credentials that would attempt to use gateway with the bandwidth credential requirement
#[clap(long)]
pub(crate) enabled_credentials_mode: Option<bool>,
/// Announced address where coconut clients will connect.
#[clap(long, hide = true)]
pub(crate) announce_address: Option<url::Url>,
/// Flag to indicate whether coconut signer authority is enabled on this API
#[clap(
long,
requires = "mnemonic",
requires = "announce_address",
hide = true
)]
pub(crate) enable_coconut: Option<bool>,
/// Ephemera configuration arguments.
#[command(flatten)]
pub(crate) ephemera_args: ephemera::cli::init::Cmd,
}
#[derive(Subcommand, Debug)]
pub(crate) enum Commands {
/// Run the Nym Api with provided configuration optionally overriding set parameters
Run(Box<run::Args>),
/// Show build information of this binary
BuildInfo(build_info::Args),
}
pub(crate) fn override_config(config: Config, args: run::Args) -> Config {
pub(crate) fn override_config(config: Config, args: CliArgs) -> Config {
config
.with_optional_env(
Config::with_custom_nyxd_validator,
@@ -103,14 +169,8 @@ pub(crate) fn override_config(config: Config, args: run::Args) -> Config {
)
}
pub(crate) fn build_config(args: run::Args) -> Result<Config> {
let id = match &args.id {
Some(id) => id.clone(),
None => {
error!("--id argument must be provided to run nym-api");
bail!("--id argument must be provided to run nym-api")
}
};
pub(crate) fn build_config(args: CliArgs) -> Result<Config> {
let id = args.id.clone();
// try to load config from the file, if it doesn't exist, use default values
let config = match try_load_current_config(&id) {
-124
View File
@@ -1,124 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::start_nym_api_tasks;
use crate::support::cli::build_config;
use nym_validator_client::nyxd;
use std::error::Error;
// explicitly defined custom parser (as opposed to just using
// #[arg(value_parser = clap::value_parser!(u8).range(0..100))]
// for better error message
fn threshold_in_range(s: &str) -> Result<u8, String> {
let threshold: usize = s
.parse()
.map_err(|_| format!("`{s}` isn't a valid threshold number"))?;
if threshold > 100 {
Err(format!("{threshold} is not within the range 0-100"))
} else {
Ok(threshold as u8)
}
}
#[derive(clap::Args, Debug)]
pub(crate) struct Args {
/// Id of the nym-api we want to run
#[clap(long)]
// ugh. we had to make it optional in case somebody wanted to run `build-info`
pub(crate) id: Option<String>,
/// Specifies whether network monitoring is enabled on this API
#[clap(short = 'm', long)]
pub(crate) enable_monitor: Option<bool>,
/// Specifies whether network rewarding is enabled on this API
#[clap(short = 'r', long, requires = "enable_monitor", requires = "mnemonic")]
pub(crate) enable_rewarding: Option<bool>,
/// Specifies whether ephemera is used to aggregate monitor data on this API
#[clap(short = 'e', long, requires = "enable_monitor")]
pub(crate) enable_ephemera: Option<bool>,
/// Endpoint to nyxd instance from which the monitor will grab nodes to test
#[clap(long)]
pub(crate) nyxd_validator: Option<url::Url>,
/// Address of the mixnet contract managing the network
#[clap(long)]
pub(crate) mixnet_contract: Option<nyxd::AccountId>,
/// Address of the vesting contract holding locked tokens
#[clap(long)]
pub(crate) vesting_contract: Option<nyxd::AccountId>,
/// Mnemonic of the network monitor used for rewarding operators
// even though we're currently converting the mnemonic to string (and then back to the concrete type)
// at least we're getting immediate validation when passing the arguments
#[clap(long)]
pub(crate) mnemonic: Option<bip39::Mnemonic>,
/// Specifies whether a config file based on provided arguments should be saved to a file
#[clap(short = 'w', long)]
pub(crate) save_config: bool,
/// Specifies the minimum percentage of monitor test run data present in order to distribute rewards for given interval.
#[clap(long, value_parser = threshold_in_range)]
pub(crate) monitor_threshold: Option<u8>,
/// Mixnodes with reliability lower the this get blacklisted by network monitor, get no traffic and cannot be selected into a rewarded set.
#[clap(long, value_parser = threshold_in_range)]
pub(crate) min_mixnode_reliability: Option<u8>,
/// Gateways with reliability lower the this get blacklisted by network monitor, get no traffic and cannot be selected into a rewarded set.
#[clap(long, value_parser = threshold_in_range)]
pub(crate) min_gateway_reliability: Option<u8>,
/// Set this nym api to work in a enabled credentials that would attempt to use gateway with the bandwidth credential requirement
#[clap(long)]
pub(crate) enabled_credentials_mode: Option<bool>,
/// Announced address where coconut clients will connect.
#[clap(long, hide = true)]
pub(crate) announce_address: Option<url::Url>,
/// Flag to indicate whether coconut signer authority is enabled on this API
#[clap(
long,
requires = "mnemonic",
requires = "announce_address",
hide = true
)]
pub(crate) enable_coconut: Option<bool>,
/// Ephemera configuration arguments.
#[command(flatten)]
pub(crate) ephemera_args: ephemera::cli::init::Cmd,
}
pub(crate) async fn execute(args: Args) -> Result<(), Box<dyn Error + Send + Sync>> {
let save_to_file = args.save_config;
let config = build_config(args)?;
// if we just wanted to write data to the config, exit, don't start any tasks
if save_to_file {
info!("Saving the configuration to a file");
config.save_to_default_location()?;
config
.get_ephemera_args()
.cmd
.clone()
.execute(Some(&config.get_id()));
return Ok(());
}
let shutdown_handlers = start_nym_api_tasks(config).await?;
let res = shutdown_handlers
.task_manager_handle
.catch_interrupt()
.await;
log::info!("Stopping nym API");
shutdown_handlers.rocket_handle.notify();
res
}
-1
View File
@@ -50,7 +50,6 @@ nym-config = { path = "../common/config" }
nym-crypto = { path = "../common/crypto", features = ["asymmetric" ]}
nym-node-requests = { path = "nym-node-requests", default-features = false, features = ["openapi"]}
nym-task = { path = "../common/task" }
nym-wireguard = { path = "../common/wireguard" }
nym-wireguard-types = { path = "../common/wireguard-types", features = ["verify"] }
[dev-dependencies]
@@ -11,8 +11,6 @@ use nym_bin_common::build_information::BinaryBuildInformationOwned;
use nym_wireguard_types::{ClientMessage, ClientRegistrationResponse};
use crate::api::v1::health::models::NodeHealth;
use crate::api::v1::network_requester::exit_policy::models::UsedExitPolicy;
use crate::api::v1::network_requester::models::NetworkRequester;
pub use http_api_client::Client;
pub type NymNodeApiClientError = HttpClientError<ErrorResponse>;
@@ -44,16 +42,6 @@ pub trait NymNodeApiClientExt: ApiClient {
.await
}
async fn get_network_requester(&self) -> Result<NetworkRequester, NymNodeApiClientError> {
self.get_json_from(routes::api::v1::network_requester_absolute())
.await
}
async fn get_exit_policy(&self) -> Result<UsedExitPolicy, NymNodeApiClientError> {
self.get_json_from(routes::api::v1::network_requester::exit_policy_absolute())
.await
}
async fn post_gateway_register_client(
&self,
client_message: &ClientMessage,
-6
View File
@@ -31,12 +31,6 @@ pub enum NymNodeError {
source: WireguardError,
},
#[error(transparent)]
KeyRecoveryError {
#[from]
source: nym_crypto::asymmetric::encryption::KeyRecoveryError,
},
#[error("unimplemented")]
Unimplemented,
}
@@ -31,7 +31,10 @@ async fn process_final_message(
}
};
if client.verify(&state.private_key, preshared_nonce).is_ok() {
if client
.verify(state.dh_keypair.private_key(), preshared_nonce)
.is_ok()
{
state.registration_in_progress.remove(&client.pub_key());
state.client_registry.insert(client.pub_key(), client);
@@ -101,7 +104,7 @@ pub(crate) async fn register_client(
// mark it as used, even though it's not final
*private_ip_ref = false;
let gateway_data = GatewayClient::new(
&state.private_key,
state.dh_keypair.private_key(),
remote_public,
*private_ip_ref.key(),
nonce,
@@ -8,9 +8,8 @@ use crate::wireguard::types::{GatewayClientRegistry, PendingRegistrations};
use axum::routing::{get, post};
use axum::Router;
use ipnetwork::IpNetwork;
use nym_crypto::asymmetric::encryption::PrivateKey;
use nym_crypto::asymmetric::encryption;
use nym_node_requests::routes::api::v1::gateway::client_interfaces::wireguard;
use nym_wireguard::setup;
use nym_wireguard_types::registration::PrivateIPs;
use std::sync::Arc;
@@ -25,16 +24,15 @@ pub struct WireguardAppState {
impl WireguardAppState {
pub fn new(
dh_keypair: Arc<encryption::KeyPair>,
client_registry: Arc<GatewayClientRegistry>,
registration_in_progress: Arc<PendingRegistrations>,
binding_port: u16,
private_ip_network: IpNetwork,
) -> Result<Self, crate::error::NymNodeError> {
Ok(WireguardAppState {
) -> Self {
WireguardAppState {
inner: Some(WireguardAppStateInner {
private_key: Arc::new(PrivateKey::from_bytes(
setup::server_static_private_key().as_ref(),
)?),
dh_keypair,
client_registry,
registration_in_progress,
binding_port,
@@ -42,7 +40,7 @@ impl WireguardAppState {
private_ip_network.iter().map(|ip| (ip, true)).collect(),
),
}),
})
}
}
// #[allow(dead_code)]
@@ -81,7 +79,7 @@ macro_rules! get_state {
#[derive(Clone)]
pub(crate) struct WireguardAppStateInner {
private_key: Arc<PrivateKey>,
dh_keypair: Arc<encryption::KeyPair>,
client_registry: Arc<GatewayClientRegistry>,
registration_in_progress: Arc<PendingRegistrations>,
binding_port: u16,
@@ -114,7 +112,6 @@ mod test {
PeerPublicKey,
};
use nym_node_requests::routes::api::v1::gateway::client_interfaces::wireguard;
use nym_wireguard::setup::server_static_private_key;
use nym_wireguard_types::registration::HmacSha256;
use std::net::IpAddr;
use std::str::FromStr;
@@ -133,21 +130,17 @@ mod test {
// 6. Gateway verifies mac digest and nonce, and stores client's public key and socket address and port
let mut rng = rand::thread_rng();
let gateway_private_key =
encryption::PrivateKey::from_bytes(server_static_private_key().as_bytes()).unwrap();
let gateway_public_key = encryption::PublicKey::from(&gateway_private_key);
let gateway_key_pair = encryption::KeyPair::from_bytes(
&gateway_private_key.to_bytes(),
&gateway_public_key.to_bytes(),
)
.unwrap();
let gateway_key_pair = encryption::KeyPair::new(&mut rng);
let client_key_pair = encryption::KeyPair::new(&mut rng);
let gateway_static_public = PublicKey::from(gateway_key_pair.public_key().to_bytes());
let gateway_static_public =
PublicKey::try_from(gateway_key_pair.public_key().to_bytes()).unwrap();
let client_static_private = StaticSecret::from(client_key_pair.private_key().to_bytes());
let client_static_public = PublicKey::from(client_key_pair.public_key().to_bytes());
let client_static_private =
StaticSecret::try_from(client_key_pair.private_key().to_bytes()).unwrap();
let client_static_public =
PublicKey::try_from(client_key_pair.public_key().to_bytes()).unwrap();
let client_dh = client_static_private.diffie_hellman(&gateway_static_public);
@@ -165,7 +158,7 @@ mod test {
let state = WireguardAppState {
inner: Some(WireguardAppStateInner {
client_registry: Arc::clone(&client_registry),
private_key: Arc::new(gateway_private_key),
dh_keypair: Arc::new(gateway_key_pair),
registration_in_progress: Arc::clone(&registration_in_progress),
binding_port: 8080,
free_private_network_ips,
+1 -1
View File
@@ -302,7 +302,7 @@ impl MixStageParameters {
&nonce.into(),
&[],
&mut buffer[self.header_range()],
tag.as_slice().into(),
tag.as_slice().try_into().unwrap(),
)
.map_err(|e| OutfoxError::ChaCha20Poly1305Error(e.to_string()))?;
+12
View File
@@ -80,6 +80,18 @@ Generated TS types will be located in `src-tauri/bindings/`
## Build
To build as a **shared library**
```
yarn build && cd src-tauri && cargo build --release --lib --features custom-protocol
#alias
yarn build:app
```
You can build for a different platform using [Cross](https://github.com/cross-rs/cross).
For example, to build for Windows on Linux:
```
cross build --target x86_64-pc-windows-gnu --release --lib --features custom-protocol
```
+3 -3
View File
@@ -4,11 +4,11 @@
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>NymVPN</title>
<title>Tauri + React + TS</title>
</head>
<body class="h-screen">
<div id="root" class="h-full bg-gray-900 text-gray-400"></div>
<body>
<div id="root" class="bg-gray-900"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
+6 -10
View File
@@ -8,7 +8,7 @@
"dev:app": "RUST_LOG=nymvpn_ui=trace tauri dev",
"dev:browser": "vite --mode dev-browser",
"build": "tsc && vite build",
"build:app": "yarn tauri build",
"build:app": "yarn build && cd src-tauri && cargo build --release --lib --features custom-protocol",
"preview": "vite preview",
"lint": "eslint --ext .ts,.tsx src/",
"lint:fix": "eslint --ext .js,.ts --fix src/",
@@ -18,21 +18,17 @@
"tauri": "tauri"
},
"dependencies": {
"@mui/base": "^5.0.0-beta.24",
"@mui/base": "^5.0.0-beta.20",
"@mui/material": "^5.14.14",
"@tauri-apps/api": "^1.5.0",
"clsx": "^2.0.0",
"i18next": "^23.7.6",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-i18next": "^13.5.0",
"react-router-dom": "^6.18.0"
"react-dom": "^18.2.0"
},
"devDependencies": {
"@tauri-apps/cli": "^1.5.0",
"@types/react": "^18.2.37",
"@types/react-dom": "^18.2.15",
"@types/react-router-dom": "^5.3.3",
"@types/react": "^18.2.31",
"@types/react-dom": "^18.2.14",
"@typescript-eslint/eslint-plugin": "^6.8.0",
"@typescript-eslint/parser": "^6.8.0",
"@vitejs/plugin-react-swc": "^3.3.2",
@@ -46,6 +42,6 @@
"prettier": "^3.0.3",
"tailwindcss": "^3.3.3",
"typescript": "^5.0.2",
"vite": "^5.0.0"
"vite": "^4.4.5"
}
}
+1 -504
View File
@@ -206,9 +206,6 @@ name = "bytes"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
dependencies = [
"serde",
]
[[package]]
name = "cairo-rs"
@@ -618,7 +615,7 @@ dependencies = [
"rustc_version",
"toml 0.8.5",
"vswhom",
"winreg 0.51.0",
"winreg",
]
[[package]]
@@ -782,12 +779,6 @@ dependencies = [
"syn 2.0.38",
]
[[package]]
name = "futures-sink"
version = "0.3.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817"
[[package]]
name = "futures-task"
version = "0.3.28"
@@ -801,11 +792,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533"
dependencies = [
"futures-core",
"futures-io",
"futures-macro",
"futures-sink",
"futures-task",
"memchr",
"pin-project-lite",
"pin-utils",
"slab",
@@ -1117,25 +1105,6 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "h2"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91fc23aa11be92976ef4729127f1a74adf36d8436f7816b185d18df956790833"
dependencies = [
"bytes",
"fnv",
"futures-core",
"futures-sink",
"futures-util",
"http",
"indexmap 1.9.3",
"slab",
"tokio",
"tokio-util",
"tracing",
]
[[package]]
name = "hashbrown"
version = "0.12.3"
@@ -1214,72 +1183,12 @@ dependencies = [
"itoa 1.0.9",
]
[[package]]
name = "http-body"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1"
dependencies = [
"bytes",
"http",
"pin-project-lite",
]
[[package]]
name = "http-range"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21dec9db110f5f872ed9699c3ecf50cf16f423502706ba5c72462e28d3157573"
[[package]]
name = "httparse"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904"
[[package]]
name = "httpdate"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
[[package]]
name = "hyper"
version = "0.14.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468"
dependencies = [
"bytes",
"futures-channel",
"futures-core",
"futures-util",
"h2",
"http",
"http-body",
"httparse",
"httpdate",
"itoa 1.0.9",
"pin-project-lite",
"socket2 0.4.10",
"tokio",
"tower-service",
"tracing",
"want",
]
[[package]]
name = "hyper-tls"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
dependencies = [
"bytes",
"hyper",
"native-tls",
"tokio",
"tokio-native-tls",
]
[[package]]
name = "iana-time-zone"
version = "0.1.58"
@@ -1399,12 +1308,6 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "ipnet"
version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
[[package]]
name = "itoa"
version = "0.4.8"
@@ -1637,18 +1540,6 @@ dependencies = [
"autocfg",
]
[[package]]
name = "mime"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
[[package]]
name = "minisign-verify"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "933dca44d65cdd53b355d0b73d380a2ff5da71f87f036053188bf1eab6a19881"
[[package]]
name = "miniz_oxide"
version = "0.7.1"
@@ -1659,35 +1550,6 @@ dependencies = [
"simd-adler32",
]
[[package]]
name = "mio"
version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0"
dependencies = [
"libc",
"wasi 0.11.0+wasi-snapshot-preview1",
"windows-sys 0.48.0",
]
[[package]]
name = "native-tls"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e"
dependencies = [
"lazy_static",
"libc",
"log",
"openssl",
"openssl-probe",
"openssl-sys",
"schannel",
"security-framework",
"security-framework-sys",
"tempfile",
]
[[package]]
name = "ndk"
version = "0.6.0"
@@ -1828,17 +1690,6 @@ dependencies = [
"objc_exception",
]
[[package]]
name = "objc-foundation"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9"
dependencies = [
"block",
"objc",
"objc_id",
]
[[package]]
name = "objc_exception"
version = "0.1.2"
@@ -1882,50 +1733,6 @@ dependencies = [
"windows-sys 0.42.0",
]
[[package]]
name = "openssl"
version = "0.10.59"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a257ad03cd8fb16ad4172fedf8094451e1af1c4b70097636ef2eac9a5f0cc33"
dependencies = [
"bitflags 2.4.1",
"cfg-if",
"foreign-types",
"libc",
"once_cell",
"openssl-macros",
"openssl-sys",
]
[[package]]
name = "openssl-macros"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.38",
]
[[package]]
name = "openssl-probe"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]]
name = "openssl-sys"
version = "0.9.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40a4130519a360279579c2053038317e40eff64d13fd3f004f9e1b72b8a6aaf9"
dependencies = [
"cc",
"libc",
"pkg-config",
"vcpkg",
]
[[package]]
name = "overload"
version = "0.1.1"
@@ -2399,70 +2206,6 @@ version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
[[package]]
name = "reqwest"
version = "0.11.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b"
dependencies = [
"base64 0.21.5",
"bytes",
"encoding_rs",
"futures-core",
"futures-util",
"h2",
"http",
"http-body",
"hyper",
"hyper-tls",
"ipnet",
"js-sys",
"log",
"mime",
"native-tls",
"once_cell",
"percent-encoding",
"pin-project-lite",
"serde",
"serde_json",
"serde_urlencoded",
"system-configuration",
"tokio",
"tokio-native-tls",
"tokio-util",
"tower-service",
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
"wasm-streams",
"web-sys",
"winreg 0.50.0",
]
[[package]]
name = "rfd"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0149778bd99b6959285b0933288206090c50e2327f47a9c463bfdbf45c8823ea"
dependencies = [
"block",
"dispatch",
"glib-sys",
"gobject-sys",
"gtk-sys",
"js-sys",
"lazy_static",
"log",
"objc",
"objc-foundation",
"objc_id",
"raw-window-handle",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"windows 0.37.0",
]
[[package]]
name = "rustc-demangle"
version = "0.1.23"
@@ -2518,15 +2261,6 @@ dependencies = [
"winapi-util",
]
[[package]]
name = "schannel"
version = "0.1.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88"
dependencies = [
"windows-sys 0.48.0",
]
[[package]]
name = "scoped-tls"
version = "1.0.1"
@@ -2539,29 +2273,6 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "security-framework"
version = "2.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de"
dependencies = [
"bitflags 1.3.2",
"core-foundation",
"core-foundation-sys",
"libc",
"security-framework-sys",
]
[[package]]
name = "security-framework-sys"
version = "2.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]]
name = "selectors"
version = "0.22.0"
@@ -2642,18 +2353,6 @@ dependencies = [
"serde",
]
[[package]]
name = "serde_urlencoded"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
dependencies = [
"form_urlencoded",
"itoa 1.0.9",
"ryu",
"serde",
]
[[package]]
name = "serde_with"
version = "3.4.0"
@@ -2762,26 +2461,6 @@ version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a"
[[package]]
name = "socket2"
version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "socket2"
version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9"
dependencies = [
"libc",
"windows-sys 0.48.0",
]
[[package]]
name = "soup2"
version = "0.2.1"
@@ -2879,27 +2558,6 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "system-configuration"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7"
dependencies = [
"bitflags 1.3.2",
"core-foundation",
"system-configuration-sys",
]
[[package]]
name = "system-configuration-sys"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]]
name = "system-deps"
version = "5.0.0"
@@ -3008,8 +2666,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9bfe673cf125ef364d6f56b15e8ce7537d9ca7e4dae1cf6fbbdeed2e024db3d9"
dependencies = [
"anyhow",
"base64 0.21.5",
"bytes",
"cocoa",
"dirs-next",
"embed_plist",
@@ -3022,7 +2678,6 @@ dependencies = [
"heck 0.4.1",
"http",
"ignore",
"minisign-verify",
"objc",
"once_cell",
"open",
@@ -3030,8 +2685,6 @@ dependencies = [
"rand 0.8.5",
"raw-window-handle",
"regex",
"reqwest",
"rfd",
"semver",
"serde",
"serde_json",
@@ -3045,14 +2698,12 @@ dependencies = [
"tauri-utils",
"tempfile",
"thiserror",
"time",
"tokio",
"url",
"uuid",
"webkit2gtk",
"webview2-com",
"windows 0.39.0",
"zip",
]
[[package]]
@@ -3316,36 +2967,8 @@ checksum = "4f38200e3ef7995e5ef13baec2f432a6da0aa9ac495b2c0e8f3b7eec2c92d653"
dependencies = [
"backtrace",
"bytes",
"libc",
"mio",
"num_cpus",
"pin-project-lite",
"socket2 0.5.5",
"windows-sys 0.48.0",
]
[[package]]
name = "tokio-native-tls"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
dependencies = [
"native-tls",
"tokio",
]
[[package]]
name = "tokio-util"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15"
dependencies = [
"bytes",
"futures-core",
"futures-sink",
"pin-project-lite",
"tokio",
"tracing",
]
[[package]]
@@ -3416,12 +3039,6 @@ dependencies = [
"winnow",
]
[[package]]
name = "tower-service"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
[[package]]
name = "tracing"
version = "0.1.40"
@@ -3492,12 +3109,6 @@ dependencies = [
"serde_json",
]
[[package]]
name = "try-lock"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed"
[[package]]
name = "ts-rs"
version = "7.0.0"
@@ -3588,12 +3199,6 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
[[package]]
name = "vcpkg"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "version-compare"
version = "0.0.11"
@@ -3642,15 +3247,6 @@ dependencies = [
"winapi-util",
]
[[package]]
name = "want"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
dependencies = [
"try-lock",
]
[[package]]
name = "wasi"
version = "0.9.0+wasi-snapshot-preview1"
@@ -3688,18 +3284,6 @@ dependencies = [
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03"
dependencies = [
"cfg-if",
"js-sys",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.87"
@@ -3729,29 +3313,6 @@ version = "0.2.87"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1"
[[package]]
name = "wasm-streams"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4609d447824375f43e1ffbc051b50ad8f4b3ae8219680c94452ea05eb240ac7"
dependencies = [
"futures-util",
"js-sys",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
]
[[package]]
name = "web-sys"
version = "0.3.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "webkit2gtk"
version = "0.18.2"
@@ -3868,19 +3429,6 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57b543186b344cc61c85b5aab0d2e3adf4e0f99bc076eff9aa5927bcc0b8a647"
dependencies = [
"windows_aarch64_msvc 0.37.0",
"windows_i686_gnu 0.37.0",
"windows_i686_msvc 0.37.0",
"windows_x86_64_gnu 0.37.0",
"windows_x86_64_msvc 0.37.0",
]
[[package]]
name = "windows"
version = "0.39.0"
@@ -3996,12 +3544,6 @@ version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_msvc"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2623277cb2d1c216ba3b578c0f3cf9cdebeddb6e66b1b218bb33596ea7769c3a"
[[package]]
name = "windows_aarch64_msvc"
version = "0.39.0"
@@ -4020,12 +3562,6 @@ version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_i686_gnu"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3925fd0b0b804730d44d4b6278c50f9699703ec49bcd628020f46f4ba07d9e1"
[[package]]
name = "windows_i686_gnu"
version = "0.39.0"
@@ -4044,12 +3580,6 @@ version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_msvc"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce907ac74fe331b524c1298683efbf598bb031bc84d5e274db2083696d07c57c"
[[package]]
name = "windows_i686_msvc"
version = "0.39.0"
@@ -4068,12 +3598,6 @@ version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_x86_64_gnu"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2babfba0828f2e6b32457d5341427dcbb577ceef556273229959ac23a10af33d"
[[package]]
name = "windows_x86_64_gnu"
version = "0.39.0"
@@ -4104,12 +3628,6 @@ version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_msvc"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4dd6dc7df2d84cf7b33822ed5b86318fb1781948e9663bacd047fc9dd52259d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.39.0"
@@ -4137,16 +3655,6 @@ dependencies = [
"memchr",
]
[[package]]
name = "winreg"
version = "0.50.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1"
dependencies = [
"cfg-if",
"windows-sys 0.48.0",
]
[[package]]
name = "winreg"
version = "0.51.0"
@@ -4224,14 +3732,3 @@ checksum = "f4686009f71ff3e5c4dbcf1a282d0a44db3f021ba69350cd42086b3e5f1c6985"
dependencies = [
"libc",
]
[[package]]
name = "zip"
version = "0.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261"
dependencies = [
"byteorder",
"crc32fast",
"crossbeam-utils",
]
+4 -1
View File
@@ -7,11 +7,14 @@ license = ""
repository = ""
edition = "2021"
[lib]
crate-type = ["cdylib"]
[build-dependencies]
tauri-build = { version = "1.5", features = [] }
[dependencies]
tauri = { version = "1.5.2", features = [ "updater", "shell-open"] }
tauri = { version = "1.5.2", features = ["shell-open"] }
tokio = { version = "1.33", features = ["rt", "sync", "time", "fs"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
@@ -1,6 +1,6 @@
use std::time::Duration;
use tauri::{Manager, State};
use tauri::State;
use tokio::time::sleep;
use tracing::{debug, instrument, trace};
@@ -9,14 +9,7 @@ use crate::{
states::{app::ConnectionState, SharedAppState},
};
const EVENT_CONNECTION: &str = "connection-state";
#[derive(Clone, serde::Serialize)]
struct EventPayload {
state: ConnectionState,
}
#[instrument(skip_all)]
#[instrument]
#[tauri::command]
pub async fn get_connection_state(
state: State<'_, SharedAppState>,
@@ -26,12 +19,8 @@ pub async fn get_connection_state(
Ok(app_state.state)
}
#[instrument(skip_all)]
#[tauri::command]
pub async fn connect(
app: tauri::AppHandle,
state: State<'_, SharedAppState>,
) -> Result<ConnectionState, CommandError> {
pub async fn connect(state: State<'_, SharedAppState>) -> Result<ConnectionState, CommandError> {
debug!("connect");
let mut app_state = state.lock().await;
let ConnectionState::Disconnected = app_state.state else {
@@ -41,46 +30,23 @@ pub async fn connect(
)));
};
// switch to "Connecting" state
app_state.state = ConnectionState::Connecting;
// unlock the mutex
drop(app_state);
app.emit_all(
EVENT_CONNECTION,
EventPayload {
state: ConnectionState::Connecting,
},
)
.ok();
// TODO fake some delay to establish connection
let app_state_cloned = state.inner().clone();
let task = tokio::spawn(async move {
sleep(Duration::from_secs(2)).await;
trace!("connected");
app_state_cloned.lock().await.state = ConnectionState::Connected;
debug!("sending event [{}]: connected", EVENT_CONNECTION);
app.emit_all(
EVENT_CONNECTION,
EventPayload {
state: ConnectionState::Connected,
},
)
.ok();
});
let _ = task.await;
let app_state = state.lock().await;
app_state.state = ConnectionState::Connecting;
Ok(app_state.state)
}
#[instrument(skip_all)]
#[instrument]
#[tauri::command]
pub async fn disconnect(
app: tauri::AppHandle,
state: State<'_, SharedAppState>,
) -> Result<ConnectionState, CommandError> {
pub async fn disconnect(state: State<'_, SharedAppState>) -> Result<ConnectionState, CommandError> {
debug!("disconnect");
let mut app_state = state.lock().await;
let ConnectionState::Connected = app_state.state else {
@@ -90,36 +56,6 @@ pub async fn disconnect(
)));
};
// switch to "Disconnecting" state
app_state.state = ConnectionState::Disconnecting;
// unlock the mutex
drop(app_state);
app.emit_all(
EVENT_CONNECTION,
EventPayload {
state: ConnectionState::Disconnecting,
},
)
.ok();
// TODO fake some delay to confirm disconnection
let app_state_cloned = state.inner().clone();
let task = tokio::spawn(async move {
sleep(Duration::from_secs(2)).await;
trace!("disconnected");
app_state_cloned.lock().await.state = ConnectionState::Disconnected;
debug!("sending event [{}]: disconnected", EVENT_CONNECTION);
app.emit_all(
EVENT_CONNECTION,
EventPayload {
state: ConnectionState::Disconnected,
},
)
.ok();
});
let _ = task.await;
let app_state = state.lock().await;
Ok(app_state.state)
}
+16
View File
@@ -0,0 +1,16 @@
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
// Learn more about Tauri commands at https://tauri.app/v1/guides/features/command
#[tauri::command]
fn greet(name: &str) -> String {
format!("Hello, {}! You've been greeted from Rust!", name)
}
#[no_mangle]
pub extern "C" fn run_tauri() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![greet])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
+1 -1
View File
@@ -26,7 +26,7 @@ const APP_DATA_FILE: &str = "app-data.toml";
const APP_CONFIG_FILE: &str = "config.toml";
fn main() -> Result<()> {
dotenvy::dotenv().ok();
dotenvy::dotenv()?;
// uses RUST_LOG value for logging level
// eg. RUST_LOG=tauri=debug,nymvpn_ui=trace
+2 -2
View File
@@ -46,8 +46,8 @@
"fullscreen": false,
"resizable": true,
"title": "NymVPN",
"width": 400,
"height": 600
"width": 200,
"height": 200
}
]
}
+46 -23
View File
@@ -1,31 +1,54 @@
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
import { Home, Settings, Error, PageLayout } from './pages';
import { MainStateProvider } from './state';
import './i18n/config';
import React, { useState } from 'react';
import { invoke } from '@tauri-apps/api/tauri';
import { Button as BaseButton, ButtonProps } from '@mui/base/Button';
import clsx from 'clsx';
const router = createBrowserRouter([
{
element: <PageLayout />,
children: [
{
path: '/',
element: <Home />,
errorElement: <Error />,
},
{
path: '/settings',
element: <Settings />,
errorElement: <Error />,
},
],
// eslint-disable-next-line react/display-name
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
(props, ref) => {
const { className, ...other } = props;
return (
<BaseButton
ref={ref}
className={clsx(
'cursor-pointer disabled:cursor-not-allowed text-sm font-sans bg-violet-500 hover:bg-violet-600 active:bg-violet-700 text-white rounded-lg font-semibold px-4 py-2 border-none disabled:opacity-50',
className,
)}
{...other}
/>
);
},
]);
);
function App() {
const [greetMsg, setGreetMsg] = useState('');
const [name, setName] = useState('');
async function greet() {
// Learn more about Tauri commands at https://tauri.app/v1/guides/features/command
setGreetMsg(await invoke('greet', { name }));
}
return (
<MainStateProvider>
<RouterProvider router={router} />
</MainStateProvider>
<div>
<h1>Welcome to Tauri!</h1>
<form
onSubmit={(e) => {
e.preventDefault();
greet();
}}
>
<input
id="greet-input"
onChange={(e) => setName(e.currentTarget.value)}
placeholder="Enter a name..."
/>
<Button type="submit">Greet</Button>
</form>
<p>{greetMsg}</p>
</div>
);
}
-1
View File
@@ -1 +0,0 @@
export const ConnectionEvent = 'connection-state';
-1
View File
@@ -1 +0,0 @@
export * from './main';
-15
View File
@@ -1,15 +0,0 @@
import { createContext, useContext } from 'react';
import { AppState } from '../types';
import { initialState, StateAction } from '../state';
export const MainStateContext = createContext<AppState>(initialState);
export const MainDispatchContext =
createContext<React.Dispatch<StateAction> | null>(null);
export const useMainState = () => {
return useContext(MainStateContext);
};
export const useMainDispatch = () => {
return useContext(MainDispatchContext);
};
+8 -33
View File
@@ -1,36 +1,11 @@
import { mockIPC, mockWindows } from '@tauri-apps/api/mocks';
import { ConnectionState } from '../types';
import { emit } from '@tauri-apps/api/event';
import { ConnectionEvent } from '../constants';
import { greet } from './tauri-cmd-mocks';
export function mockTauriIPC() {
mockWindows('main');
mockWindows('main');
mockIPC(async (cmd, args) => {
console.log(`IPC call mocked "${cmd}"`);
console.log(args);
if (cmd === 'connect') {
await emit(ConnectionEvent, { state: 'Connecting' });
return new Promise<ConnectionState>((resolve) =>
setTimeout(async () => {
await emit(ConnectionEvent, { state: 'Connected' });
resolve('Connected');
}, 2000),
);
}
if (cmd === 'disconnect') {
await emit(ConnectionEvent, { state: 'Disconnecting' });
return new Promise<ConnectionState>((resolve) =>
setTimeout(async () => {
await emit(ConnectionEvent, { state: 'Disconnected' });
resolve('Disconnected');
}, 2000),
);
}
if (cmd === 'get_connection_state') {
return new Promise<ConnectionState>((resolve) =>
setTimeout(() => resolve('Disconnected'), 2000),
);
}
});
}
mockIPC(async (cmd, args) => {
console.log(`IPC call mocked "${cmd}"`);
if (cmd === 'greet') {
return greet(args.name as string);
}
});
@@ -0,0 +1,3 @@
export default function greet(name: string): string {
return `Hello, ${name}!`;
}
@@ -0,0 +1 @@
export { default as greet } from './greet';
-22
View File
@@ -1,22 +0,0 @@
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import main from './en/main.json';
const defaultNS = 'main';
i18n.use(initReactI18next).init({
lng: 'en',
debug: import.meta.env.DEV,
resources: {
en: {
main,
},
},
defaultNS,
interpolation: {
escapeValue: false, // not needed for react as it escapes by default
},
});
export default i18n;
-4
View File
@@ -1,4 +0,0 @@
{
"connect": "Connect",
"disconnect": "Disconnect"
}
+1 -2
View File
@@ -1,12 +1,11 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import { mockTauriIPC } from './dev/setup';
import './styles.css';
if (import.meta.env.MODE === 'dev-browser') {
console.log('Running in dev-browser mode. Mocking tauri window and IPCs');
mockTauriIPC();
import('./dev/setup');
}
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
-21
View File
@@ -1,21 +0,0 @@
import { useRouteError } from 'react-router-dom';
type routerErrorType = {
statusText: string;
message: string;
};
export default function ErrorPage() {
const error: routerErrorType = useRouteError() as unknown as routerErrorType;
console.error(error);
return (
<div id="error-page">
<h1>Oops!</h1>
<p>Sorry, an unexpected error has occurred.</p>
<p>
<i>{error.statusText || error.message}</i>
</p>
</div>
);
}
-41
View File
@@ -1,41 +0,0 @@
import { useTranslation } from 'react-i18next';
import { invoke } from '@tauri-apps/api';
import { useMainDispatch, useMainState } from '../contexts';
import { StateDispatch } from '../types';
function Home() {
const state = useMainState();
const dispatch = useMainDispatch() as StateDispatch;
const { t } = useTranslation();
const handleClick = async () => {
if (state.state === 'Connected') {
dispatch({ type: 'disconnect' });
invoke('disconnect').then((result) => {
console.log(result);
});
} else if (state.state === 'Disconnected') {
dispatch({ type: 'connect' });
invoke('connect').then((result) => {
console.log(result);
});
}
};
return (
<div>
<h2>NymVPN</h2>
connection state: {state.state}
{state.loading ? (
'loading…'
) : (
<button onClick={handleClick}>
{state.state === 'Disconnected' ? t('connect') : t('disconnect')}
</button>
)}
</div>
);
}
export default Home;
-4
View File
@@ -1,4 +0,0 @@
export { default as PageLayout } from './pageLayout';
export { default as Home } from './home';
export { default as Settings } from './settings';
export { default as Error } from './error';
-11
View File
@@ -1,11 +0,0 @@
import { Outlet } from 'react-router-dom';
function PageLayout() {
return (
<div>
<Outlet />
</div>
);
}
export default PageLayout;
-5
View File
@@ -1,5 +0,0 @@
function Settings() {
return <div>Settings</div>;
}
export default Settings;
-2
View File
@@ -1,2 +0,0 @@
export * from './main';
export * from './provider';
-35
View File
@@ -1,35 +0,0 @@
import { AppState, ConnectionState } from '../types';
export type StateAction =
| { type: 'change-connection-state'; state: ConnectionState }
| { type: 'connect' }
| { type: 'disconnect' }
| { type: 'reset' };
export const initialState: AppState = {
state: 'Disconnected',
loading: false,
privacyMode: 'High',
tunnel: { name: 'nym', id: 'nym' },
};
export function reducer(state: AppState, action: StateAction): AppState {
switch (action.type) {
case 'change-connection-state': {
return {
...state,
state: action.state,
loading:
action.state === 'Connecting' || action.state === 'Disconnecting',
};
}
case 'connect': {
return { ...state, state: 'Connecting', loading: true };
}
case 'disconnect': {
return { ...state, state: 'Disconnecting', loading: true };
}
case 'reset':
return initialState;
}
}
-35
View File
@@ -1,35 +0,0 @@
import { useEffect, useReducer } from 'react';
import { invoke } from '@tauri-apps/api';
import { initialState, reducer } from './main';
import { useTauriEvents } from './useTauriEvents';
import { MainDispatchContext, MainStateContext } from '../contexts';
import { ConnectionState } from '../types';
type Props = {
children?: React.ReactNode;
};
export function MainStateProvider({ children }: Props) {
const [state, dispatch] = useReducer(reducer, initialState);
useTauriEvents(dispatch);
// initialize connection state
useEffect(() => {
const getInitialConnectionState = async () => {
return await invoke<ConnectionState>('get_connection_state');
};
getInitialConnectionState().then((state) =>
dispatch({ type: 'change-connection-state', state }),
);
}, []);
return (
<MainStateContext.Provider value={state}>
<MainDispatchContext.Provider value={dispatch}>
{children}
</MainDispatchContext.Provider>
</MainStateContext.Provider>
);
}
-39
View File
@@ -1,39 +0,0 @@
import { useCallback, useEffect } from 'react';
import { listen } from '@tauri-apps/api/event';
import { EventPayload, StateDispatch } from '../types';
import { ConnectionEvent } from '../constants';
export function useTauriEvents(dispatch: StateDispatch) {
const registerListener = useCallback(() => {
return listen<EventPayload>(ConnectionEvent, (event) => {
console.log(
`received event ${event.event}, state: ${event.payload.state}`,
);
switch (event.payload.state) {
case 'Connected':
dispatch({ type: 'change-connection-state', state: 'Connected' });
break;
case 'Disconnected':
dispatch({ type: 'change-connection-state', state: 'Disconnected' });
break;
case 'Connecting':
dispatch({ type: 'change-connection-state', state: 'Connecting' });
break;
case 'Disconnecting':
dispatch({ type: 'change-connection-state', state: 'Disconnecting' });
break;
case 'Error':
break;
}
});
}, [dispatch]);
// register/unregister event listener
useEffect(() => {
const unlisten = registerListener();
return () => {
unlisten.then((f) => f());
};
}, [registerListener]);
}
-29
View File
@@ -1,29 +0,0 @@
import { Dispatch } from 'react';
import { StateAction } from '../state';
export type ConnectionState =
| 'Connected'
| 'Disconnected'
| 'Connecting'
| 'Disconnecting'
| 'Error';
export type PrivacyMode = 'High' | 'Medium' | 'Low';
export interface TunnelConfig {
id: string;
name: string;
}
export type AppState = {
state: ConnectionState;
loading: boolean;
privacyMode: PrivacyMode;
tunnel: TunnelConfig;
};
export type EventPayload = {
state: ConnectionState;
};
export type StateDispatch = Dispatch<StateAction>;
-1
View File
@@ -1 +0,0 @@
export * from './app-state';
+697 -843
View File
File diff suppressed because it is too large Load Diff

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