Compare commits

..

24 Commits

Author SHA1 Message Date
mfahampshire 294903bede remove quick test change - reverting to normal 2023-11-27 12:26:38 +01:00
mfahampshire 105cb67f06 uncomment CI deploy to test 2023-11-27 12:01:53 +01:00
Jon Häggblad d4ee0708ad ubuntu 20.04 2023-11-27 12:01:53 +01:00
Jon Häggblad e4b52b4a32 apt 2023-11-27 12:01:53 +01:00
Jon Häggblad 5177e55b22 test things 2023-11-27 12:01:53 +01:00
Drazen Urch 3b8cff8b32 Feature/ppa repo (#4165)
* Test GH pages

* Add workflow

* Maybe fix path

* Restructure

* Rename list file

* Naming

* Restructure again

* Add readme, final touches

* Add script to update PPA

* Update change log

* Update Makefile

* Avoid commiting keys by accident

* Documnet PPA_SIGNING_KEY in script
2023-11-23 13:51:58 +01:00
Jon Häggblad 177cb22a3f Fix action controller stopping too early (#4170)
* Stop ActionController only after both incoming channels closed

* Downgrade task getting dropped message to debug level

* The warning to the user is not a warning that things didn't work

* rustfmt

* fix channel close logic

* Don't close real traffic stream and sent notification listener too aggressively
2023-11-23 12:44:25 +01:00
Pierre Dommerc 809fabfc2b feat(vpn-ui): init frontend NC-134 (#4160) 2023-11-22 15:04:02 +01:00
mx 8ee31d94ea Merge pull request #4115 from nymtech/feature/increase-default-sdk-surbs
Feature/increase default sdk surbs
2023-11-21 14:30:21 +00:00
Jon Häggblad 818d7ee13e Add logging for client fetching gateways and pretty print error (#4164)
* Add logging for client fetching gateways

* Pretty print error in client

* update
2023-11-21 11:41:03 +01:00
Jon Häggblad 81c692d305 Extract out tun crate (#4159)
* wip

* Use common interface request response

* wip: extract crates

* wip

* new structure compiles

* Create tun_common subdir

* Comment out return_mix_delays for now

* Remove unused boringtun dependency

* Revert to previous packet encoding

* Allow dead code on non-linux

* Add new function
2023-11-21 09:55:35 +01:00
Jon Häggblad 1728de57b9 Fix bunch of warnings on latest rustc beta (#4161) 2023-11-21 09:02:48 +01:00
Jędrzej Stuczyński 53fcebfd86 added 'build-info' command to nym-api (#4162)
* added 'build-info' command to nym-api

* moved run logic to cli::run

* clippy
2023-11-20 17:10:03 +00:00
Jędrzej Stuczyński 4a5ceddeac Feature/gateway described nr (#4147)
* adding embedded NR information to gateways/described endpoint

* using default for NR if not available
2023-11-20 16:18:32 +01:00
Bogdan-Ștefan Neacşu a5c1e4abf0 Expose the same pub key that's used for wg (#4157) 2023-11-17 13:04:22 +00:00
Jon Häggblad 3a1003c564 Create TaggedPacket (#4156)
* Create TaggedPacket

* Fix bug passing the correct data
2023-11-17 12:30:15 +01:00
Jon Häggblad 1cdd8f6c08 Rework error handling in tun device (#4146)
* Rework error handling in tun device

* Extract out timeout constants

* Experiment with timeouts

* Update error msg

* try_send in one direction as hotfix for deadlock

* Downgrade some log from info to debug

* Update comment

* rustfmt
2023-11-17 09:52:05 +01:00
mfahampshire f47e1793a2 typo fix 2023-11-13 10:45:52 +01:00
mfahampshire 8e3f4ce08d format change 2023-11-09 17:45:19 +01:00
mfahampshire 9b4262bb36 added flag to socks5 info 2023-11-09 17:44:05 +01:00
mfahampshire 6d37d7df8e dropped default surbs to 10 for uniformity across sdks 2023-11-09 16:38:07 +01:00
mfahampshire 427f205a58 upped default attached number of surbs from 0 -> 10 2023-11-09 16:26:58 +01:00
mfahampshire 9549d3b681 revert change of file name 2023-11-07 15:12:33 +01:00
mfahampshire 1be81b96b3 upped default sdk surbs from 5 -> 20 2023-11-07 14:56:14 +01:00
116 changed files with 2840 additions and 1253 deletions
+7 -6
View File
@@ -75,28 +75,29 @@ jobs:
uses: actions-rs/cargo@v1
with:
command: build
args: --workspace
# Enable wireguard by default on linux only
args: --workspace --features wireguard
- name: Build all examples
if: matrix.os == 'custom-linux'
uses: actions-rs/cargo@v1
with:
command: build
args: --workspace --examples
args: --workspace --examples --features wireguard
- name: Run all tests
if: matrix.os == 'custom-linux'
uses: actions-rs/cargo@v1
with:
command: test
args: --workspace
args: --workspace --features wireguard
- 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 -- --ignored
args: --workspace --features wireguard -- --ignored
- name: Annotate with clippy checks
if: matrix.os == 'custom-linux'
@@ -104,10 +105,10 @@ jobs:
continue-on-error: true
with:
token: ${{ secrets.GITHUB_TOKEN }}
args: --workspace
args: --workspace --features wireguard
- name: Clippy
uses: actions-rs/cargo@v1
with:
command: clippy
args: --workspace --all-targets -- -D warnings
args: --workspace --all-targets --features wireguard -- -D warnings
+11 -7
View File
@@ -9,9 +9,11 @@ on:
jobs:
build:
runs-on: custom-linux
runs-on: ubuntu-20.04-16-core
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
@@ -28,20 +30,22 @@ jobs:
command: build
args: --workspace --release
- name: Install mdbook
run: (test -x $HOME/.cargo/bin/mdbook || cargo install --vers "^0.4.35" mdbook)
run: cargo install mdbook
# run: (test -x $HOME/.cargo/bin/mdbook || cargo install --vers "^0.4.35" mdbook)
- name: Install mdbook plugins
run: |
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 \
cargo install mdbook-variables
cargo install mdbook-admonish
cargo install mdbook-last-changed
cargo install mdbook-theme
cargo install 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 }} --lib --features custom-protocol
args: --manifest-path ${{ env.CARGOTOML_PATH }} --features custom-protocol
# - name: Run all tests
# uses: actions-rs/cargo@v1
+43
View File
@@ -0,0 +1,43 @@
# 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
+3 -1
View File
@@ -45,4 +45,6 @@ envs/qwerty.env
cpu-cycles/libcpucycles/build
foxyfox.env
.next
.next
ppa-private-key.b64
ppa-private-key.asc
+2
View File
@@ -10,11 +10,13 @@ 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,10 +6657,22 @@ 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",
@@ -6668,11 +6680,13 @@ 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",
@@ -6920,6 +6934,7 @@ dependencies = [
"nym-crypto",
"nym-node-requests",
"nym-task",
"nym-wireguard",
"nym-wireguard-types",
"rand 0.7.3",
"serde",
@@ -7462,6 +7477,18 @@ 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"
@@ -7581,6 +7608,7 @@ version = "0.1.0"
dependencies = [
"async-recursion",
"base64 0.21.4",
"bincode",
"boringtun",
"bytes",
"dashmap",
@@ -7589,7 +7617,9 @@ dependencies = [
"ip_network",
"ip_network_table",
"log",
"nym-sphinx",
"nym-task",
"nym-tun",
"nym-wireguard-types",
"rand 0.8.5",
"serde",
@@ -7605,14 +7635,19 @@ 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,6 +49,7 @@ members = [
"common/exit-policy",
"common/http-api-client",
"common/inclusion-probability",
"common/ip-packet-requests",
"common/ledger",
"common/mixnode-common",
"common/network-defaults",
@@ -74,6 +75,7 @@ members = [
"common/store-cipher",
"common/task",
"common/topology",
"common/tun",
"common/types",
"common/wasm/client-core",
"common/wasm/storage",
+4
View File
@@ -168,3 +168,7 @@ 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
+6 -1
View File
@@ -22,5 +22,10 @@ async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
}
setup_logging();
commands::execute(args).await
if let Err(err) = commands::execute(args).await {
log::error!("{err}");
println!("An error occurred: {err}");
std::process::exit(1);
}
Ok(())
}
+6 -1
View File
@@ -21,5 +21,10 @@ async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
}
setup_logging();
commands::execute(args).await
if let Err(err) = commands::execute(args).await {
log::error!("{err}");
println!("An error occurred: {err}");
std::process::exit(1);
}
Ok(())
}
@@ -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");
while !shutdown.is_shutdown() {
loop {
tokio::select! {
action = self.incoming_actions.next() => match action {
Some(action) => self.process_action(action),
@@ -283,6 +283,7 @@ 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");
while !shutdown.is_shutdown() {
loop {
tokio::select! {
frag_id = self.sent_notifier.next() => match frag_id {
Some(frag_id) => {
@@ -53,6 +53,7 @@ impl SentNotificationListener {
},
_ = shutdown.recv_with_delay() => {
log::trace!("SentNotificationListener: Received shutdown");
break;
}
}
}
@@ -500,11 +500,12 @@ where
{
let mut status_timer = tokio::time::interval(Duration::from_secs(5));
while !shutdown.is_shutdown() {
loop {
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, warn};
use log::{debug, error, info};
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");
warn!("you MUST NOT forcefully shutdown now or you risk data corruption!");
info!("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,13 +68,23 @@ 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,6 +94,8 @@ 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 {
@@ -210,6 +212,7 @@ 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.get(0)
})) = msgs.first()
{
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.get(0)
})) = msgs.first()
{
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
.get(0)
.first()
.ok_or(VestingContractError::UnpopulatedVestingPeriods {
owner: self.owner_address.clone(),
})
+1 -5
View File
@@ -115,11 +115,7 @@ 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())
.map(|(share, key)| (share, key))
.collect::<Vec<_>>();
let remote_share_key_pairs = shares.iter().zip(receivers.values()).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
@@ -0,0 +1,17 @@
[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
@@ -0,0 +1,40 @@
#[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
.get(0)
.first()
.ok_or_else(|| CoconutError::Aggregation("Empty set of signatures".to_string()))?
.sig1();
+5 -2
View File
@@ -493,10 +493,13 @@ impl TaskClient {
impl Drop for TaskClient {
fn drop(&mut self) {
if !self.mode.should_signal_on_drop() {
self.log(Level::Debug, "the task client is getting dropped");
self.log(
Level::Trace,
"the task client is getting dropped (but instructed to not signal)",
);
return;
} else {
self.log(Level::Info, "the task client is getting dropped");
self.log(Level::Debug, "the task client is getting dropped");
}
if !self.is_shutdown_poll() {
+24
View File
@@ -0,0 +1,24 @@
[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
@@ -0,0 +1,7 @@
#[cfg(target_os = "linux")]
mod linux;
pub mod tun_task_channel;
#[cfg(target_os = "linux")]
pub use linux::tun_device;
@@ -1,22 +1,64 @@
use std::{
collections::HashMap,
net::{IpAddr, Ipv4Addr},
sync::Arc,
time::Duration,
};
use etherparse::{InternetSlice, SlicedPacket};
use tap::TapFallible;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use crate::{
event::Event,
tun_task_channel::{
tun_task_channel, tun_task_response_channel, TunTaskPayload, TunTaskResponseRx,
TunTaskResponseTx, TunTaskRx, TunTaskTx,
},
udp_listener::PeersByIp,
use tokio::{
io::{AsyncReadExt, AsyncWriteExt},
time::timeout,
};
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},
event::Event,
};
#[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.
@@ -51,6 +93,7 @@ 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
@@ -65,13 +108,27 @@ impl RoutingMode {
})
}
pub fn new_allowed_ips(peers_by_ip: Arc<tokio::sync::Mutex<PeersByIp>>) -> Self {
#[cfg(feature = "wireguard")]
pub fn new_allowed_ips(peers_by_ip: std::sync::Arc<tokio::sync::Mutex<PeersByIp>>) -> Self {
RoutingMode::AllowedIps(AllowedIpsInner { peers_by_ip })
}
}
#[cfg(feature = "wireguard")]
pub struct AllowedIpsInner {
peers_by_ip: Arc<tokio::sync::Mutex<PeersByIp>>,
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)
}
}
pub struct NatInner {
@@ -114,48 +171,33 @@ impl TunDevice {
}
// Send outbound packets out on the wild internet
async fn handle_tun_write(&mut self, data: TunTaskPayload) {
async fn handle_tun_write(&mut self, data: TunTaskPayload) -> Result<(), TunDeviceError> {
let (tag, packet) = data;
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!(
let ParsedAddresses { src_addr, dst_addr } = parse_src_dst_address(&packet)?;
log::debug!(
"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);
}
tokio::time::timeout(
std::time::Duration::from_millis(1000),
timeout(
Duration::from_millis(TUN_WRITE_TIMEOUT_MS),
self.tun.write_all(&packet),
)
.await
.tap_err(|err| {
log::error!("iface: write error: {err}");
})
.ok();
.map_err(|_| TunDeviceError::TunWriteTimeout)?
.map_err(|err| TunDeviceError::TunWriteError { source: err })
}
// Receive reponse packets from the wild internet
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!(
async fn handle_tun_read(&self, packet: &[u8]) -> Result<(), TunDeviceError> {
let ParsedAddresses { src_addr, dst_addr } = parse_src_dst_address(packet)?;
log::debug!(
"iface: read Packet({src_addr} -> {dst_addr}, {} bytes)",
packet.len(),
);
@@ -164,47 +206,32 @@ 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 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;
};
let peers = peers_by_ip.lock().await?;
if let Some(peer_tx) = peers.longest_match(dst_addr).map(|(_, tx)| tx) {
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;
log::debug!("Forward packet to wg tunnel");
return peer_tx
.send(Event::Ip(packet.to_vec().into()))
.await
.map_err(|err| err.into());
}
}
// 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::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::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!("No peer found, packet dropped");
Ok(())
}
pub async fn run(mut self) {
@@ -216,13 +243,9 @@ impl TunDevice {
len = self.tun.read(&mut buf) => match len {
Ok(len) => {
let packet = &buf[..len];
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();
if let Err(err) = self.handle_tun_read(packet).await {
log::error!("iface: handle_tun_read failed: {err}")
}
},
Err(err) => {
log::info!("iface: read error: {err}");
@@ -231,13 +254,9 @@ impl TunDevice {
},
// Writing to the TUN device
Some(data) = self.tun_task_rx.recv() => {
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();
if let Err(err) = self.handle_tun_write(data).await {
log::error!("ifcae: handle_tun_write failed: {err}");
}
}
}
}
@@ -249,12 +268,22 @@ impl TunDevice {
}
}
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(),
})
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),
}
}
+84
View File
@@ -0,0 +1,84 @@
#![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),
)
}
+6 -1
View File
@@ -12,9 +12,14 @@ 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"] }
@@ -45,4 +50,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,6 +4,8 @@
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;
+2 -4
View File
@@ -93,8 +93,7 @@ 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::try_from(local_secret.to_bytes())
.expect("conversion between x25519 private keys is infallible");
let static_secret = boringtun::x25519::StaticSecret::from(local_secret.to_bytes());
let local_public: boringtun::x25519::PublicKey = (&static_secret).into();
let remote_public = boringtun::x25519::PublicKey::from(remote_public.to_bytes());
@@ -123,8 +122,7 @@ 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::try_from(gateway_key.to_bytes())
.expect("conversion between x25519 private keys is infallible");
let static_secret = boringtun::x25519::StaticSecret::from(gateway_key.to_bytes());
let dh = static_secret.diffie_hellman(&self.pub_key);
@@ -1,4 +1,4 @@
use std::net::SocketAddr;
use std::{net::SocketAddr, time::Duration};
use boringtun::x25519;
use dashmap::{
@@ -7,26 +7,47 @@ use dashmap::{
};
use tokio::sync::mpsc::{self};
use crate::event::Event;
use crate::tun_common::{event::Event, network_table::NetworkTable};
// Registered peers
pub type PeersByIp = NetworkTable<PeerEventSender>;
// Channels that are used to communicate with the various tunnels
#[derive(Clone)]
pub struct PeerEventSender(mpsc::Sender<Event>);
pub(crate) struct PeerEventReceiver(mpsc::Receiver<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>,
},
}
impl PeerEventSender {
pub(crate) async fn send(&self, event: Event) -> Result<(), mpsc::error::SendError<Event>> {
self.0.send(event).await
pub async fn send(&self, event: Event) -> Result<(), PeerEventSenderError> {
Ok(self
.0
.send_timeout(event, Duration::from_millis(1000))
.await?)
}
}
impl PeerEventReceiver {
pub(crate) async fn recv(&mut self) -> Option<Event> {
pub async fn recv(&mut self) -> Option<Event> {
self.0.recv().await
}
}
pub(crate) fn peer_event_channel() -> (PeerEventSender, PeerEventReceiver) {
pub fn peer_event_channel() -> (PeerEventSender, PeerEventReceiver) {
let (tx, rx) = mpsc::channel(16);
(PeerEventSender(tx), PeerEventReceiver(rx))
}
@@ -35,20 +56,20 @@ pub(crate) type PeersByKey = DashMap<x25519::PublicKey, PeerEventSender>;
pub(crate) type PeersByAddr = DashMap<SocketAddr, PeerEventSender>;
#[derive(Default)]
pub(crate) struct ActivePeers {
pub struct ActivePeers {
active_peers: PeersByKey,
active_peers_by_addr: PeersByAddr,
}
impl ActivePeers {
pub(crate) fn remove(&self, public_key: &x25519::PublicKey) {
pub 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(crate) fn insert(
pub fn insert(
&self,
public_key: x25519::PublicKey,
addr: SocketAddr,
@@ -58,17 +79,14 @@ impl ActivePeers {
self.active_peers_by_addr.insert(addr, peer_tx);
}
pub(crate) fn get_by_key_mut(
pub fn get_by_key_mut(
&self,
public_key: &x25519::PublicKey,
) -> Option<RefMut<'_, x25519::PublicKey, PeerEventSender>> {
self.active_peers.get_mut(public_key)
}
pub(crate) fn get_by_addr(
&self,
addr: &SocketAddr,
) -> Option<Ref<'_, SocketAddr, PeerEventSender>> {
pub fn get_by_addr(&self, addr: &SocketAddr) -> Option<Ref<'_, SocketAddr, PeerEventSender>> {
self.active_peers_by_addr.get(addr)
}
}
@@ -0,0 +1,3 @@
pub mod active_peers;
pub mod event;
pub mod network_table;
@@ -9,7 +9,7 @@ pub struct NetworkTable<T> {
}
impl<T> NetworkTable<T> {
pub(crate) fn new() -> Self {
pub fn new() -> Self {
Self {
ips: IpNetworkTable::new(),
}
+3
View File
@@ -13,6 +13,7 @@ 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.
@@ -27,6 +28,8 @@ 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
+7 -8
View File
@@ -3,15 +3,10 @@
// #![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;
mod setup;
pub mod tun_task_channel;
pub mod setup;
mod udp_listener;
mod wg_tunnel;
@@ -20,7 +15,9 @@ use std::sync::Arc;
// Currently the module related to setting up the virtual network device is platform specific.
#[cfg(target_os = "linux")]
pub use platform::linux::tun_device;
use nym_tun::tun_device;
use nym_tun::tun_task_channel;
/// Start wireguard UDP listener and TUN device
///
@@ -36,7 +33,9 @@ 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.
let peers_by_ip = Arc::new(tokio::sync::Mutex::new(network_table::NetworkTable::new()));
use nym_wireguard_types::tun_common::network_table::NetworkTable;
let peers_by_ip = Arc::new(tokio::sync::Mutex::new(NetworkTable::new()));
// Alternative 1:
let routing_mode = tun_device::RoutingMode::new_allowed_ips(peers_by_ip.clone());
+3 -5
View File
@@ -3,11 +3,9 @@ use std::{collections::HashMap, sync::Arc};
use tap::TapFallible;
use tokio::sync::mpsc::{self};
use crate::{
active_peers::PeerEventSender,
event::Event,
tun_task_channel::{TunTaskResponseRx, TunTaskTx},
};
use crate::tun_task_channel::{TunTaskResponseRx, TunTaskTx};
use nym_wireguard_types::tun_common::{active_peers::PeerEventSender, event::Event};
#[derive(Clone)]
pub struct PacketRelaySender(pub(crate) mpsc::Sender<(u64, Vec<u8>)>);
-2
View File
@@ -1,2 +0,0 @@
#[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::try_from(static_private_bytes).unwrap();
let static_private = x25519::StaticSecret::from(static_private_bytes);
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::try_from(peer_static_public_bytes).unwrap();
let peer_static_public = x25519::PublicKey::from(peer_static_public_bytes);
info!(
"Adding wg peer public key: {}",
general_purpose::STANDARD.encode(peer_static_public)
-54
View File
@@ -1,54 +0,0 @@
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),
)
}
+8 -7
View File
@@ -7,15 +7,19 @@ use boringtun::{
use futures::StreamExt;
use log::error;
use nym_task::TaskClient;
use nym_wireguard_types::{registration::GatewayClientRegistry, PeerPublicKey, WG_PORT};
use nym_wireguard_types::{
registration::GatewayClientRegistry,
tun_common::{
active_peers::{ActivePeers, PeersByIp},
event::Event,
},
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},
@@ -24,9 +28,6 @@ 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;
+6 -8
View File
@@ -7,18 +7,16 @@ 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::{
active_peers::{peer_event_channel, PeerEventReceiver, PeerEventSender},
error::WgError,
event::Event,
network_table::NetworkTable,
packet_relayer::PacketRelaySender,
registered_peers::PeerIdx,
};
use crate::{error::WgError, 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 --provider Entztfv6Uaz2hpYHQJ6JKoaCTpDL5dja18SuQWVJAmmx.Cvhn9rBJw5Ay9wgHcbgCnVg89MPSV5s2muPV2YF1BXYu@Fo4f4SQLdoyoGkFae5TpVhRVoXCF8UiypLVGtGjujVPf
./nym-socks5-client init --id docs-example --use-reply-surbs true --provider Entztfv6Uaz2hpYHQJ6JKoaCTpDL5dja18SuQWVJAmmx.Cvhn9rBJw5Ay9wgHcbgCnVg89MPSV5s2muPV2YF1BXYu@Fo4f4SQLdoyoGkFae5TpVhRVoXCF8UiypLVGtGjujVPf
```
~~~admonish example collapsible=true title="Console output"
@@ -100,6 +100,8 @@ 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.
@@ -148,7 +150,7 @@ Alternatively, a custom host can be set in the `config.toml` file under the `soc
### Running the socks5 client
You can run the initalised client by doing this:
You can run the initialised 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.
IoError(#[from] std::io::Error),
Io(#[from] std::io::Error),
/// This is returned if configuration file is invalid.
#[error("Configuration file is invalid: '{0}'")]
InvalidFormat(String),
+4 -1
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" }
nym-wireguard = { path = "../common/wireguard", optional = true }
nym-ip-packet-router = { path = "../service-providers/ip-packet-router" }
[dev-dependencies]
@@ -92,3 +92,6 @@ 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.map(|client_registry| {
let wg_state = self.client_registry.and_then(|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);
+10 -12
View File
@@ -200,6 +200,7 @@ impl<St> Gateway<St> {
mixnet_handling::Listener::new(listening_address, shutdown).start(connection_handler);
}
#[cfg(feature = "wireguard")]
async fn start_wireguard(
&self,
shutdown: TaskClient,
@@ -519,18 +520,15 @@ impl<St> Gateway<St> {
Arc::new(coconut_verifier),
);
// 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}")
}
// 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}")
}
info!("Finished nym gateway startup procedure - it should now be able to receive mix and client traffic!");
+42
View File
@@ -14,3 +14,45 @@ 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,6 +164,7 @@ 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" }
nym-bin-common = { path = "../common/bin-common", features = ["output_format"] }
nym-node-tester-utils = { path = "../common/node-tester-utils" }
nym-node-requests = { path = "../nym-node/nym-node-requests" }
+13 -1
View File
@@ -363,11 +363,14 @@ 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, Serialize, Deserialize, schemars::JsonSchema)]
#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema)]
pub struct DescribedGateway {
pub bond: GatewayBond,
pub self_described: Option<NymNodeDescription>,
@@ -381,3 +384,12 @@ 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.get(0).cloned().unwrap_or_default();
let deposit_value_plain = public_attributes_plain.first().cloned().unwrap_or_default();
if deposit_value != deposit_value_plain {
return Err(CoconutError::DifferentPublicAttributes(
deposit_value.to_string(),
+14 -33
View File
@@ -9,8 +9,7 @@ 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;
use crate::support::cli::CliArgs;
use crate::support::cli::{self, Commands};
use crate::support::config::Config;
use crate::support::storage;
use crate::support::storage::NymApiStorage;
@@ -20,7 +19,6 @@ 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;
@@ -57,9 +55,20 @@ async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
}}
setup_logging();
let args = cli::CliArgs::parse();
let args = cli::Cli::parse();
trace!("{:#?}", args);
setup_env(args.config_env_file.as_ref());
run_nym_api(args).await
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,
}
}
async fn start_nym_api_tasks(
@@ -208,31 +217,3 @@ 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
}
+21 -2
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::NymNodeDescription;
use nym_config::defaults::DEFAULT_NYM_NODE_HTTP_PORT;
use nym_api_requests::models::{NetworkRequesterDetails, NymNodeDescription};
use nym_config::defaults::{mainnet, 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,9 +152,28 @@ 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
@@ -0,0 +1,15 @@
// 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!()))
}
+31 -91
View File
@@ -5,13 +5,15 @@ 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::Result;
use clap::Parser;
use anyhow::{bail, Result};
use clap::{Parser, Subcommand};
use lazy_static::lazy_static;
use nym_bin_common::bin_info;
use nym_config::defaults::var_names::NYXD;
use nym_config::OptionalSet;
use nym_validator_client::nyxd;
pub(crate) mod build_info;
pub(crate) mod run;
lazy_static! {
pub static ref PRETTY_BUILD_INFORMATION: String = bin_info!().pretty_print();
@@ -22,100 +24,32 @@ fn pretty_build_info_static() -> &'static str {
&PRETTY_BUILD_INFORMATION
}
// 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)]
#[derive(Parser, Debug)]
#[command(args_conflicts_with_subcommands = true)]
#[clap(author = "Nymtech", version, long_version = pretty_build_info_static(), about)]
pub(crate) struct CliArgs {
pub(crate) struct Cli {
/// Path pointing to an env file that configures the Nym API.
#[clap(short, long)]
pub(crate) config_env_file: Option<std::path::PathBuf>,
/// Id of the nym-api we want to run
#[clap(long)]
pub(crate) id: String,
#[clap(subcommand)]
pub(crate) command: Option<Commands>,
/// 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,
// this shouldn't really be here, but we don't want to break backwards compat
#[clap(flatten)]
pub(crate) run: run::Args,
}
pub(crate) fn override_config(config: Config, args: CliArgs) -> Config {
#[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 {
config
.with_optional_env(
Config::with_custom_nyxd_validator,
@@ -169,8 +103,14 @@ pub(crate) fn override_config(config: Config, args: CliArgs) -> Config {
)
}
pub(crate) fn build_config(args: CliArgs) -> Result<Config> {
let id = args.id.clone();
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")
}
};
// 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
@@ -0,0 +1,124 @@
// 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,6 +50,7 @@ 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,6 +11,8 @@ 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>;
@@ -42,6 +44,16 @@ 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,6 +31,12 @@ pub enum NymNodeError {
source: WireguardError,
},
#[error(transparent)]
KeyRecoveryError {
#[from]
source: nym_crypto::asymmetric::encryption::KeyRecoveryError,
},
#[error("unimplemented")]
Unimplemented,
}
@@ -31,10 +31,7 @@ async fn process_final_message(
}
};
if client
.verify(state.dh_keypair.private_key(), preshared_nonce)
.is_ok()
{
if client.verify(&state.private_key, preshared_nonce).is_ok() {
state.registration_in_progress.remove(&client.pub_key());
state.client_registry.insert(client.pub_key(), client);
@@ -104,7 +101,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.dh_keypair.private_key(),
&state.private_key,
remote_public,
*private_ip_ref.key(),
nonce,
@@ -8,8 +8,9 @@ use crate::wireguard::types::{GatewayClientRegistry, PendingRegistrations};
use axum::routing::{get, post};
use axum::Router;
use ipnetwork::IpNetwork;
use nym_crypto::asymmetric::encryption;
use nym_crypto::asymmetric::encryption::PrivateKey;
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;
@@ -24,15 +25,16 @@ 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,
) -> Self {
WireguardAppState {
) -> Result<Self, crate::error::NymNodeError> {
Ok(WireguardAppState {
inner: Some(WireguardAppStateInner {
dh_keypair,
private_key: Arc::new(PrivateKey::from_bytes(
setup::server_static_private_key().as_ref(),
)?),
client_registry,
registration_in_progress,
binding_port,
@@ -40,7 +42,7 @@ impl WireguardAppState {
private_ip_network.iter().map(|ip| (ip, true)).collect(),
),
}),
}
})
}
// #[allow(dead_code)]
@@ -79,7 +81,7 @@ macro_rules! get_state {
#[derive(Clone)]
pub(crate) struct WireguardAppStateInner {
dh_keypair: Arc<encryption::KeyPair>,
private_key: Arc<PrivateKey>,
client_registry: Arc<GatewayClientRegistry>,
registration_in_progress: Arc<PendingRegistrations>,
binding_port: u16,
@@ -112,6 +114,7 @@ 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;
@@ -130,17 +133,21 @@ 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::new(&mut rng);
let gateway_key_pair = encryption::KeyPair::from_bytes(
&gateway_private_key.to_bytes(),
&gateway_public_key.to_bytes(),
)
.unwrap();
let client_key_pair = encryption::KeyPair::new(&mut rng);
let gateway_static_public =
PublicKey::try_from(gateway_key_pair.public_key().to_bytes()).unwrap();
let gateway_static_public = PublicKey::from(gateway_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_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_dh = client_static_private.diffie_hellman(&gateway_static_public);
@@ -158,7 +165,7 @@ mod test {
let state = WireguardAppState {
inner: Some(WireguardAppStateInner {
client_registry: Arc::clone(&client_registry),
dh_keypair: Arc::new(gateway_key_pair),
private_key: Arc::new(gateway_private_key),
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().try_into().unwrap(),
tag.as_slice().into(),
)
.map_err(|e| OutfoxError::ChaCha20Poly1305Error(e.to_string()))?;
-12
View File
@@ -80,18 +80,6 @@ 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>Tauri + React + TS</title>
<title>NymVPN</title>
</head>
<body>
<div id="root" class="bg-gray-900"></div>
<body class="h-screen">
<div id="root" class="h-full bg-gray-900 text-gray-400"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
+10 -6
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 build && cd src-tauri && cargo build --release --lib --features custom-protocol",
"build:app": "yarn tauri build",
"preview": "vite preview",
"lint": "eslint --ext .ts,.tsx src/",
"lint:fix": "eslint --ext .js,.ts --fix src/",
@@ -18,17 +18,21 @@
"tauri": "tauri"
},
"dependencies": {
"@mui/base": "^5.0.0-beta.20",
"@mui/base": "^5.0.0-beta.24",
"@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-dom": "^18.2.0",
"react-i18next": "^13.5.0",
"react-router-dom": "^6.18.0"
},
"devDependencies": {
"@tauri-apps/cli": "^1.5.0",
"@types/react": "^18.2.31",
"@types/react-dom": "^18.2.14",
"@types/react": "^18.2.37",
"@types/react-dom": "^18.2.15",
"@types/react-router-dom": "^5.3.3",
"@typescript-eslint/eslint-plugin": "^6.8.0",
"@typescript-eslint/parser": "^6.8.0",
"@vitejs/plugin-react-swc": "^3.3.2",
@@ -42,6 +46,6 @@
"prettier": "^3.0.3",
"tailwindcss": "^3.3.3",
"typescript": "^5.0.2",
"vite": "^4.4.5"
"vite": "^5.0.0"
}
}
+504 -1
View File
@@ -206,6 +206,9 @@ name = "bytes"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
dependencies = [
"serde",
]
[[package]]
name = "cairo-rs"
@@ -615,7 +618,7 @@ dependencies = [
"rustc_version",
"toml 0.8.5",
"vswhom",
"winreg",
"winreg 0.51.0",
]
[[package]]
@@ -779,6 +782,12 @@ 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"
@@ -792,8 +801,11 @@ 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",
@@ -1105,6 +1117,25 @@ 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"
@@ -1183,12 +1214,72 @@ 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"
@@ -1308,6 +1399,12 @@ 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"
@@ -1540,6 +1637,18 @@ 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"
@@ -1550,6 +1659,35 @@ 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"
@@ -1690,6 +1828,17 @@ 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"
@@ -1733,6 +1882,50 @@ 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"
@@ -2206,6 +2399,70 @@ 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"
@@ -2261,6 +2518,15 @@ 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"
@@ -2273,6 +2539,29 @@ 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"
@@ -2353,6 +2642,18 @@ 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"
@@ -2461,6 +2762,26 @@ 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"
@@ -2558,6 +2879,27 @@ 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"
@@ -2666,6 +3008,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9bfe673cf125ef364d6f56b15e8ce7537d9ca7e4dae1cf6fbbdeed2e024db3d9"
dependencies = [
"anyhow",
"base64 0.21.5",
"bytes",
"cocoa",
"dirs-next",
"embed_plist",
@@ -2678,6 +3022,7 @@ dependencies = [
"heck 0.4.1",
"http",
"ignore",
"minisign-verify",
"objc",
"once_cell",
"open",
@@ -2685,6 +3030,8 @@ dependencies = [
"rand 0.8.5",
"raw-window-handle",
"regex",
"reqwest",
"rfd",
"semver",
"serde",
"serde_json",
@@ -2698,12 +3045,14 @@ dependencies = [
"tauri-utils",
"tempfile",
"thiserror",
"time",
"tokio",
"url",
"uuid",
"webkit2gtk",
"webview2-com",
"windows 0.39.0",
"zip",
]
[[package]]
@@ -2967,8 +3316,36 @@ 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]]
@@ -3039,6 +3416,12 @@ 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"
@@ -3109,6 +3492,12 @@ 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"
@@ -3199,6 +3588,12 @@ 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"
@@ -3247,6 +3642,15 @@ 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"
@@ -3284,6 +3688,18 @@ 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"
@@ -3313,6 +3729,29 @@ 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"
@@ -3429,6 +3868,19 @@ 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"
@@ -3544,6 +3996,12 @@ 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"
@@ -3562,6 +4020,12 @@ 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"
@@ -3580,6 +4044,12 @@ 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"
@@ -3598,6 +4068,12 @@ 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"
@@ -3628,6 +4104,12 @@ 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"
@@ -3655,6 +4137,16 @@ 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"
@@ -3732,3 +4224,14 @@ 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",
]
+1 -4
View File
@@ -7,14 +7,11 @@ license = ""
repository = ""
edition = "2021"
[lib]
crate-type = ["cdylib"]
[build-dependencies]
tauri-build = { version = "1.5", features = [] }
[dependencies]
tauri = { version = "1.5.2", features = ["shell-open"] }
tauri = { version = "1.5.2", features = [ "updater", "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::State;
use tauri::{Manager, State};
use tokio::time::sleep;
use tracing::{debug, instrument, trace};
@@ -9,7 +9,14 @@ use crate::{
states::{app::ConnectionState, SharedAppState},
};
#[instrument]
const EVENT_CONNECTION: &str = "connection-state";
#[derive(Clone, serde::Serialize)]
struct EventPayload {
state: ConnectionState,
}
#[instrument(skip_all)]
#[tauri::command]
pub async fn get_connection_state(
state: State<'_, SharedAppState>,
@@ -19,8 +26,12 @@ pub async fn get_connection_state(
Ok(app_state.state)
}
#[instrument(skip_all)]
#[tauri::command]
pub async fn connect(state: State<'_, SharedAppState>) -> Result<ConnectionState, CommandError> {
pub async fn connect(
app: tauri::AppHandle,
state: State<'_, SharedAppState>,
) -> Result<ConnectionState, CommandError> {
debug!("connect");
let mut app_state = state.lock().await;
let ConnectionState::Disconnected = app_state.state else {
@@ -30,23 +41,46 @@ pub async fn connect(state: State<'_, SharedAppState>) -> Result<ConnectionState
)));
};
// 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;
app_state.state = ConnectionState::Connecting;
let app_state = state.lock().await;
Ok(app_state.state)
}
#[instrument]
#[instrument(skip_all)]
#[tauri::command]
pub async fn disconnect(state: State<'_, SharedAppState>) -> Result<ConnectionState, CommandError> {
pub async fn disconnect(
app: tauri::AppHandle,
state: State<'_, SharedAppState>,
) -> Result<ConnectionState, CommandError> {
debug!("disconnect");
let mut app_state = state.lock().await;
let ConnectionState::Connected = app_state.state else {
@@ -56,6 +90,36 @@ pub async fn disconnect(state: State<'_, SharedAppState>) -> Result<ConnectionSt
)));
};
// 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
@@ -1,16 +0,0 @@
// 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()?;
dotenvy::dotenv().ok();
// 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": 200,
"height": 200
"width": 400,
"height": 600
}
]
}
+23 -46
View File
@@ -1,54 +1,31 @@
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';
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
import { Home, Settings, Error, PageLayout } from './pages';
import { MainStateProvider } from './state';
import './i18n/config';
// 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}
/>
);
const router = createBrowserRouter([
{
element: <PageLayout />,
children: [
{
path: '/',
element: <Home />,
errorElement: <Error />,
},
{
path: '/settings',
element: <Settings />,
errorElement: <Error />,
},
],
},
);
]);
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 (
<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>
<MainStateProvider>
<RouterProvider router={router} />
</MainStateProvider>
);
}
+1
View File
@@ -0,0 +1 @@
export const ConnectionEvent = 'connection-state';
+1
View File
@@ -0,0 +1 @@
export * from './main';
+15
View File
@@ -0,0 +1,15 @@
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);
};
+33 -8
View File
@@ -1,11 +1,36 @@
import { mockIPC, mockWindows } from '@tauri-apps/api/mocks';
import { greet } from './tauri-cmd-mocks';
import { ConnectionState } from '../types';
import { emit } from '@tauri-apps/api/event';
import { ConnectionEvent } from '../constants';
mockWindows('main');
export function mockTauriIPC() {
mockWindows('main');
mockIPC(async (cmd, args) => {
console.log(`IPC call mocked "${cmd}"`);
if (cmd === 'greet') {
return greet(args.name as string);
}
});
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),
);
}
});
}
@@ -1,3 +0,0 @@
export default function greet(name: string): string {
return `Hello, ${name}!`;
}
@@ -1 +0,0 @@
export { default as greet } from './greet';
+22
View File
@@ -0,0 +1,22 @@
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
@@ -0,0 +1,4 @@
{
"connect": "Connect",
"disconnect": "Disconnect"
}
+2 -1
View File
@@ -1,11 +1,12 @@
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');
import('./dev/setup');
mockTauriIPC();
}
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
+21
View File
@@ -0,0 +1,21 @@
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
@@ -0,0 +1,41 @@
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
@@ -0,0 +1,4 @@
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
@@ -0,0 +1,11 @@
import { Outlet } from 'react-router-dom';
function PageLayout() {
return (
<div>
<Outlet />
</div>
);
}
export default PageLayout;
+5
View File
@@ -0,0 +1,5 @@
function Settings() {
return <div>Settings</div>;
}
export default Settings;
+2
View File
@@ -0,0 +1,2 @@
export * from './main';
export * from './provider';
+35
View File
@@ -0,0 +1,35 @@
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
@@ -0,0 +1,35 @@
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
@@ -0,0 +1,39 @@
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
@@ -0,0 +1,29 @@
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
@@ -0,0 +1 @@
export * from './app-state';
+843 -697
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