Compare commits
26 Commits
simon/quic
...
wireguard
| Author | SHA1 | Date | |
|---|---|---|---|
| 8f0c427734 | |||
| 7dfc396f4f | |||
| 2bf44db72f | |||
| ebfecba933 | |||
| 0134030341 | |||
| 97c775bc68 | |||
| bbce67902b | |||
| e6930046c4 | |||
| 0c9402503a | |||
| 81e133b789 | |||
| 31bc439f65 | |||
| 6479480cf7 | |||
| 4af70ef255 | |||
| eba58f6451 | |||
| 35206655e0 | |||
| e14db00fc2 | |||
| 72b92784cc | |||
| 38b95c2673 | |||
| f0f9899f82 | |||
| 09c46e3403 | |||
| 8f57919571 | |||
| 3cdca0ad8d | |||
| 4c13d91bfb | |||
| 8355e6ce5e | |||
| bbb1e5e15a | |||
| 6d30e7ea8e |
@@ -68,8 +68,10 @@ jobs:
|
|||||||
working-directory: nym-connect/native/android
|
working-directory: nym-connect/native/android
|
||||||
env:
|
env:
|
||||||
ANDROID_SDK_ROOT: ${{ env.ANDROID_HOME }}
|
ANDROID_SDK_ROOT: ${{ env.ANDROID_HOME }}
|
||||||
|
SENTRY_AUTH_TOKEN: ${{ secrets.NYMS5_ANDROID_SENTRY_AUTH_TOKEN }}
|
||||||
# build for arm64 and x86_64
|
# build for arm64 and x86_64
|
||||||
run: |
|
run: |
|
||||||
|
echo "auth.token=$SENTRY_AUTH_TOKEN" | tee -a sentry.properties
|
||||||
./gradlew :app:assembleArch64Debug
|
./gradlew :app:assembleArch64Debug
|
||||||
./gradlew :app:assembleArch64Release
|
./gradlew :app:assembleArch64Release
|
||||||
|
|
||||||
@@ -107,4 +109,3 @@ jobs:
|
|||||||
files: |
|
files: |
|
||||||
apk/nyms5-arch64-debug.apk
|
apk/nyms5-arch64-debug.apk
|
||||||
apk/nyms5-arch64-release.apk
|
apk/nyms5-arch64-release.apk
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -43,4 +43,5 @@ envs/qwerty.env
|
|||||||
.parcel-cache
|
.parcel-cache
|
||||||
**/.DS_Store
|
**/.DS_Store
|
||||||
cpu-cycles/libcpucycles/build
|
cpu-cycles/libcpucycles/build
|
||||||
foxyfox.env
|
foxyfox.env
|
||||||
|
gateway/deploy.sh
|
||||||
Generated
+1074
-255
File diff suppressed because it is too large
Load Diff
Generated
+17
-3
@@ -2240,7 +2240,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nym-bin-common"
|
name = "nym-bin-common"
|
||||||
version = "0.5.0"
|
version = "0.6.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atty",
|
"atty",
|
||||||
"clap",
|
"clap",
|
||||||
@@ -2255,7 +2255,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nym-client-core"
|
name = "nym-client-core"
|
||||||
version = "1.1.14"
|
version = "1.1.15"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"base64 0.21.2",
|
"base64 0.21.2",
|
||||||
@@ -2300,7 +2300,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nym-client-wasm"
|
name = "nym-client-wasm"
|
||||||
version = "1.1.0"
|
version = "1.1.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@@ -2697,6 +2697,7 @@ dependencies = [
|
|||||||
"nym-pemstore",
|
"nym-pemstore",
|
||||||
"nym-sphinx-addressing",
|
"nym-sphinx-addressing",
|
||||||
"nym-sphinx-params",
|
"nym-sphinx-params",
|
||||||
|
"nym-sphinx-routing",
|
||||||
"nym-sphinx-types",
|
"nym-sphinx-types",
|
||||||
"nym-topology",
|
"nym-topology",
|
||||||
"rand 0.7.3",
|
"rand 0.7.3",
|
||||||
@@ -2723,6 +2724,7 @@ dependencies = [
|
|||||||
"nym-crypto",
|
"nym-crypto",
|
||||||
"nym-sphinx-addressing",
|
"nym-sphinx-addressing",
|
||||||
"nym-sphinx-params",
|
"nym-sphinx-params",
|
||||||
|
"nym-sphinx-routing",
|
||||||
"nym-sphinx-types",
|
"nym-sphinx-types",
|
||||||
"nym-topology",
|
"nym-topology",
|
||||||
"rand 0.7.3",
|
"rand 0.7.3",
|
||||||
@@ -2753,6 +2755,7 @@ dependencies = [
|
|||||||
"nym-sphinx-chunking",
|
"nym-sphinx-chunking",
|
||||||
"nym-sphinx-forwarding",
|
"nym-sphinx-forwarding",
|
||||||
"nym-sphinx-params",
|
"nym-sphinx-params",
|
||||||
|
"nym-sphinx-routing",
|
||||||
"nym-sphinx-types",
|
"nym-sphinx-types",
|
||||||
"nym-topology",
|
"nym-topology",
|
||||||
"rand 0.7.3",
|
"rand 0.7.3",
|
||||||
@@ -2884,6 +2887,7 @@ dependencies = [
|
|||||||
"nym-service-provider-directory-common",
|
"nym-service-provider-directory-common",
|
||||||
"nym-vesting-contract",
|
"nym-vesting-contract",
|
||||||
"nym-vesting-contract-common",
|
"nym-vesting-contract-common",
|
||||||
|
"openssl",
|
||||||
"prost",
|
"prost",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"serde",
|
"serde",
|
||||||
@@ -2974,6 +2978,15 @@ version = "0.1.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "openssl-src"
|
||||||
|
version = "111.26.0+1.1.1u"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "efc62c9f12b22b8f5208c23a7200a442b2e5999f8bdf80233852122b5a4f6f37"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "openssl-sys"
|
name = "openssl-sys"
|
||||||
version = "0.9.87"
|
version = "0.9.87"
|
||||||
@@ -2982,6 +2995,7 @@ checksum = "8e17f59264b2809d77ae94f0e1ebabc434773f370d6ca667bd223ea10e06cc7e"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
"libc",
|
"libc",
|
||||||
|
"openssl-src",
|
||||||
"pkg-config",
|
"pkg-config",
|
||||||
"vcpkg",
|
"vcpkg",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -116,11 +116,11 @@ async function testWithTester() {
|
|||||||
// B) first get topology directly from nym-api
|
// B) first get topology directly from nym-api
|
||||||
// const validator = 'https://qwerty-validator-api.qa.nymte.ch/api';
|
// const validator = 'https://qwerty-validator-api.qa.nymte.ch/api';
|
||||||
// const topology = await current_network_topology(validator)
|
// const topology = await current_network_topology(validator)
|
||||||
// const nodeTester = await new NymNodeTester(topology, preferredGateway);
|
// const nodeTester = await new NymNodeTester(topology, undefined, preferredGateway);
|
||||||
//
|
//
|
||||||
// C) use nym-api in the constructor (note: it does no filtering for 'good' nodes on other layers)
|
// C) use nym-api in the constructor (note: it does no filtering for 'good' nodes on other layers)
|
||||||
// const validator = 'https://qwerty-validator-api.qa.nymte.ch/api';
|
// const validator = 'https://qwerty-validator-api.qa.nymte.ch/api';
|
||||||
// const nodeTester = await NymNodeTester.new_with_api(validator, preferredGateway)
|
// const nodeTester = await NymNodeTester.new_with_api(validator, undefined, preferredGateway)
|
||||||
|
|
||||||
// D, E, F) you also don't have to specify the gateway. if you don't, a random one (from your topology) will be used
|
// D, E, F) you also don't have to specify the gateway. if you don't, a random one (from your topology) will be used
|
||||||
// const topology = dummyTopology()
|
// const topology = dummyTopology()
|
||||||
@@ -139,6 +139,40 @@ async function testWithTester() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function testerReconnection() {
|
||||||
|
const validator = 'https://qwerty-validator-api.qa.nymte.ch/api';
|
||||||
|
const nodeTester = await NymNodeTester.new_with_api(validator);
|
||||||
|
|
||||||
|
self.onmessage = async event => {
|
||||||
|
if (event.data && event.data.kind) {
|
||||||
|
switch (event.data.kind) {
|
||||||
|
case 'TestPacket': {
|
||||||
|
const {mixnodeIdentity} = event.data.args;
|
||||||
|
console.log("starting node test...");
|
||||||
|
|
||||||
|
let result1 = await nodeTester.test_node(mixnodeIdentity);
|
||||||
|
console.log("sleeping for 5s");
|
||||||
|
await new Promise(r => setTimeout(r, 5000));
|
||||||
|
await nodeTester.disconnect_from_gateway();
|
||||||
|
|
||||||
|
console.log("sleeping for 5s");
|
||||||
|
await new Promise(r => setTimeout(r, 5000));
|
||||||
|
|
||||||
|
await nodeTester.reconnect_to_gateway();
|
||||||
|
let result2 = await nodeTester.test_node(mixnodeIdentity);
|
||||||
|
|
||||||
|
printAndDisplayTestResult(result1)
|
||||||
|
printAndDisplayTestResult(result2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function testWithNymClient() {
|
async function testWithNymClient() {
|
||||||
@@ -330,14 +364,17 @@ async function main() {
|
|||||||
// sets up better stack traces in case of in-rust panics
|
// sets up better stack traces in case of in-rust panics
|
||||||
set_panic_hook();
|
set_panic_hook();
|
||||||
|
|
||||||
|
// show reconnection capabilities
|
||||||
|
// await testerReconnection()
|
||||||
|
|
||||||
// run test on simplified and dedicated tester:
|
// run test on simplified and dedicated tester:
|
||||||
// await testWithTester()
|
await testWithTester()
|
||||||
|
|
||||||
// hook-up the whole client for testing
|
// hook-up the whole client for testing
|
||||||
// await testWithNymClient()
|
// await testWithNymClient()
|
||||||
|
|
||||||
// 'Normal' client setup (to send 'normal' messages)
|
// 'Normal' client setup (to send 'normal' messages)
|
||||||
await normalNymClientUsage()
|
// await normalNymClientUsage()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Let's get started!
|
// Let's get started!
|
||||||
|
|||||||
@@ -4,6 +4,9 @@
|
|||||||
// due to expansion of #[wasm_bindgen] macro on NodeTestResult
|
// due to expansion of #[wasm_bindgen] macro on NodeTestResult
|
||||||
#![allow(clippy::drop_non_drop)]
|
#![allow(clippy::drop_non_drop)]
|
||||||
|
|
||||||
|
use crate::error::WasmClientError;
|
||||||
|
use crate::tester::LockedGatewayClient;
|
||||||
|
use js_sys::Promise;
|
||||||
use nym_node_tester_utils::processor::Received;
|
use nym_node_tester_utils::processor::Received;
|
||||||
use nym_node_tester_utils::receiver::ReceivedReceiver;
|
use nym_node_tester_utils::receiver::ReceivedReceiver;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@@ -12,6 +15,7 @@ use std::sync::atomic::{AtomicBool, Ordering};
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tokio::sync::{Mutex as AsyncMutex, MutexGuard as AsyncMutexGuard};
|
use tokio::sync::{Mutex as AsyncMutex, MutexGuard as AsyncMutexGuard};
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
use wasm_bindgen_futures::future_to_promise;
|
||||||
use wasm_utils::{console_log, console_warn};
|
use wasm_utils::{console_log, console_warn};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@@ -107,3 +111,37 @@ impl Drop for TestMarker {
|
|||||||
self.value.store(false, Ordering::SeqCst)
|
self.value.store(false, Ordering::SeqCst)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) trait GatewayReconnection {
|
||||||
|
fn disconnect_from_gateway(&self) -> Promise;
|
||||||
|
|
||||||
|
fn reconnect_to_gateway(&self) -> Promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GatewayReconnection for LockedGatewayClient {
|
||||||
|
fn disconnect_from_gateway(&self) -> Promise {
|
||||||
|
let this = self.clone();
|
||||||
|
|
||||||
|
future_to_promise(async move {
|
||||||
|
let mut guard = this.lock().await;
|
||||||
|
guard
|
||||||
|
.disconnect()
|
||||||
|
.await
|
||||||
|
.map_err(|err| JsValue::from(WasmClientError::from(err)))?;
|
||||||
|
Ok(JsValue::undefined())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reconnect_to_gateway(&self) -> Promise {
|
||||||
|
let this = self.clone();
|
||||||
|
|
||||||
|
future_to_promise(async move {
|
||||||
|
let mut guard = this.lock().await;
|
||||||
|
guard
|
||||||
|
.try_reconnect()
|
||||||
|
.await
|
||||||
|
.map_err(|err| JsValue::from(WasmClientError::from(err)))?;
|
||||||
|
Ok(JsValue::undefined())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use crate::helpers::{current_network_topology_async, setup_from_topology};
|
|||||||
use crate::storage::ClientStorage;
|
use crate::storage::ClientStorage;
|
||||||
use crate::tester::ephemeral_receiver::EphemeralTestReceiver;
|
use crate::tester::ephemeral_receiver::EphemeralTestReceiver;
|
||||||
use crate::tester::helpers::{
|
use crate::tester::helpers::{
|
||||||
NodeTestResult, ReceivedReceiverWrapper, TestMarker, WasmTestMessageExt,
|
GatewayReconnection, NodeTestResult, ReceivedReceiverWrapper, TestMarker, WasmTestMessageExt,
|
||||||
};
|
};
|
||||||
use crate::topology::WasmNymTopology;
|
use crate::topology::WasmNymTopology;
|
||||||
use futures::channel::mpsc;
|
use futures::channel::mpsc;
|
||||||
@@ -73,6 +73,7 @@ pub struct NymNodeTester {
|
|||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
pub struct NymNodeTesterBuilder {
|
pub struct NymNodeTesterBuilder {
|
||||||
gateway: Option<IdentityKey>,
|
gateway: Option<IdentityKey>,
|
||||||
|
id: Option<String>,
|
||||||
|
|
||||||
base_topology: NymTopology,
|
base_topology: NymTopology,
|
||||||
|
|
||||||
@@ -94,9 +95,11 @@ impl NymNodeTesterBuilder {
|
|||||||
#[wasm_bindgen(constructor)]
|
#[wasm_bindgen(constructor)]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
base_topology: WasmNymTopology,
|
base_topology: WasmNymTopology,
|
||||||
|
id: Option<String>,
|
||||||
gateway: Option<IdentityKey>,
|
gateway: Option<IdentityKey>,
|
||||||
) -> NymNodeTesterBuilder {
|
) -> NymNodeTesterBuilder {
|
||||||
NymNodeTesterBuilder {
|
NymNodeTesterBuilder {
|
||||||
|
id,
|
||||||
gateway,
|
gateway,
|
||||||
base_topology: base_topology.into(),
|
base_topology: base_topology.into(),
|
||||||
bandwidth_controller: None,
|
bandwidth_controller: None,
|
||||||
@@ -105,15 +108,20 @@ impl NymNodeTesterBuilder {
|
|||||||
|
|
||||||
async fn _new_with_api(
|
async fn _new_with_api(
|
||||||
api_url: String,
|
api_url: String,
|
||||||
|
id: Option<String>,
|
||||||
gateway: Option<IdentityKey>,
|
gateway: Option<IdentityKey>,
|
||||||
) -> Result<Self, WasmClientError> {
|
) -> Result<Self, WasmClientError> {
|
||||||
let topology = current_network_topology_async(api_url).await?;
|
let topology = current_network_topology_async(api_url).await?;
|
||||||
Ok(NymNodeTesterBuilder::new(topology, gateway))
|
Ok(NymNodeTesterBuilder::new(topology, id, gateway))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_with_api(gateway: Option<IdentityKey>, api_url: String) -> Promise {
|
pub fn new_with_api(
|
||||||
|
api_url: String,
|
||||||
|
id: Option<String>,
|
||||||
|
gateway: Option<IdentityKey>,
|
||||||
|
) -> Promise {
|
||||||
future_to_promise(async move {
|
future_to_promise(async move {
|
||||||
Self::_new_with_api(api_url, gateway)
|
Self::_new_with_api(api_url, id, gateway)
|
||||||
.await
|
.await
|
||||||
.into_promise_result()
|
.into_promise_result()
|
||||||
})
|
})
|
||||||
@@ -133,7 +141,13 @@ impl NymNodeTesterBuilder {
|
|||||||
async fn _setup_client(mut self) -> Result<NymNodeTester, WasmClientError> {
|
async fn _setup_client(mut self) -> Result<NymNodeTester, WasmClientError> {
|
||||||
let task_manager = TaskManager::default();
|
let task_manager = TaskManager::default();
|
||||||
|
|
||||||
let client_store = ClientStorage::new_async(NODE_TESTER_ID, None).await?;
|
let storage_id = if let Some(client_id) = &self.id {
|
||||||
|
format!("{NODE_TESTER_ID}-{client_id}")
|
||||||
|
} else {
|
||||||
|
NODE_TESTER_ID.to_owned()
|
||||||
|
};
|
||||||
|
|
||||||
|
let client_store = ClientStorage::new_async(&storage_id, None).await?;
|
||||||
|
|
||||||
let init_details = self.gateway_info(&client_store).await?;
|
let init_details = self.gateway_info(&client_store).await?;
|
||||||
let gateway_endpoint = init_details.gateway_details;
|
let gateway_endpoint = init_details.gateway_details;
|
||||||
@@ -234,29 +248,46 @@ async fn test_mixnode(
|
|||||||
impl NymNodeTester {
|
impl NymNodeTester {
|
||||||
#[wasm_bindgen(constructor)]
|
#[wasm_bindgen(constructor)]
|
||||||
#[allow(clippy::new_ret_no_self)]
|
#[allow(clippy::new_ret_no_self)]
|
||||||
pub fn new(topology: WasmNymTopology, gateway: Option<IdentityKey>) -> Promise {
|
pub fn new(
|
||||||
|
topology: WasmNymTopology,
|
||||||
|
id: Option<String>,
|
||||||
|
gateway: Option<IdentityKey>,
|
||||||
|
) -> Promise {
|
||||||
console_log!("constructing node tester!");
|
console_log!("constructing node tester!");
|
||||||
NymNodeTesterBuilder::new(topology, gateway).setup_client()
|
NymNodeTesterBuilder::new(topology, id, gateway).setup_client()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn _new_with_api(
|
async fn _new_with_api(
|
||||||
api_url: String,
|
api_url: String,
|
||||||
|
id: Option<String>,
|
||||||
gateway: Option<IdentityKey>,
|
gateway: Option<IdentityKey>,
|
||||||
) -> Result<Self, WasmClientError> {
|
) -> Result<Self, WasmClientError> {
|
||||||
NymNodeTesterBuilder::_new_with_api(api_url, gateway)
|
NymNodeTesterBuilder::_new_with_api(api_url, id, gateway)
|
||||||
.await?
|
.await?
|
||||||
._setup_client()
|
._setup_client()
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_with_api(api_url: String, gateway: Option<IdentityKey>) -> Promise {
|
pub fn new_with_api(
|
||||||
|
api_url: String,
|
||||||
|
id: Option<String>,
|
||||||
|
gateway: Option<IdentityKey>,
|
||||||
|
) -> Promise {
|
||||||
future_to_promise(async move {
|
future_to_promise(async move {
|
||||||
Self::_new_with_api(api_url, gateway)
|
Self::_new_with_api(api_url, id, gateway)
|
||||||
.await
|
.await
|
||||||
.into_promise_result()
|
.into_promise_result()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn disconnect_from_gateway(&self) -> Promise {
|
||||||
|
self.gateway_client.disconnect_from_gateway()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reconnect_to_gateway(&self) -> Promise {
|
||||||
|
self.gateway_client.reconnect_to_gateway()
|
||||||
|
}
|
||||||
|
|
||||||
fn prepare_test_packets(
|
fn prepare_test_packets(
|
||||||
&self,
|
&self,
|
||||||
mixnode_identity: String,
|
mixnode_identity: String,
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ use log::*;
|
|||||||
use nym_sphinx::acknowledgements::AckKey;
|
use nym_sphinx::acknowledgements::AckKey;
|
||||||
use nym_sphinx::addressing::clients::Recipient;
|
use nym_sphinx::addressing::clients::Recipient;
|
||||||
use nym_sphinx::cover::generate_loop_cover_packet;
|
use nym_sphinx::cover::generate_loop_cover_packet;
|
||||||
use nym_sphinx::params::PacketSize;
|
use nym_sphinx::params::{PacketSize, PacketType};
|
||||||
use nym_sphinx::utils::sample_poisson_duration;
|
use nym_sphinx::utils::sample_poisson_duration;
|
||||||
use rand::{rngs::OsRng, CryptoRng, Rng};
|
use rand::{rngs::OsRng, CryptoRng, Rng};
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
@@ -63,6 +63,8 @@ where
|
|||||||
|
|
||||||
/// Optional secondary predefined packet size used for the loop cover messages.
|
/// Optional secondary predefined packet size used for the loop cover messages.
|
||||||
secondary_packet_size: Option<PacketSize>,
|
secondary_packet_size: Option<PacketSize>,
|
||||||
|
|
||||||
|
packet_type: PacketType,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R> Stream for LoopCoverTrafficStream<R>
|
impl<R> Stream for LoopCoverTrafficStream<R>
|
||||||
@@ -135,6 +137,7 @@ impl LoopCoverTrafficStream<OsRng> {
|
|||||||
topology_access,
|
topology_access,
|
||||||
primary_packet_size: traffic_config.primary_packet_size,
|
primary_packet_size: traffic_config.primary_packet_size,
|
||||||
secondary_packet_size: traffic_config.secondary_packet_size,
|
secondary_packet_size: traffic_config.secondary_packet_size,
|
||||||
|
packet_type: traffic_config.packet_type,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,7 +197,7 @@ impl LoopCoverTrafficStream<OsRng> {
|
|||||||
self.average_ack_delay,
|
self.average_ack_delay,
|
||||||
self.cover_traffic.loop_cover_traffic_average_delay,
|
self.cover_traffic.loop_cover_traffic_average_delay,
|
||||||
cover_traffic_packet_size,
|
cover_traffic_packet_size,
|
||||||
nym_sphinx::params::PacketType::Mix,
|
self.packet_type,
|
||||||
)
|
)
|
||||||
.expect("Somehow failed to generate a loop cover message with a valid topology");
|
.expect("Somehow failed to generate a loop cover message with a valid topology");
|
||||||
|
|
||||||
|
|||||||
@@ -724,7 +724,7 @@ impl<C, St> GatewayClient<C, St> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn try_reconnect(&mut self) -> Result<(), GatewayClientError> {
|
pub async fn try_reconnect(&mut self) -> Result<(), GatewayClientError> {
|
||||||
if !self.connection.is_established() {
|
if !self.connection.is_established() {
|
||||||
self.establish_connection().await?;
|
self.establish_connection().await?;
|
||||||
}
|
}
|
||||||
@@ -738,6 +738,12 @@ impl<C, St> GatewayClient<C, St> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn disconnect(&mut self) -> Result<(), GatewayClientError> {
|
||||||
|
self.recover_socket_connection().await?;
|
||||||
|
self.connection = SocketState::NotConnected;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn authenticate_and_start(&mut self) -> Result<Arc<SharedKeys>, GatewayClientError>
|
pub async fn authenticate_and_start(&mut self) -> Result<Arc<SharedKeys>, GatewayClientError>
|
||||||
where
|
where
|
||||||
C: DkgQueryClient + Send + Sync,
|
C: DkgQueryClient + Send + Sync,
|
||||||
|
|||||||
@@ -48,17 +48,21 @@ impl PacketRouter {
|
|||||||
// data he takes the SURB-ACK and first hop address.
|
// data he takes the SURB-ACK and first hop address.
|
||||||
// currently SURB-ACKs are attached in EVERY packet, even cover, so this is always true
|
// currently SURB-ACKs are attached in EVERY packet, even cover, so this is always true
|
||||||
let ack_overhead = PacketSize::AckPacket.size() + MAX_NODE_ADDRESS_UNPADDED_LEN;
|
let ack_overhead = PacketSize::AckPacket.size() + MAX_NODE_ADDRESS_UNPADDED_LEN;
|
||||||
|
let outfox_ack_overhead =
|
||||||
|
PacketSize::OutfoxAckPacket.size() + MAX_NODE_ADDRESS_UNPADDED_LEN;
|
||||||
|
|
||||||
for received_packet in unwrapped_packets {
|
for received_packet in unwrapped_packets {
|
||||||
if received_packet.len() == PacketSize::AckPacket.plaintext_size()
|
if received_packet.len() == PacketSize::AckPacket.plaintext_size()
|
||||||
|| received_packet.len() == PacketSize::OutfoxAckPacket.plaintext_size()
|
// we don't know the real size of the payload, it could be anything <= 48 bytes
|
||||||
|
|| received_packet.len() <= PacketSize::OutfoxAckPacket.plaintext_size()
|
||||||
{
|
{
|
||||||
received_acks.push(received_packet);
|
received_acks.push(received_packet);
|
||||||
} else if received_packet.len()
|
} else if received_packet.len()
|
||||||
== PacketSize::RegularPacket.plaintext_size() - ack_overhead
|
== PacketSize::RegularPacket.plaintext_size() - ack_overhead
|
||||||
|| received_packet.len()
|
|| received_packet.len()
|
||||||
== PacketSize::OutfoxRegularPacket.plaintext_size() - ack_overhead
|
== PacketSize::OutfoxRegularPacket.plaintext_size() - outfox_ack_overhead
|
||||||
|| received_packet.len() == PacketSize::OutfoxRegularPacket.size() - 6
|
|| received_packet.len()
|
||||||
|
== PacketSize::OutfoxRegularPacket.size() - outfox_ack_overhead
|
||||||
{
|
{
|
||||||
trace!("routing regular packet");
|
trace!("routing regular packet");
|
||||||
received_messages.push(received_packet);
|
received_messages.push(received_packet);
|
||||||
|
|||||||
@@ -41,3 +41,6 @@ nym-multisig-contract-common = { path = "../cosmwasm-smart-contracts/multisig-co
|
|||||||
nym-service-provider-directory-common = { path = "../cosmwasm-smart-contracts/service-provider-directory" }
|
nym-service-provider-directory-common = { path = "../cosmwasm-smart-contracts/service-provider-directory" }
|
||||||
nym-name-service-common = { path = "../cosmwasm-smart-contracts/name-service" }
|
nym-name-service-common = { path = "../cosmwasm-smart-contracts/name-service" }
|
||||||
nym-sphinx = { path = "../../common/nymsphinx" }
|
nym-sphinx = { path = "../../common/nymsphinx" }
|
||||||
|
|
||||||
|
nym-pemstore = { path = "../../common/pemstore", version = "0.3.0" }
|
||||||
|
nym-types = { path = "../../common/types" }
|
||||||
|
|||||||
@@ -0,0 +1,68 @@
|
|||||||
|
use clap::{Args, Parser, Subcommand};
|
||||||
|
use nym_bin_common::output_format::OutputFormat;
|
||||||
|
use nym_crypto::asymmetric::identity;
|
||||||
|
use nym_types::helpers::ConsoleSigningOutput;
|
||||||
|
use nym_validator_client::nyxd::error::NyxdError;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
#[derive(Debug, Args)]
|
||||||
|
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
|
||||||
|
pub struct MixnetOperatorsIdentityKey {
|
||||||
|
#[clap(subcommand)]
|
||||||
|
pub command: MixnetOperatorsIdentityKeyCommands,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Subcommand)]
|
||||||
|
pub enum MixnetOperatorsIdentityKeyCommands {
|
||||||
|
/// Register a name alias for a nym address
|
||||||
|
Sign(SignArgs),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Parser)]
|
||||||
|
pub struct SignArgs {
|
||||||
|
/// Path to private identity key (example: private_identity_key.pem)
|
||||||
|
#[clap(long)]
|
||||||
|
private_key: PathBuf,
|
||||||
|
|
||||||
|
/// Base58 encoded message to sign
|
||||||
|
#[clap(long)]
|
||||||
|
base58_msg: String,
|
||||||
|
|
||||||
|
#[clap(short, long, default_value_t = OutputFormat::default())]
|
||||||
|
output: OutputFormat,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn sign(args: SignArgs) -> Result<(), NyxdError> {
|
||||||
|
eprintln!(">>> loading: {}", args.private_key.display());
|
||||||
|
let private_identity_key: identity::PrivateKey =
|
||||||
|
nym_pemstore::load_key(args.private_key).expect("failed to load key");
|
||||||
|
|
||||||
|
print_signed_msg(&private_identity_key, &args.base58_msg, args.output);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_signed_msg(private_key: &identity::PrivateKey, raw_msg: &str, output: OutputFormat) {
|
||||||
|
let trimmed = raw_msg.trim();
|
||||||
|
eprintln!(">>> attempting to sign: {trimmed}");
|
||||||
|
|
||||||
|
let Ok(decoded) = bs58::decode(trimmed).into_vec() else {
|
||||||
|
println!("failed to base58 decode the message, did you copy it correctly?");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
eprintln!(">>> decoding the message...");
|
||||||
|
|
||||||
|
// we don't really care about what particular information is embedded inside of it,
|
||||||
|
// we just want to know if user correctly copied the string, i.e. whether it's a valid bs58 encoded json
|
||||||
|
if serde_json::from_slice::<serde_json::Value>(&decoded).is_err() {
|
||||||
|
println!("failed to parse the message after decoding, did you copy it correctly?");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
// if this is a valid json, it MUST be a valid string
|
||||||
|
let decoded_string = String::from_utf8(decoded.clone()).unwrap();
|
||||||
|
let signature = private_key.sign(&decoded).to_base58_string();
|
||||||
|
|
||||||
|
let sign_output = ConsoleSigningOutput::new(decoded_string, signature);
|
||||||
|
println!("{}", output.format(&sign_output));
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
use clap::{Args, Subcommand};
|
use clap::{Args, Subcommand};
|
||||||
|
|
||||||
pub mod gateway;
|
pub mod gateway;
|
||||||
|
pub mod identity_key;
|
||||||
pub mod mixnode;
|
pub mod mixnode;
|
||||||
pub mod name;
|
pub mod name;
|
||||||
pub mod service;
|
pub mod service;
|
||||||
@@ -26,4 +27,6 @@ pub enum MixnetOperatorsCommands {
|
|||||||
ServiceProvider(service::MixnetOperatorsService),
|
ServiceProvider(service::MixnetOperatorsService),
|
||||||
/// Manage your registered name
|
/// Manage your registered name
|
||||||
Name(name::MixnetOperatorsName),
|
Name(name::MixnetOperatorsName),
|
||||||
|
/// Sign messages using your private identity key
|
||||||
|
IdentityKey(identity_key::MixnetOperatorsIdentityKey),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
use nym_sphinx_acknowledgements::surb_ack::SurbAckRecoveryError;
|
use nym_sphinx_acknowledgements::surb_ack::SurbAckRecoveryError;
|
||||||
use nym_sphinx_addressing::nodes::NymNodeRoutingAddressError;
|
use nym_sphinx_addressing::nodes::NymNodeRoutingAddressError;
|
||||||
use nym_sphinx_types::{NymPacketError, SphinxError};
|
use nym_sphinx_types::{NymPacketError, OutfoxError, SphinxError};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
@@ -25,4 +25,7 @@ pub enum MixProcessingError {
|
|||||||
|
|
||||||
#[error("the received packet was set to use the very old and very much deprecated 'VPN' mode")]
|
#[error("the received packet was set to use the very old and very much deprecated 'VPN' mode")]
|
||||||
ReceivedOldTypeVpnPacket,
|
ReceivedOldTypeVpnPacket,
|
||||||
|
|
||||||
|
#[error("failed to process received outfox packet: {0}")]
|
||||||
|
OutfoxProcessingError(#[from] OutfoxError),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,12 +20,14 @@ use tracing::instrument;
|
|||||||
|
|
||||||
type ForwardAck = MixPacket;
|
type ForwardAck = MixPacket;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct ProcessedFinalHop {
|
pub struct ProcessedFinalHop {
|
||||||
pub destination: DestinationAddressBytes,
|
pub destination: DestinationAddressBytes,
|
||||||
pub forward_ack: Option<ForwardAck>,
|
pub forward_ack: Option<ForwardAck>,
|
||||||
pub message: Vec<u8>,
|
pub message: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum MixProcessingResult {
|
pub enum MixProcessingResult {
|
||||||
/// Contains unwrapped data that should first get delayed before being sent to next hop.
|
/// Contains unwrapped data that should first get delayed before being sent to next hop.
|
||||||
ForwardHop(MixPacket, Option<SphinxDelay>),
|
ForwardHop(MixPacket, Option<SphinxDelay>),
|
||||||
@@ -141,7 +143,7 @@ impl SphinxPacketProcessor {
|
|||||||
match SurbAck::try_recover_first_hop_packet(&ack_data, packet_type) {
|
match SurbAck::try_recover_first_hop_packet(&ack_data, packet_type) {
|
||||||
Ok((first_hop, packet)) => (first_hop, packet),
|
Ok((first_hop, packet)) => (first_hop, packet),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
debug!("Failed to recover first hop from ack data: {err}");
|
info!("Failed to recover first hop from ack data: {err}");
|
||||||
return Err(err.into());
|
return Err(err.into());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -205,7 +207,7 @@ impl SphinxPacketProcessor {
|
|||||||
if packet.is_final_hop() {
|
if packet.is_final_hop() {
|
||||||
self.process_final_hop(
|
self.process_final_hop(
|
||||||
DestinationAddressBytes::from_bytes(next_address),
|
DestinationAddressBytes::from_bytes(next_address),
|
||||||
packet.recover_plaintext().to_vec(),
|
packet.recover_plaintext()?.to_vec(),
|
||||||
packet_size,
|
packet_size,
|
||||||
packet_type,
|
packet_type,
|
||||||
)
|
)
|
||||||
@@ -239,7 +241,14 @@ impl SphinxPacketProcessor {
|
|||||||
|
|
||||||
// for forward packets, extract next hop and set delay (but do NOT delay here)
|
// for forward packets, extract next hop and set delay (but do NOT delay here)
|
||||||
// for final packets, extract SURBAck
|
// for final packets, extract SURBAck
|
||||||
self.perform_final_processing(processed_packet, packet_size, packet_type)
|
let final_processing_result =
|
||||||
|
self.perform_final_processing(processed_packet, packet_size, packet_type);
|
||||||
|
|
||||||
|
if final_processing_result.is_err() {
|
||||||
|
error!("{:?}", final_processing_result)
|
||||||
|
}
|
||||||
|
|
||||||
|
final_processing_result
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,11 +15,12 @@ thiserror = { workspace = true }
|
|||||||
zeroize = { workspace = true }
|
zeroize = { workspace = true }
|
||||||
|
|
||||||
nym-crypto = { path = "../../crypto", features = ["symmetric", "rand"] }
|
nym-crypto = { path = "../../crypto", features = ["symmetric", "rand"] }
|
||||||
|
nym-pemstore = { path = "../../pemstore" }
|
||||||
nym-sphinx-addressing = { path = "../addressing" }
|
nym-sphinx-addressing = { path = "../addressing" }
|
||||||
nym-sphinx-params = { path = "../params" }
|
nym-sphinx-params = { path = "../params" }
|
||||||
|
nym-sphinx-routing = { path = "../routing" }
|
||||||
nym-sphinx-types = { path = "../types" }
|
nym-sphinx-types = { path = "../types" }
|
||||||
nym-pemstore = { path = "../../pemstore" }
|
|
||||||
nym-topology = { path = "../../topology" }
|
nym-topology = { path = "../../topology" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
serde = ["serde_crate", "generic-array"]
|
serde = ["serde_crate", "generic-array"]
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use nym_sphinx_addressing::nodes::{
|
|||||||
};
|
};
|
||||||
use nym_sphinx_params::packet_sizes::PacketSize;
|
use nym_sphinx_params::packet_sizes::PacketSize;
|
||||||
use nym_sphinx_params::{PacketType, DEFAULT_NUM_MIX_HOPS};
|
use nym_sphinx_params::{PacketType, DEFAULT_NUM_MIX_HOPS};
|
||||||
use nym_sphinx_types::delays::{self, Delay};
|
use nym_sphinx_types::delays::Delay;
|
||||||
use nym_sphinx_types::{NymPacket, NymPacketError, MIN_PACKET_SIZE};
|
use nym_sphinx_types::{NymPacket, NymPacketError, MIN_PACKET_SIZE};
|
||||||
use nym_topology::{NymTopology, NymTopologyError};
|
use nym_topology::{NymTopology, NymTopologyError};
|
||||||
use rand::{CryptoRng, RngCore};
|
use rand::{CryptoRng, RngCore};
|
||||||
@@ -51,7 +51,7 @@ impl SurbAck {
|
|||||||
{
|
{
|
||||||
let route =
|
let route =
|
||||||
topology.random_route_to_gateway(rng, DEFAULT_NUM_MIX_HOPS, recipient.gateway())?;
|
topology.random_route_to_gateway(rng, DEFAULT_NUM_MIX_HOPS, recipient.gateway())?;
|
||||||
let delays = delays::generate_from_average_duration(route.len(), average_delay);
|
let delays = nym_sphinx_routing::generate_hop_delays(average_delay, route.len());
|
||||||
let destination = recipient.as_sphinx_destination();
|
let destination = recipient.as_sphinx_destination();
|
||||||
|
|
||||||
let surb_ack_payload = prepare_identifier(rng, ack_key, marshaled_fragment_id);
|
let surb_ack_payload = prepare_identifier(rng, ack_key, marshaled_fragment_id);
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ thiserror = "1"
|
|||||||
nym-crypto = { path = "../../crypto", features = ["symmetric", "rand"] }
|
nym-crypto = { path = "../../crypto", features = ["symmetric", "rand"] }
|
||||||
nym-sphinx-addressing = { path = "../addressing" }
|
nym-sphinx-addressing = { path = "../addressing" }
|
||||||
nym-sphinx-params = { path = "../params" }
|
nym-sphinx-params = { path = "../params" }
|
||||||
|
nym-sphinx-routing = { path = "../routing" }
|
||||||
nym-sphinx-types = { path = "../types" }
|
nym-sphinx-types = { path = "../types" }
|
||||||
nym-topology = { path = "../../topology" }
|
nym-topology = { path = "../../topology" }
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use nym_sphinx_addressing::clients::Recipient;
|
|||||||
use nym_sphinx_addressing::nodes::{NymNodeRoutingAddress, MAX_NODE_ADDRESS_UNPADDED_LEN};
|
use nym_sphinx_addressing::nodes::{NymNodeRoutingAddress, MAX_NODE_ADDRESS_UNPADDED_LEN};
|
||||||
use nym_sphinx_params::packet_sizes::PacketSize;
|
use nym_sphinx_params::packet_sizes::PacketSize;
|
||||||
use nym_sphinx_params::{PacketType, ReplySurbKeyDigestAlgorithm, DEFAULT_NUM_MIX_HOPS};
|
use nym_sphinx_params::{PacketType, ReplySurbKeyDigestAlgorithm, DEFAULT_NUM_MIX_HOPS};
|
||||||
use nym_sphinx_types::{delays, NymPacket, SURBMaterial, SphinxError, SURB};
|
use nym_sphinx_types::{NymPacket, SURBMaterial, SphinxError, SURB};
|
||||||
use nym_topology::{NymTopology, NymTopologyError};
|
use nym_topology::{NymTopology, NymTopologyError};
|
||||||
use rand::{CryptoRng, RngCore};
|
use rand::{CryptoRng, RngCore};
|
||||||
use serde::de::{Error as SerdeError, Visitor};
|
use serde::de::{Error as SerdeError, Visitor};
|
||||||
@@ -96,7 +96,7 @@ impl ReplySurb {
|
|||||||
{
|
{
|
||||||
let route =
|
let route =
|
||||||
topology.random_route_to_gateway(rng, DEFAULT_NUM_MIX_HOPS, recipient.gateway())?;
|
topology.random_route_to_gateway(rng, DEFAULT_NUM_MIX_HOPS, recipient.gateway())?;
|
||||||
let delays = delays::generate_from_average_duration(route.len(), average_delay);
|
let delays = nym_sphinx_routing::generate_hop_delays(average_delay, route.len());
|
||||||
let destination = recipient.as_sphinx_destination();
|
let destination = recipient.as_sphinx_destination();
|
||||||
|
|
||||||
let surb_material = SURBMaterial::new(route, delays, destination);
|
let surb_material = SURBMaterial::new(route, delays, destination);
|
||||||
|
|||||||
@@ -15,7 +15,8 @@ nym-crypto = { path = "../../crypto" }
|
|||||||
nym-sphinx-acknowledgements = { path = "../acknowledgements" }
|
nym-sphinx-acknowledgements = { path = "../acknowledgements" }
|
||||||
nym-sphinx-addressing = { path = "../addressing" }
|
nym-sphinx-addressing = { path = "../addressing" }
|
||||||
nym-sphinx-chunking = { path = "../chunking" }
|
nym-sphinx-chunking = { path = "../chunking" }
|
||||||
nym-sphinx-params = { path = "../params" }
|
|
||||||
nym-sphinx-forwarding = { path = "../forwarding" }
|
nym-sphinx-forwarding = { path = "../forwarding" }
|
||||||
|
nym-sphinx-params = { path = "../params" }
|
||||||
|
nym-sphinx-routing = { path = "../routing" }
|
||||||
nym-sphinx-types = { path = "../types" }
|
nym-sphinx-types = { path = "../types" }
|
||||||
nym-topology = { path = "../../topology" }
|
nym-topology = { path = "../../topology" }
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ use nym_sphinx_params::packet_sizes::PacketSize;
|
|||||||
use nym_sphinx_params::{
|
use nym_sphinx_params::{
|
||||||
PacketEncryptionAlgorithm, PacketHkdfAlgorithm, PacketType, DEFAULT_NUM_MIX_HOPS,
|
PacketEncryptionAlgorithm, PacketHkdfAlgorithm, PacketType, DEFAULT_NUM_MIX_HOPS,
|
||||||
};
|
};
|
||||||
use nym_sphinx_types::{delays, NymPacket};
|
use nym_sphinx_types::NymPacket;
|
||||||
use nym_topology::{NymTopology, NymTopologyError};
|
use nym_topology::{NymTopology, NymTopologyError};
|
||||||
use rand::{CryptoRng, RngCore};
|
use rand::{CryptoRng, RngCore};
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
@@ -91,6 +91,7 @@ where
|
|||||||
>(rng, full_address.encryption_key());
|
>(rng, full_address.encryption_key());
|
||||||
|
|
||||||
let public_key_bytes = ephemeral_keypair.public_key().to_bytes();
|
let public_key_bytes = ephemeral_keypair.public_key().to_bytes();
|
||||||
|
|
||||||
let cover_size = packet_size.plaintext_size() - public_key_bytes.len() - ack_bytes.len();
|
let cover_size = packet_size.plaintext_size() - public_key_bytes.len() - ack_bytes.len();
|
||||||
|
|
||||||
let mut cover_content: Vec<_> = LOOP_COVER_MESSAGE_PAYLOAD
|
let mut cover_content: Vec<_> = LOOP_COVER_MESSAGE_PAYLOAD
|
||||||
@@ -119,22 +120,38 @@ where
|
|||||||
|
|
||||||
let route =
|
let route =
|
||||||
topology.random_route_to_gateway(rng, DEFAULT_NUM_MIX_HOPS, full_address.gateway())?;
|
topology.random_route_to_gateway(rng, DEFAULT_NUM_MIX_HOPS, full_address.gateway())?;
|
||||||
let delays = delays::generate_from_average_duration(route.len(), average_packet_delay);
|
let delays = nym_sphinx_routing::generate_hop_delays(average_packet_delay, route.len());
|
||||||
let destination = full_address.as_sphinx_destination();
|
let destination = full_address.as_sphinx_destination();
|
||||||
|
|
||||||
// once merged, that's an easy rng injection point for sphinx packets : )
|
|
||||||
let packet = NymPacket::sphinx_build(
|
|
||||||
packet_size.payload_size(),
|
|
||||||
packet_payload,
|
|
||||||
&route,
|
|
||||||
&destination,
|
|
||||||
&delays,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let first_hop_address =
|
let first_hop_address =
|
||||||
NymNodeRoutingAddress::try_from(route.first().unwrap().address).unwrap();
|
NymNodeRoutingAddress::try_from(route.first().unwrap().address).unwrap();
|
||||||
|
|
||||||
Ok(MixPacket::new(first_hop_address, packet, PacketType::Mix))
|
// once merged, that's an easy rng injection point for sphinx packets : )
|
||||||
|
let packet = match packet_type {
|
||||||
|
PacketType::Mix => NymPacket::sphinx_build(
|
||||||
|
packet_size.payload_size(),
|
||||||
|
packet_payload,
|
||||||
|
&route,
|
||||||
|
&destination,
|
||||||
|
&delays,
|
||||||
|
)?,
|
||||||
|
#[allow(deprecated)]
|
||||||
|
PacketType::Vpn => NymPacket::sphinx_build(
|
||||||
|
packet_size.payload_size(),
|
||||||
|
packet_payload,
|
||||||
|
&route,
|
||||||
|
&destination,
|
||||||
|
&delays,
|
||||||
|
)?,
|
||||||
|
PacketType::Outfox => NymPacket::outfox_build(
|
||||||
|
packet_payload,
|
||||||
|
&route,
|
||||||
|
&destination,
|
||||||
|
Some(packet_size.plaintext_size()),
|
||||||
|
)?,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(MixPacket::new(first_hop_address, packet, packet_type))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper function used to determine if given message represents a loop cover message.
|
/// Helper function used to determine if given message represents a loop cover message.
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
use nym_sphinx_addressing::clients::Recipient;
|
use nym_sphinx_addressing::clients::Recipient;
|
||||||
use nym_sphinx_types::Node;
|
use nym_sphinx_types::{delays, Delay, Node};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
pub trait SphinxRouteMaker {
|
pub trait SphinxRouteMaker {
|
||||||
@@ -41,3 +43,11 @@ impl SphinxRouteMaker for Vec<Node> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn generate_hop_delays(average_packet_delay: Duration, num_hops: usize) -> Vec<Delay> {
|
||||||
|
if average_packet_delay.is_zero() {
|
||||||
|
vec![nym_sphinx_types::Delay::new_from_millis(0); num_hops]
|
||||||
|
} else {
|
||||||
|
delays::generate_from_average_duration(num_hops, average_packet_delay)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ use nym_sphinx_chunking::fragment::{Fragment, FragmentIdentifier};
|
|||||||
use nym_sphinx_forwarding::packet::MixPacket;
|
use nym_sphinx_forwarding::packet::MixPacket;
|
||||||
use nym_sphinx_params::packet_sizes::PacketSize;
|
use nym_sphinx_params::packet_sizes::PacketSize;
|
||||||
use nym_sphinx_params::{PacketType, ReplySurbKeyDigestAlgorithm, DEFAULT_NUM_MIX_HOPS};
|
use nym_sphinx_params::{PacketType, ReplySurbKeyDigestAlgorithm, DEFAULT_NUM_MIX_HOPS};
|
||||||
use nym_sphinx_types::{delays, Delay, NymPacket};
|
use nym_sphinx_types::{Delay, NymPacket};
|
||||||
use nym_topology::{NymTopology, NymTopologyError};
|
use nym_topology::{NymTopology, NymTopologyError};
|
||||||
use rand::{CryptoRng, Rng};
|
use rand::{CryptoRng, Rng};
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
@@ -233,7 +233,7 @@ pub trait FragmentPreparer {
|
|||||||
|
|
||||||
// including set of delays
|
// including set of delays
|
||||||
let delays =
|
let delays =
|
||||||
delays::generate_from_average_duration(route.len(), self.average_packet_delay());
|
nym_sphinx_routing::generate_hop_delays(self.average_packet_delay(), route.len());
|
||||||
|
|
||||||
// create the actual sphinx packet here. With valid route and correct payload size,
|
// create the actual sphinx packet here. With valid route and correct payload size,
|
||||||
// there's absolutely no reason for this call to fail.
|
// there's absolutely no reason for this call to fail.
|
||||||
|
|||||||
@@ -177,20 +177,29 @@ impl TaskManager {
|
|||||||
drop(notify_rx);
|
drop(notify_rx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
let interrupt_future = tokio::signal::ctrl_c();
|
||||||
|
|
||||||
// in wasm we'll never get our shutdown anyway...
|
// in wasm we'll never get our shutdown anyway...
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
futures::future::pending::<()>().await;
|
let interrupt_future = futures::future::pending::<()>();
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
let wait_future = tokio::time::sleep(Duration::from_secs(self.shutdown_timer_secs));
|
||||||
|
|
||||||
|
// TODO: we should be using a `Delay` here for wasm
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
let wait_future = futures::future::pending::<()>();
|
||||||
|
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
_ = self.notify_tx.closed() => {
|
_ = self.notify_tx.closed() => {
|
||||||
log::info!("All registered tasks succesfully shutdown");
|
log::info!("All registered tasks succesfully shutdown");
|
||||||
},
|
},
|
||||||
_ = tokio::signal::ctrl_c() => {
|
_ = interrupt_future => {
|
||||||
log::info!("Forcing shutdown");
|
log::info!("Forcing shutdown");
|
||||||
}
|
}
|
||||||
_ = tokio::time::sleep(Duration::from_secs(self.shutdown_timer_secs)) => {
|
_ = wait_future => {
|
||||||
log::info!("Timout reached, forcing shutdown");
|
log::info!("Timeout reached, forcing shutdown");
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+30
-3
@@ -34,16 +34,41 @@ pretty_env_logger = "0.4"
|
|||||||
rand = "0.7"
|
rand = "0.7"
|
||||||
serde = { workspace = true, features = ["derive"] }
|
serde = { workspace = true, features = ["derive"] }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
sqlx = { version = "0.5", features = [ "runtime-tokio-rustls", "sqlite", "macros", "migrate", ] }
|
sqlx = { version = "0.5", features = [
|
||||||
|
"runtime-tokio-rustls",
|
||||||
|
"sqlite",
|
||||||
|
"macros",
|
||||||
|
"migrate",
|
||||||
|
] }
|
||||||
subtle-encoding = { version = "0.5", features = ["bech32-preview"] }
|
subtle-encoding = { version = "0.5", features = ["bech32-preview"] }
|
||||||
thiserror = "1"
|
thiserror = "1"
|
||||||
tokio = { version = "1.24.1", features = [ "rt-multi-thread", "net", "signal", "fs", ] }
|
tokio = { version = "1.24.1", features = [
|
||||||
|
"rt-multi-thread",
|
||||||
|
"net",
|
||||||
|
"signal",
|
||||||
|
"fs",
|
||||||
|
] }
|
||||||
tokio-stream = { version = "0.1.11", features = ["fs"] }
|
tokio-stream = { version = "0.1.11", features = ["fs"] }
|
||||||
tokio-tungstenite = "0.14"
|
tokio-tungstenite = "0.14"
|
||||||
tokio-util = { version = "0.7.4", features = ["codec"] }
|
tokio-util = { version = "0.7.4", features = ["codec"] }
|
||||||
url = { version = "2.2", features = ["serde"] }
|
url = { version = "2.2", features = ["serde"] }
|
||||||
zeroize = { workspace = true }
|
zeroize = { workspace = true }
|
||||||
|
|
||||||
|
# wireguard
|
||||||
|
# Forked it to be able to bump x25519-dalek to rc.3
|
||||||
|
boringtun = { git = "https://github.com/durch/boringtun.git" }
|
||||||
|
base64 = "0.21"
|
||||||
|
x25519-dalek = { version = "=2.0.0-rc.3", features = [
|
||||||
|
"reusable_secrets",
|
||||||
|
"static_secrets",
|
||||||
|
] }
|
||||||
|
etherparse = "0.13.0"
|
||||||
|
pnet = "0.34.0"
|
||||||
|
bytes = "1.4.0"
|
||||||
|
async-recursion = "1.0.4"
|
||||||
|
smoltcp = "0.10.0"
|
||||||
|
tun-tap = "0.1.3"
|
||||||
|
|
||||||
# internal
|
# internal
|
||||||
nym-api-requests = { path = "../nym-api/nym-api-requests" }
|
nym-api-requests = { path = "../nym-api/nym-api-requests" }
|
||||||
nym-bin-common = { path = "../common/bin-common", features = ["output_format"] }
|
nym-bin-common = { path = "../common/bin-common", features = ["output_format"] }
|
||||||
@@ -60,7 +85,9 @@ nym-sphinx = { path = "../common/nymsphinx" }
|
|||||||
nym-statistics-common = { path = "../common/statistics" }
|
nym-statistics-common = { path = "../common/statistics" }
|
||||||
nym-task = { path = "../common/task" }
|
nym-task = { path = "../common/task" }
|
||||||
nym-types = { path = "../common/types" }
|
nym-types = { path = "../common/types" }
|
||||||
nym-validator-client = { path = "../common/client-libs/validator-client", features = [ "nyxd-client" ] }
|
nym-validator-client = { path = "../common/client-libs/validator-client", features = [
|
||||||
|
"nyxd-client",
|
||||||
|
] }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
tokio = { version = "1.24.1", features = ["rt-multi-thread", "macros"] }
|
tokio = { version = "1.24.1", features = ["rt-multi-thread", "macros"] }
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
gA3NCDl+xOorR3heFVB47FlGunsZgS4RDX2M0IY73lc=
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
mxV/mw7WZTe+0Msa0kvJHMHERDA/cSskiZWQce+TdEs=
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
AEqXrLFT4qjYq3wmX0456iv94uM6nDj5ugp6Jedcflg=
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
WM8s8bYegwMa0TJ+xIwhk+dImk2IpDUKslDBCZPizlE=
|
||||||
@@ -10,6 +10,7 @@ use crate::node::client_handling::websocket::connection_handler::coconut::Coconu
|
|||||||
use crate::node::mixnet_handling::receiver::connection_handler::ConnectionHandler;
|
use crate::node::mixnet_handling::receiver::connection_handler::ConnectionHandler;
|
||||||
use crate::node::statistics::collector::GatewayStatisticsCollector;
|
use crate::node::statistics::collector::GatewayStatisticsCollector;
|
||||||
use crate::node::storage::Storage;
|
use crate::node::storage::Storage;
|
||||||
|
use crate::node::wireguard::wireguard;
|
||||||
use log::*;
|
use log::*;
|
||||||
use nym_bin_common::output_format::OutputFormat;
|
use nym_bin_common::output_format::OutputFormat;
|
||||||
use nym_crypto::asymmetric::{encryption, identity};
|
use nym_crypto::asymmetric::{encryption, identity};
|
||||||
@@ -28,6 +29,8 @@ pub(crate) mod client_handling;
|
|||||||
pub(crate) mod mixnet_handling;
|
pub(crate) mod mixnet_handling;
|
||||||
pub(crate) mod statistics;
|
pub(crate) mod statistics;
|
||||||
pub(crate) mod storage;
|
pub(crate) mod storage;
|
||||||
|
mod wg;
|
||||||
|
pub(crate) mod wireguard;
|
||||||
|
|
||||||
/// Wire up and create Gateway instance
|
/// Wire up and create Gateway instance
|
||||||
pub(crate) async fn create_gateway(config: Config) -> Gateway<PersistentStorage> {
|
pub(crate) async fn create_gateway(config: Config) -> Gateway<PersistentStorage> {
|
||||||
@@ -297,6 +300,8 @@ impl<St> Gateway<St> {
|
|||||||
Arc::new(coconut_verifier),
|
Arc::new(coconut_verifier),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
tokio::spawn(wireguard());
|
||||||
|
|
||||||
info!("Finished nym gateway startup procedure - it should now be able to receive mix and client traffic!");
|
info!("Finished nym gateway startup procedure - it should now be able to receive mix and client traffic!");
|
||||||
|
|
||||||
self.wait_for_interrupt(shutdown).await
|
self.wait_for_interrupt(shutdown).await
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
use bytes::Bytes;
|
||||||
|
use std::fmt::{Display, Formatter};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum Event {
|
||||||
|
/// Dumb event with no data.
|
||||||
|
Dumb,
|
||||||
|
/// IP packet received from the WireGuard tunnel that should be passed through to the corresponding virtual device/internet.
|
||||||
|
/// Original implementation also has protocol here since it understands it, but we'll have to infer it downstream
|
||||||
|
WgPacket(Bytes),
|
||||||
|
/// IP packet to be sent through the WireGuard tunnel as crafted by the virtual device.
|
||||||
|
IpPacket(Bytes),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Event {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Event::Dumb => {
|
||||||
|
write!(f, "Dumb{{}}")
|
||||||
|
}
|
||||||
|
Event::WgPacket(data) => {
|
||||||
|
let size = data.len();
|
||||||
|
write!(f, "WgPacket{{ size={} }}", size)
|
||||||
|
}
|
||||||
|
Event::IpPacket(data) => {
|
||||||
|
let size = data.len();
|
||||||
|
write!(f, "IpPacket{{ size={} }}", size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,437 @@
|
|||||||
|
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use async_recursion::async_recursion;
|
||||||
|
use base64::engine::general_purpose;
|
||||||
|
use base64::Engine as _;
|
||||||
|
use boringtun::noise::errors::WireGuardError;
|
||||||
|
use boringtun::noise::{Tunn, TunnResult};
|
||||||
|
use etherparse::{InternetSlice, PacketBuilder, SlicedPacket, TransportSlice};
|
||||||
|
use log::{debug, info, warn};
|
||||||
|
use pnet::packet::ip::IpNextHeaderProtocols;
|
||||||
|
use pnet::packet::ipv4::{Ipv4Packet, MutableIpv4Packet};
|
||||||
|
use pnet::packet::{MutablePacket, Packet, PacketSize};
|
||||||
|
use pnet::transport::{ipv4_packet_iter, transport_channel};
|
||||||
|
use tokio::net::UdpSocket;
|
||||||
|
use tokio::sync::{Mutex, RwLock};
|
||||||
|
use tokio::time::{sleep, timeout};
|
||||||
|
use x25519_dalek::StaticSecret;
|
||||||
|
|
||||||
|
use crate::error;
|
||||||
|
|
||||||
|
use self::events::Event;
|
||||||
|
|
||||||
|
pub mod events;
|
||||||
|
|
||||||
|
const MAX_PACKET: usize = 65536;
|
||||||
|
|
||||||
|
/// A WireGuard tunnel. Encapsulates and decapsulates IP packets,
|
||||||
|
/// recieves packets from the client on the udp_rx channel,
|
||||||
|
/// and events from the internet on the eth_rx channel,
|
||||||
|
/// sends data through udp socket or datalink sender directly.
|
||||||
|
/// For now all tunnels recieve all events and filter on the source_peer_addr
|
||||||
|
pub struct WireGuardTunnel {
|
||||||
|
source_peer_addr: Arc<RwLock<Option<(Ipv4Addr, u16)>>>,
|
||||||
|
/// `boringtun` peer/tunnel implementation, used for crypto & WG protocol.
|
||||||
|
peer: Arc<Mutex<Tunn>>,
|
||||||
|
udp: Arc<UdpSocket>,
|
||||||
|
peer_endpoint: SocketAddr,
|
||||||
|
bus_rx: tokio::sync::broadcast::Receiver<Event>,
|
||||||
|
bus_tx: tokio::sync::broadcast::Sender<Event>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_l3_packet(data: &[u8], destination_addr: Ipv4Addr) -> Vec<u8> {
|
||||||
|
let (mut tx, mut rx) = transport_channel(
|
||||||
|
65535,
|
||||||
|
pnet::transport::TransportChannelType::Layer3(IpNextHeaderProtocols::Tcp),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut rx_iterator = ipv4_packet_iter(&mut rx);
|
||||||
|
|
||||||
|
let mut must_send = true;
|
||||||
|
let mut cnt = 0;
|
||||||
|
while let Ok((packet, addr)) = rx_iterator.next() {
|
||||||
|
if must_send {
|
||||||
|
let data = data.to_vec();
|
||||||
|
let incoming_packet = Ipv4Packet::new(&data).unwrap();
|
||||||
|
let mut new_packet = vec![0; incoming_packet.packet_size()];
|
||||||
|
let mut outgoing_packet = MutableIpv4Packet::new(&mut new_packet).unwrap();
|
||||||
|
outgoing_packet.clone_from(&incoming_packet);
|
||||||
|
info!(
|
||||||
|
"Sending (ttl={}, proto={} from {} to {}({})",
|
||||||
|
outgoing_packet.get_ttl(),
|
||||||
|
outgoing_packet.get_next_level_protocol(),
|
||||||
|
outgoing_packet.get_source(),
|
||||||
|
outgoing_packet.get_destination(),
|
||||||
|
destination_addr
|
||||||
|
);
|
||||||
|
outgoing_packet.set_source("95.217.227.118".parse().unwrap());
|
||||||
|
let sent = tx
|
||||||
|
.send_to(outgoing_packet, IpAddr::V4(destination_addr))
|
||||||
|
.unwrap();
|
||||||
|
info!("Sent L3 packet ({sent})");
|
||||||
|
must_send = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
cnt += 1;
|
||||||
|
let source = packet.get_source();
|
||||||
|
let destination = packet.get_destination();
|
||||||
|
info!("Ignoring packet from {source}");
|
||||||
|
|
||||||
|
if source == destination_addr {
|
||||||
|
info!("({addr}){source} -> {destination}");
|
||||||
|
return packet.payload().to_vec();
|
||||||
|
}
|
||||||
|
if cnt >= 10 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WireGuardTunnel {
|
||||||
|
async fn set_source_peer_addr(&self, source_addr: Ipv4Addr, source_port: Option<u16>) {
|
||||||
|
{
|
||||||
|
if self.source_peer_addr.read().await.is_some() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut source_peer_addr = self.source_peer_addr.write().await;
|
||||||
|
*source_peer_addr = Some((source_addr, source_port.unwrap_or(0)))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn spin_off(mut self) {
|
||||||
|
info!("Spun off WG tunnel");
|
||||||
|
// We'll receive both inbound and outbound packages on the same channel, and filter on packet type
|
||||||
|
loop {
|
||||||
|
tokio::select! {
|
||||||
|
packet = self.bus_rx.recv() => {
|
||||||
|
match packet {
|
||||||
|
Ok(p) => {
|
||||||
|
info!("{p}");
|
||||||
|
match p {
|
||||||
|
Event::IpPacket(data) => self.consume_eth(&data).await,
|
||||||
|
Event::WgPacket(data) => self.consume_wg(&data).await,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => error!("{e}")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ = sleep(Duration::from_millis(5))=> {
|
||||||
|
let mut send_buf = [0u8; MAX_PACKET];
|
||||||
|
let tun_result = {
|
||||||
|
let mut tun = timeout(Duration::from_millis(100), self.peer()).await.unwrap();
|
||||||
|
tun.update_timers(&mut send_buf)
|
||||||
|
};
|
||||||
|
self.handle_routine_tun_result(tun_result).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn consume_eth(&self, data: &[u8]) {
|
||||||
|
let parsed_packet = SlicedPacket::from_ethernet(data).unwrap();
|
||||||
|
debug!("{parsed_packet:?}");
|
||||||
|
let (source_addr, destination_addr) = match parsed_packet.ip.unwrap() {
|
||||||
|
InternetSlice::Ipv4(ip, _) => (ip.source_addr(), ip.destination_addr()),
|
||||||
|
_ => unimplemented!(),
|
||||||
|
};
|
||||||
|
let (source_port, destination_port, icmp_type) = match parsed_packet.transport.as_ref() {
|
||||||
|
Some(TransportSlice::Tcp(tcp)) => {
|
||||||
|
(Some(tcp.source_port()), Some(tcp.destination_port()), None)
|
||||||
|
}
|
||||||
|
Some(TransportSlice::Udp(udp)) => {
|
||||||
|
(Some(udp.source_port()), Some(udp.destination_port()), None)
|
||||||
|
}
|
||||||
|
Some(TransportSlice::Icmpv4(icmp)) => (None, None, Some(icmp.icmp_type())),
|
||||||
|
Some(TransportSlice::Icmpv6(_)) => panic!("ICMPv6"),
|
||||||
|
Some(TransportSlice::Unknown(_)) => panic!("Unknown"),
|
||||||
|
None => panic!("No transport layer"),
|
||||||
|
};
|
||||||
|
debug!(
|
||||||
|
"{:?}:{:?} -> {:?}:{:?} - ({:?})",
|
||||||
|
source_addr, source_port, destination_addr, destination_port, icmp_type
|
||||||
|
);
|
||||||
|
|
||||||
|
if destination_addr == self.source_peer_addr.read().await.unwrap().0 {
|
||||||
|
info!("Sending {} to {}", data.len(), self.peer_endpoint);
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let response_packet_builder =
|
||||||
|
PacketBuilder::ipv4(source_addr.octets(), destination_addr.octets(), 64);
|
||||||
|
|
||||||
|
let mut response_packet =
|
||||||
|
Vec::<u8>::with_capacity(response_packet_builder.size(parsed_packet.payload.len()));
|
||||||
|
|
||||||
|
match parsed_packet.transport.as_ref() {
|
||||||
|
Some(TransportSlice::Udp(udp)) => {
|
||||||
|
debug!("UDP: {}, {}", udp.length(), udp.destination_port());
|
||||||
|
let response_packet_builder =
|
||||||
|
response_packet_builder.udp(source_port.unwrap(), destination_port.unwrap());
|
||||||
|
response_packet_builder
|
||||||
|
.write(&mut response_packet, parsed_packet.payload)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
Some(TransportSlice::Tcp(tcp)) => {
|
||||||
|
let response_packet_builder = response_packet_builder.tcp(
|
||||||
|
destination_port.unwrap(),
|
||||||
|
source_port.unwrap(),
|
||||||
|
tcp.sequence_number(),
|
||||||
|
tcp.window_size(),
|
||||||
|
);
|
||||||
|
response_packet_builder
|
||||||
|
.write(&mut response_packet, parsed_packet.payload)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
Some(TransportSlice::Icmpv4(icmp)) => {
|
||||||
|
info!("{:?}", icmp.icmp_type());
|
||||||
|
let response_packet_builder = response_packet_builder.icmpv4(icmp.icmp_type());
|
||||||
|
response_packet_builder
|
||||||
|
.write(&mut response_packet, parsed_packet.payload)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
|
_ => unimplemented!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let encapsulated_response_packet = self.encapsulate_packet(&response_packet).await;
|
||||||
|
// let packet = Tunn::parse_incoming_packet(&response_packet).unwrap();
|
||||||
|
// info!("Sending {packet:?} to {addr}");
|
||||||
|
let sent = self
|
||||||
|
.udp
|
||||||
|
.send_to(&encapsulated_response_packet, self.peer_endpoint)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
info!(
|
||||||
|
"[{}:{} ({sent})-> {}:{}] -> {}",
|
||||||
|
destination_addr,
|
||||||
|
destination_port.unwrap_or(0),
|
||||||
|
source_addr,
|
||||||
|
source_port.unwrap_or(0),
|
||||||
|
self.peer_endpoint
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: extend to work with IPv6
|
||||||
|
pub async fn produce_eth(&self, packet_bytes: &[u8]) -> Vec<u8> {
|
||||||
|
let outgoing_packet = SlicedPacket::from_ip(packet_bytes).unwrap();
|
||||||
|
let (source_addr, destination_addr) = match outgoing_packet.ip.unwrap() {
|
||||||
|
InternetSlice::Ipv4(ip, _) => (ip.source_addr(), ip.destination_addr()),
|
||||||
|
_ => unimplemented!(),
|
||||||
|
};
|
||||||
|
let (source_port, destination_port, icmp_type) = match outgoing_packet.transport.as_ref() {
|
||||||
|
Some(TransportSlice::Tcp(tcp)) => {
|
||||||
|
(Some(tcp.source_port()), Some(tcp.destination_port()), None)
|
||||||
|
}
|
||||||
|
Some(TransportSlice::Udp(udp)) => {
|
||||||
|
(Some(udp.source_port()), Some(udp.destination_port()), None)
|
||||||
|
}
|
||||||
|
Some(TransportSlice::Icmpv4(icmp)) => (None, None, Some(icmp.icmp_type())),
|
||||||
|
Some(TransportSlice::Icmpv6(_)) => panic!("ICMPv6"),
|
||||||
|
Some(TransportSlice::Unknown(_)) => panic!("Unknown"),
|
||||||
|
None => panic!("No transport layer"),
|
||||||
|
};
|
||||||
|
info!(
|
||||||
|
"{:?}:{:?} -> {:?}:{:?} - ({:?})",
|
||||||
|
source_addr, source_port, destination_addr, destination_port, icmp_type
|
||||||
|
);
|
||||||
|
self.set_source_peer_addr(source_addr, source_port).await;
|
||||||
|
handle_l3_packet(packet_bytes, destination_addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// WireGuard consumption task. Receives encrypted packets from the WireGuard peer,
|
||||||
|
/// decapsulates them, and dispatches newly received IP packets.
|
||||||
|
async fn consume_wg(&self, data: &[u8]) {
|
||||||
|
let mut send_buf = [0u8; MAX_PACKET];
|
||||||
|
let mut peer = self.peer().await;
|
||||||
|
match peer.decapsulate(None, data, &mut send_buf) {
|
||||||
|
TunnResult::WriteToNetwork(packet) => {
|
||||||
|
match self.udp.send_to(packet, self.peer_endpoint).await {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(e) => {
|
||||||
|
error!("Failed to send decapsulation-instructed packet to WireGuard endpoint: {:?}", e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
loop {
|
||||||
|
let mut send_buf = [0u8; MAX_PACKET];
|
||||||
|
match peer.decapsulate(None, &[], &mut send_buf) {
|
||||||
|
TunnResult::WriteToNetwork(packet) => {
|
||||||
|
match self.udp.send_to(packet, self.peer_endpoint).await {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(e) => {
|
||||||
|
error!("Failed to send decapsulation-instructed packet to WireGuard endpoint: {:?}", e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TunnResult::WriteToTunnelV4(packet, _) | TunnResult::WriteToTunnelV6(packet, _) => {
|
||||||
|
info!(
|
||||||
|
"WireGuard endpoint sent IP packet of {} bytes",
|
||||||
|
packet.len()
|
||||||
|
);
|
||||||
|
let response = self.produce_eth(packet).await;
|
||||||
|
if !response.is_empty() {
|
||||||
|
self.bus_tx.send(Event::IpPacket(response.into())).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
x => warn!("{x:?}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn encapsulate_packet(&self, payload: &[u8]) -> Vec<u8> {
|
||||||
|
let len = 148.max(payload.len() + 32);
|
||||||
|
let mut dst = vec![0; len];
|
||||||
|
let mut t = self.peer().await;
|
||||||
|
let packet = t.encapsulate(payload, &mut dst);
|
||||||
|
match packet {
|
||||||
|
TunnResult::WriteToNetwork(p) => p.to_vec(),
|
||||||
|
unexpected => {
|
||||||
|
error!("{:?}", unexpected);
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn peer(&self) -> tokio::sync::MutexGuard<'_, Tunn> {
|
||||||
|
self.peer.lock().await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn new(
|
||||||
|
peer_static_public: x25519_dalek::PublicKey,
|
||||||
|
udp: Arc<UdpSocket>,
|
||||||
|
peer_endpoint: SocketAddr,
|
||||||
|
bus_tx: tokio::sync::broadcast::Sender<Event>,
|
||||||
|
) -> Self {
|
||||||
|
let peer = Arc::new(Mutex::new(Self::create_tunnel(peer_static_public)));
|
||||||
|
|
||||||
|
Self {
|
||||||
|
source_peer_addr: Arc::new(RwLock::new(None)),
|
||||||
|
peer,
|
||||||
|
udp,
|
||||||
|
peer_endpoint,
|
||||||
|
bus_rx: bus_tx.subscribe(),
|
||||||
|
bus_tx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_tunnel(peer_static_public: x25519_dalek::PublicKey) -> Tunn {
|
||||||
|
let secret_bytes: [u8; 32] = general_purpose::STANDARD
|
||||||
|
.decode("AEqXrLFT4qjYq3wmX0456iv94uM6nDj5ugp6Jedcflg=")
|
||||||
|
.unwrap()
|
||||||
|
.try_into()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let private_key = StaticSecret::try_from(secret_bytes).unwrap();
|
||||||
|
|
||||||
|
Tunn::new(private_key, peer_static_public, None, None, 0, None).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Encapsulates and sends an IP packet back to the WireGuard client.
|
||||||
|
pub async fn send_ip_packet(&self, packet: &[u8]) -> anyhow::Result<()> {
|
||||||
|
let mut send_buf = [0u8; MAX_PACKET];
|
||||||
|
match self.peer().await.encapsulate(packet, &mut send_buf) {
|
||||||
|
TunnResult::WriteToNetwork(packet) => {
|
||||||
|
self.udp.send_to(packet, self.peer_endpoint).await.unwrap();
|
||||||
|
debug!(
|
||||||
|
"Sent {} bytes to WireGuard endpoint (encrypted IP packet)",
|
||||||
|
packet.len()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
TunnResult::Err(e) => {
|
||||||
|
error!("Failed to encapsulate IP packet: {:?}", e);
|
||||||
|
}
|
||||||
|
TunnResult::Done => {
|
||||||
|
// Ignored
|
||||||
|
}
|
||||||
|
other => {
|
||||||
|
error!(
|
||||||
|
"Unexpected WireGuard state during encapsulation: {:?}",
|
||||||
|
other
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_recursion]
|
||||||
|
async fn handle_routine_tun_result<'a: 'async_recursion>(&self, result: TunnResult<'a>) -> () {
|
||||||
|
match result {
|
||||||
|
TunnResult::WriteToNetwork(packet) => {
|
||||||
|
info!(
|
||||||
|
"Sending routine packet of {} bytes to WireGuard endpoint",
|
||||||
|
packet.len()
|
||||||
|
);
|
||||||
|
match self.udp.send_to(packet, self.peer_endpoint).await {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(e) => {
|
||||||
|
error!(
|
||||||
|
"Failed to send routine packet to WireGuard endpoint: {:?}",
|
||||||
|
e
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
TunnResult::Err(WireGuardError::ConnectionExpired) => {
|
||||||
|
warn!("Wireguard handshake has expired!");
|
||||||
|
|
||||||
|
let mut buf = vec![0u8; MAX_PACKET];
|
||||||
|
let result = self
|
||||||
|
.peer()
|
||||||
|
.await
|
||||||
|
.format_handshake_initiation(&mut buf[..], false);
|
||||||
|
|
||||||
|
self.handle_routine_tun_result(result).await
|
||||||
|
}
|
||||||
|
TunnResult::Err(e) => {
|
||||||
|
error!(
|
||||||
|
"Failed to prepare routine packet for WireGuard endpoint: {:?}",
|
||||||
|
e
|
||||||
|
);
|
||||||
|
}
|
||||||
|
TunnResult::Done => {
|
||||||
|
// Sleep for a bit
|
||||||
|
// tokio::time::sleep(Duration::from_millis(1)).await;
|
||||||
|
}
|
||||||
|
other => {
|
||||||
|
warn!("Unexpected WireGuard routine task state: {:?}", other);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// fn route_protocol(&self, packet: &[u8]) -> Option<Protocol> {
|
||||||
|
// match IpVersion::of_packet(packet) {
|
||||||
|
// Ok(IpVersion::Ipv4) => Ipv4Packet::new_checked(&packet)
|
||||||
|
// .ok()
|
||||||
|
// // Only care if the packet is destined for this tunnel
|
||||||
|
// .filter(|packet| Ipv4Addr::from(packet.dst_addr()) == self.source_peer_ip)
|
||||||
|
// .and_then(|packet| match packet.next_header() {
|
||||||
|
// IpProtocol::Tcp => Some(Protocol::Tcp),
|
||||||
|
// IpProtocol::Udp => Some(Protocol::Udp),
|
||||||
|
// // Unrecognized protocol, so we cannot determine where to route
|
||||||
|
// _ => None,
|
||||||
|
// }),
|
||||||
|
// Ok(IpVersion::Ipv6) => Ipv6Packet::new_checked(&packet)
|
||||||
|
// .ok()
|
||||||
|
// // Only care if the packet is destined for this tunnel
|
||||||
|
// .filter(|packet| Ipv6Addr::from(packet.dst_addr()) == self.source_peer_ip)
|
||||||
|
// .and_then(|packet| match packet.next_header() {
|
||||||
|
// IpProtocol::Tcp => Some(Protocol::Tcp),
|
||||||
|
// IpProtocol::Udp => Some(Protocol::Udp),
|
||||||
|
// // Unrecognized protocol, so we cannot determine where to route
|
||||||
|
// _ => None,
|
||||||
|
// }),
|
||||||
|
// _ => None,
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
use base64::engine::general_purpose;
|
||||||
|
use base64::Engine as _;
|
||||||
|
use log::{error, info};
|
||||||
|
use std::collections::HashSet;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use tokio::net::UdpSocket;
|
||||||
|
use tokio::sync::broadcast;
|
||||||
|
use x25519_dalek::{PublicKey, StaticSecret};
|
||||||
|
|
||||||
|
use crate::node::wg::events::Event;
|
||||||
|
use crate::node::wg::WireGuardTunnel;
|
||||||
|
|
||||||
|
pub async fn wireguard() {
|
||||||
|
let wg_address = "0.0.0.0:51820";
|
||||||
|
let sock = Arc::new(UdpSocket::bind(wg_address).await.unwrap());
|
||||||
|
info!("wg listening on {wg_address}");
|
||||||
|
|
||||||
|
// Secret key ofthe gateway, we'll need a way to generate this from the IdentityKey, might be enough to do some base58 -> base64 conversion
|
||||||
|
let secret_bytes: [u8; 32] = general_purpose::STANDARD
|
||||||
|
.decode("AEqXrLFT4qjYq3wmX0456iv94uM6nDj5ugp6Jedcflg=")
|
||||||
|
.unwrap()
|
||||||
|
.try_into()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Hardcoded peer public key, we'll need a way to register those, private key for that one is `aMUcuAgTiFCHQ/fHqEQRvpLWBxh8sKA7f7lSyWymrGE=`
|
||||||
|
// Wireguard configuration that works with this setup is below, this needs to be put into the wireguard client of choice.
|
||||||
|
// Working in this case means that they go through the handshake, and client
|
||||||
|
// starts sending data packets to the gateway.
|
||||||
|
//
|
||||||
|
// [Interface]
|
||||||
|
// PrivateKey = aMUcuAgTiFCHQ/fHqEQRvpLWBxh8sKA7f7lSyWymrGE=
|
||||||
|
// Address = 10.8.0.0/24
|
||||||
|
// DNS = 1.1.1.1
|
||||||
|
//
|
||||||
|
// [Peer]
|
||||||
|
// PublicKey = y6/iGYraJjON6pw9fcBa5vLRbGsQqprFLfWKyJQnlWs=
|
||||||
|
// AllowedIPs = 0.0.0.0/0
|
||||||
|
// Endpoint = 127.0.0.1:51820
|
||||||
|
let peer_public_bytes: [u8; 32] = general_purpose::STANDARD
|
||||||
|
.decode("mxV/mw7WZTe+0Msa0kvJHMHERDA/cSskiZWQce+TdEs=")
|
||||||
|
.unwrap()
|
||||||
|
.try_into()
|
||||||
|
.unwrap();
|
||||||
|
let peer_public = PublicKey::from(peer_public_bytes);
|
||||||
|
let secret = StaticSecret::try_from(secret_bytes).unwrap();
|
||||||
|
let public = PublicKey::from(&secret);
|
||||||
|
info!(
|
||||||
|
"wg public key: {}",
|
||||||
|
general_purpose::STANDARD.encode(public)
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut buf = [0; 1024];
|
||||||
|
let mut peers = HashSet::new();
|
||||||
|
|
||||||
|
let (bus_tx, _) = broadcast::channel(128);
|
||||||
|
|
||||||
|
while let Ok((len, addr)) = sock.recv_from(&mut buf).await {
|
||||||
|
info!("Received {} bytes from {}", len, addr);
|
||||||
|
if peers.contains(&addr) {
|
||||||
|
bus_tx
|
||||||
|
.send(Event::WgPacket(buf[..len].to_vec().into()))
|
||||||
|
.map_err(|e| error!("{e}"))
|
||||||
|
.unwrap();
|
||||||
|
} else {
|
||||||
|
info!("New peer with endpoint {addr}");
|
||||||
|
let tun =
|
||||||
|
WireGuardTunnel::new(peer_public, Arc::clone(&sock), addr, bus_tx.clone()).await;
|
||||||
|
peers.insert(addr);
|
||||||
|
tokio::spawn(tun.spin_off());
|
||||||
|
bus_tx
|
||||||
|
.send(Event::WgPacket(buf[..len].to_vec().into()))
|
||||||
|
.map_err(|e| error!("{e}"))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panic!("Not OK");
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
## [v1.1.14] (2023-06-20)
|
## [v1.1.13] (2023-06-20)
|
||||||
|
|
||||||
- NymConnect - add sentry.io reporting ([#3421])
|
- NymConnect - add sentry.io reporting ([#3421])
|
||||||
|
|
||||||
|
|||||||
Generated
+17
-2
@@ -3220,7 +3220,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nym-bin-common"
|
name = "nym-bin-common"
|
||||||
version = "0.5.0"
|
version = "0.6.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atty",
|
"atty",
|
||||||
"clap",
|
"clap",
|
||||||
@@ -3235,7 +3235,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nym-client-core"
|
name = "nym-client-core"
|
||||||
version = "1.1.14"
|
version = "1.1.15"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"base64 0.21.2",
|
"base64 0.21.2",
|
||||||
@@ -3365,6 +3365,7 @@ dependencies = [
|
|||||||
"nym-credential-storage",
|
"nym-credential-storage",
|
||||||
"nym-crypto",
|
"nym-crypto",
|
||||||
"nym-socks5-client-core",
|
"nym-socks5-client-core",
|
||||||
|
"nym-sphinx",
|
||||||
"nym-task",
|
"nym-task",
|
||||||
"pretty_env_logger",
|
"pretty_env_logger",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
@@ -3735,6 +3736,7 @@ dependencies = [
|
|||||||
"nym-pemstore",
|
"nym-pemstore",
|
||||||
"nym-sphinx-addressing",
|
"nym-sphinx-addressing",
|
||||||
"nym-sphinx-params",
|
"nym-sphinx-params",
|
||||||
|
"nym-sphinx-routing",
|
||||||
"nym-sphinx-types",
|
"nym-sphinx-types",
|
||||||
"nym-topology",
|
"nym-topology",
|
||||||
"rand 0.7.3",
|
"rand 0.7.3",
|
||||||
@@ -3760,6 +3762,7 @@ dependencies = [
|
|||||||
"nym-crypto",
|
"nym-crypto",
|
||||||
"nym-sphinx-addressing",
|
"nym-sphinx-addressing",
|
||||||
"nym-sphinx-params",
|
"nym-sphinx-params",
|
||||||
|
"nym-sphinx-routing",
|
||||||
"nym-sphinx-types",
|
"nym-sphinx-types",
|
||||||
"nym-topology",
|
"nym-topology",
|
||||||
"rand 0.7.3",
|
"rand 0.7.3",
|
||||||
@@ -3790,6 +3793,7 @@ dependencies = [
|
|||||||
"nym-sphinx-chunking",
|
"nym-sphinx-chunking",
|
||||||
"nym-sphinx-forwarding",
|
"nym-sphinx-forwarding",
|
||||||
"nym-sphinx-params",
|
"nym-sphinx-params",
|
||||||
|
"nym-sphinx-routing",
|
||||||
"nym-sphinx-types",
|
"nym-sphinx-types",
|
||||||
"nym-topology",
|
"nym-topology",
|
||||||
"rand 0.7.3",
|
"rand 0.7.3",
|
||||||
@@ -3906,6 +3910,7 @@ dependencies = [
|
|||||||
"nym-service-provider-directory-common",
|
"nym-service-provider-directory-common",
|
||||||
"nym-vesting-contract",
|
"nym-vesting-contract",
|
||||||
"nym-vesting-contract-common",
|
"nym-vesting-contract-common",
|
||||||
|
"openssl",
|
||||||
"prost",
|
"prost",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"serde",
|
"serde",
|
||||||
@@ -4045,6 +4050,15 @@ version = "0.1.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "openssl-src"
|
||||||
|
version = "111.26.0+1.1.1u"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "efc62c9f12b22b8f5208c23a7200a442b2e5999f8bdf80233852122b5a4f6f37"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "openssl-sys"
|
name = "openssl-sys"
|
||||||
version = "0.9.82"
|
version = "0.9.82"
|
||||||
@@ -4054,6 +4068,7 @@ dependencies = [
|
|||||||
"autocfg",
|
"autocfg",
|
||||||
"cc",
|
"cc",
|
||||||
"libc",
|
"libc",
|
||||||
|
"openssl-src",
|
||||||
"pkg-config",
|
"pkg-config",
|
||||||
"vcpkg",
|
"vcpkg",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@nym/nym-connect",
|
"name": "@nym/nym-connect",
|
||||||
"version": "1.1.14",
|
"version": "1.1.13",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ nym-crypto = { path = "../../../common/crypto" }
|
|||||||
nym-credential-storage = { path = "../../../common/credential-storage" }
|
nym-credential-storage = { path = "../../../common/credential-storage" }
|
||||||
nym-bin-common = { path = "../../../common/bin-common"}
|
nym-bin-common = { path = "../../../common/bin-common"}
|
||||||
nym-socks5-client-core = { path = "../../../common/socks5-client-core" }
|
nym-socks5-client-core = { path = "../../../common/socks5-client-core" }
|
||||||
|
nym-sphinx = { path = "../../../common/nymsphinx" }
|
||||||
nym-task = { path = "../../../common/task" }
|
nym-task = { path = "../../../common/task" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|||||||
@@ -160,7 +160,11 @@ pub async fn init_socks5_config(provider_address: String, chosen_gateway_id: Str
|
|||||||
config.core.base.client.nym_api_urls = nym_config_common::parse_urls(&raw_validators);
|
config.core.base.client.nym_api_urls = nym_config_common::parse_urls(&raw_validators);
|
||||||
}
|
}
|
||||||
|
|
||||||
let gateway_setup = GatewaySetup::new_fresh(Some(chosen_gateway_id), None);
|
let gateway_setup = if register_gateway {
|
||||||
|
GatewaySetup::new_fresh(Some(chosen_gateway_id), None)
|
||||||
|
} else {
|
||||||
|
GatewaySetup::MustLoad
|
||||||
|
};
|
||||||
|
|
||||||
// Setup gateway by either registering a new one, or reusing exiting keys
|
// Setup gateway by either registering a new one, or reusing exiting keys
|
||||||
let key_store = OnDiskKeys::new(config.storage_paths.common_paths.keys.clone());
|
let key_store = OnDiskKeys::new(config.storage_paths.common_paths.keys.clone());
|
||||||
|
|||||||
@@ -25,6 +25,11 @@ mod tasks;
|
|||||||
mod window;
|
mod window;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
if std::env::var("NYM_CONNECT_ENABLE_MEDIUM").is_ok() {
|
||||||
|
std::env::set_var("NYM_CONNECT_DISABLE_COVER", "1");
|
||||||
|
std::env::set_var("NYM_CONNECT_ENABLE_MIXED_SIZE_PACKETS", "1");
|
||||||
|
std::env::set_var("NYM_CONNECT_DISABLE_PER_HOP_DELAYS", "1");
|
||||||
|
}
|
||||||
setup_env(None);
|
setup_env(None);
|
||||||
println!("Starting up...");
|
println!("Starting up...");
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,10 @@ use nym_client_core::client::base_client::storage::{MixnetClientStorage, OnDiskP
|
|||||||
use nym_client_core::{config::GatewayEndpointConfig, error::ClientCoreStatusMessage};
|
use nym_client_core::{config::GatewayEndpointConfig, error::ClientCoreStatusMessage};
|
||||||
use nym_socks5_client_core::NymClient as Socks5NymClient;
|
use nym_socks5_client_core::NymClient as Socks5NymClient;
|
||||||
use nym_socks5_client_core::Socks5ControlMessageSender;
|
use nym_socks5_client_core::Socks5ControlMessageSender;
|
||||||
|
use nym_sphinx::params::PacketSize;
|
||||||
use nym_task::manager::TaskStatus;
|
use nym_task::manager::TaskStatus;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use std::time::Duration;
|
||||||
use tap::TapFallible;
|
use tap::TapFallible;
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
|
|
||||||
@@ -39,7 +41,7 @@ pub async fn start_nym_socks5_client(
|
|||||||
GatewayEndpointConfig,
|
GatewayEndpointConfig,
|
||||||
)> {
|
)> {
|
||||||
log::info!("Loading config from file: {id}");
|
log::info!("Loading config from file: {id}");
|
||||||
let config = Config::read_from_default_path(id)
|
let mut config = Config::read_from_default_path(id)
|
||||||
.tap_err(|_| log::warn!("Failed to load configuration file"))?;
|
.tap_err(|_| log::warn!("Failed to load configuration file"))?;
|
||||||
|
|
||||||
let storage =
|
let storage =
|
||||||
@@ -53,6 +55,21 @@ pub async fn start_nym_socks5_client(
|
|||||||
.expect("failed to load gateway details")
|
.expect("failed to load gateway details")
|
||||||
.into();
|
.into();
|
||||||
|
|
||||||
|
// Disable both the loop cover traffic that runs in the background as well as the Poisson
|
||||||
|
// process that injects cover traffic into the traffic stream.
|
||||||
|
if std::env::var("NYM_CONNECT_DISABLE_COVER").is_ok() {
|
||||||
|
config.core.base.set_no_cover_traffic();
|
||||||
|
}
|
||||||
|
|
||||||
|
if std::env::var("NYM_CONNECT_ENABLE_MIXED_SIZE_PACKETS").is_ok() {
|
||||||
|
config.core.base.debug.traffic.secondary_packet_size = Some(PacketSize::ExtendedPacket16);
|
||||||
|
}
|
||||||
|
|
||||||
|
if std::env::var("NYM_CONNECT_DISABLE_PER_HOP_DELAY").is_ok() {
|
||||||
|
config.core.base.debug.traffic.average_packet_delay = Duration::ZERO;
|
||||||
|
config.core.base.debug.acknowledgements.average_ack_delay = Duration::ZERO;
|
||||||
|
}
|
||||||
|
|
||||||
log::info!("Starting socks5 client");
|
log::info!("Starting socks5 client");
|
||||||
|
|
||||||
// Channel to send control messages to the socks5 client
|
// Channel to send control messages to the socks5 client
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ plugins {
|
|||||||
id 'com.android.application'
|
id 'com.android.application'
|
||||||
id 'org.jetbrains.kotlin.android'
|
id 'org.jetbrains.kotlin.android'
|
||||||
id 'org.jetbrains.kotlin.plugin.serialization' version '1.8.21'
|
id 'org.jetbrains.kotlin.plugin.serialization' version '1.8.21'
|
||||||
|
id "io.sentry.android.gradle" version "3.11.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
@@ -101,18 +102,20 @@ dependencies {
|
|||||||
|
|
||||||
implementation 'androidx.core:core-ktx:1.10.1'
|
implementation 'androidx.core:core-ktx:1.10.1'
|
||||||
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.1'
|
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.1'
|
||||||
implementation 'androidx.activity:activity-compose:1.5.1'
|
implementation 'androidx.activity:activity-compose:1.7.2'
|
||||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4'
|
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4'
|
||||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4'
|
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4'
|
||||||
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0'
|
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0'
|
||||||
implementation platform('androidx.compose:compose-bom:2022.10.00')
|
implementation platform('androidx.compose:compose-bom:2022.10.00')
|
||||||
implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.6.1'
|
implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.6.1'
|
||||||
|
implementation 'androidx.navigation:navigation-compose:2.6.0'
|
||||||
implementation 'androidx.compose.runtime:runtime-livedata'
|
implementation 'androidx.compose.runtime:runtime-livedata'
|
||||||
implementation 'androidx.compose.ui:ui'
|
implementation 'androidx.compose.ui:ui'
|
||||||
implementation 'androidx.compose.ui:ui-graphics'
|
implementation 'androidx.compose.ui:ui-graphics'
|
||||||
implementation 'androidx.compose.ui:ui-tooling-preview'
|
implementation 'androidx.compose.ui:ui-tooling-preview'
|
||||||
implementation 'androidx.compose.material3:material3'
|
implementation 'androidx.compose.material3:material3'
|
||||||
implementation 'androidx.work:work-runtime-ktx:2.8.1'
|
implementation 'androidx.work:work-runtime-ktx:2.8.1'
|
||||||
|
implementation 'androidx.datastore:datastore-preferences:1.0.0'
|
||||||
testImplementation 'junit:junit:4.13.2'
|
testImplementation 'junit:junit:4.13.2'
|
||||||
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
|
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
|
||||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/Theme.Nyms5"
|
android:theme="@style/Theme.Nyms5"
|
||||||
|
android:enableOnBackInvokedCallback="true"
|
||||||
tools:targetApi="31">
|
tools:targetApi="31">
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
@@ -34,6 +35,20 @@
|
|||||||
android:name="androidx.startup.InitializationProvider"
|
android:name="androidx.startup.InitializationProvider"
|
||||||
android:authorities="${applicationId}.androidx-startup"
|
android:authorities="${applicationId}.androidx-startup"
|
||||||
tools:node="remove" />
|
tools:node="remove" />
|
||||||
|
|
||||||
|
<!-- Sentry -->
|
||||||
|
<meta-data
|
||||||
|
android:name="io.sentry.auto-init"
|
||||||
|
android:value="false" />
|
||||||
|
<!-- enable screenshot for crashes -->
|
||||||
|
<meta-data
|
||||||
|
android:name="io.sentry.attach-screenshot"
|
||||||
|
android:value="true" />
|
||||||
|
<!-- enable view hierarchy for crashes -->
|
||||||
|
<meta-data
|
||||||
|
android:name="io.sentry.attach-view-hierarchy"
|
||||||
|
android:value="true" />
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|||||||
@@ -1,9 +1,21 @@
|
|||||||
package net.nymtech.nyms5
|
package net.nymtech.nyms5
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
|
import android.content.Context
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import androidx.datastore.core.DataStore
|
||||||
|
import androidx.datastore.preferences.core.Preferences
|
||||||
|
import androidx.datastore.preferences.core.booleanPreferencesKey
|
||||||
|
import androidx.datastore.preferences.preferencesDataStore
|
||||||
import androidx.work.Configuration
|
import androidx.work.Configuration
|
||||||
import androidx.work.DelegatingWorkerFactory
|
import androidx.work.DelegatingWorkerFactory
|
||||||
|
import io.sentry.android.core.SentryAndroid
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
|
||||||
|
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")
|
||||||
|
val monitoringKey = booleanPreferencesKey("monitoring")
|
||||||
|
|
||||||
class App : Application(), Configuration.Provider {
|
class App : Application(), Configuration.Provider {
|
||||||
companion object {
|
companion object {
|
||||||
@@ -13,6 +25,30 @@ class App : Application(), Configuration.Provider {
|
|||||||
|
|
||||||
private val tag = "App"
|
private val tag = "App"
|
||||||
|
|
||||||
|
override fun onCreate() {
|
||||||
|
super.onCreate()
|
||||||
|
val app = this
|
||||||
|
|
||||||
|
runBlocking {
|
||||||
|
val monitoring = applicationContext.dataStore.data.map { preferences ->
|
||||||
|
preferences[monitoringKey] ?: false
|
||||||
|
}.first()
|
||||||
|
|
||||||
|
if (monitoring) {
|
||||||
|
Log.i(tag, "Performance monitoring and error reporting enabled")
|
||||||
|
SentryAndroid.init(app) { options ->
|
||||||
|
options.dsn =
|
||||||
|
"https://6872f5818bc147ef9c0fce114fcaac8a@o967446.ingest.sentry.io/4505306218102784"
|
||||||
|
options.enableAllAutoBreadcrumbs(true)
|
||||||
|
|
||||||
|
// TODO should be adjusted in production env
|
||||||
|
options.tracesSampleRate = 1.0
|
||||||
|
options.profilesSampleRate = 1.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun getWorkManagerConfiguration(): Configuration {
|
override fun getWorkManagerConfiguration(): Configuration {
|
||||||
val workerFactory = DelegatingWorkerFactory()
|
val workerFactory = DelegatingWorkerFactory()
|
||||||
// pass in the NymProxy class instance
|
// pass in the NymProxy class instance
|
||||||
|
|||||||
@@ -14,11 +14,21 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
|||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.foundation.verticalScroll
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.ArrowBack
|
||||||
|
import androidx.compose.material.icons.filled.Menu
|
||||||
|
import androidx.compose.material3.CenterAlignedTopAppBar
|
||||||
|
import androidx.compose.material3.DropdownMenu
|
||||||
|
import androidx.compose.material3.DropdownMenuItem
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.LinearProgressIndicator
|
import androidx.compose.material3.LinearProgressIndicator
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.material3.Switch
|
import androidx.compose.material3.Switch
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
@@ -34,6 +44,8 @@ import androidx.lifecycle.repeatOnLifecycle
|
|||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import net.nymtech.nyms5.ui.theme.NymTheme
|
import net.nymtech.nyms5.ui.theme.NymTheme
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
@@ -42,8 +54,17 @@ import androidx.compose.ui.res.painterResource
|
|||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.AnnotatedString
|
import androidx.compose.ui.text.AnnotatedString
|
||||||
import androidx.compose.ui.text.font.FontStyle
|
import androidx.compose.ui.text.font.FontStyle
|
||||||
|
import androidx.datastore.core.DataStore
|
||||||
|
import androidx.datastore.preferences.core.Preferences
|
||||||
|
import androidx.datastore.preferences.core.edit
|
||||||
|
import androidx.navigation.compose.NavHost
|
||||||
|
import androidx.navigation.compose.composable
|
||||||
|
import androidx.navigation.compose.currentBackStackEntryAsState
|
||||||
|
import androidx.navigation.compose.rememberNavController
|
||||||
import androidx.work.WorkInfo
|
import androidx.work.WorkInfo
|
||||||
import androidx.work.WorkManager
|
import androidx.work.WorkManager
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
private val tag = "MainActivity"
|
private val tag = "MainActivity"
|
||||||
@@ -81,18 +102,16 @@ class MainActivity : ComponentActivity() {
|
|||||||
|
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||||
Log.d(tag, "____uiState collect")
|
Log.d(tag, "____UI recompose")
|
||||||
viewModel.uiState.collect {
|
applicationContext.dataStore.data.map { preferences ->
|
||||||
setContent {
|
preferences[monitoringKey] ?: false
|
||||||
NymTheme {
|
}.collect { monitoring ->
|
||||||
// A surface container using the 'background' color from the theme
|
viewModel.uiState.collect {
|
||||||
Surface(
|
setContent {
|
||||||
modifier = Modifier.fillMaxSize(),
|
NymTheme {
|
||||||
color = MaterialTheme.colorScheme.background
|
|
||||||
) {
|
|
||||||
val loading = it.loading
|
val loading = it.loading
|
||||||
|
|
||||||
S5ClientSwitch(it.connected, loading, {
|
HomeScreen(it, monitoring, applicationContext.dataStore) {
|
||||||
if (!loading) {
|
if (!loading) {
|
||||||
when {
|
when {
|
||||||
it -> {
|
it -> {
|
||||||
@@ -106,7 +125,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -114,6 +133,90 @@ class MainActivity : ComponentActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onStart() {
|
||||||
|
super.onStart()
|
||||||
|
viewModel.checkStateSync()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun HomeScreen(
|
||||||
|
proxyState: MainViewModel.ProxyState,
|
||||||
|
monitoring: Boolean,
|
||||||
|
dataStore: DataStore<Preferences>,
|
||||||
|
onSwitch: (value: Boolean) -> Unit,
|
||||||
|
) {
|
||||||
|
val navController = rememberNavController()
|
||||||
|
var expanded by remember { mutableStateOf(false) }
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
|
Scaffold(topBar = {
|
||||||
|
CenterAlignedTopAppBar(
|
||||||
|
title = {
|
||||||
|
Text(stringResource(R.string.app_name))
|
||||||
|
},
|
||||||
|
navigationIcon = {
|
||||||
|
val navBackStackEntry by navController.currentBackStackEntryAsState()
|
||||||
|
val currentRoute = navBackStackEntry?.destination?.route
|
||||||
|
|
||||||
|
if (currentRoute === "proxy") {
|
||||||
|
IconButton(onClick = { expanded = true }) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Filled.Menu,
|
||||||
|
contentDescription = "Main menu"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
DropdownMenu(
|
||||||
|
expanded = expanded,
|
||||||
|
onDismissRequest = { expanded = false }
|
||||||
|
) {
|
||||||
|
DropdownMenuItem(onClick = {
|
||||||
|
navController.navigate("monitoring") {
|
||||||
|
popUpTo("proxy")
|
||||||
|
}
|
||||||
|
expanded = false
|
||||||
|
}, text = {
|
||||||
|
Text("Error reporting")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
IconButton(onClick = {
|
||||||
|
navController.navigate("proxy") {
|
||||||
|
popUpTo("proxy")
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Filled.ArrowBack,
|
||||||
|
contentDescription = "Back home"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}) { contentPadding ->
|
||||||
|
NavHost(
|
||||||
|
navController = navController,
|
||||||
|
startDestination = "proxy",
|
||||||
|
modifier = Modifier.padding(contentPadding)
|
||||||
|
) {
|
||||||
|
composable("proxy") {
|
||||||
|
S5ClientSwitch(
|
||||||
|
connected = proxyState.connected,
|
||||||
|
loading = proxyState.loading,
|
||||||
|
onSwitch = onSwitch
|
||||||
|
)
|
||||||
|
}
|
||||||
|
composable("monitoring") {
|
||||||
|
Monitoring(initialValue = monitoring) {
|
||||||
|
scope.launch(Dispatchers.IO) {
|
||||||
|
dataStore.edit { settings -> settings[monitoringKey] = it }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@@ -198,6 +301,46 @@ fun S5ClientSwitch(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun Monitoring(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
initialValue: Boolean,
|
||||||
|
onSwitch: (value: Boolean) -> Unit,
|
||||||
|
) {
|
||||||
|
var monitoring by remember { mutableStateOf(initialValue) }
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = modifier
|
||||||
|
.padding(16.dp)
|
||||||
|
.verticalScroll(rememberScrollState())
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Text("Enable error reporting")
|
||||||
|
Spacer(modifier = modifier.width(16.dp))
|
||||||
|
Switch(checked = monitoring, onCheckedChange = {
|
||||||
|
monitoring = it
|
||||||
|
onSwitch(it)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Spacer(modifier = modifier.height(18.dp))
|
||||||
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(R.drawable.warning_24),
|
||||||
|
contentDescription = "copy to clipboard",
|
||||||
|
tint = Color.Yellow
|
||||||
|
)
|
||||||
|
Spacer(modifier = modifier.width(16.dp))
|
||||||
|
Text(stringResource(R.string.monitoring_desc_3), color = Color.Yellow)
|
||||||
|
}
|
||||||
|
Spacer(modifier = modifier.height(18.dp))
|
||||||
|
Text(stringResource(R.string.monitoring_desc_1))
|
||||||
|
Spacer(modifier = modifier.height(18.dp))
|
||||||
|
Text(stringResource(R.string.monitoring_desc_2))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Preview
|
@Preview
|
||||||
@Composable
|
@Composable
|
||||||
fun PreviewSocks5Client() {
|
fun PreviewSocks5Client() {
|
||||||
@@ -220,3 +363,18 @@ fun PreviewSocks5Client() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Preview
|
||||||
|
@Composable
|
||||||
|
fun PreviewMonitoring() {
|
||||||
|
NymTheme {
|
||||||
|
Surface(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
color = MaterialTheme.colorScheme.background
|
||||||
|
) {
|
||||||
|
Monitoring(initialValue = false) {
|
||||||
|
Log.d("Monitoring", "switch $it")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ class MainViewModel(
|
|||||||
private val workManager: WorkManager,
|
private val workManager: WorkManager,
|
||||||
private val nymProxy: NymProxy
|
private val nymProxy: NymProxy
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
private val tag = "viewModel"
|
private val tag = "MainViewModel"
|
||||||
|
|
||||||
private val workRequest: OneTimeWorkRequest =
|
private val workRequest: OneTimeWorkRequest =
|
||||||
OneTimeWorkRequestBuilder<ProxyWorker>()
|
OneTimeWorkRequestBuilder<ProxyWorker>()
|
||||||
@@ -56,8 +56,7 @@ class MainViewModel(
|
|||||||
// this viewModel instance is cleared
|
// this viewModel instance is cleared
|
||||||
// use GlobalScope instead
|
// use GlobalScope instead
|
||||||
GlobalScope.launch(Dispatchers.IO) {
|
GlobalScope.launch(Dispatchers.IO) {
|
||||||
// if the proxy process is still running ie. connected
|
// if the proxy process is still running ie. connected kill it
|
||||||
// kill it
|
|
||||||
if (nymProxy.getState() == NymProxy.Companion.State.CONNECTED) {
|
if (nymProxy.getState() == NymProxy.Companion.State.CONNECTED) {
|
||||||
Log.d(tag, "stopping proxy")
|
Log.d(tag, "stopping proxy")
|
||||||
nymProxy.stop()
|
nymProxy.stop()
|
||||||
@@ -131,6 +130,29 @@ class MainViewModel(
|
|||||||
setDisconnected()
|
setDisconnected()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun checkStateSync() {
|
||||||
|
Log.d(tag, "check state sync")
|
||||||
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
val proxyState = nymProxy.getState()
|
||||||
|
val workInfo = workManager.getWorkInfoById(ProxyWorker.workId).get()
|
||||||
|
Log.d(tag, "proxy state $proxyState, work state ${workInfo?.state}")
|
||||||
|
if (proxyState == NymProxy.Companion.State.CONNECTED &&
|
||||||
|
workInfo?.state != WorkInfo.State.RUNNING
|
||||||
|
) {
|
||||||
|
Log.w(tag, "⚠ state desync")
|
||||||
|
Log.i(tag, "stopping proxy")
|
||||||
|
cancelProxyWork()
|
||||||
|
}
|
||||||
|
if (proxyState == NymProxy.Companion.State.DISCONNECTED &&
|
||||||
|
workInfo?.state == WorkInfo.State.RUNNING
|
||||||
|
) {
|
||||||
|
Log.w(tag, "⚠ state desync")
|
||||||
|
Log.i(tag, "stopping worker")
|
||||||
|
workManager.cancelAllWorkByTag(ProxyWorker.workTag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MainViewModelFactory(
|
class MainViewModelFactory(
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<vector android:height="24dp" android:tint="#000000"
|
||||||
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
|
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M1,21h22L12,2 1,21zM13,18h-2v-2h2v2zM13,14h-2v-4h2v4z"/>
|
||||||
|
</vector>
|
||||||
@@ -11,4 +11,8 @@
|
|||||||
<string name="sp_url">https://harbourmaster.nymtech.net/v1/services?size=100</string>
|
<string name="sp_url">https://harbourmaster.nymtech.net/v1/services?size=100</string>
|
||||||
<string name="default_sp">DpB3cHAchJiNBQi5FrZx2csXb1mrHkpYh9Wzf8Rjsuko.ANNWrvHqMYuertHGHUrZdBntQhpzfbWekB39qez9U2Vx@2BuMSfMW3zpeAjKXyKLhmY4QW1DXurrtSPEJ6CjX3SEh</string>
|
<string name="default_sp">DpB3cHAchJiNBQi5FrZx2csXb1mrHkpYh9Wzf8Rjsuko.ANNWrvHqMYuertHGHUrZdBntQhpzfbWekB39qez9U2Vx@2BuMSfMW3zpeAjKXyKLhmY4QW1DXurrtSPEJ6CjX3SEh</string>
|
||||||
<string name="connected_text">Connected to the mixnet</string>
|
<string name="connected_text">Connected to the mixnet</string>
|
||||||
|
<string name="monitoring_desc_1">Help Nym developers to fix errors, crashes and improve the application by enabling this option. If errors occur or if the app crashes, it will automatically send a report. Also it tracks various performance metrics. We use sentry.io service to handle this.</string>
|
||||||
|
<string name="monitoring_desc_2">Note: A report can include your external IP, this can be useful to catch issues related to IP location.
|
||||||
|
All recorded data is used by Nym developers and for app development purposes only.</string>
|
||||||
|
<string name="monitoring_desc_3">You must restart the application for the change to take effect.</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
defaults.project=nym-connect-android
|
||||||
|
defaults.org=nymtech
|
||||||
|
# auth.token=xxx
|
||||||
@@ -9,9 +9,11 @@ pub const DEFAULT_HOPS: usize = 4;
|
|||||||
pub const ROUTING_INFORMATION_LENGTH_BY_STAGE: [u8; DEFAULT_HOPS] =
|
pub const ROUTING_INFORMATION_LENGTH_BY_STAGE: [u8; DEFAULT_HOPS] =
|
||||||
[DEFAULT_ROUTING_INFO_SIZE; DEFAULT_HOPS];
|
[DEFAULT_ROUTING_INFO_SIZE; DEFAULT_HOPS];
|
||||||
pub const MIN_PACKET_SIZE: usize = 48;
|
pub const MIN_PACKET_SIZE: usize = 48;
|
||||||
|
pub const MAGIC_SLICE: &[u8] = &[111, 102, 120];
|
||||||
|
|
||||||
pub const OUTFOX_PACKET_OVERHEAD: usize = MIX_PARAMS_LEN
|
pub const OUTFOX_PACKET_OVERHEAD: usize = MIX_PARAMS_LEN
|
||||||
+ (groupelementbytes() + tagbytes() + DEFAULT_ROUTING_INFO_SIZE as usize) * DEFAULT_HOPS;
|
+ (groupelementbytes() + tagbytes() + DEFAULT_ROUTING_INFO_SIZE as usize) * DEFAULT_HOPS
|
||||||
|
+ MAGIC_SLICE.len();
|
||||||
|
|
||||||
pub const fn groupelementbytes() -> usize {
|
pub const fn groupelementbytes() -> usize {
|
||||||
GROUPELEMENTBYTES as usize
|
GROUPELEMENTBYTES as usize
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use crate::constants::MAGIC_SLICE;
|
||||||
use crate::constants::MIN_MESSAGE_LEN;
|
use crate::constants::MIN_MESSAGE_LEN;
|
||||||
use crate::constants::MIX_PARAMS_LEN;
|
use crate::constants::MIX_PARAMS_LEN;
|
||||||
use chacha20::cipher::InvalidLength;
|
use chacha20::cipher::InvalidLength;
|
||||||
@@ -25,4 +26,6 @@ pub enum OutfoxError {
|
|||||||
},
|
},
|
||||||
#[error("Header length must be {MIX_PARAMS_LEN}, got {0}")]
|
#[error("Header length must be {MIX_PARAMS_LEN}, got {0}")]
|
||||||
InvalidHeaderLength(usize),
|
InvalidHeaderLength(usize),
|
||||||
|
#[error("Invalid magic bytes, expected: {:?}, got: {:?}", MAGIC_SLICE, 0)]
|
||||||
|
InvalidMagicBytes(Vec<u8>),
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-11
@@ -7,7 +7,7 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
constants::{DEFAULT_HOPS, MIN_PACKET_SIZE, MIX_PARAMS_LEN},
|
constants::{DEFAULT_HOPS, MAGIC_SLICE, MIN_PACKET_SIZE, MIX_PARAMS_LEN},
|
||||||
error::OutfoxError,
|
error::OutfoxError,
|
||||||
format::{MixCreationParameters, MixStageParameters},
|
format::{MixCreationParameters, MixStageParameters},
|
||||||
};
|
};
|
||||||
@@ -60,16 +60,19 @@ impl TryFrom<&[u8]> for OutfoxPacket {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl OutfoxPacket {
|
impl OutfoxPacket {
|
||||||
pub fn recover_plaintext(&self) -> Vec<u8> {
|
pub fn recover_plaintext(&self) -> Result<Vec<u8>, OutfoxError> {
|
||||||
let plaintext = self.payload()[self.payload_range()].to_vec();
|
let plaintext = self.payload()[self.payload_range()].to_vec();
|
||||||
if plaintext.starts_with(&[0]) {
|
let mut plaintext = VecDeque::from_iter(plaintext);
|
||||||
let mut plaintext = VecDeque::from_iter(plaintext);
|
while let Some(0) = plaintext.front() {
|
||||||
while let Some(0) = plaintext.front() {
|
plaintext.pop_front();
|
||||||
plaintext.pop_front();
|
}
|
||||||
}
|
let mut plaintext = plaintext.make_contiguous().to_vec();
|
||||||
return plaintext.make_contiguous().to_vec();
|
let payload = plaintext.split_off(MAGIC_SLICE.len());
|
||||||
|
if plaintext != MAGIC_SLICE {
|
||||||
|
Err(OutfoxError::InvalidMagicBytes(plaintext))
|
||||||
|
} else {
|
||||||
|
Ok(payload)
|
||||||
}
|
}
|
||||||
plaintext
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
@@ -100,11 +103,12 @@ impl OutfoxPacket {
|
|||||||
MIN_PACKET_SIZE
|
MIN_PACKET_SIZE
|
||||||
} else {
|
} else {
|
||||||
packet_size
|
packet_size
|
||||||
};
|
} + MAGIC_SLICE.len();
|
||||||
let mix_params = MixCreationParameters::new(packet_size as u16);
|
let mix_params = MixCreationParameters::new(packet_size as u16);
|
||||||
|
|
||||||
let padding = mix_params.total_packet_length() - payload.as_ref().len();
|
let padding = mix_params.total_packet_length() - payload.as_ref().len() - MAGIC_SLICE.len();
|
||||||
let mut buffer = vec![0; padding];
|
let mut buffer = vec![0; padding];
|
||||||
|
buffer.extend_from_slice(MAGIC_SLICE);
|
||||||
buffer.extend_from_slice(payload.as_ref());
|
buffer.extend_from_slice(payload.as_ref());
|
||||||
|
|
||||||
// Last node in the route is a gateway, it will decrypt last, and get the final destination address
|
// Last node in the route is a gateway, it will decrypt last, and get the final destination address
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_packet_params() {
|
fn test_packet_params_short() {
|
||||||
let (node1_pk, node1_pub) = sphinx_packet::crypto::keygen();
|
let (node1_pk, node1_pub) = sphinx_packet::crypto::keygen();
|
||||||
let node1 = Node::new(
|
let node1 = Node::new(
|
||||||
NodeAddressBytes::from_bytes([0u8; NODE_ADDRESS_LENGTH]),
|
NodeAddressBytes::from_bytes([0u8; NODE_ADDRESS_LENGTH]),
|
||||||
@@ -118,7 +118,7 @@ mod tests {
|
|||||||
|
|
||||||
let route = [node1, node2.clone(), node3.clone(), gateway.clone()];
|
let route = [node1, node2.clone(), node3.clone(), gateway.clone()];
|
||||||
|
|
||||||
let payload = randombytes(21);
|
let payload = vec![0, 0, 1, 1, 1, 0, 0];
|
||||||
|
|
||||||
let packet =
|
let packet =
|
||||||
OutfoxPacket::build(&payload, &route, &destination, Some(payload.len())).unwrap();
|
OutfoxPacket::build(&payload, &route, &destination, Some(payload.len())).unwrap();
|
||||||
@@ -140,6 +140,62 @@ mod tests {
|
|||||||
let destination_address = packet.decode_next_layer(&gateway_pk).unwrap();
|
let destination_address = packet.decode_next_layer(&gateway_pk).unwrap();
|
||||||
assert_eq!(destination_address, destination.address.as_bytes());
|
assert_eq!(destination_address, destination.address.as_bytes());
|
||||||
|
|
||||||
assert_eq!(payload, packet.recover_plaintext());
|
assert_eq!(payload, packet.recover_plaintext().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_packet_params_long() {
|
||||||
|
let (node1_pk, node1_pub) = sphinx_packet::crypto::keygen();
|
||||||
|
let node1 = Node::new(
|
||||||
|
NodeAddressBytes::from_bytes([0u8; NODE_ADDRESS_LENGTH]),
|
||||||
|
node1_pub,
|
||||||
|
);
|
||||||
|
let (node2_pk, node2_pub) = sphinx_packet::crypto::keygen();
|
||||||
|
let node2 = Node::new(
|
||||||
|
NodeAddressBytes::from_bytes([1u8; NODE_ADDRESS_LENGTH]),
|
||||||
|
node2_pub,
|
||||||
|
);
|
||||||
|
let (node3_pk, node3_pub) = sphinx_packet::crypto::keygen();
|
||||||
|
let node3 = Node::new(
|
||||||
|
NodeAddressBytes::from_bytes([2u8; NODE_ADDRESS_LENGTH]),
|
||||||
|
node3_pub,
|
||||||
|
);
|
||||||
|
|
||||||
|
let (gateway_pk, gateway_pub) = sphinx_packet::crypto::keygen();
|
||||||
|
let gateway = Node::new(
|
||||||
|
NodeAddressBytes::from_bytes([3u8; NODE_ADDRESS_LENGTH]),
|
||||||
|
gateway_pub,
|
||||||
|
);
|
||||||
|
|
||||||
|
let destination = Destination::new(
|
||||||
|
DestinationAddressBytes::from_bytes([9u8; NODE_ADDRESS_LENGTH]),
|
||||||
|
[0u8; 16],
|
||||||
|
);
|
||||||
|
|
||||||
|
let route = [node1, node2.clone(), node3.clone(), gateway.clone()];
|
||||||
|
|
||||||
|
let payload = randombytes(2048);
|
||||||
|
|
||||||
|
let packet =
|
||||||
|
OutfoxPacket::build(&payload, &route, &destination, Some(payload.len())).unwrap();
|
||||||
|
let packet_bytes = packet.to_bytes().unwrap();
|
||||||
|
println!(
|
||||||
|
"packet bytes length, {}, declared {}",
|
||||||
|
packet_bytes.len(),
|
||||||
|
packet.len()
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut packet = OutfoxPacket::try_from(packet_bytes.as_slice()).unwrap();
|
||||||
|
|
||||||
|
let next_address = packet.decode_next_layer(&node1_pk).unwrap();
|
||||||
|
assert_eq!(next_address, node2.address.as_bytes());
|
||||||
|
let next_address = packet.decode_next_layer(&node2_pk).unwrap();
|
||||||
|
assert_eq!(next_address, node3.address.as_bytes());
|
||||||
|
let next_address = packet.decode_next_layer(&node3_pk).unwrap();
|
||||||
|
assert_eq!(next_address, gateway.address.as_bytes());
|
||||||
|
let destination_address = packet.decode_next_layer(&gateway_pk).unwrap();
|
||||||
|
assert_eq!(destination_address, destination.address.as_bytes());
|
||||||
|
|
||||||
|
assert_eq!(payload, packet.recover_plaintext().unwrap());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+13
-2
@@ -2915,7 +2915,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nym-bin-common"
|
name = "nym-bin-common"
|
||||||
version = "0.5.0"
|
version = "0.6.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atty",
|
"atty",
|
||||||
"clap",
|
"clap",
|
||||||
@@ -3220,6 +3220,7 @@ dependencies = [
|
|||||||
"nym-service-provider-directory-common",
|
"nym-service-provider-directory-common",
|
||||||
"nym-vesting-contract",
|
"nym-vesting-contract",
|
||||||
"nym-vesting-contract-common",
|
"nym-vesting-contract-common",
|
||||||
|
"openssl",
|
||||||
"prost",
|
"prost",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"serde",
|
"serde",
|
||||||
@@ -3297,7 +3298,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nym_wallet"
|
name = "nym_wallet"
|
||||||
version = "1.2.4"
|
version = "1.2.5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"base64 0.13.1",
|
"base64 0.13.1",
|
||||||
@@ -3447,6 +3448,15 @@ version = "0.1.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "openssl-src"
|
||||||
|
version = "111.26.0+1.1.1u"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "efc62c9f12b22b8f5208c23a7200a442b2e5999f8bdf80233852122b5a4f6f37"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "openssl-sys"
|
name = "openssl-sys"
|
||||||
version = "0.9.80"
|
version = "0.9.80"
|
||||||
@@ -3456,6 +3466,7 @@ dependencies = [
|
|||||||
"autocfg",
|
"autocfg",
|
||||||
"cc",
|
"cc",
|
||||||
"libc",
|
"libc",
|
||||||
|
"openssl-src",
|
||||||
"pkg-config",
|
"pkg-config",
|
||||||
"vcpkg",
|
"vcpkg",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"name": "@nymproject/nym-client-wasm",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"sideEffects": false
|
||||||
|
}
|
||||||
@@ -24,6 +24,7 @@
|
|||||||
"prebuild": "yarn build:dependencies",
|
"prebuild": "yarn build:dependencies",
|
||||||
"build": "yarn build:only-this",
|
"build": "yarn build:only-this",
|
||||||
"build:only-this": "scripts/build-prod.sh",
|
"build:only-this": "scripts/build-prod.sh",
|
||||||
|
"prebuild:dev": "yarn build:dependencies",
|
||||||
"build:dev": "yarn build:dev:only-this",
|
"build:dev": "yarn build:dev:only-this",
|
||||||
"build:dev:only-this": "scripts/build.sh"
|
"build:dev:only-this": "scripts/build.sh"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
use nym_cli_commands::context::ClientArgs;
|
||||||
|
use nym_network_defaults::NymNetworkDetails;
|
||||||
|
|
||||||
|
pub(crate) async fn execute(
|
||||||
|
_global_args: ClientArgs,
|
||||||
|
identity_key: nym_cli_commands::validator::mixnet::operators::identity_key::MixnetOperatorsIdentityKey,
|
||||||
|
_network_details: &NymNetworkDetails,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
let res = match identity_key.command {
|
||||||
|
nym_cli_commands::validator::mixnet::operators::identity_key::MixnetOperatorsIdentityKeyCommands::Sign(sign_args) => {
|
||||||
|
nym_cli_commands::validator::mixnet::operators::identity_key::sign(sign_args).await
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(res?)
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ use nym_cli_commands::context::ClientArgs;
|
|||||||
use nym_network_defaults::NymNetworkDetails;
|
use nym_network_defaults::NymNetworkDetails;
|
||||||
|
|
||||||
pub(crate) mod gateways;
|
pub(crate) mod gateways;
|
||||||
|
pub(crate) mod identity_key;
|
||||||
pub(crate) mod mixnodes;
|
pub(crate) mod mixnodes;
|
||||||
pub(crate) mod name;
|
pub(crate) mod name;
|
||||||
pub(crate) mod services;
|
pub(crate) mod services;
|
||||||
@@ -23,5 +24,6 @@ pub(crate) async fn execute(
|
|||||||
) => mixnodes::execute(global_args, mixnode, network_details).await,
|
) => mixnodes::execute(global_args, mixnode, network_details).await,
|
||||||
nym_cli_commands::validator::mixnet::operators::MixnetOperatorsCommands::ServiceProvider(service) => services::execute(global_args, service, network_details).await,
|
nym_cli_commands::validator::mixnet::operators::MixnetOperatorsCommands::ServiceProvider(service) => services::execute(global_args, service, network_details).await,
|
||||||
nym_cli_commands::validator::mixnet::operators::MixnetOperatorsCommands::Name(name) => name::execute(global_args, name, network_details).await,
|
nym_cli_commands::validator::mixnet::operators::MixnetOperatorsCommands::Name(name) => name::execute(global_args, name, network_details).await,
|
||||||
|
nym_cli_commands::validator::mixnet::operators::MixnetOperatorsCommands::IdentityKey(identity_key) => identity_key::execute(global_args, identity_key, network_details).await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user