Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 294903bede | |||
| 105cb67f06 | |||
| d4ee0708ad | |||
| e4b52b4a32 | |||
| 5177e55b22 | |||
| 3b8cff8b32 | |||
| 177cb22a3f | |||
| 809fabfc2b | |||
| 8ee31d94ea | |||
| 818d7ee13e | |||
| 81c692d305 | |||
| 1728de57b9 | |||
| 53fcebfd86 | |||
| 4a5ceddeac | |||
| a5c1e4abf0 | |||
| 3a1003c564 | |||
| 1cdd8f6c08 | |||
| f47e1793a2 | |||
| 8e3f4ce08d | |||
| 9b4262bb36 | |||
| 6d37d7df8e | |||
| 427f205a58 | |||
| 9549d3b681 | |||
| 1be81b96b3 |
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -45,4 +45,6 @@ envs/qwerty.env
|
||||
cpu-cycles/libcpucycles/build
|
||||
foxyfox.env
|
||||
|
||||
.next
|
||||
.next
|
||||
ppa-private-key.b64
|
||||
ppa-private-key.asc
|
||||
@@ -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
@@ -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",
|
||||
]
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
+2
-1
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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(),
|
||||
})
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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"] }
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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"]
|
||||
@@ -0,0 +1,7 @@
|
||||
#[cfg(target_os = "linux")]
|
||||
mod linux;
|
||||
|
||||
pub mod tun_task_channel;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub use linux::tun_device;
|
||||
+118
-89
@@ -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),
|
||||
}
|
||||
}
|
||||
@@ -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),
|
||||
)
|
||||
}
|
||||
@@ -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"]
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
+33
-15
@@ -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;
|
||||
+1
-1
@@ -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(),
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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,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>)>);
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
#[cfg(target_os = "linux")]
|
||||
pub(crate) mod linux;
|
||||
@@ -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)
|
||||
|
||||
@@ -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),
|
||||
)
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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}}
|
||||
```
|
||||
|
||||
@@ -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
@@ -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"]
|
||||
|
||||
@@ -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
@@ -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!");
|
||||
|
||||
@@ -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>
|
||||
```
|
||||
@@ -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
@@ -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" }
|
||||
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
|
||||
@@ -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!()))
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -31,6 +31,12 @@ pub enum NymNodeError {
|
||||
source: WireguardError,
|
||||
},
|
||||
|
||||
#[error(transparent)]
|
||||
KeyRecoveryError {
|
||||
#[from]
|
||||
source: nym_crypto::asymmetric::encryption::KeyRecoveryError,
|
||||
},
|
||||
|
||||
#[error("unimplemented")]
|
||||
Unimplemented,
|
||||
}
|
||||
|
||||
+2
-5
@@ -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(®istration_in_progress),
|
||||
binding_port: 8080,
|
||||
free_private_network_ips,
|
||||
|
||||
@@ -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()))?;
|
||||
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
@@ -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
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+504
-1
@@ -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",
|
||||
]
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -46,8 +46,8 @@
|
||||
"fullscreen": false,
|
||||
"resizable": true,
|
||||
"title": "NymVPN",
|
||||
"width": 200,
|
||||
"height": 200
|
||||
"width": 400,
|
||||
"height": 600
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
+23
-46
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export const ConnectionEvent = 'connection-state';
|
||||
@@ -0,0 +1 @@
|
||||
export * from './main';
|
||||
@@ -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);
|
||||
};
|
||||
@@ -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';
|
||||
@@ -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;
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"connect": "Connect",
|
||||
"disconnect": "Disconnect"
|
||||
}
|
||||
@@ -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(
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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';
|
||||
@@ -0,0 +1,11 @@
|
||||
import { Outlet } from 'react-router-dom';
|
||||
|
||||
function PageLayout() {
|
||||
return (
|
||||
<div>
|
||||
<Outlet />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default PageLayout;
|
||||
@@ -0,0 +1,5 @@
|
||||
function Settings() {
|
||||
return <div>Settings</div>;
|
||||
}
|
||||
|
||||
export default Settings;
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './main';
|
||||
export * from './provider';
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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]);
|
||||
}
|
||||
@@ -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>;
|
||||
@@ -0,0 +1 @@
|
||||
export * from './app-state';
|
||||
+843
-697
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
Reference in New Issue
Block a user