Compare commits

..

3 Commits

Author SHA1 Message Date
Tommy Verrall f3cc665d78 Update ci-docs.yml
correct missing install
2023-11-02 17:38:50 +01:00
Tommy Verrall f557e000a0 Update ci-docs.yml 2023-11-02 17:37:11 +01:00
Tommy Verrall f12c909f5a Update ci-docs.yml 2023-11-02 17:36:01 +01:00
95 changed files with 473 additions and 1210 deletions
+1 -1
View File
@@ -43,7 +43,7 @@ jobs:
uses: actions-rs/cargo@v1
with:
command: build
args: --workspace --release --features wireguard
args: --workspace --release
- name: Upload Artifact
uses: actions/upload-artifact@v3
@@ -51,7 +51,7 @@ jobs:
uses: actions-rs/cargo@v1
with:
command: build
args: --workspace --release --features wireguard
args: --workspace --release
- name: Prepare build output
shell: bash
+1
View File
@@ -35,6 +35,7 @@ jobs:
--vers "^1.8.0" mdbook-admonish --force && cargo install --vers \
"^0.1.2" mdbook-last-changed && cargo install --vers "^0.1.2" mdbook-theme \
&& cargo install --vers "^0.7.7" mdbook-linkcheck \
&& cargo install mdbook-cmdrun \
# && cd documentation \
# && mdbook-admonish install dev-portal \
# && mdbook-admonish install docs \
+3 -17
View File
@@ -11,7 +11,7 @@ jobs:
fail-fast: false
matrix:
rust: [stable, beta]
os: [ubuntu-20.04, windows-latest, macos-latest]
os: [ubuntu-20.04, windows10, macos-latest]
runs-on: ${{ matrix.os }}
env:
CARGO_TERM_COLOR: always
@@ -34,7 +34,7 @@ jobs:
- name: Install Protoc
uses: arduino/setup-protoc@v2
if: matrix.os == 'macos-latest' || matrix.os == 'windows-latest'
if: matrix.os == 'macos-latest'
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
@@ -56,20 +56,11 @@ jobs:
command: build
args: --release --workspace --examples
# To avoid running out of disk space, skip generating debug symbols
- name: Set debug to false (unix)
if: matrix.os == 'ubuntu-20.04' || matrix.os == 'macos-latest'
- name: Set debug to false
run: |
sed -i.bak 's/\[profile.dev\]/\[profile.dev\]\ndebug = false/' Cargo.toml
git diff
- name: Set debug to false (win)
if: matrix.os == 'windows-latest'
shell: pwsh
run: |
(Get-Content Cargo.toml) -replace '\[profile.dev\]', "`$&`ndebug = false" | Set-Content Cargo.toml
git diff
- name: Run unit tests
uses: actions-rs/cargo@v1
with:
@@ -82,11 +73,6 @@ jobs:
command: test
args: --workspace -- --ignored
- name: Clean
uses: actions-rs/cargo@v1
with:
command: clean
- name: Clippy
uses: actions-rs/cargo@v1
with:
@@ -1,4 +1,4 @@
name: nightly-nym-connect-desktop-build
name: nightly-nym-connect-build
on:
workflow_dispatch:
@@ -10,7 +10,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ubuntu-20.04, macos-latest, windows-latest]
os: [ubuntu-20.04, macos-latest, windows10]
runs-on: ${{ matrix.os }}
env:
CARGO_TERM_COLOR: always
@@ -10,7 +10,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ubuntu-20.04, macos-latest, windows-latest]
os: [ubuntu-20.04, macos-latest, windows10]
runs-on: ${{ matrix.os }}
env:
CARGO_TERM_COLOR: always
Generated
+5 -21
View File
@@ -2994,7 +2994,7 @@ dependencies = [
[[package]]
name = "extension-storage"
version = "1.2.1"
version = "1.2.0"
dependencies = [
"bip39",
"console_error_panic_hook",
@@ -4282,15 +4282,6 @@ version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6"
[[package]]
name = "ipnetwork"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8eca9f51da27bc908ef3dd85c21e1bbba794edaf94d7841e37356275b82d31e"
dependencies = [
"serde",
]
[[package]]
name = "ipnetwork"
version = "0.18.0"
@@ -5568,7 +5559,7 @@ dependencies = [
[[package]]
name = "mix-fetch-wasm"
version = "1.2.1"
version = "1.2.0"
dependencies = [
"async-trait",
"futures",
@@ -6269,7 +6260,7 @@ dependencies = [
[[package]]
name = "nym-client-wasm"
version = "1.2.1"
version = "1.2.0"
dependencies = [
"anyhow",
"futures",
@@ -6541,7 +6532,6 @@ dependencies = [
"futures",
"humantime-serde",
"hyper",
"ipnetwork 0.16.0",
"lazy_static",
"log",
"nym-api-requests",
@@ -6659,7 +6649,6 @@ dependencies = [
name = "nym-ip-packet-router"
version = "0.1.0"
dependencies = [
"etherparse",
"futures",
"log",
"nym-bin-common",
@@ -6669,13 +6658,9 @@ dependencies = [
"nym-service-providers-common",
"nym-sphinx",
"nym-task",
"nym-wireguard",
"nym-wireguard-types",
"serde",
"serde_json",
"tap",
"thiserror",
"tokio",
]
[[package]]
@@ -6908,7 +6893,6 @@ dependencies = [
"fastrand 2.0.1",
"hmac 0.12.1",
"hyper",
"ipnetwork 0.16.0",
"mime",
"nym-config",
"nym-crypto",
@@ -6969,7 +6953,7 @@ dependencies = [
[[package]]
name = "nym-node-tester-wasm"
version = "1.2.1"
version = "1.2.0"
dependencies = [
"futures",
"js-sys",
@@ -7571,7 +7555,7 @@ dependencies = [
[[package]]
name = "nym-wasm-sdk"
version = "1.2.1"
version = "1.2.0"
dependencies = [
"mix-fetch-wasm",
"nym-client-wasm",
+2 -2
View File
@@ -74,8 +74,8 @@ pub async fn current_gateways<R: Rng>(
.collect::<Vec<gateway::Node>>();
// we were always filtering by version so I'm not removing that 'feature'
// let filtered_gateways = valid_gateways.filter_by_version(env!("CARGO_PKG_VERSION"));
Ok(valid_gateways)
let filtered_gateways = valid_gateways.filter_by_version(env!("CARGO_PKG_VERSION"));
Ok(filtered_gateways)
}
pub async fn current_mixnodes<R: Rng>(
+1 -16
View File
@@ -6,7 +6,6 @@ use crate::PeerPublicKey;
use base64::{engine::general_purpose, Engine};
use dashmap::DashMap;
use serde::{Deserialize, Serialize};
use std::net::IpAddr;
use std::{fmt, ops::Deref, str::FromStr};
#[cfg(feature = "verify")]
@@ -18,13 +17,11 @@ use sha2::Sha256;
pub type GatewayClientRegistry = DashMap<PeerPublicKey, GatewayClient>;
pub type PendingRegistrations = DashMap<PeerPublicKey, Nonce>;
pub type PrivateIPs = DashMap<IpAddr, Free>;
#[cfg(feature = "verify")]
pub type HmacSha256 = Hmac<Sha256>;
pub type Nonce = u64;
pub type Free = bool;
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(tag = "type", rename_all = "camelCase")]
@@ -75,9 +72,6 @@ pub struct GatewayClient {
#[cfg_attr(feature = "openapi", schema(value_type = String, format = Byte))]
pub pub_key: PeerPublicKey,
/// Assigned private IP
pub private_ip: IpAddr,
/// Sha256 hmac on the data (alongside the prior nonce)
#[cfg_attr(feature = "openapi", schema(value_type = String, format = Byte))]
pub mac: ClientMac,
@@ -85,12 +79,7 @@ pub struct GatewayClient {
impl GatewayClient {
#[cfg(feature = "verify")]
pub fn new(
local_secret: &PrivateKey,
remote_public: PublicKey,
private_ip: IpAddr,
nonce: u64,
) -> Self {
pub fn new(local_secret: &PrivateKey, remote_public: PublicKey, nonce: u64) -> Self {
// convert from 1.0 x25519-dalek private key into 2.0 x25519-dalek
#[allow(clippy::expect_used)]
let static_secret = boringtun::x25519::StaticSecret::try_from(local_secret.to_bytes())
@@ -107,12 +96,10 @@ impl GatewayClient {
.expect("x25519 shared secret is always 32 bytes long");
mac.update(local_public.as_bytes());
mac.update(private_ip.to_string().as_bytes());
mac.update(&nonce.to_le_bytes());
GatewayClient {
pub_key: PeerPublicKey::new(local_public),
private_ip,
mac: ClientMac(mac.finalize().into_bytes().to_vec()),
}
}
@@ -134,7 +121,6 @@ impl GatewayClient {
.expect("x25519 shared secret is always 32 bytes long");
mac.update(self.pub_key.as_bytes());
mac.update(self.private_ip.to_string().as_bytes());
mac.update(&nonce.to_le_bytes());
mac.verify_slice(&self.mac)
@@ -223,7 +209,6 @@ mod tests {
let client = GatewayClient::new(
client_key_pair.private_key(),
*gateway_key_pair.public_key(),
"10.0.0.42".parse().unwrap(),
nonce,
);
assert!(client.verify(gateway_key_pair.private_key(), nonce).is_ok())
+6 -14
View File
@@ -11,7 +11,7 @@ mod packet_relayer;
mod platform;
mod registered_peers;
mod setup;
pub mod tun_task_channel;
mod tun_task_channel;
mod udp_listener;
mod wg_tunnel;
@@ -20,7 +20,7 @@ use std::sync::Arc;
// Currently the module related to setting up the virtual network device is platform specific.
#[cfg(target_os = "linux")]
pub use platform::linux::tun_device;
use platform::linux::tun_device;
/// Start wireguard UDP listener and TUN device
///
@@ -32,24 +32,16 @@ pub async fn start_wireguard(
task_client: nym_task::TaskClient,
gateway_client_registry: Arc<GatewayClientRegistry>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
// TODO: make this configurable
// We can optionally index peers by their IP like standard wireguard. If we don't then we do
// plain NAT where we match incoming destination IP with outgoing source IP.
// We can either index peers by their IP like standard wireguard
let peers_by_ip = Arc::new(tokio::sync::Mutex::new(network_table::NetworkTable::new()));
// Alternative 1:
let routing_mode = tun_device::RoutingMode::new_allowed_ips(peers_by_ip.clone());
// Alternative 2:
//let routing_mode = tun_device::RoutingMode::new_nat();
// ... or by their tunnel tag, which is a random number assigned to them
let peers_by_tag = Arc::new(tokio::sync::Mutex::new(wg_tunnel::PeersByTag::new()));
// Start the tun device that is used to relay traffic outbound
let (tun, tun_task_tx, tun_task_response_rx) = tun_device::TunDevice::new(routing_mode);
let (tun, tun_task_tx, tun_task_response_rx) = tun_device::TunDevice::new(peers_by_ip.clone());
tun.start();
// We also index peers by a tag
let peers_by_tag = Arc::new(tokio::sync::Mutex::new(wg_tunnel::PeersByTag::new()));
// If we want to have the tun device on a separate host, it's the tun_task and
// tun_task_response channels that needs to be sent over the network to the host where the tun
// device is running.
+1 -1
View File
@@ -1 +1 @@
pub mod tun_device;
pub(crate) mod tun_device;
@@ -42,42 +42,17 @@ pub struct TunDevice {
// And when we get replies, this is where we should send it
tun_task_response_tx: TunTaskResponseTx,
routing_mode: RoutingMode,
}
pub enum RoutingMode {
// The routing table, as how wireguard does it
AllowedIps(AllowedIpsInner),
peers_by_ip: Arc<tokio::sync::Mutex<PeersByIp>>,
// This is an alternative to the routing table, where we just match outgoing source IP with
// incoming destination IP.
Nat(NatInner),
}
impl RoutingMode {
pub fn new_nat() -> Self {
RoutingMode::Nat(NatInner {
nat_table: HashMap::new(),
})
}
pub fn new_allowed_ips(peers_by_ip: Arc<tokio::sync::Mutex<PeersByIp>>) -> Self {
RoutingMode::AllowedIps(AllowedIpsInner { peers_by_ip })
}
}
pub struct AllowedIpsInner {
peers_by_ip: Arc<tokio::sync::Mutex<PeersByIp>>,
}
pub struct NatInner {
nat_table: HashMap<IpAddr, u64>,
}
impl TunDevice {
pub fn new(
routing_mode: RoutingMode,
// peers_by_ip: Option<Arc<tokio::sync::Mutex<PeersByIp>>>,
peers_by_ip: Arc<tokio::sync::Mutex<PeersByIp>>,
) -> (Self, TunTaskTx, TunTaskResponseRx) {
let tun = setup_tokio_tun_device(
format!("{TUN_BASE_NAME}%d").as_str(),
@@ -94,7 +69,8 @@ impl TunDevice {
tun_task_rx,
tun_task_response_tx,
tun,
routing_mode,
peers_by_ip,
nat_table: HashMap::new(),
};
(tun_device, tun_task_tx, tun_task_response_rx)
@@ -117,9 +93,7 @@ impl TunDevice {
);
// TODO: expire old entries
if let RoutingMode::Nat(nat_table) = &mut self.routing_mode {
nat_table.nat_table.insert(src_addr, tag);
}
self.nat_table.insert(src_addr, tag);
self.tun
.write_all(&packet)
@@ -147,32 +121,30 @@ impl TunDevice {
// Route packet to the correct peer.
match self.routing_mode {
// This is how wireguard does it, by consulting the AllowedIPs table.
RoutingMode::AllowedIps(ref peers_by_ip) => {
let peers = peers_by_ip.peers_by_ip.as_ref().lock().await;
if let Some(peer_tx) = peers.longest_match(dst_addr).map(|(_, tx)| tx) {
log::info!("Forward packet to wg tunnel");
peer_tx
.send(Event::Ip(packet.to_vec().into()))
.await
.tap_err(|err| log::error!("{err}"))
.ok();
return;
}
// This is how wireguard does it, by consulting the AllowedIPs table.
if false {
let peers = self.peers_by_ip.lock().await;
if let Some(peer_tx) = peers.longest_match(dst_addr).map(|(_, tx)| tx) {
log::info!("Forward packet to wg tunnel");
peer_tx
.send(Event::Ip(packet.to_vec().into()))
.await
.tap_err(|err| log::error!("{err}"))
.ok();
return;
}
}
// But we do it by consulting the NAT table.
RoutingMode::Nat(ref nat_table) => {
if let Some(tag) = nat_table.nat_table.get(&dst_addr) {
log::info!("Forward packet to wg tunnel with tag: {tag}");
self.tun_task_response_tx
.send((*tag, packet.to_vec()))
.await
.tap_err(|err| log::error!("{err}"))
.ok();
return;
}
// But we do it by consulting the NAT table.
{
if let Some(tag) = self.nat_table.get(&dst_addr) {
log::info!("Forward packet to wg tunnel with tag: {tag}");
self.tun_task_response_tx
.send((*tag, packet.to_vec()))
.await
.tap_err(|err| log::error!("{err}"))
.ok();
return;
}
}
+2 -2
View File
@@ -7,7 +7,7 @@ pub struct TunTaskTx(mpsc::Sender<TunTaskPayload>);
pub(crate) struct TunTaskRx(mpsc::Receiver<TunTaskPayload>);
impl TunTaskTx {
pub async fn send(
pub(crate) async fn send(
&self,
data: TunTaskPayload,
) -> Result<(), tokio::sync::mpsc::error::SendError<TunTaskPayload>> {
@@ -40,7 +40,7 @@ impl TunTaskResponseTx {
}
impl TunTaskResponseRx {
pub async fn recv(&mut self) -> Option<TunTaskPayload> {
pub(crate) async fn recv(&mut self) -> Option<TunTaskPayload> {
self.0.recv().await
}
}
+2 -1
View File
@@ -9,4 +9,5 @@ Each directory contains a readme with more information about running and contrib
## Scripts
* `bump_versions.sh` allows you to update the ~~`platform_release_version` and~~ `wallet_release_version` variable~~s~~ in the `book.toml` of each mdbook project at once. You can also optionally update the `minimum_rust_version` as well. Helpful for lazy-updating when cutting a new version of the docs.
* `build_all_to_dist.sh` is used by the `ci-dev.yml` and `cd-dev.yml` scripts for building all mdbook projects and moving the rendered html to `../dist/` to be rsynced with various servers.
* `post_process.sh` is a script called by the github CI and CD workflows to post process CSS/image/href links for serving several mdbooks from a subdirectory.
+1 -3
View File
@@ -19,6 +19,4 @@ theme/
theme
theme/*
.idea
notes
.idea
+11 -24
View File
@@ -18,21 +18,14 @@
# User Manuals
- [NymConnect X Monero](tutorials/monero.md)
- [NymConnect X Matrix](tutorials/matrix.md)
- [NymConnect X Telegram](tutorials/telegram.md)
# Code Examples
- [Custom Service Providers](examples/custom-services.md)
- [Apps Using Network Requesters](examples/using-nrs.md)
- [Browser only](examples/browser-only.md)
- [Monorepo examples](examples/monorepo-examples.md)
- [NymConnect Monero](tutorials/monero.md)
- [NymConnect Matrix](tutorials/matrix.md)
- [NymConnect Telegram](tutorials/telegram.md)
# Integrations
- [Integration Options](integrations/integration-options.md)
[//]: # (- [Mixnet Integration]&#40;integrations/mixnet-integration.md&#41;)
- [Mixnet Integration](integrations/mixnet-integration.md)
- [Payment Integration](integrations/payment-integration.md)
# Tutorials
@@ -47,21 +40,15 @@
- [Preparing Your Service](tutorials/cosmos-service/service.md)
- [Preparing Your Service pt2](tutorials/cosmos-service/service-src.md)
- [Querying the Chain](tutorials/cosmos-service/querying.md)
- [Typescript](tutorials/typescript.md)
- [[DEPRECATED] Simple Service Provider](tutorials/simple-service-provider/simple-service-provider.md)
- [Tutorial Overview](tutorials/simple-service-provider/overview.md)
- [Preparing Your User Client Environment](tutorials/simple-service-provider/preparating-env.md)
- [Building Your User Client](tutorials/simple-service-provider/user-client.md)
- [Preparing Your Service Provider Environment](tutorials/simple-service-provider/preparating-env2.md)
- [Building Your Service Provider](tutorials/simple-service-provider/service-provider.md)
- [Sending a Message Through the Mixnet](tutorials/simple-service-provider/sending-message.md)
- [Simple Service Provider](tutorials/simple-service-provider/simple-service-provider.md)
- [Tutorial Overview](tutorials/simple-service-provider/overview.md)
- [Preparing Your User Client Environment](tutorials/simple-service-provider/preparating-env.md)
- [Building Your User Client](tutorials/simple-service-provider/user-client.md)
- [Preparing Your Service Provider Environment](tutorials/simple-service-provider/preparating-env2.md)
- [Building Your Service Provider](tutorials/simple-service-provider/service-provider.md)
- [Sending a Message Through the Mixnet](tutorials/simple-service-provider/sending-message.md)
# Shipyard Builders Hackathon 2023
- [General Info & Resources](shipyard/general.md)
- [Hackathon Challenges](shipyard/challenges-overview.md)
- [A Note on Infrastructure](shipyard/infra.md)
- [Submission Guidelines](shipyard/guidelines.md)
# Events
@@ -1,6 +1,8 @@
# Community Applications
If you would like to share your application here, please submit a pull request to the `main` branch of the `nymtech/dev-portal` [repository](https://github.com/nymtech/dev-portal).
We love seeing our developer community create applications using Nym. If you would like to share your application with the community, please submit a pull request to the `main` branch of the `nymtech/dev-portal` [repository](https://github.com/nymtech/dev-portal).
## <img src='../images/profile_picture/pastenym_ntv_pp.png' style="float: right; width: 75px; height: 75px;">Pastenym
@@ -1,10 +0,0 @@
# Browser only
With the Typescript SDK you can run a Nym client in a webworker - meaning you can connect to the mixnet through the browser without having to worry about any other code than your web framework.
- [NoTrustVerify](https://notrustverify.ch/) have set up an example application using [`mixFetch`](https://sdk.nymtech.net/examples/mix-fetch) to fetch crypto prices from CoinGecko over the mixnet.
- [Website](https://notrustverify.github.io/mixfetch-examples/)
- [Codebase](https://github.com/notrustverify/mixfetch-examples)
- There is a coconut-scheme based Credential Library playground [here](https://coco-demo.nymtech.net/). This is a WASM implementation of our Coconut libraries which generate raw Coconut credentials. Test it to create and re-randomize your own credentials. For more information on what is happening here check out the [Coconut docs](https://nymtech.net/docs/coconut.html).
- You can find a browser-based 'hello world' chat app [here](https://chat-demo.nymtech.net). Either open in two browser windows and send messages to yourself, or share with a friend and send messages to each other through the mixnet.
@@ -1,16 +0,0 @@
# Custom Services
Custom services involve two pieces of code that communicate via the mixnet: a client, and a custom server/service. This custom service will most likely interact with the wider internet / a clearnet service on your behalf, with the mixnet between you and the service, acting as a privacy shield.
- PasteNym is a private pastebin alternative. It involves a browser-based frontend utilising the Typescript SDK and a Python-based backend service communicating with a standalone Nym Websocket Client. **If you're a Python developer, start here!**.
- [Frontend codebase](https://github.com/notrustverify/pastenym)
- [Backend codebase](https://github.com/notrustverify/pastenym-frontend)
- Nostr-Nym is another application written by [NoTrustVerify](https://notrustverify.ch/), standing between mixnet users and a Nostr server in order to protect their metadata from being revealed when gossiping. **Useful for Go and Python developers**.
- [Codebase](https://github.com/notrustverify/nostr-nym)
- Spook and Nym-Ethtx are both examples of Ethereum transaction broadcasters utilising the mixnet, written in Rust. Since they were written before the release of the Rust SDK, they utilise standalone clients to communicate with the mixnet.
- [Spook](https://github.com/EdenBlockVC/spook) (**Typescript**)
- [Nym-Ethtx](https://github.com/noot/nym-ethtx) (**Rust**)
- NymDrive is an early proof of concept application for privacy-enhanced file storage on IPFS. **JS and CSS**, and a good example of packaging as an Electrum app.
- [Codebase](https://github.com/saleel/nymdrive)
@@ -1,5 +0,0 @@
# Monorepo examples
As well as these examples, there are a bunch of examples for each SDK in the Nym monorepo.
- [Rust SDK examples](https://github.com/nymtech/nym/tree/develop/sdk/rust/nym-sdk/examples)
- [Typescript SDK examples](https://github.com/nymtech/nym/tree/develop/sdk/typescript/examples)
@@ -1,17 +0,0 @@
# Apps Using Network Requesters
These applications utilise custom app logic in the user-facing apps in order to communicate using the mixnet as a transport layer, without having to rely on custom server-side logic. Instead, they utilise existing Nym infrastructure - [Network Requesters](https://nymtech.net/operators/nodes/network-requester-setup.html) - with a custom whitelist addition.
If you are sending 'normal' application traffic, and/or don't require and custom logic to be happening on the 'other side' of the mixnet, this is most likely the best option to take as a developer who wishes to privacy-enhance their application.
> Nym will soon be switching from a whitelist-based approach to a blocklist-based approach to filtering traffic. As such, it will soon be even easier for developers to utilise the mixnet, as they will not have to run their own NRs or have to add their domains to the whitelist
- DarkFi over Nym leverages Nyms mixnet as a pluggable transport for DarkIRC, their p2p IRC variant. Users can anonymously connect to peers over the network, ensuring secure and private communication within the DarkFi ecosystem. Written in **Rust**.
- [Docs](https://darkrenaissance.github.io/darkfi/clients/nym_outbound.html?highlight=nym#3--run)
- [Github](https://github.com/darkrenaissance/darkfi/tree/master/doc)
- MiniBolt is a complete guide to building a Bitcoin & Lightning full node on a personal computer. It has the capacity to run network traffic (transactions and syncing) over the mixnet, so you can privately sync your node and not expose your home IP to the wider world when interacting with the rest of the network!
- [Docs](https://v2.minibolt.info/bonus-guides/system/nym-mixnet#proxying-bitcoin-core)
- [Codebase](https://github.com/minibolt-guide/minibolt)
- Email over Nym is a set of configuration options to set up a Network Requester to send and recieve emails over Nym, using something like Thunderbird.
- [Codebase](https://github.com/dial0ut/nymstr-email)
@@ -4,11 +4,43 @@ Discover the workings of Nym's privacy-enhancing mixnet infrastructure through t
<iframe width="700" height="400" src="https://www.youtube.com/embed/rnPpEsJS4FM" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
### Mixnet Infrastructure
There are few types of Nym infrastructure nodes:
#### Mix Nodes
Mix nodes play a critical role in the Nym network by providing enhanced security and privacy to network content and metadata. They are part of the three-layer mixnet that ensures that communication remains anonymous and untraceable. Mix nodes receive `NYM` tokens as compensation for their quality of service, which is measured by the network validators.
Mix nodes anonymously relay encrypted Sphinx packets between each other, adding an extra layer of protection by reordering and delaying the packets before forwarding them to the intended recipient. Additionally, cover traffic is maintained through mix nodes sending Sphinx packets to other mix nodes, making it appear as if there is a constant flow of user messages and further protecting the privacy of legitimate data packets.
With the ability to hide, reorder and add a delay to network traffic, mix nodes make it difficult for attackers to perform time-based correlation attacks and deanonymize users. By consistently delivering high-quality service, mix nodes are rewarded with NYM tokens, reinforcing the integrity of the Nym network.
#### Gateways
Gateways serve as the point of entry for user data into the mixnet, verifying that users have acquired sufficient NYM-based bandwidth credentials before allowing encrypted packets to be forwarded to mixnodes. They are also responsible for safeguarding against denial of service attacks and act as a message storage for users who may go offline.
Gateways receive bandwidth credentials from users, which are periodically redeemed for `NYM` tokens as payment for their services. Users have the flexibility to choose a single gateway, split traffic across multiple gateways, run their own gateways, or a combination of these options.
In addition, gateways also cache messages, functioning as an inbox for users who are offline. By providing secure, reliable access to the mixnet and ensuring that data remains protected, gateways play a crucial role in maintaining the integrity of the Nym network.
#### Validators
Validators are essential to the security and integrity of the Nym network, tasked with several key responsibilities. They utilize proof-of-stake Sybil defense measures to secure the network and determine which nodes are included within it. Through their collaborative efforts, validators create Coconut threshold credentials which provide anonymous access to network data and resources.
Validators also play a critical role in maintaining the Nym Cosmos blockchain, a secure, public ledger that records network-wide information such as node public information and keys, network configuration parameters, CosmWasm smart contracts, and `NYM` and credential transactions.
#### Service Providers
Service Providers are a crucial aspect of the Nym infrastructure that support the application layer of the Nym network. Any application built with Nym will require a Service Provider, which can be created by anyone. Service Providers run a piece of binary code that enables them to handle requests from Nym users or other services, and then make requests to external servers on behalf of the users.
For example, a Service Provider could receive a request to check a mail server and then forward the response to the user. The presence of Service Providers in the Nym network enhances its security and privacy, making it a reliable and robust platform for anonymous communication and data exchange.
### Where do I go from here? 💭
For more in-depth information on the network architecture, head to the [Network Overview page](https://nymtech.net/docs/architecture/network-overview.html), and check out the [Operators book](https://nymtech.net/operators) if you want to run a node yourself.
Maybe you would like to concentrate on building a application that uses the mixnet:
If you would like to concentrate on building an application that uses the mixnet:
* Explore the [Quickstart](../quickstart/overview.md) options.
* Check out examples of [Community Apps](../community-resources/community-applications-and-guides.md).
* Run through the [Rust SDK](../tutorials/rust-sdk.md) or [Typescript](../tutorials/typescript.md) tutorials.
* Explore the Tutorials section of the Developer Portal. Our in-depth tutorial on [Building a Simple Service Provider](../tutorials/simple-service-provider/simple-service-provider.md) give a good understanding of building User Clients and Service Providers in TypeScript, and how to configure Nym Websocket Clients for seamless communication with the mixnet.
* Get started with using the Nym Mixnet quickly and easily by exploring the [Quickstart](../quickstart/overview.md) options, such a NymConnect, proxying traffic through the Nym Socks5 client, or dive into integrating Nym into your existing application with the [Integrations](../integrations/integration-options.md) section.
Or perhaps you a developer that would like to run a infrastructure node such as a Gateway, Mix node or Network Requestor:
* Check out the [Network Overview](https://nymtech.net/docs/architecture/network-overview.html) docs page.
* Take a look at our [Node Setup Guide](https://nymtech.net/operators/nodes/setup-guides.html) with our Nym Docs, containing setup guides for setting up you own infrastructure node.
@@ -1,14 +1,10 @@
# Integration Options
If you've already gone through the different [Quick Start](../quickstart/overview.md) options and had a look at the tutorials, you have seen the possibilities available to you for quickly connecting existing application code to another Nym process.
If you've already gone through the different [Quick Start](../quickstart/overview.md) options, you have seen the possibilities avaliable to you for quickly connecting existing application code to another Nym process.
Below are a resources that will be useful for either beginning to integrate mixnet functionality into existing application code or build a new app using Nym.
This section assumes you wish to integrate with Nym into your application code.
- **We suggest you begin with this [integration decision tree](https://sdk.nymtech.net/integrations)**. This will give you a better idea of what pieces of software (SDKs, standalone clients, service providers) your integration might involve, and what is currently possible to do with as little custom code as possible.
- The [integrations FAQ](../faq/integrations-faq.md) has a list of common questions regarding integrating with Nym and Nyx, as well as commonly required links.
- To get an idea of what is possible / has already been built, check the [community applications and resources](../community-resources/community-applications-and-guides.md) page, as well as the [developer tutorials codebase](https://github.com/nymtech/developer-tutorials).
> If you wish to integrate with the Nyx blockchain to use `NYM` for payments, start with the [payment integration](./payment-integration.md) page.
The [integrations FAQ](../faq/integrations-faq.md) has a list of common questions regarding integrating with Nym and Nyx, as well as commonly required links. _This is a good place to start to get an overall idea of the tools and software avaliable to you_.
If you wish to integrate with Nym to use the mixnet for application traffic, start with the [mixnet integration](./mixnet-integration.md) page.
If you wish to integrate with the Nyx blockchain to use `NYM` for payments, start with the [payment integration](./payment-integration.md) page.
@@ -13,7 +13,7 @@ As outlined in the [clients overview documentation](https://nymtech.net/docs/cli
#### Websocket client
Your first option is the native websocket client. This is a compiled program that can run on Linux, Mac OS X, and Windows machines. It runs as a persistent process on a desktop or server machine. You can connect to it with any language that supports websockets.
[//]: # (You can see an example of how to connect to and manage interactions with this client in the [Simple Service Provider tutorial]&#40;../tutorials/simple-service-provider/simple-service-provider.md&#41;.)
You can see an example of how to connect to and manage interactions with this client in the [Simple Service Provider tutorial](../tutorials/simple-service-provider/simple-service-provider.md).
#### Webassembly client
If youre working in JavaScript or Typescript in the browser, or building an edge computing app, youll likely want to choose the webassembly client.
@@ -1,53 +0,0 @@
# Hackathon Challenges
There are a few different challenges to choose from, each with different approaches. It is also recommended to check out the _**Examples**_ directory above for inspiration.
## Tooling challenge
The tooling challenge involves creating tooling for users, operators, or developers of Nym.
### Examples of user-centric tools:
- Facilitate onboarding new users more easily to staking their Nym, and understanding the pros and cons, as well as finding a good node to stake on. Examples of tools like this:
- [ExploreNym dashboard](https://explorenym.net/)
- Show information on a dashboard about the network. NOTE due to the amount of dashboards currently available, we expect a good justification for why / something to set this apart from existing ones e.g. it is presenting information that is not already presented, or it is presented in a different manner, such as a TUI or CLI app instead of a web dashboard - maybe an onion service, or no-JS site for those who do not wish to enable Javascript in their day-to-day browsing. Examples of tools like this:
- [NTV's node dashboard](https://status.notrustverify.ch/d/CW3L7dVVk/nym-mixnet?orgId=1)
- [IsNymUp dashboard](https://isnymup.com/)
### Examples of operator-centric tooling:
- An APY calculator for determining different financial outcomes of running a node in different situations.
- Scripting for updating and maintaining nodes. Examples of tools like this:
- [ExploreNym's bash scripts](https://github.com/ExploreNYM/bash-tool)
- Scripting for packaging node binaries for different OSes.
### Examples of developer-centric tooling:
- Tooling for use in development: are there pain points youve found when developing apps with Nym that you have created scripts/hacks/workarounds for? Is there a pain point that youve thought oh it would be great if I could just do X? These are often the best places to start for building out developer tooling - if youve run into this issue, it's very likely someone else already has, or will!
- Interacting with one of the SDKs via FFI: perhaps youre a Go developer who would love to have the functionality of one of the Nym SDKs. Building an FFI tool might be something that would make your life easier, and can be shared with other developers in your situation.
## Integrations challenge
Integration options for Nym are currently relatively restrictive due to the manner in which Nym handles sending and receiving traffic (as unordered Sphinx packets). This challenge will involve (most likely) implementing custom logic for handling Nym traffic for an existing application.
There are several potential avenues developers can take here:
- If your application (or the application you wish to modify) is written in either Javascript or Typescript, and relies on the `fetch` library to make API calls, then you can use its drop-in replacement: [`mixfetch`](). Perhaps you wish to interact with Coingecko, or a private search engine like Kagi without leaking your IP and metadata, or an RPC endpoint.
- Example with [NTVs privacy-preserving Coingecko API](https://github.com/notrustverify/mixfetch-examples)
- [Mixfetch docs examples](https://github.com/nymtech/nym/tree/develop/sdk/typescript/examples)
- If you instead have an application that is able to use any of the SOCKS5, 4a, or 4 protocols (a rule of thumb: if it can communicate over Tor, it will) then you can experiment with using Nym as the transport layer.
- For Rustaceans, check out our [socks5 rust sdk example](https://nymtech.net/docs/sdk/rust.html#socks-client-example).
- For those of you who arent Crustaceans, then you will have to run the [Socks Client]() alongside your application as a separate process. _NOTE If you are taking this route, please make sure to include detailed documentation on how you expect users to do this, as well as including any process management tools, scripts, and configs (e.g. if you use systemd then include the configuration file for the client, as well as initialisation logic) that may streamline this process._
- [NTV's PasteNym backend](https://github.com/notrustverify/pastenym) is a great example of an application with this architecture.
- Nym is not only useful for blockchain-related apps, but for anything that requires network level privacy! Email clients, messaging clients, and decentralised storage are all key elements of the privacy-enabled web. Several of these sorts of apps can be found in the [community apps page](../community-resources/community-applications-and-guides.md).
- There is currently a proof of concept using Rust Libp2p with Nym as a transport layer. Perhaps you can think of an app that uses Gossipsub for p2p communication could benefit from network-level privacy.
- [GossipSub chat example](https://github.com/nymtech/nym/tree/develop/sdk/rust/nym-sdk/examples/libp2p_chat)
- [Chainsafe's Lighthouse Nym PoC](https://github.com/ChainSafe/lighthouse/blob/nym/USE_NYM.md#usage)
- Alternatively if you know of an app that is written in Rust or TS and could benefit from using Nym, you could fork and modify it using the SDKs. Applications such as:
- Magic Wormhole (has a [rust implementation](https://github.com/magic-wormhole/magic-wormhole.rs))
- [Qual](https://github.com/qaul/qaul.net) (uses Rust Libp2p)
- [Syncthing](https://github.com/syncthing/syncthing)
### MiniApp challenge
Write an app, either using one of the SDKs or a standalone client (harder). Think of what you can nymify e.g. a version of the [TorBirdy](https://support.torproject.org/glossary/torbirdy/) extension that uses Nym instead of Tor. This is very similar to the Integration challenge in terms of the different potential _architectures_ and approaches, but just for new applications.
@@ -1,16 +0,0 @@
# General Info & Resources
Discussions and announcements will be taking place in the [builders channel on Matrix](https://matrix.to/#/#shipyardbuilders:nymtech.chat). This channel can be used for all discussions.
There will be daily office horse between 12-14:00 CET.
This is an open call and questions will be answered on a first come first serve basis.
The timetable can be found on the [Shipyard website](https://nymtech.net/learn/shipyard).
## Links
- You can find **code examples**, **tutorials**, & **quickstart** information here, on the Developer Portal.
- [Rust SDK docs](https://nymtech.net/docs/sdk/rust.html)
- [Typescript SDK docs](https://sdk.nymtech.net)
- [Platform docs](https://nymtech.net/docs)
- [NoTrustVerify's Awesome Nym list](https://github.com/notrustverify/awesome-nym)
- [Builders channel Matrix](https://matrix.to/#/#shipyardbuilders:nymtech.chat)
@@ -1,12 +0,0 @@
# Submission Guidelines
We expect to see the following for submissions:
- Working code demos hosted publicly (Gitlab, Github, some other git instance).
- Quality > quantity here: wed prefer to see a contained, working, and well documented Proof of Concept over a sprawling and messy app that does more but is poorly explained and presented. _The repo must be open source and able to be used and modified by others. The license is up to you._
- If you already have existing apps / projects you are more than welcome to extend them, instead of starting from scratch - we will only be looking at the NEW additions to make this fair. If you are doing this, make sure to write a detailed account of what it is you;ve added to the existing project, preferably with the possibility to see the old version as well as the new one.
- Proper documentation:
- If an app / tool:
- How do you install and run the code? How is it to be used?
- An overview of the application architecture: what is it doing? Is it relying on other services?
- If a UI-based solution:
- How to run it locally? We are happy to also accept staging deployments as part of the submission (e.g. via Vercel) but this does not replace being able to run it locally.
- Please make sure that your application works on commonly reproducible system environments (e.g. if youre developing on Artix Linux please check for the necessary dependencies for more common-place OSes such as Debian, or Arch). If you are developing on Windows please make sure that it works on non-Windows machines also. Where possible please try to include build and install instructions for a variety of OSes.
@@ -1,12 +0,0 @@
# A Note on Infrastructure
If you are writing an application that requires sending messages through the mixnet, then you will either be relying on existing infrastructure nodes (network requesters), or writing your own custom service (for example, the service written as part of the Rust SDK tutorial).
If you are relying on network requesters then chances are that the IPs or domains your app relies on will not already be on the whitelist. Ideally, you would [run your own,](https://nymtech.net/operators/nodes/network-requester-setup.html) but we will also run a few nodes in open proxy mode and share the addresses so that you can use them when beginning to develop.
## Node Details:
- NR1
- Location: Singapore
- Nym Address: `FDeWfd8q686PWLXJDCqNJTCbydTk1KSux5HVftimsPyx.9XyThN4yh92eTMuLp1NvWicRZob8Ei5xpba9dvcMLxcN@9Byd9VAtyYMnbVAcqdoQxJnq76XEg2dbxbiF5Aa5Jj9J`
- NR2
- Location: Frankfurt
- Nym Address: `BNypKaGiGY8GNRN4gpV95GcaVS8n7CrHuoZNgQ2ezqv2.ACpaixzuaSzuMajVQj6aR7cbpbvp676tm21MiLbX1gni@678qVUJ21uwxZBhp3r56z7GRf6gMh3NYDHruTegPtgMf`
@@ -1,12 +1,5 @@
# Building a Simple Service Provider
```admonish warning
This tutorial was written before the creation of the [Typescript SDK](https://sdk.nymtech.net), and involves running a Nym Client alongside your application processes, instead of relying on the SDK to integrate the Client process into your application logic.
As such, although this tutorial is still a valid way of approaching building on Nym, it is a little less streamlined than it could be.
A more streamlined rewrite of this tutorial will be coming soon.
```
This tutorial is the best place to start for developers new to Nym. You will learn how to build a minimum viable privacy-enabled application (PEApp) able to send and receive traffic via the mixnet.
This tutorial is less about building an immediately useful application, and more about beginning to understand:
-3
View File
@@ -6,9 +6,6 @@ language = "en"
multilingual = false # for the moment - ideally work on chinese, brazillian, spanish next
src = "src"
[rust]
edition = "2018"
#################
# PREPROCESSORS #
#################
+1 -14
View File
@@ -29,20 +29,7 @@
# SDK
- [Typescript SDK](sdk/typescript.md)
- [Rust SDK](sdk/rust/rust.md)
- [Message Types](sdk/rust/message-types.md)
- [Message Helpers](sdk/rust/message-helpers.md)
- [Troubleshooting](sdk/rust/troubleshooting.md)
- [Examples](sdk/rust/examples.md)
- [Simple Send](sdk/rust/examples/simple.md)
- [Create and Store Keys](sdk/rust/examples/keys.md)
- [Manual Storage](sdk/rust/examples/storage.md)
- [Anonymous Replies](sdk/rust/examples/surbs.md)
- [Use Custom Network Topology](sdk/rust/examples/custom-network.md)
- [Socks Proxy](sdk/rust/examples/socks.md)
- [Split Send and Receive](sdk/rust/examples/split-send.md)
- [Testnet Bandwidth Cred](sdk/rust/examples/credential.md)
- [Example Cargo file](sdk/rust/examples/cargo.md)
- [Rust SDK](sdk/rust.md)
# Wallet
- [Desktop Wallet](wallet/desktop-wallet.md)
@@ -5,7 +5,7 @@ When you send data across the internet, it can be recorded by a wide range of ob
Even if the content of a network request is encrypted, observers can still see that data was transmitted, its size, frequency of transmission, and gather metadata from unencrypted parts of the data (such as IP routing information). Adversaries may then combine all the leaked information to probabilistically de-anonymize users.
The Nym mixnet provides very strong security guarantees against this sort of surveillance. It _packetises_ and _mixes_ together IP traffic from many users inside the _mixnet_.
The Nym mixnet provides very strong security guarantees against this sort of surveillance. It _packetizes_ and _mixes_ together IP traffic from many users inside the _mixnet_.
> If you're into comparisons, the Nym mixnet is conceptually similar to other systems such as Tor, but provides improved protections against end-to-end timing attacks which can de-anonymize users. When Tor was first fielded, in 2002, those kinds of attacks were regarded as science fiction. But the future is now here.
@@ -69,7 +69,7 @@ From your Nym client, your encrypted traffic is sent to:
Whatever is on the 'other side' of the mixnet from your client, all traffic will travel this way through the mixnet. If you are sending traffic to a service external to Nym (such as a chat application's servers) then your traffic will be sent from the recieving Nym client to an application that will proxy it 'out' of the mixnet to these servers, shielding your metadata from them. P2P (peer-to-peer) applications, unlike the majority of apps, might want to keep all of their traffic entirely 'within' the mixnet, as they don't have to necessarily make outbound network requests to application servers. They would simply have their local application code communicate with their Nym clients, and not forward traffic anywhere 'outside' of the mixnet.
## Acks & Package Retransmission
Whenever a hop is completed, the receiving node will send back an acknowledgement ('ack') so that the sending node knows that the packet was received. If it does not receive an ack after sending, it will resend the packet, as it assumes that the packet was dropped for some reason. This is done under the hood by the binaries themselves, and is never something that developers and node operators have to worry about dealing with themselves.
Whenever a hop is completed, the recieving node will send back an acknowledgement ('ack') so that the sending node knows that the packet was recieved. If it does not recieve an ack after sending, it will resend the packet, as it assumes that the packet was dropped for some reason. This is done under the hood by the binaries themselves, and is never something that developers and node operators have to worry about dealing with themselves.
Packet retransmission means that if a client sends 100 packets to a gateway, but only receives an acknowledgement ('ack') for 95 of them, it will resend those 5 packets to the gateway again, to make sure that all packets are received. All nodes in the mixnet support packet retransmission.
+2 -2
View File
@@ -25,7 +25,7 @@ You need to choose which one you want incorporate into your app. Which one you u
### The websocket client
Your first option is the native websocket client (`nym-client`). This is a compiled program that can run on Linux, Mac OS X, and Windows machines. It can be run as a persistent process on a desktop or server machine. You can connect to it with **any language that supports websockets**.
_Rust developers can import websocket client functionality into their code via the [Rust SDK](../sdk/rust/rust.md)_.
_Rust developers can import websocket client functionality into their code via the [Rust SDK](../sdk/rust.md)_.
### The webassembly client
If you're working in JavaScript or Typescript in the browser, or building an [edge computing](https://en.wikipedia.org/wiki/Edge_computing) app, you'll likely want to choose the webassembly client.
@@ -39,7 +39,7 @@ The `nym-socks5-client` is useful for allowing existing applications to use the
When used as a standalone client, it's less flexible as a way of writing custom applications than the other clients, but able to be used to proxy application traffic through the mixnet without having to make any code changes.
_Rust developers can import socks client functionality into their code via the [Rust SDK](../sdk/rust/rust.md)_.
_Rust developers can import socks client functionality into their code via the [Rust SDK](../sdk/rust.md)_.
## Commonalities between clients
All Nym client packages present basically the same capabilities to the privacy application developer. They need to run as a persistent process in order to stay connected and ready to receive any incoming messages from their gateway nodes. They register and authenticate to gateways, and encrypt Sphinx packets.
+1 -1
View File
@@ -15,7 +15,7 @@ If you're specically looking for TypeScript/JavaScript related information such
**SDK examples:**
* [Typescript SDK](https://sdk.nymtech.net/)
* [Rust SDK](sdk/rust/rust.md)
* [Rust SDK](./sdk/rust.md)
**Nyx**
* [Interacting with the Nyx chain](./nyx/interacting-with-chain.md)
+144
View File
@@ -0,0 +1,144 @@
# Rust SDK
The Rust SDK allows developers building applications in Rust to import and interact with Nym clients as they would any other dependency, instead of running the client as a seperate process on their machine. This makes both developing and running applications much easier, reducing complexity in the development process (not having to restart another client in a seperate console window/tab) and being able to have a single binary for other people to use.
Currently developers can use the Rust SDK to import either websocket client ([`nym-client`](../clients/websocket-client.md)) or [`socks-client`](../clients/socks5-client.md) functionality into their Rust code.
## Development status
The SDK is still somewhat a work in progress: interfaces are fairly stable but still may change in subsequent releases.
The `nym-sdk` crate is **not yet availiable via [crates.io](https://crates.io)**. As such, in order to import the crate you must specify the Nym monorepo in your `Cargo.toml` file:
```toml
nym-sdk = { git = "https://github.com/nymtech/nym" }
```
In order to generate the crate docs run `cargo doc --open` from `nym/sdk/rust/nym-sdk/`
In the future the SDK will be made up of several components, each of which will allow developers to interact with different parts of Nym's infrastructure.
| Component | Functionality | Released |
| --------- | ------------------------------------------------------------------------------------- | -------- |
| Mixnet | Create / load clients & keypairs, subscribe to Mixnet events, send & receive messages | ✔️ |
| Coconut | Create & verify Coconut credentials | 🛠️ |
| Validator | Sign & broadcast Nyx blockchain transactions, query the blockchain | ❌ |
The `mixnet` component currently exposes the logic of two clients: the [websocket client](../clients/websocket-client.md), and the [socks](../clients/socks5-client.md) client.
The `coconut` component is currently being worked on. Right now it exposes logic allowing for the creation of coconut credentials on the Sandbox testnet.
## Websocket client examples
> All the codeblocks below can be found in the `nym-sdk` [examples directory](https://github.com/nymtech/nym/tree/master/sdk/rust/nym-sdk/examples) in the monorepo. Just navigate to `nym/sdk/rust/nym-sdk/examples/` and run the files from there. If you wish to run these outside of the workspace - such as if you want to use one as the basis for your own project - then make sure to import the `sdk`, `tokio`, and `nym_bin_common` crates.
### Different message types
There are two methods for sending messages through the mixnet using your client:
* `send_plain_message()` is the most simple: pass the recipient address and the message you wish to send as a string (this was previously `send_str()`). This is a nicer-to-use wrapper around `send_message()`.
* `send_message()` allows you to also define the amount of SURBs to send along with your message (which is sent as bytes).
### Simple example
Lets look at a very simple example of how you can import and use the websocket client in a piece of Rust code (`examples/simple.rs`):
```rust,noplayground
{{#include ../../../../sdk/rust/nym-sdk/examples/simple.rs}}
```
Simply importing the `nym_sdk` crate into your project allows you to create a client and send traffic through the mixnet.
### Creating and storing keypairs
The example above involves ephemeral keys - if we want to create and then maintain a client identity over time, our code becomes a little more complex as we need to create, store, and conditionally load these keys (`examples/builder_with_storage`):
```rust,noplayground
{{#include ../../../../sdk/rust/nym-sdk/examples/builder_with_storage.rs}}
```
As seen in the example above, the `mixnet::MixnetClientBuilder::new()` function handles checking for keys in a storage location, loading them if present, or creating them and storing them if not, making client key management very simple.
Assuming our client config is stored in `/tmp/mixnet-client`, the following files are generated:
```
$ tree /tmp/mixnet-client
mixnet-client
├── ack_key.pem
├── db.sqlite
├── db.sqlite-shm
├── db.sqlite-wal
├── gateway_details.json
├── gateway_shared.pem
├── persistent_reply_store.sqlite
├── private_encryption.pem
├── private_identity.pem
├── public_encryption.pem
└── public_identity.pem
1 directory, 11 files
```
### Manually handling storage
If you're integrating mixnet functionality into an existing app and want to integrate saving client configs and keys into your existing storage logic, you can manually perform the actions taken automatically above (`examples/manually_handle_keys_and_config.rs`)
```rust,noplayground
{{#include ../../../../sdk/rust/nym-sdk/examples/manually_handle_storage.rs}}
```
### Anonymous replies with SURBs
Both functions used to send messages through the mixnet (`send_message` and `send_plain_message`) send a pre-determined number of SURBs along with their messages by default.
The number of SURBs is set [here](https://github.com/nymtech/nym/blob/master/sdk/rust/nym-sdk/src/mixnet/client.rs#L33).
```rust,noplayground
{{#include ../../../../sdk/rust/nym-sdk/src/mixnet/client.rs:33}}
```
You can read more about how SURBs function under the hood [here](../architecture/traffic-flow.md#private-replies-using-surbs).
In order to reply to an incoming message using SURBs, you can construct a `recipient` from the `sender_tag` sent along with the message you wish to reply to:
```rust,noplayground
{{#include ../../../../sdk/rust/nym-sdk/examples/surb-reply.rs}}
```
### Importing and using a custom network topology
If you want to send traffic through a sub-set of nodes (for instance, ones you control, or a small test setup) when developing, debugging, or performing research, you will need to import these nodes as a custom network topology, instead of grabbing it from the [`Mainnet Nym-API`](https://validator.nymtech.net/api/swagger/index.html) (`examples/custom_topology_provider.rs`).
There are two ways to do this:
#### Import a custom Nym API endpoint
If you are also running a Validator and Nym API for your network, you can specify that endpoint as such and interact with it as clients usually do (under the hood):
```rust,noplayground
{{#include ../../../../sdk/rust/nym-sdk/examples/custom_topology_provider.rs}}
```
#### Import a specific topology manually
If you aren't running a Validator and Nym API, and just want to import a specific sub-set of mix nodes, you can simply overwrite the grabbed topology manually:
```rust,noplayground
{{#include ../../../../sdk/rust/nym-sdk/examples/manually_overwrite_topology.rs}}
```
### Send and receive in different tasks
If you need to split the different actions of your client across different tasks, you can do so like this:
```rust, noplayground
{{#include ../../../../sdk/rust/nym-sdk/examples/parallel_sending_and_receiving.rs}}
```
## Socks client example
There is also the option to embed the [`socks5-client`](../clients/socks5-client.md) into your app code (`examples/socks5.rs`):
```rust,noplayground
{{#include ../../../../sdk/rust/nym-sdk/examples/socks5.rs}}
```
```admonish info
If you are looking at implementing Nym as a transport layer for a crypto wallet or desktop app, this is probably the best place to start.
```
## Coconut credential generation
The following code shows how you can use the SDK to create and use a [credential](../bandwidth-credentials.md) representing paid bandwidth on the Sandbox testnet.
```rust,noplayground
{{#include ../../../../sdk/rust/nym-sdk/examples/bandwidth.rs}}
```
You can read more about Coconut credentials (also referred to as `zk-Nym`) [here](../coconut.md).
@@ -1,12 +0,0 @@
# Examples
All the following examples can be found in the `nym-sdk` [examples directory](https://github.com/nymtech/nym/tree/master/sdk/rust/nym-sdk/examples) in the monorepo. Just navigate to `nym/sdk/rust/nym-sdk/examples/` and run the files from there with:
```sh
cargo run --example <NAME_OF_FILE>
```
If you wish to run these outside of the workspace - such as if you want to use one as the basis for your own project - then make sure to import the `sdk`, `tokio`, and `nym_bin_common` crates.
An example `Cargo.toml` file can be found [here](examples/cargo.md).
@@ -1,35 +0,0 @@
# Example Cargo File
This file imports the basic requirements for running these pieces of example code, and can be used as the basis for your own cargo project.
```toml
[package]
name = "your_app"
version = "x.y.z"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
# Async runtime
tokio = { version = "1.24.1", features = ["rt-multi-thread", "macros"] }
# Used for (de)serialising incoming and outgoing messages
serde = "1.0.152"
serde_json = "1.0.91"
# Nym clients, addressing, etc
nym-sdk = { git = "https://github.com/nymtech/nym", branch = "master" }
nym-sphinx-addressing = { git = "https://github.com/nymtech/nym", branch = "master" }
nym-bin-common = { git = "https://github.com/nymtech/nym", branch = "master" }
nym-sphinx-anonymous-replies = { git = "https://github.com/nymtech/nym", branch = "master" }
# Additional dependencies if you're interacting with Nyx or another Cosmos SDK blockchain
cosmrs = "=0.14.0"
nym-validator-client = { git = "https://github.com/nymtech/nym", branch = "master" }
# If you're building an app with a client and server / serivce this might be a useful structure for your repo
[[bin]]
name = "client"
path = "bin/client.rs"
[[bin]]
name = "service"
path = "bin/service.rs"
```
@@ -1,9 +0,0 @@
# Coconut credential generation
The following code shows how you can use the SDK to create and use a [credential](../../../bandwidth-credentials.md) representing paid bandwidth on the Sandbox testnet.
```rust,noplayground
{{#include ../../../../../../sdk/rust/nym-sdk/examples/bandwidth.rs}}
```
You can read more about Coconut credentials (also referred to as `zk-Nym`) [here](../../../coconut.md).
@@ -1,18 +0,0 @@
# Importing and using a custom network topology
If you want to send traffic through a sub-set of nodes (for instance, ones you control, or a small test setup) when developing, debugging, or performing research, you will need to import these nodes as a custom network topology, instead of grabbing it from the [`Mainnet Nym-API`](https://validator.nymtech.net/api/swagger/index.html) (`examples/custom_topology_provider.rs`).
There are two ways to do this:
## Import a custom Nym API endpoint
If you are also running a Validator and Nym API for your network, you can specify that endpoint as such and interact with it as clients usually do (under the hood):
```rust,noplayground
{{#include ../../../../../../sdk/rust/nym-sdk/examples/custom_topology_provider.rs}}
```
## Import a specific topology manually
If you aren't running a Validator and Nym API, and just want to import a specific sub-set of mix nodes, you can simply overwrite the grabbed topology manually:
```rust,noplayground
{{#include ../../../../../../sdk/rust/nym-sdk/examples/manually_overwrite_topology.rs}}
```
@@ -1,28 +0,0 @@
# Key Creation and Use
The previous example involves ephemeral keys - if we want to create and then maintain a client identity over time, our code becomes a little more complex as we need to create, store, and conditionally load these keys (`examples/builder_with_storage`):
```rust,noplayground
{{#include ../../../../../../sdk/rust/nym-sdk/examples/builder_with_storage.rs}}
```
As seen in the example above, the `mixnet::MixnetClientBuilder::new()` function handles checking for keys in a storage location, loading them if present, or creating them and storing them if not, making client key management very simple.
Assuming our client config is stored in `/tmp/mixnet-client`, the following files are generated:
```
$ tree /tmp/mixnet-client
mixnet-client
├── ack_key.pem
├── db.sqlite
├── db.sqlite-shm
├── db.sqlite-wal
├── gateway_details.json
├── gateway_shared.pem
├── persistent_reply_store.sqlite
├── private_encryption.pem
├── private_identity.pem
├── public_encryption.pem
└── public_identity.pem
1 directory, 11 files
```
@@ -1,8 +0,0 @@
# Simple Send
Lets look at a very simple example of how you can import and use the websocket client in a piece of Rust code (`examples/simple.rs`).
Simply importing the `nym_sdk` crate into your project allows you to create a client and send traffic through the mixnet.
```rust,noplayground
{{#include ../../../../../../sdk/rust/nym-sdk/examples/simple.rs}}
```
@@ -1,10 +0,0 @@
# Socks Proxy
There is also the option to embed the [`socks5-client`](../../../clients/socks5-client.md) into your app code (`examples/socks5.rs`):
```admonish info
If you are looking at implementing Nym as a transport layer for a crypto wallet or desktop app, this is probably the best place to start if they can speak SOCKS5, 4a, or 4.
```
```rust,noplayground
{{#include ../../../../../../sdk/rust/nym-sdk/examples/socks5.rs}}
```
@@ -1,6 +0,0 @@
# Send and Receive in Different Tasks
If you need to split the different actions of your client across different tasks, you can do so like this:
```rust, noplayground
{{#include ../../../../../../sdk/rust/nym-sdk/examples/parallel_sending_and_receiving.rs}}
```
@@ -1,6 +0,0 @@
# Manually Handled Storage
If you're integrating mixnet functionality into an existing app and want to integrate saving client configs and keys into your existing storage logic, you can manually perform the actions taken automatically above (`examples/manually_handle_keys_and_config.rs`)
```rust,noplayground
{{#include ../../../../../../sdk/rust/nym-sdk/examples/manually_handle_storage.rs}}
```
@@ -1,16 +0,0 @@
# Anonymous Replies with SURBs (Single Use Reply Blocks)
Both functions used to send messages through the mixnet (`send_message` and `send_plain_message`) send a pre-determined number of SURBs along with their messages by default.
The number of SURBs is set [here](https://github.com/nymtech/nym/blob/master/sdk/rust/nym-sdk/src/mixnet/client.rs#L33).
```rust,noplayground
{{#include ../../../../../../sdk/rust/nym-sdk/src/mixnet/client.rs:33}}
```
You can read more about how SURBs function under the hood [here](../../../architecture/traffic-flow.md#private-replies-using-surbs).
In order to reply to an incoming message using SURBs, you can construct a `recipient` from the `sender_tag` sent along with the message you wish to reply to:
```rust,noplayground
{{#include ../../../../../../sdk/rust/nym-sdk/examples/surb-reply.rs}}
```
@@ -1,70 +0,0 @@
# Message Helpers
## Handling incoming messages
As seen in the [Chain querier tutorial](https://github.com/nymtech/developer-tutorials/blob/0130ee5a61cd6801bdcfc84608b2a520b5392714/rust/chain-query-service/) when listening out for a response to a sent message (e.g. if you have sent a request to a service, and are awaiting the response) you will want to await [non-empty messages (if you don't know why, read the info on this here)](troubleshooting.md#client-receives-empty-messages-when-listening-for-response). This can be done with something like the helper functions [here](https://github.com/nymtech/developer-tutorials/blob/0130ee5a61cd6801bdcfc84608b2a520b5392714/rust/chain-query-service/src/lib.rs#L71):
```rust
use nym_sdk::mixnet::ReconstructedMessage;
pub async fn wait_for_non_empty_message(
client: &mut MixnetClient,
) -> anyhow::Result<ReconstructedMessage> {
while let Some(mut new_message) = client.wait_for_messages().await {
if !new_message.is_empty() {
return Ok(new_message.pop().unwrap());
}
}
bail!("did not receive any non-empty message")
}
pub fn handle_response(message: ReconstructedMessage) -> anyhow::Result<ResponseTypes> {
ResponseTypes::try_deserialize(message.message)
}
// Note here that the only difference between handling a request and a response
// is that a request will have a sender_tag to parse.
//
// This is used for anonymous replies with SURBs.
pub fn handle_request(
message: ReconstructedMessage,
) -> anyhow::Result<(RequestTypes, Option<AnonymousSenderTag>)> {
let request = RequestTypes::try_deserialize(message.message)?;
Ok((request, message.sender_tag))
}
```
The above helper functions are used as such by the client in tutorial example: it sends a message to the service (what the message is isn't important - just that your client has sent a message _somewhere_ and you are awaiting a response), waits for a _non_empty_ message, then handles it (then logs it - but you can do whatever you want, parse it, etc):
```rust
// [snip]
// Send serialised request to service via mixnet what is await-ed here is
// placing the message in the client's message queue, NOT the sending itself.
let _ = client
.send_message(sp_address, message.serialize(), Default::default())
.await;
// Await a non-empty message
let received = wait_for_non_empty_message(client).await?;
// Handle the response received (the non-empty message awaited above)
let sp_response = handle_response(received)?;
// Match JSON -> ResponseType
let res = match sp_response {
crate::ResponseTypes::Balance(response) => {
println!("{:#?}", response);
response.balance
}
};
// [snip]
```
([repo code on Github here](https://github.com/nymtech/developer-tutorials/blob/0130ee5a61cd6801bdcfc84608b2a520b5392714/rust/chain-query-service/src/client.rs#L19))
## Iterating over incoming messages
It is recommended to use `nym_client.next().await` over `nym_client.wait_for_messages().await` as the latter will return one message at a time which will probably be easier to deal with. See the [parallel send and receive example](https://github.com/nymtech/nym/blob/2993e85c7a17bd5b68171751a48b731b2394ee03/sdk/rust/nym-sdk/examples/parallel_sending_and_receiving.rs#L23-L25) for an example.
## Remember to disconnect your client
You should always **manually disconnect your client** with `client.disconnect().await` as seen in the code examples. This is important as your client is writing to a local DB and dealing with SURB storage.
@@ -1,5 +0,0 @@
# Message Types
[//]: # (TODO expand! )
There are two methods for sending messages through the mixnet using your client:
* `send_plain_message()` is the most simple: pass the recipient address and the message you wish to send as a string (this was previously `send_str()`). This is a nicer-to-use wrapper around `send_message()`.
* `send_message()` allows you to also define the amount of SURBs to send along with your message (which is sent as bytes).
-48
View File
@@ -1,48 +0,0 @@
# Rust SDK
The Rust SDK allows developers building applications in Rust to import and interact with Nym clients as they would any other dependency, instead of running the client as a separate process on their machine. This makes both developing and running applications much easier, reducing complexity in the development process (not having to restart another client in a separate console window/tab) and being able to have a single binary for other people to use.
Currently developers can use the Rust SDK to import either websocket client ([`nym-client`](../../clients/websocket-client.md)) or [`socks-client`](../../clients/socks5-client.md) functionality into their Rust code.
In the future the SDK will be made up of several components, each of which will allow developers to interact with different parts of Nym infrastructure.
| Component | Functionality | Released |
|-----------|---------------------------------------------------------------------------------------|----------|
| Mixnet | Create / load clients & keypairs, subscribe to Mixnet events, send & receive messages | ✔️ |
| Coconut | Create & verify Coconut credentials | 🛠️ |
| Validator | Sign & broadcast Nyx blockchain transactions, query the blockchain | ❌ |
The `mixnet` component currently exposes the logic of two clients: the [websocket client](../../clients/websocket-client.md), and the [socks](../../clients/socks5-client.md) client.
The `coconut` component is currently being worked on. Right now it exposes logic allowing for the creation of coconut credentials on the Sandbox testnet.
### Development status
The SDK is still somewhat a work in progress: interfaces are fairly stable but still may change in subsequent releases.
### Installation
The `nym-sdk` crate is **not yet available via [crates.io](https://crates.io)**. As such, in order to import the crate you must specify the Nym monorepo in your `Cargo.toml` file:
```toml
nym-sdk = { git = "https://github.com/nymtech/nym" }
```
By default the above command will import the current `HEAD` of the default branch, which in our case is `develop`. Assuming instead you wish to pull in another branch (e.g. `master` or a particular release) you can specify this like so:
```toml
# importing HEAD of master branch
nym-sdk = { git = "https://github.com/nymtech/nym", branch = "master" }
# importing HEAD of the third release of 2023, codename 'kinder'
nym-sdk = { git = "https://github.com/nymtech/nym", branch = "release/2023.3-kinder" }
```
You can also define a particular git commit to use as your import like so:
```toml
nym-sdk = { git = "https://github.com/nymtech/nym", rev = "85a7ec9f02ca8262d47eebb6c3b19d832341b55d" }
```
Since the `HEAD` of `master` is always the most recent release, we recommend developers use that for their imports, unless they have a reason to pull in a specific historic version of the code.
### Generate Crate Docs
In order to generate the crate docs run `cargo doc --open` from `nym/sdk/rust/nym-sdk/`
@@ -1,115 +0,0 @@
# Troubleshooting
Below are several common issues or questions you may have.
If you come across something that isn't explained here, [PRs are welcome](https://github.com/nymtech/nym/issues/new/choose).
## Verbose `task client is being dropped` logging
### On client shutdown (expected)
If this is happening at the end of your code when disconnecting your client, this is fine; we just have a verbose client! When calling `client.disconnect().await` this is simply informing you that the client is shutting down.
On client shutdown / disconnect this is to be expected - this can be seen in many of the code examples as well. We use the [`nym_bin_common::logging`](https://github.com/nymtech/nym/blob/develop/common/bin-common/src/logging/mod.rs) import to set logging in our example code. This defaults to `INFO` level.
If you wish to quickly lower the verbosity of your client process logs when developing you can prepend your command with `RUST_LOG=<LOGGING_LEVEL>`.
If you want to run the `builder.rs` example with only `WARN` level logging and below:
```sh
cargo run --example builder
```
Becomes:
```sh
RUST_LOG=warn cargo run --example builder
```
You can also make the logging _more_ verbose with:
```sh
RUST_LOG=debug cargo run --example builder
```
### Not on client shutdown (unexpected)
If this is happening unexpectedly then you might be shutting your client process down too early. See the [accidentally killing your client process](#accidentally-killing-your-client-process-too-early) below for possible explanations and how to fix this issue.
[//]: # (TODO note on poisson dance and not immediately killing client process)
## Accidentally killing your client process too early
If you are seeing either of the following errors when trying to run a client, specifically sending a message, then you may be accidentally killing your client process.
```sh
2023-11-02T10:31:03.930Z INFO TaskClient-BaseNymClient-real_traffic_controller-ack_control-action_controller > the task client is getting dropped
2023-11-02T10:31:04.625Z INFO TaskClient-BaseNymClient-received_messages_buffer-request_receiver > the task client is getting dropped
2023-11-02T10:31:04.626Z DEBUG nym_client_core::client::real_messages_control::acknowledgement_control::input_message_listener > InputMessageListener: Exiting
2023-11-02T10:31:04.626Z INFO TaskClient-BaseNymClient-real_traffic_controller-ack_control-input_message_listener > the task client is getting dropped
2023-11-02T10:31:04.626Z INFO TaskClient-BaseNymClient-real_traffic_controller-reply_control > the task client is getting dropped
2023-11-02T10:31:04.626Z DEBUG nym_client_core::client::real_messages_control > The reply controller has finished execution!
2023-11-02T10:31:04.626Z DEBUG nym_client_core::client::real_messages_control::acknowledgement_control > The input listener has finished execution!
2023-11-02T10:31:04.626Z INFO nym_task::manager > All registered tasks succesfully shutdown
```
```sh
2023-11-02T11:22:08.408Z ERROR TaskClient-BaseNymClient-topology_refresher > Assuming this means we should shutdown...
2023-11-02T11:22:08.408Z ERROR TaskClient-BaseNymClient-mix_traffic_controller > Polling shutdown failed: channel closed
2023-11-02T11:22:08.408Z INFO TaskClient-BaseNymClient-gateway_transceiver-child > the task client is getting dropped
2023-11-02T11:22:08.408Z ERROR TaskClient-BaseNymClient-mix_traffic_controller > Assuming this means we should shutdown...
thread 'tokio-runtime-worker' panicked at 'action control task has died: TrySendError { kind: Disconnected }', /home/.local/share/cargo/git/checkouts/nym-fbd2f6ea2e760da9/a800cba/common/client-core/src/client/real_messages_control/message_handler.rs:634:14
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
2023-11-02T11:22:08.477Z INFO TaskClient-BaseNymClient-real_traffic_controller-ack_control-input_message_listener > the task client is getting dropped
2023-11-02T11:22:08.477Z ERROR TaskClient-BaseNymClient-real_traffic_controller-ack_control-input_message_listener > Polling shutdown failed: channel closed
2023-11-02T11:22:08.477Z ERROR TaskClient-BaseNymClient-real_traffic_controller-ack_control-input_message_listener > Assuming this means we should shutdown...
```
Using the following piece of code as an example:
```rust
use nym_sdk::mixnet::{MixnetClient, MixnetMessageSender, Recipient};
use clap::Parser;
#[derive(Debug, Clone, Parser)]
enum Opts {
Client {
recipient: Recipient
}
}
#[tokio::main]
async fn main() {
let opts: Opts = Parser::parse();
nym_bin_common::logging::setup_logging();
let mut nym_client = MixnetClient::connect_new().await.expect("Could not build Nym client");
match opts {
Opts::Client { recipient } => {
nym_client.send_plain_message(recipient, "some message string").await.expect("send failed");
}
}
}
```
This is a simplified snippet of code for sending a simple hardcoded message with the following command:
```sh
cargo run client <RECIPIENT_NYM_ADDRESS>
```
You might assume that `send`-ing your message would _just work_ as `nym_client.send_plain_message()` is an async function; you might expect that the client will block until the message is actually sent into the mixnet, then shutdown.
However, this is not true.
**This will only block until the message is put into client's internal queue**. Therefore in the above example, the client is being shut down before the message is _actually sent to the mixnet_; after being placed in the client's internal queue, there is still work to be done under the hood, such as route encrypting the message and placing it amongst the stream of cover traffic.
The simple solution? Make sure the program/client stays active, either by calling `sleep`, or listening out for new messages. As sending a one-shot message without listening out for a response is likely not what you'll be doing, then you will be then awaiting a response (see the [message helpers page](message-helpers.md) for an example of this).
Furthermore, you should always **manually disconnect your client** with `client.disconnect().await` as seen in the code examples. This is important as your client is writing to a local DB and dealing with SURB storage.
## Client receives empty messages when listening for response
If you are sending out a message, it makes sense for your client to then listen out for incoming messages; this would probably be the reply you get from the service you've sent a message to.
You might however be receiving messages without data attached to them / empty payloads. This is most likely because your client is receiving a message containing a [SURB request](https://nymtech.net/docs/architecture/traffic-flow.html#private-replies-using-surbs) - a SURB requesting more SURB packets to be sent to the service, in order for them to have enough packets (with a big enough overall payload) to split the entire response to your initial request across.
Whether the `data` of a SURB request being empty is a feature or a bug is to be decided - there is some discussion surrounding whether we can use SURB requests to send additional data to streamline the process of sending large replies across the mixnet.
You can find a few helper functions [here](message-helpers.md) to help deal with this issue in the meantime.
> If you can think of a more succinct or different way of handling this do reach out - we're happy to hear other opinions
-1
View File
@@ -27,7 +27,6 @@ dirs = "4.0"
dotenvy = { workspace = true }
futures = { workspace = true }
humantime-serde = "1.0.1"
ipnetwork = "0.16"
lazy_static = "1.4.0"
log = { workspace = true }
once_cell = "1.7.2"
-1
View File
@@ -128,7 +128,6 @@ impl From<ConfigV1_1_31> for Config {
enabled: value.wireguard.enabled,
bind_address: value.wireguard.bind_address,
announced_port: value.wireguard.announced_port,
private_network_prefix: Default::default(),
storage_paths: nym_node::config::persistence::WireguardPaths {
// no fields (yet)
},
+2 -8
View File
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
use crate::node::storage::error::StorageError;
use nym_ip_packet_router::error::IpPacketRouterError;
use nym_ip_packet_router::error::IpForwarderError;
use nym_network_requester::error::{ClientCoreError, NetworkRequesterError};
use nym_validator_client::nyxd::error::NyxdError;
use nym_validator_client::nyxd::AccountId;
@@ -110,7 +110,7 @@ pub(crate) enum GatewayError {
#[error("there was an issue with the local ip packet router: {source}")]
IpPacketRouterFailure {
#[from]
source: IpPacketRouterError,
source: IpForwarderError,
},
#[error("failed to startup local network requester")]
@@ -134,12 +134,6 @@ pub(crate) enum GatewayError {
// TODO: in the future this should work the other way, i.e. NymNode depending on Gateway errors
#[error(transparent)]
NymNodeError(#[from] nym_node::error::NymNodeError),
#[error("there was an issue with wireguard IP network: {source}")]
IpNetworkError {
#[from]
source: ipnetwork::IpNetworkError,
},
}
impl From<ClientCoreError> for GatewayError {
-7
View File
@@ -4,7 +4,6 @@
use crate::config::Config;
use crate::error::GatewayError;
use crate::node::helpers::load_public_key;
use ipnetwork::IpNetwork;
use log::warn;
use nym_bin_common::bin_info_owned;
use nym_crypto::asymmetric::{encryption, identity};
@@ -17,7 +16,6 @@ use nym_node::http::router::WireguardAppState;
use nym_node::wireguard::types::GatewayClientRegistry;
use nym_sphinx::addressing::clients::Recipient;
use nym_task::TaskClient;
use std::net::{IpAddr, Ipv4Addr};
use std::sync::Arc;
fn load_gateway_details(
@@ -225,17 +223,12 @@ impl<'a> HttpApiBuilder<'a> {
}
}
let wireguard_private_network = IpNetwork::new(
IpAddr::from(Ipv4Addr::new(10, 0, 0, 0)),
self.gateway_config.wireguard.private_network_prefix,
)?;
let wg_state = self.client_registry.map(|client_registry| {
WireguardAppState::new(
self.sphinx_keypair,
client_registry,
Default::default(),
self.gateway_config.wireguard.bind_address.port(),
wireguard_private_network,
)
});
+5 -6
View File
@@ -346,12 +346,11 @@ impl<St> Gateway<St> {
// TODO: well, wire it up internally to gateway traffic, shutdowns, etc.
let (on_start_tx, on_start_rx) = oneshot::channel();
let mut ip_builder =
nym_ip_packet_router::IpPacketRouterBuilder::new(ip_opts.config.clone())
.with_shutdown(shutdown)
.with_custom_gateway_transceiver(Box::new(transceiver))
.with_wait_for_gateway(true)
.with_on_start(on_start_tx);
let mut ip_builder = nym_ip_packet_router::IpForwarderBuilder::new(ip_opts.config.clone())
.with_shutdown(shutdown)
.with_custom_gateway_transceiver(Box::new(transceiver))
.with_wait_for_gateway(true)
.with_on_start(on_start_tx);
if let Some(custom_mixnet) = &ip_opts.custom_mixnet_path {
ip_builder = ip_builder.with_stored_topology(custom_mixnet)?
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "extension-storage"
version = "1.2.1"
version = "1.2.0"
edition = "2021"
license = "Apache-2.0"
repository = "https://github.com/nymtech/nym"
-2
View File
@@ -14,8 +14,6 @@ license.workspace = true
anyhow = { workspace = true }
bytes = "1.5.0"
colored = "2"
ipnetwork = "0.16"
rand = "0.7.3"
serde = { workspace = true, features = ["derive"] }
serde_yaml = "0.9.25"
serde_json = { workspace = true }
-6
View File
@@ -12,7 +12,6 @@ pub mod persistence;
mod serde_helpers;
pub const DEFAULT_WIREGUARD_PORT: u16 = WG_PORT;
pub const DEFAULT_WIREGUARD_PREFIX: u8 = 16;
pub const DEFAULT_HTTP_PORT: u16 = DEFAULT_NYM_NODE_HTTP_PORT;
// TODO: this is very much a WIP. we need proper ssl certificate support here
@@ -76,10 +75,6 @@ pub struct Wireguard {
/// Useful in the instances where the node is behind a proxy.
pub announced_port: u16,
/// The prefix denoting the maximum number of the clients that can be connected via Wireguard.
/// The maximum value for IPv4 is 32 and for IPv6 is 128
pub private_network_prefix: u8,
/// Paths for wireguard keys, client registries, etc.
pub storage_paths: persistence::WireguardPaths,
}
@@ -93,7 +88,6 @@ impl Default for Wireguard {
DEFAULT_WIREGUARD_PORT,
),
announced_port: DEFAULT_WIREGUARD_PORT,
private_network_prefix: DEFAULT_WIREGUARD_PREFIX,
storage_paths: persistence::WireguardPaths {},
}
}
@@ -14,7 +14,6 @@ use nym_crypto::asymmetric::encryption::PublicKey;
use nym_node_requests::api::v1::gateway::client_interfaces::wireguard::models::{
ClientMessage, ClientRegistrationResponse, GatewayClient, InitMessage, Nonce, PeerPublicKey,
};
use rand::{prelude::IteratorRandom, thread_rng};
async fn process_final_message(
client: GatewayClient,
@@ -92,23 +91,8 @@ pub(crate) async fn register_client(
let remote_public = PublicKey::from_bytes(init.pub_key().as_bytes())
.map_err(|_| RequestError::new_status(StatusCode::BAD_REQUEST))?;
let nonce = process_init_message(init, state).await;
let mut private_ip_ref = state
.free_private_network_ips
.iter_mut()
.filter(|r| **r)
.choose(&mut thread_rng())
.ok_or(RequestError::new(
"No more space in the network",
StatusCode::SERVICE_UNAVAILABLE,
))?;
// mark it as used, even though it's not final
*private_ip_ref = false;
let gateway_data = GatewayClient::new(
state.dh_keypair.private_key(),
remote_public,
*private_ip_ref.key(),
nonce,
);
let gateway_data =
GatewayClient::new(state.dh_keypair.private_key(), remote_public, nonce);
let response = ClientRegistrationResponse::PendingRegistration {
nonce,
gateway_data,
@@ -7,10 +7,8 @@ use crate::http::api::v1::gateway::client_interfaces::wireguard::client_registry
use crate::wireguard::types::{GatewayClientRegistry, PendingRegistrations};
use axum::routing::{get, post};
use axum::Router;
use ipnetwork::IpNetwork;
use nym_crypto::asymmetric::encryption;
use nym_node_requests::routes::api::v1::gateway::client_interfaces::wireguard;
use nym_wireguard_types::registration::PrivateIPs;
use std::sync::Arc;
pub(crate) mod client_registry;
@@ -28,7 +26,6 @@ impl WireguardAppState {
client_registry: Arc<GatewayClientRegistry>,
registration_in_progress: Arc<PendingRegistrations>,
binding_port: u16,
private_ip_network: IpNetwork,
) -> Self {
WireguardAppState {
inner: Some(WireguardAppStateInner {
@@ -36,9 +33,6 @@ impl WireguardAppState {
client_registry,
registration_in_progress,
binding_port,
free_private_network_ips: Arc::new(
private_ip_network.iter().map(|ip| (ip, true)).collect(),
),
}),
}
}
@@ -83,7 +77,6 @@ pub(crate) struct WireguardAppStateInner {
client_registry: Arc<GatewayClientRegistry>,
registration_in_progress: Arc<PendingRegistrations>,
binding_port: u16,
free_private_network_ips: Arc<PrivateIPs>,
}
pub(crate) fn routes<S>(initial_state: WireguardAppState) -> Router<S> {
@@ -105,7 +98,6 @@ mod test {
use axum::http::StatusCode;
use dashmap::DashMap;
use hmac::Mac;
use ipnetwork::IpNetwork;
use nym_crypto::asymmetric::encryption;
use nym_node_requests::api::v1::gateway::client_interfaces::wireguard::models::{
ClientMac, ClientMessage, ClientRegistrationResponse, GatewayClient, InitMessage,
@@ -113,8 +105,6 @@ mod test {
};
use nym_node_requests::routes::api::v1::gateway::client_interfaces::wireguard;
use nym_wireguard_types::registration::HmacSha256;
use std::net::IpAddr;
use std::str::FromStr;
use std::sync::Arc;
use tower::Service;
use tower::ServiceExt;
@@ -146,14 +136,6 @@ mod test {
let registration_in_progress = Arc::new(DashMap::new());
let client_registry = Arc::new(DashMap::new());
let free_private_network_ips = Arc::new(
IpNetwork::from_str("10.0.0.0/24")
.unwrap()
.iter()
.map(|ip| (ip, true))
.collect(),
);
let client_private_ip = IpAddr::from_str("10.0.0.42").unwrap();
let state = WireguardAppState {
inner: Some(WireguardAppStateInner {
@@ -161,7 +143,6 @@ mod test {
dh_keypair: Arc::new(gateway_key_pair),
registration_in_progress: Arc::clone(&registration_in_progress),
binding_port: 8080,
free_private_network_ips,
}),
};
@@ -205,13 +186,11 @@ mod test {
let mut mac = HmacSha256::new_from_slice(client_dh.as_bytes()).unwrap();
mac.update(client_static_public.as_bytes());
mac.update(client_private_ip.to_string().as_bytes());
mac.update(&nonce.to_le_bytes());
let mac = mac.finalize().into_bytes();
let finalized_message = ClientMessage::Final(GatewayClient {
pub_key: PeerPublicKey::new(client_static_public),
private_ip: client_private_ip,
mac: ClientMac::new(mac.as_slice().to_vec()),
});
@@ -21,8 +21,7 @@ export const MultiAccountHowTo = ({ show, handleClose }: { show: boolean; handle
}
/>
<Typography>
If you dont have a password set for your account, log out, click on login with password and follow the forgot
my password flow
If you dont have a password set for your account, log out, click on login with password and follow the forgot my password flow
</Typography>
<Typography>
If you already have a password, log in to the wallet using your password then try create/import accounts
+1 -6
View File
@@ -12,11 +12,6 @@
"sdk/typescript/packages/validator-client",
"sdk/typescript/packages/mix-fetch-node",
"sdk/typescript/packages/nodejs-client",
"sdk/typescript/packages/node-tester",
"sdk/typescript/packages/sdk-react",
"sdk/typescript/packages/mix-fetch",
"sdk/typescript/packages/sdk",
"sdk/typescript/codegen/contract-clients",
"ts-packages/*",
"nym-wallet",
"nym-connect/**",
@@ -41,7 +36,7 @@
"prebuild:ci": "yarn dev:on && yarn",
"build:ci": "run-s build:types build:packages build:wasm build:ci:sdk",
"postbuild:ci": "yarn dev:off",
"build:ci:sdk": "lerna run --scope '{@nymproject/sdk,@nymproject/node-tester,@nymproject/contract-clients,@nymproject/sdk-react,@nymproject/mix-fetch,@nymproject/nodejs-client,@nymproject/mix-fetch-node}' build --stream",
"build:ci:sdk": "lerna run --scope '{@nymproject/sdk,@nymproject/node-tester,@nymproject/sdk-react,@nymproject/mix-fetch,@nymproject/nodejs-client,@nymproject/mix-fetch-node}' build --stream",
"docs:prod:build": "run-s docs:prod:build:ws",
"docs:prod:build:ws": "lerna run docs:prod:build --stream",
"sdk:build": "./sdk/typescript/scripts/build-prod-sdk.sh",
@@ -1,6 +1,6 @@
{
"name": "@nymproject/contract-clients",
"version": "1.2.1",
"version": "1.2.0",
"description": "A client for all Nym smart contracts",
"license": "Apache-2.0",
"author": "Nym Technologies SA",
@@ -27,4 +27,4 @@
},
"private": false,
"types": "./dist/index.d.ts"
}
}
@@ -6,8 +6,8 @@
import { CosmWasmClient, SigningCosmWasmClient, ExecuteResult } from "@cosmjs/cosmwasm-stargate";
import { StdFee } from "@cosmjs/amino";
import { Uint128, InstantiateMsg, Coin, ExecuteMsg, Address, NymName, MessageSignature, NameDetails, QueryMsg, MigrateMsg, Addr, PagedNamesListResponse, RegisteredName, NamesListResponse, ConfigResponse, ContractVersion, ContractBuildInformation, Uint32 } from "./NameService.types";
import { GetCw2ContractVersionResponse } from './types';
import { Uint128, InstantiateMsg, Coin, ExecuteMsg, Address, NymName, MessageSignature, NameDetails, NymAddressInner, QueryMsg, MigrateMsg, Addr, PagedNamesListResponse, RegisteredName, NamesListResponse, ConfigResponse, ContractVersion, ContractBuildInformation, Uint32 } from "./NameService.types";
export interface NameServiceReadOnlyInterface {
contractAddress: string;
nameId: ({
@@ -32,7 +32,7 @@ export type ExecuteMsg = {
};
};
export type Address = {
nym_address: NymAddressInner;
nym_address: string;
};
export type NymName = string;
export type MessageSignature = number[];
@@ -41,11 +41,6 @@ export interface NameDetails {
identity_key: string;
name: NymName;
}
export interface NymAddressInner {
client_enc: string;
client_id: string;
gateway_id: string;
}
export type QueryMsg = {
name_id: {
name_id: number;
+6 -6
View File
@@ -1,6 +1,6 @@
{
"name": "@nymproject/ts-sdk-docs",
"version": "1.2.1",
"version": "1.2.0",
"description": "Nym Typescript SDK Docs",
"license": "Apache-2.0",
"author": "Nym Technologies SA",
@@ -28,10 +28,10 @@
"@mui/icons-material": "^5.14.9",
"@mui/lab": "^5.0.0-alpha.145",
"@mui/material": "^5.14.8",
"@nymproject/contract-clients": ">=1.2.1-rc.0 || ^1",
"@nymproject/mix-fetch": ">=1.2.1-rc.0 || ^1",
"@nymproject/mix-fetch-full-fat": ">=1.2.1-rc.0 || ^1",
"@nymproject/sdk-full-fat": ">=1.2.1-rc.0 || ^1",
"@nymproject/contract-clients": ">=1.2.0-rc.10 || ^1",
"@nymproject/mix-fetch": ">=1.2.0-rc.10 || ^1",
"@nymproject/mix-fetch-full-fat": ">=1.2.0-rc.10 || ^1",
"@nymproject/sdk-full-fat": ">=1.2.0-rc.10 || ^1",
"chain-registry": "^1.19.0",
"cosmjs-types": "^0.8.0",
"next": "^13.4.19",
@@ -51,4 +51,4 @@
"typescript": "^4.9.3"
},
"private": false
}
}
@@ -1,21 +1,12 @@
{
"name": "@nymproject/sdk-example-plain-html-parcel",
"version": "1.0.1",
"description": "An example project that uses WASM and plain HTML bundled with Parcel v2",
"version": "1.0.0",
"license": "Apache-2.0",
"scripts": {
"build": "npx parcel build",
"build:serve": "npx serve dist",
"lint": "eslint src",
"lint:fix": "eslint src --fix",
"start": "npx parcel",
"test": "jest",
"test:watch": "jest --watch",
"tsc": "tsc",
"tsc:watch": "tsc --watch"
},
"source": "src/index.html",
"browserslist": "> 0.5%, last 2 versions, not dead",
"dependencies": {
"@nymproject/sdk": ">=1.2.1-rc.0 || ^1"
"@nymproject/sdk": ">=1.2.0-rc.10 || ^1"
},
"devDependencies": {
"@types/jest": "^27.0.1",
@@ -38,7 +29,15 @@
"ts-jest": "^27.0.5",
"typescript": "^4.6.2"
},
"private": false,
"browserslist": "> 0.5%, last 2 versions, not dead",
"source": "src/index.html"
}
"scripts": {
"start": "npx parcel",
"build": "npx parcel build",
"build:serve": "npx serve dist",
"test": "jest",
"test:watch": "jest --watch",
"tsc": "tsc",
"tsc:watch": "tsc --watch",
"lint": "eslint src",
"lint:fix": "eslint src --fix"
}
}
@@ -1,22 +1,10 @@
{
"name": "@nymproject/sdk-example-plain-html",
"version": "1.0.1",
"description": "An example project that uses WASM and plain HTML",
"version": "1.0.0",
"license": "Apache-2.0",
"scripts": {
"build": "webpack build --progress --config webpack.prod.js",
"build:dev": "webpack build --progress",
"build:serve": "npx serve dist",
"lint": "eslint src",
"lint:fix": "eslint src --fix",
"start": "webpack serve --progress --port 3000",
"test": "jest",
"test:watch": "jest --watch",
"tsc": "tsc",
"tsc:watch": "tsc --watch"
},
"dependencies": {
"@nymproject/sdk": ">=1.2.1-rc.0 || ^1"
"@nymproject/sdk": ">=1.2.0-rc.10 || ^1"
},
"devDependencies": {
"@babel/core": "^7.15.0",
@@ -61,5 +49,16 @@
"webpack-dev-server": "^4.5.0",
"webpack-merge": "^5.8.0"
},
"private": false
}
"scripts": {
"start": "webpack serve --progress --port 3000",
"build": "webpack build --progress --config webpack.prod.js",
"build:dev": "webpack build --progress",
"build:serve": "npx serve dist",
"test": "jest",
"test:watch": "jest --watch",
"tsc": "tsc",
"tsc:watch": "tsc --watch",
"lint": "eslint src",
"lint:fix": "eslint src --fix"
}
}
@@ -1,26 +1,15 @@
{
"name": "@nymproject/sdk-example-react-webpack-wasm",
"version": "1.0.1",
"description": "An example project that uses WASM, React, Webpack, Typescript and the Nym theme + components library",
"version": "1.0.0",
"license": "Apache-2.0",
"scripts": {
"build": "webpack build --progress --config webpack.prod.js",
"build:dev": "webpack build --progress",
"build:serve": "npx serve dist",
"lint": "eslint src",
"lint:fix": "eslint src --fix",
"start": "webpack serve --progress --port 3000",
"test": "jest",
"test:watch": "jest --watch",
"tsc": "tsc",
"tsc:watch": "tsc --watch"
},
"dependencies": {
"@mui/icons-material": "^5.5.0",
"@mui/lab": "^5.0.0-alpha.72",
"@mui/material": "^5.0.1",
"@mui/styles": "^5.0.1",
"@nymproject/sdk": ">=1.2.1-rc.0 || ^1",
"react-mui-dropzone": "^4.0.6",
"@nymproject/sdk": ">=1.2.0-rc.10 || ^1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-dropzone": "^14.2.3",
@@ -83,11 +72,22 @@
"webpack-favicons": "^1.3.8",
"webpack-merge": "^5.8.0"
},
"private": false,
"overrides": {
"@types/react": "^18.0.26",
"@types/react-dom": "^18.0.10",
"react": "^18.2.0",
"react-dom": "^18.2.0"
"react-dom": "^18.2.0",
"@types/react": "^18.0.26",
"@types/react-dom": "^18.0.10"
},
"scripts": {
"start": "webpack serve --progress --port 3000",
"build": "webpack build --progress --config webpack.prod.js",
"build:dev": "webpack build --progress",
"build:serve": "npx serve dist",
"test": "jest",
"test:watch": "jest --watch",
"tsc": "tsc",
"tsc:watch": "tsc --watch",
"lint": "eslint src",
"lint:fix": "eslint src --fix"
}
}
}
@@ -1,21 +1,20 @@
{
"name": "@nymproject/sdk-example-chrome-extension",
"version": "1.0.1",
"version": "1.0.0",
"description": "This is an example of how Nym can be used within the context of a Chrome extension.",
"license": "ISC",
"author": "",
"main": "index.js",
"scripts": {
"build": "webpack"
},
"dependencies": {
"@nymproject/sdk": ">=1.2.1-rc.0 || ^1"
},
"author": "",
"license": "ISC",
"devDependencies": {
"clean-webpack-plugin": "^4.0.0",
"copy-webpack-plugin": "^11.0.0",
"webpack": "^5.88.1",
"webpack-cli": "^5.1.4"
},
"private": false
}
"dependencies": {
"@nymproject/sdk": ">=1.2.0-rc.10 || ^1"
}
}
@@ -1,21 +1,20 @@
{
"name": "@nymproject/sdk-example-firefox-extension",
"version": "1.0.1",
"version": "1.0.0",
"description": "This is an example of how Nym can be used within the context of a Firefox extension.",
"license": "ISC",
"author": "",
"main": "index.js",
"scripts": {
"build": "yarn webpack"
},
"dependencies": {
"@nymproject/sdk": ">=1.2.1-rc.0 || ^1"
},
"author": "",
"license": "ISC",
"devDependencies": {
"copy-webpack-plugin": "^11.0.0",
"webpack": "^5.88.1",
"webpack-cli": "^5.1.4",
"worker-loader": "^3.0.8"
},
"private": false
}
"scripts": {
"build": "yarn webpack"
},
"dependencies": {
"@nymproject/sdk": ">=1.2.0-rc.10 || ^1"
}
}
@@ -1,16 +1,15 @@
{
"name": "@nymproject/mix-fetch-example-parcel",
"version": "1.0.1",
"version": "1.0.0",
"license": "Apache-2.0",
"scripts": {
"build": "parcel build --no-cache --no-content-hash",
"serve": "serve dist",
"start": "parcel --no-cache"
},
"source": "src/index.html",
"dependencies": {
"@nymproject/mix-fetch": ">=1.2.1-rc.0 || ^1",
"parcel": "^2.9.3"
"parcel": "^2.9.3",
"@nymproject/mix-fetch": ">=1.2.0-rc.10 || ^1"
},
"private": false,
"source": "src/index.html"
}
"scripts": {
"start": "parcel --no-cache",
"build": "parcel build --no-cache --no-content-hash",
"serve": "serve dist"
}
}
@@ -1,21 +1,12 @@
{
"name": "@nymproject/sdk-example-node-tester-plain-html-parcel",
"version": "1.0.1",
"description": "An example project that uses WASM and plain HTML bundled with Parcel v2",
"version": "1.0.0",
"license": "Apache-2.0",
"scripts": {
"build": "npx parcel build",
"build:serve": "npx serve dist",
"lint": "eslint src",
"lint:fix": "eslint src --fix",
"start": "npx parcel",
"test": "jest",
"test:watch": "jest --watch",
"tsc": "tsc",
"tsc:watch": "tsc --watch"
},
"source": "src/index.html",
"browserslist": "> 0.5%, last 2 versions, not dead",
"dependencies": {
"@nymproject/sdk": ">=1.2.1-rc.0 || ^1"
"@nymproject/sdk": ">=1.2.0-rc.10 || ^1"
},
"devDependencies": {
"@types/jest": "^27.0.1",
@@ -38,7 +29,15 @@
"ts-jest": "^27.0.5",
"typescript": "^4.6.2"
},
"private": false,
"browserslist": "> 0.5%, last 2 versions, not dead",
"source": "src/index.html"
}
"scripts": {
"start": "npx parcel",
"build": "npx parcel build",
"build:serve": "npx serve dist",
"test": "jest",
"test:watch": "jest --watch",
"tsc": "tsc",
"tsc:watch": "tsc --watch",
"lint": "eslint src",
"lint:fix": "eslint src --fix"
}
}
@@ -1,22 +1,10 @@
{
"name": "@nymproject/sdk-example-node-tester-plain-html",
"version": "1.0.1",
"description": "An example project that uses WASM node tester and plain HTML",
"version": "1.0.0",
"license": "Apache-2.0",
"scripts": {
"build": "webpack build --progress --config webpack.prod.js",
"build:dev": "webpack build --progress",
"build:serve": "npx serve dist",
"lint": "eslint src",
"lint:fix": "eslint src --fix",
"start": "webpack serve --progress --port 3000",
"test": "jest",
"test:watch": "jest --watch",
"tsc": "tsc",
"tsc:watch": "tsc --watch"
},
"dependencies": {
"@nymproject/sdk": ">=1.2.1-rc.0 || ^1"
"@nymproject/sdk": ">=1.2.0-rc.10 || ^1"
},
"devDependencies": {
"@babel/core": "^7.15.0",
@@ -61,5 +49,16 @@
"webpack-dev-server": "^4.5.0",
"webpack-merge": "^5.8.0"
},
"private": false
}
"scripts": {
"start": "webpack serve --progress --port 3000",
"build": "webpack build --progress --config webpack.prod.js",
"build:dev": "webpack build --progress",
"build:serve": "npx serve dist",
"test": "jest",
"test:watch": "jest --watch",
"tsc": "tsc",
"tsc:watch": "tsc --watch",
"lint": "eslint src",
"lint:fix": "eslint src --fix"
}
}
@@ -1,20 +1,19 @@
{
"name": "@nymproject/sdk-example-node-tester-react",
"version": "1.0.1",
"description": "An example project that uses WASM node tester and React",
"version": "1.0.0",
"license": "Apache-2.0",
"scripts": {
"start": "parcel index.html"
},
"dependencies": {
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
"@mui/icons-material": "^5.14.0",
"@mui/material": "^5.14.0",
"@nymproject/sdk": ">=1.2.1-rc.0 || ^1",
"@nymproject/sdk": ">=1.2.0-rc.10 || ^1",
"parcel": "^2.9.3",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"private": false
}
"scripts": {
"start": "parcel index.html"
}
}
@@ -1,6 +1,6 @@
{
"name": "@nymproject/mix-fetch-node",
"version": "1.2.1",
"version": "1.2.1-rc.3",
"description": "This package is a drop-in replacement for `fetch` in NodeJS to send HTTP requests over the Nym Mixnet.",
"license": "Apache-2.0",
"author": "Nym Technologies SA",
@@ -28,7 +28,7 @@
"tsc": "tsc --noEmit true"
},
"dependencies": {
"@nymproject/mix-fetch-wasm-node": ">=1.2.1-rc.0 || ^1",
"@nymproject/mix-fetch-wasm-node": ">=1.2.0 || ^1",
"comlink": "^4.3.1",
"fake-indexeddb": "^5.0.0",
"node-fetch": "^3.3.2",
@@ -1,9 +1,9 @@
/* eslint-disable no-console */
import type { IMixFetchWebWorker, LoadedEvent } from '../types';
import * as Comlink from 'comlink';
import { parentPort } from 'node:worker_threads';
import { setupMixFetch, disconnectMixFetch } from '@nymproject/mix-fetch-wasm-node';
import type { IMixFetchWebWorker, LoadedEvent } from '../types';
import nodeEndpoint from '../node-adapter';
import { EventKinds, ResponseBodyConfigMap, ResponseBodyConfigMapDefaults } from '../types';
@@ -1,14 +1,9 @@
{
"name": "@nymproject/mix-fetch-tester-webpack",
"version": "1.0.1",
"version": "1.0.0",
"license": "Apache-2.0",
"scripts": {
"build": "webpack build --progress --config webpack.prod.js",
"serve": "npx serve dist",
"start": "webpack serve --progress --port 3000"
},
"dependencies": {
"@nymproject/mix-fetch": ">=1.2.1-rc.0 || ^1"
"@nymproject/mix-fetch": ">=1.2.0-rc.10 || ^1"
},
"devDependencies": {
"@babel/core": "^7.22.10",
@@ -54,5 +49,9 @@
"webpack-dev-server": "^4.15.1",
"webpack-merge": "^5.9.0"
},
"private": false
}
"scripts": {
"start": "webpack serve --progress --port 3000",
"build": "webpack build --progress --config webpack.prod.js",
"serve": "npx serve dist"
}
}
@@ -1,15 +1,14 @@
{
"name": "@nymproject/mix-fetch-tester-parcel",
"version": "1.0.1",
"version": "1.0.0",
"license": "Apache-2.0",
"scripts": {
"build": "npx parcel build --no-cache --no-content-hash",
"serve": "npx serve dist",
"start": "npx parcel --no-cache"
},
"source": "../src/index.html",
"dependencies": {
"@nymproject/mix-fetch": ">=1.2.1-rc.0 || ^1"
"@nymproject/mix-fetch": ">=1.2.0-rc.10 || ^1"
},
"private": false,
"source": "../src/index.html"
}
"scripts": {
"start": "npx parcel --no-cache",
"build": "npx parcel build --no-cache --no-content-hash",
"serve": "npx serve dist"
}
}
@@ -1,6 +1,6 @@
{
"name": "@nymproject/mix-fetch",
"version": "1.2.1",
"version": "1.2.0",
"description": "This package is a drop-in replacement for `fetch` to send HTTP requests over the Nym Mixnet.",
"license": "Apache-2.0",
"author": "Nym Technologies SA",
@@ -33,7 +33,7 @@
"tsc": "tsc --noEmit true"
},
"dependencies": {
"@nymproject/mix-fetch-wasm": ">=1.2.1-rc.0 || ^1",
"@nymproject/mix-fetch-wasm": ">=1.2.0-rc.10 || ^1",
"comlink": "^4.3.1"
},
"devDependencies": {
@@ -1,6 +1,6 @@
{
"name": "@nymproject/node-tester",
"version": "1.2.1",
"version": "1.2.0",
"description": "This package provides a tester that can send test packets to mixnode that is part of the Nym Mixnet.",
"license": "Apache-2.0",
"author": "Nym Technologies SA",
@@ -25,7 +25,7 @@
"tsc": "tsc --noEmit true"
},
"dependencies": {
"@nymproject/nym-node-tester-wasm": ">=1.2.1-rc.0 || ^1",
"@nymproject/nym-node-tester-wasm": ">=1.2.0-rc.10 || ^1",
"comlink": "^4.3.1"
},
"devDependencies": {
@@ -1,6 +1,6 @@
{
"name": "@nymproject/nodejs-client",
"version": "1.2.1",
"version": "1.2.1-rc.3",
"license": "Apache-2.0",
"author": "Nym Technologies SA",
"files": [
@@ -25,7 +25,7 @@
"tsc": "tsc --noEmit true"
},
"dependencies": {
"@nymproject/nym-client-wasm-node": ">=1.2.1-rc.0 || ^1",
"@nymproject/nym-client-wasm-node": ">=1.2.0 || ^1",
"comlink": "^4.3.1",
"fake-indexeddb": "^4.0.2",
"rollup-plugin-polyfill": "^4.2.0",
@@ -1,6 +1,6 @@
{
"name": "@nymproject/sdk-react",
"version": "1.2.1",
"version": "1.2.0",
"license": "Apache-2.0",
"author": "Nym Technologies SA",
"files": [
@@ -20,7 +20,7 @@
"tsc": "tsc --noEmit true"
},
"dependencies": {
"@nymproject/sdk": ">=1.2.1-rc.0 || ^1"
"@nymproject/sdk": ">=1.2.0-rc.10 || ^1"
},
"devDependencies": {
"@babel/core": "^7.17.5",
@@ -67,4 +67,4 @@
"private": false,
"type": "module",
"types": "./dist/index.d.ts"
}
}
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@nymproject/sdk",
"version": "1.2.1",
"version": "1.2.0",
"license": "Apache-2.0",
"author": "Nym Technologies SA",
"files": [
@@ -30,7 +30,7 @@
"tsc": "tsc --noEmit true"
},
"dependencies": {
"@nymproject/nym-client-wasm": ">=1.2.1-rc.0 || ^1",
"@nymproject/nym-client-wasm": ">=1.2.0-rc.10 || ^1",
"comlink": "^4.3.1"
},
"devDependencies": {
+4 -40
View File
@@ -10,46 +10,9 @@ set -o pipefail
cd dist
#packages=(
#chat-app/parcel
#chat-app/plain-html
#chat-app/react-webpack-with-theme-example
#chrome-extension
#firefox-extension
#node-tester/parcel
#node-tester/plain-html
#node-tester/react
#react/mui-theme
#react/sdk-react
#)
packages=(
"wasm/client"
"wasm/mix-fetch"
"wasm/node-tester"
"wasm/extension-storage"
"node/wasm/client"
"node/wasm/mix-fetch"
"ts/sdk/mix-fetch/cjs"
"ts/sdk/mix-fetch/cjs-full-fat"
"ts/sdk/mix-fetch/esm"
"ts/sdk/mix-fetch/esm-full-fat"
"ts/sdk/nodejs-client/cjs"
"ts/sdk/mix-fetch-node/cjs"
"ts/sdk/node-tester/cjs"
"ts/sdk/node-tester/cjs-full-fat"
"ts/sdk/node-tester/esm"
"ts/sdk/node-tester/esm-full-fat"
"ts/sdk/sdk/cjs"
"ts/sdk/sdk/cjs-full-fat"
"ts/sdk/sdk/esm"
"ts/sdk/sdk/esm-full-fat"
"ts/sdk/contract-clients"
)
pushd () {
@@ -76,13 +39,14 @@ COUNTER=0
for item in "${packages[@]}"
do
(( COUNTER+=1 ))
(( COUNTER++ ))
pushd "$item"
echo "🚀 Publishing $item... (${COUNTER} of ${#packages[@]})"
cat package.json | jq -r '. | .name + " " +.version'
npm publish --access=public --verbose --workspaces false || true
jq -r '. | .name + " " +.version' < package.json
npm publish --access=public --verbose
popd
echo ""
done
echo ""
echo "✅ Done"
@@ -9,7 +9,6 @@ edition.workspace = true
license.workspace = true
[dependencies]
etherparse = "0.13.0"
futures = { workspace = true }
log = { workspace = true }
nym-bin-common = { path = "../../common/bin-common" }
@@ -19,10 +18,6 @@ nym-sdk = { path = "../../sdk/rust/nym-sdk" }
nym-service-providers-common = { path = "../common" }
nym-sphinx = { path = "../../common/nymsphinx" }
nym-task = { path = "../../common/task" }
nym-wireguard = { path = "../../common/wireguard" }
nym-wireguard-types = { path = "../../common/wireguard-types" }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
tap.workspace = true
thiserror = { workspace = true }
tokio = { workspace = true, features = ["rt-multi-thread", "net", "io-util"] }
@@ -12,28 +12,28 @@ use std::{
path::{Path, PathBuf},
};
use crate::config::persistence::IpPacketRouterPaths;
use crate::config::persistence::IpForwarderPaths;
use self::template::CONFIG_TEMPLATE;
mod persistence;
mod template;
const DEFAULT_IP_PACKET_ROUTER_DIR: &str = "ip-packet-router";
const DEFAULT_IP_FORWARDERS_DIR: &str = "ip-forwarder";
/// Derive default path to ip packet routers' config directory.
/// It should get resolved to `$HOME/.nym/service-providers/ip-packet-router/<id>/config`
/// Derive default path to ip forwarder's config directory.
/// It should get resolved to `$HOME/.nym/service-providers/ip-forwareder/<id>/config`
pub fn default_config_directory<P: AsRef<Path>>(id: P) -> PathBuf {
must_get_home()
.join(NYM_DIR)
.join(DEFAULT_SERVICE_PROVIDERS_DIR)
.join(DEFAULT_IP_PACKET_ROUTER_DIR)
.join(DEFAULT_IP_FORWARDERS_DIR)
.join(id)
.join(DEFAULT_CONFIG_DIR)
}
/// Derive default path to ip packet routers' config file.
/// It should get resolved to `$HOME/.nym/service-providers/ip-packet-router/<id>/config/config.toml`
/// Derive default path to ip forwarder's config file.
/// It should get resolved to `$HOME/.nym/service-providers/ip-forwarder/<id>/config/config.toml`
pub fn default_config_filepath<P: AsRef<Path>>(id: P) -> PathBuf {
default_config_directory(id).join(DEFAULT_CONFIG_FILENAME)
}
@@ -44,7 +44,7 @@ pub fn default_data_directory<P: AsRef<Path>>(id: P) -> PathBuf {
must_get_home()
.join(NYM_DIR)
.join(DEFAULT_SERVICE_PROVIDERS_DIR)
.join(DEFAULT_IP_PACKET_ROUTER_DIR)
.join(DEFAULT_IP_FORWARDERS_DIR)
.join(id)
.join(DEFAULT_DATA_DIR)
}
@@ -55,7 +55,7 @@ pub struct Config {
#[serde(flatten)]
pub base: BaseClientConfig,
pub storage_paths: IpPacketRouterPaths,
pub storage_paths: IpForwarderPaths,
pub logging: LoggingSettings,
}
@@ -70,13 +70,13 @@ impl Config {
pub fn new<S: AsRef<str>>(id: S) -> Self {
Config {
base: BaseClientConfig::new(id.as_ref(), env!("CARGO_PKG_VERSION")),
storage_paths: IpPacketRouterPaths::new_base(default_data_directory(id.as_ref())),
storage_paths: IpForwarderPaths::new_base(default_data_directory(id.as_ref())),
logging: Default::default(),
}
}
pub fn with_data_directory<P: AsRef<Path>>(mut self, data_directory: P) -> Self {
self.storage_paths = IpPacketRouterPaths::new_base(data_directory);
self.storage_paths = IpForwarderPaths::new_base(data_directory);
self
}
@@ -8,21 +8,21 @@ use std::path::{Path, PathBuf};
pub const DEFAULT_DESCRIPTION_FILENAME: &str = "description.toml";
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize, Clone)]
pub struct IpPacketRouterPaths {
pub struct IpForwarderPaths {
#[serde(flatten)]
pub common_paths: CommonClientPaths,
/// Location of the file containing our description
pub ip_packet_router_description: PathBuf,
pub ip_forwarder_description: PathBuf,
}
impl IpPacketRouterPaths {
impl IpForwarderPaths {
pub fn new_base<P: AsRef<Path>>(base_data_directory: P) -> Self {
let base_dir = base_data_directory.as_ref();
Self {
common_paths: CommonClientPaths::new_base(base_dir),
ip_packet_router_description: base_dir.join(DEFAULT_DESCRIPTION_FILENAME),
ip_forwarder_description: base_dir.join(DEFAULT_DESCRIPTION_FILENAME),
}
}
}
@@ -76,7 +76,7 @@ allowed_list_location = '{{ storage_paths.allowed_list_location }}'
unknown_list_location = '{{ storage_paths.unknown_list_location }}'
# Path to file containing description of this network-requester.
ip_packet_router_description = '{{ storage_paths.ip_packet_router_description }}'
ip_forwarder_description = '{{ storage_paths.ip_forwarder_description }}'
##### logging configuration options #####
@@ -1,7 +1,7 @@
pub use nym_client_core::error::ClientCoreError;
#[derive(thiserror::Error, Debug)]
pub enum IpPacketRouterError {
pub enum IpForwarderError {
#[error("I/O error: {0}")]
IoError(#[from] std::io::Error),
@@ -12,7 +12,7 @@ pub enum IpPacketRouterError {
FailedToLoadConfig(String),
// TODO: add more details here
#[error("failed to validate the loaded config")]
#[error("Failed to validate the loaded config")]
ConfigValidationFailure,
#[error("failed local version check, client and config mismatch")]
@@ -26,10 +26,4 @@ pub enum IpPacketRouterError {
#[error("the entity wrapping the network requester has disconnected")]
DisconnectedParent,
#[error("failed to parse incoming packet: {source}")]
PacketParseFailed { source: etherparse::ReadError },
#[error("parsed packet is missing IP header")]
PacketMissingHeader,
}
+17 -153
View File
@@ -1,18 +1,13 @@
use std::{net::IpAddr, path::Path};
use std::path::Path;
use error::IpPacketRouterError;
use futures::{channel::oneshot, StreamExt};
use error::IpForwarderError;
use futures::channel::oneshot;
use nym_client_core::{
client::mix_traffic::transceiver::GatewayTransceiver,
config::disk_persistence::CommonClientPaths, HardcodedTopologyProvider, TopologyProvider,
};
use nym_sdk::{
mixnet::{InputMessage, MixnetMessageSender, Recipient},
NymNetworkDetails,
};
use nym_sphinx::receiver::ReconstructedMessage;
use nym_task::{connections::TransmissionLane, TaskClient, TaskHandle};
use tap::TapFallible;
use nym_sdk::{mixnet::Recipient, NymNetworkDetails};
use nym_task::{TaskClient, TaskHandle};
use crate::config::BaseClientConfig;
@@ -32,8 +27,7 @@ impl OnStartData {
}
}
pub struct IpPacketRouterBuilder {
#[allow(unused)]
pub struct IpForwarderBuilder {
config: Config,
wait_for_gateway: bool,
custom_topology_provider: Option<Box<dyn TopologyProvider + Send + Sync>>,
@@ -42,7 +36,7 @@ pub struct IpPacketRouterBuilder {
on_start: Option<oneshot::Sender<OnStartData>>,
}
impl IpPacketRouterBuilder {
impl IpForwarderBuilder {
pub fn new(config: Config) -> Self {
Self {
config,
@@ -93,26 +87,20 @@ impl IpPacketRouterBuilder {
pub fn with_stored_topology<P: AsRef<Path>>(
mut self,
file: P,
) -> Result<Self, IpPacketRouterError> {
) -> Result<Self, IpForwarderError> {
self.custom_topology_provider =
Some(Box::new(HardcodedTopologyProvider::new_from_file(file)?));
Ok(self)
}
#[cfg(not(target_os = "linux"))]
pub async fn run_service_provider(self) -> Result<(), IpPacketRouterError> {
todo!("service provider is not yet supported on this platform")
}
#[cfg(target_os = "linux")]
pub async fn run_service_provider(self) -> Result<(), IpPacketRouterError> {
pub async fn run_service_provider(self) -> Result<(), IpForwarderError> {
// Used to notify tasks to shutdown. Not all tasks fully supports this (yet).
let task_handle: TaskHandle = self.shutdown.map(Into::into).unwrap_or_default();
let shutdown: TaskHandle = self.shutdown.map(Into::into).unwrap_or_default();
// Connect to the mixnet
let mixnet_client = create_mixnet_client(
&self.config.base,
task_handle.get_handle().named("nym_sdk::MixnetClient"),
shutdown.get_handle().named("nym_sdk::MixnetClient"),
self.custom_gateway_transceiver,
self.custom_topology_provider,
self.wait_for_gateway,
@@ -122,140 +110,17 @@ impl IpPacketRouterBuilder {
let self_address = *mixnet_client.nym_address();
// Create the TUN device that we interact with the rest of the world with
let (tun, tun_task_tx, tun_task_response_rx) = nym_wireguard::tun_device::TunDevice::new(
nym_wireguard::tun_device::RoutingMode::new_nat(),
);
tun.start();
let ip_packet_router_service = IpPacketRouter {
_config: self.config,
// tun,
tun_task_tx,
tun_task_response_rx,
mixnet_client,
task_handle,
};
log::info!("The address of this client is: {self_address}");
log::info!("All systems go. Press CTRL-C to stop the server.");
if let Some(on_start) = self.on_start {
if on_start.send(OnStartData::new(self_address)).is_err() {
// the parent has dropped the channel before receiving the response
return Err(IpPacketRouterError::DisconnectedParent);
return Err(IpForwarderError::DisconnectedParent);
}
}
ip_packet_router_service.run().await
}
}
#[allow(unused)]
struct IpPacketRouter {
_config: Config,
// tun: nym_wireguard::tun_device::TunDevice,
tun_task_tx: nym_wireguard::tun_task_channel::TunTaskTx,
tun_task_response_rx: nym_wireguard::tun_task_channel::TunTaskResponseRx,
mixnet_client: nym_sdk::mixnet::MixnetClient,
task_handle: TaskHandle,
}
#[allow(unused)]
impl IpPacketRouter {
async fn run(mut self) -> Result<(), IpPacketRouterError> {
let mut task_client = self.task_handle.fork("main_loop");
while !task_client.is_shutdown() {
tokio::select! {
_ = task_client.recv() => {
log::debug!("IpPacketRouter [main loop]: received shutdown");
},
msg = self.mixnet_client.next() => {
if let Some(msg) = msg {
self.on_message(msg).await.ok();
} else {
log::trace!("IpPacketRouter [main loop]: stopping since channel closed");
break;
};
},
packet = self.tun_task_response_rx.recv() => {
if let Some((_tag, packet)) = packet {
// Read recipient from env variable NYM_CLIENT_ADDR which is a base58
// string of the nym-address of the client that the packet should be
// sent back to.
//
// In the near future we will let the client expose it's nym-address
// directly, and after that, provide SURBS
let recipient = std::env::var("NYM_CLIENT_ADDR").ok().and_then(|addr| {
Recipient::try_from_base58_string(addr).ok()
});
if let Some(recipient) = recipient {
let lane = TransmissionLane::General;
let packet_type = None;
let input_message = InputMessage::new_regular(recipient, packet, lane, packet_type);
self.mixnet_client
.send(input_message)
.await
.tap_err(|err| {
log::error!("IpPacketRouter [main loop]: failed to send packet to mixnet: {err}");
})
.ok();
} else {
log::error!("NYM_CLIENT_ADDR not set or invalid");
}
} else {
log::trace!("IpPacketRouter [main loop]: stopping since channel closed");
break;
}
}
}
}
log::info!("IpPacketRouter: stopping");
Ok(())
}
async fn on_message(
&mut self,
reconstructed: ReconstructedMessage,
) -> Result<(), IpPacketRouterError> {
log::info!("Received message: {:?}", reconstructed.sender_tag);
let headers = etherparse::SlicedPacket::from_ip(&reconstructed.message).map_err(|err| {
log::warn!("Received non-IP packet: {err}");
IpPacketRouterError::PacketParseFailed { source: err }
})?;
let (src_addr, dst_addr): (IpAddr, IpAddr) = match headers.ip {
Some(etherparse::InternetSlice::Ipv4(ipv4_header, _)) => (
ipv4_header.source_addr().into(),
ipv4_header.destination_addr().into(),
),
Some(etherparse::InternetSlice::Ipv6(ipv6_header, _)) => (
ipv6_header.source_addr().into(),
ipv6_header.destination_addr().into(),
),
None => {
log::warn!("Received non-IP packet");
return Err(IpPacketRouterError::PacketMissingHeader);
}
};
log::info!("Received packet: {src_addr} -> {dst_addr}");
// TODO: set the tag correctly. Can we just reuse sender_tag?
let peer_tag = 0;
self.tun_task_tx
.send((peer_tag, reconstructed.message))
.await
.tap_err(|err| {
log::error!("Failed to send packet to tun device: {err}");
})
.ok();
Ok(())
todo!();
}
}
@@ -263,7 +128,6 @@ impl IpPacketRouter {
// This is NOT in the SDK since we don't want to expose any of the client-core config types.
// We could however consider moving it to a crate in common in the future.
// TODO: refactor this function and its arguments
#[allow(unused)]
async fn create_mixnet_client(
config: &BaseClientConfig,
shutdown: TaskClient,
@@ -271,7 +135,7 @@ async fn create_mixnet_client(
custom_topology_provider: Option<Box<dyn TopologyProvider + Send + Sync>>,
wait_for_gateway: bool,
paths: &CommonClientPaths,
) -> Result<nym_sdk::mixnet::MixnetClient, IpPacketRouterError> {
) -> Result<nym_sdk::mixnet::MixnetClient, IpForwarderError> {
let debug_config = config.debug;
let storage_paths = nym_sdk::mixnet::StoragePaths::from(paths.clone());
@@ -279,7 +143,7 @@ async fn create_mixnet_client(
let mut client_builder =
nym_sdk::mixnet::MixnetClientBuilder::new_with_default_storage(storage_paths)
.await
.map_err(|err| IpPacketRouterError::FailedToSetupMixnetClient { source: err })?
.map_err(|err| IpForwarderError::FailedToSetupMixnetClient { source: err })?
.network_details(NymNetworkDetails::new_from_env())
.debug_config(debug_config)
.custom_shutdown(shutdown)
@@ -296,10 +160,10 @@ async fn create_mixnet_client(
let mixnet_client = client_builder
.build()
.map_err(|err| IpPacketRouterError::FailedToSetupMixnetClient { source: err })?;
.map_err(|err| IpForwarderError::FailedToSetupMixnetClient { source: err })?;
mixnet_client
.connect_to_mixnet()
.await
.map_err(|err| IpPacketRouterError::FailedToConnectToMixnet { source: err })
.map_err(|err| IpForwarderError::FailedToConnectToMixnet { source: err })
}
@@ -186,14 +186,12 @@ fn initialise_internal_packages<P: AsRef<Path>>(root: P) -> InternalPackages {
packages.register_json("sdk/typescript/examples/node-tester/plain-html");
packages.register_json("sdk/typescript/examples/node-tester/react");
packages.register_json("sdk/typescript/packages/mix-fetch");
packages.register_json("sdk/typescript/packages/mix-fetch-node");
packages.register_json("sdk/typescript/packages/mix-fetch/internal-dev");
packages.register_json("sdk/typescript/packages/mix-fetch/internal-dev/parcel");
packages.register_json("sdk/typescript/packages/node-tester");
packages.register_json("sdk/typescript/packages/nodejs-client");
packages.register_json("sdk/typescript/packages/sdk");
packages.register_json("sdk/typescript/packages/sdk-react");
packages.register_json("sdk/typescript/codegen/contract-clients");
// dependencies that will have their versions adjusted in the above packages
packages.register_known_js_dependency("@nymproject/mix-fetch");
+1 -1
View File
@@ -1,7 +1,7 @@
[package]
name = "nym-client-wasm"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jedrzej Stuczynski <andrew@nymtech.net>"]
version = "1.2.1"
version = "1.2.0"
edition = "2021"
keywords = ["nym", "sphinx", "wasm", "webassembly", "privacy", "client"]
license = "Apache-2.0"
+1 -1
View File
@@ -1,7 +1,7 @@
[package]
name = "nym-wasm-sdk"
authors = ["Jedrzej Stuczynski <andrew@nymtech.net>"]
version = "1.2.1"
version = "1.2.0"
edition = "2021"
keywords = ["nym", "sphinx", "wasm", "webassembly", "privacy"]
license = "Apache-2.0"
+1 -1
View File
@@ -1,7 +1,7 @@
[package]
name = "mix-fetch-wasm"
authors = ["Jedrzej Stuczynski <andrew@nymtech.net>"]
version = "1.2.1"
version = "1.2.0"
edition = "2021"
keywords = ["nym", "fetch", "wasm", "webassembly", "privacy"]
license = "Apache-2.0"
+1 -1
View File
@@ -1,7 +1,7 @@
[package]
name = "nym-node-tester-wasm"
authors = ["Jedrzej Stuczynski <andrew@nymtech.net>"]
version = "1.2.1"
version = "1.2.0"
edition = "2021"
keywords = ["nym", "sphinx", "webassembly", "privacy", "tester"]
license = "Apache-2.0"