Introduction
This is Nym’s technical documentation, containing information and setup guides about the various pieces of Nym software such as different mixnet infrastructure nodes, application clients, and existing applications like the desktop wallet and mixnet explorer.
If you are looking for information and setup guides for the various pieces of Nym mixnet infrastructure (mix nodes, gateways and network requesters) and Nyx blockchain validators see the new Operators Guides book.
If you are new to Nym and want to learn about the mixnet, explore kickstart options and demos, learn how to integrate with the network, and follow developer tutorials check out the Developer Portal where you can find also our FAQ section.
Popular pages
Network Architecture:
SDK examples:
Nyx
Client setup and usage guides:
Wallets
Network Overview
Nym is a privacy platform. It provides strong network-level privacy against sophisticated end-to-end attackers, and anonymous access control using blinded, re-randomizable, decentralized credentials. Our goal is to allow developers to build new applications, or upgrade existing apps, with privacy features unavailable in other systems.
The Nym platform knits together several privacy technologies, integrating them into a system of cooperating networked nodes.
At a high level, our technologies include:
- a mixnet, which encrypts and mixes Sphinx packet traffic so that it cannot be determined who is communicating with whom. Our mixnet is based on a modified version of the Loopix design.
- a privacy enhancing signature scheme called Coconut. Coconut allows a shift in thinking about resource access control, from an identity-based paradigm based on who you are to a privacy-preserving paradigm based on right to use.
- Sphinx, a way of transmitting armoured, layer-encrypted information packets which are indistinguishable from each other at a binary level.
- the Nyx blockchain, a general-purpose CosmWasm-enabled smart contract platform, and the home of the smart contracts which keep track of the mixnet.
The most important thing to note is that these technologies ensure privacy at two different levels of the stack: network data transmission, and transactions.
Here’s an overview diagram of the different types of nodes making up the network:
Developers can think of the network as being comprised of infrastructure nodes and clients for interacting with this infrastructure via Privacy-enhanced applications (PEApps).
Mixnet Infrastructure
The mixnet - the different pieces of software that your traffic will pass through when using an privacy-enhanced app (PEApp) - is made up of several different types of nodes:
-
Mix Nodes provide network security for network content and metadata, making it impossible to see who is communicating with who, by performing packet-mixing on traffic travelling through the network.
-
Gateways act as message storage for clients which may go offline and come back online again, and defend against denial of service attacks. The default gateway implementation included in the Nym platform code holds packets for later retrieval. For many applications (such as simple chat), this is usable out of the box, as it provides a place that potentially offline clients can retrieve packets from. The access token allows clients to pull messages from the gateway node.
-
Services are applications that communicate with nym clients, listening and sending traffic to the mixnet. This is an umbrella term for a variety of different pieces of code, such as the network requester binary.
-
Nyx Blockchain Validators secure the network with proof-of-stake Sybil defenses, determine which nodes are included within the network, and work together to create Coconut threshold credentials which provide anonymous access to data and resources. They also produce blocks and secure the Nyx Blockchain. Initially, this chain was used only to house the CosmWasm smart contracts keeping track of Nym’s network topology, token vesting contracts, and the
NYM
token itself. In recent months, we’ve decided to expand the role of Nyx and instead expand its role by making it an open smart contract platform for anyone to upload CosmWasm smart contracts to. Validators also provide privacy-enhanced credentials based on the testimony of a set of decentralized, blockchain-based issuing authorities. Nym validators use the Coconut signature scheme to issue credentials. This allows privacy apps to generate anonymous resource claims through decentralised authorities, then use them with Service Providers.
Privacy-enhanced applications (PEApps)
PEApps use a Nym client to connect to the network in order to get the available Network Topology for traffic routing, and send/receive packets to other users and services. Clients, in order to send traffic through the mixnet, connect to gateways. Since applications may go online and offline, a client’s gateway provides a sort of mailbox where apps can receive their messages.
Nym clients connect to gateways. Messages are automatically piped to connected clients and deleted from the gateway’s disk storage. If a client is offline when a message arrives, it will be stored for later retrieval. When the client connects, all messages will be delivered, and deleted from the gateway’s disk.
When it starts up, a client registers itself with a gateway, and the gateway returns an access token. The access token plus the gateway’s IP can then be used as a form of addressing for delivering packets.
There are two basic kinds of privacy enhanced applications:
- Client apps running on mobile or desktop devices. These will typically expose a user interface (UI) to a human user. These might be existing apps such as crypto wallets that communicate with Nym via our SOCKS5 proxy, or entirely new apps.
- Service Providers, which will usually run on a server, and take actions on behalf of users without knowing who they are.
Service Providers (SPs) may interact with external systems on behalf of a user. For example, an SP might submit a Bitcoin, Ethereum or Cosmos transaction, proxy a network request, talk to a chat server, or provide anonymous access to a medical system such as a privacy-friendly coronavirus tracker.
There is also a special category of Service Provider, namely SPs that do not visibly interact with any external systems. You might think of these as crypto-utopiapps: they’re doing something, but it’s not possible from outside to say with any certainty what their function is, or who is interacting with them.
All apps talk with gateways using Sphinx packets and a small set of simple control messages. These messages are sent to gateways over websockets. Each app client has a long-lived relationship with its gateway; Nym defines messages for clients registering and authenticating with gateways, as well as sending encrypted Sphinx packets.
Mixnet Traffic Flow
Technical Motivations
When you send data across the internet, it can be recorded by a wide range of observers: your ISP, internet infrastructure providers, large tech companies, and governments.
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 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.
Mixnet Traffic Flow
The Nym mixnet re-orders encrypted, indistinguishable Sphinx packets as they travel through the gateways and mix nodes.
Traffic to send through the mixnet is broken up into uniformly-sized packets, encrypted in the Sphinx packet format according to the route the packet will take, and sent through the mixnet to be mixed among other real traffic and fake - but identical - ‘dummy traffic’.
At each ‘hop’ (i.e. as a packet is forwarded from one node in the sequence to another) a layer of decryption is removed from the Sphinx packet, revealing the address of the next hop, and another Sphinx packet. The packet is then held by the node for a variable amount of time, before being forwarded on to the next node in the route.
Traffic always travels through the nodes of the mixnet like such:
+----------+ +----------+ +----------+
| Mix Node |<-----------> | Mix Node |<----------->| Mix Node |
| Layer 1 | | Layer 2 | | Layer 3 |
+----------+ +----------+ +----------+
^ ^
| |
| |
v v
+--------------+ +-----------------+
| Your gateway | | Service gateway |
+--------------+ +-----------------+
^ ^
| |
| |
v v
+-------------------+ +-------------------+
| +---------------+ | | +---------------+ |
| | Nym client | | | | Nym Client | |
| +---------------+ | | +---------------+ |
| ^ | | ^ |
| | | | | |
| | | | | |
| v | | v |
| +---------------+ | | +---------------+ |
| | Your app code | | | | Service Code | |
| +---------------+ | | +---------------+ |
+-------------------+ +-------------------+
Your Local Machine** Service Provider Machine**
** note that depending on the technical setup, the Nym client running on these machines may
be either a seperate process or embedded in the same process as the app code via one of our SDKs.
From your Nym client, your encrypted traffic is sent to:
- the gateway your client has registered with,
- a mix node on layer 1 of the Mixnet,
- a mix node on layer 2 of the Mixnet,
- a mix node on layer 3 of the Mixnet,
- the recipient’s gateway, which forwards it finally to…
- the recipient’s Nym client, which communicates with an application.
If the recipient’s Nym client is offline at the time then the packets will be held by the Gateway their Nym client has registered with until they come online.
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 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.
+-------------------+ +-------------------+
| +---------------+ | | | Packet lost in transmission - no ack recieved!
| | Nym client | | | |-----------------?
| +-------^-------+ |Send 100 packets | |
| | |----------------->| Gateway your | Resend packet +------------------+ etc...
| | | | client is |------------------>| |------------------>
| | | | connected to | | Mix node layer 1 |
| v | Send 100 acks | |<------------------| |
| +---------------+ |<-----------------| | Send ack +------------------+
| | Your app code | | | |
| +---------------+ | | |
+-------------------+ +-------------------+
Your Local Machine
Private Replies using SURBs
SURBs (‘Single Use Reply Blocks’) allow apps to reply to other apps anonymously.
It will often be the case that a client app wants to interact with a service of some kind, or a P2P application on someone else’s machine. It sort of defeats the purpose of the whole system if your client app needs to reveal its own gateway public key and client public key in order to get a response from the service/app.
Luckily, SURBs allow for anonymous replies. A SURB is a layer encrypted set of Sphinx headers detailing a reply path ending in the original app’s address. SURBs are encrypted by the client, so the recieving service/app can attach its response and send back the resulting Sphinx packet, but it never has sight of who it is replying to.
MultiSURBs were implemented in v1.1.4
. Clients, when sending a message to another client, attach a bundle of SURBs which can be used by the receiver to construct large anonymous replies, such as files. If a reply is too large still (i.e. it would use more SURBs than sent with the original message), the receiver will use a SURB to ask the sender for more SURBs.
What this means in practice is that files can now be sent via anonymous replies!
Pre-built Binaries
The Github releases page has pre-built binaries which should work on Ubuntu 20.04 and other Debian-based systems, but at this stage cannot be guaranteed to work everywhere.
If the pre-built binaries don’t work or are unavailable for your system, you will need to build the platform yourself.
Binary Initialisation and Configuration
All Nym binaries must first be made executable and initialised with init
before being run
.
To make a binary executable, open terminal in the same directory and run:
chmod +x <BINARY_NAME>
# for example: chmod +x nym-mixnode
The init
command is usually where you pass flags specifying configuration arguments such as the gateway you wish to communicate with, the ports you wish your binary to listen on, etc.
The init
command will also create the necessary keypairs and configuration files at ~/.nym/<BINARY_TYPE>/<BINARY_ID>/
if these files do not already exist. It will not overwrite existing keypairs if they are present.
You can reconfigure your binaries at any time by editing the config file located at ~/.nym/<BINARY_TYPE>/<BINARY_ID>/config/config.toml
and restarting the binary process.
Once you have run init
, you can start your binary with the run
command, usually only accompanied by the id
of the binary that you specified.
This id
is never transmitted over the network, and is used to select which local config and key files to use for startup.
Building from Source
Nym runs on Mac OS X, Linux, and Windows. All nodes except the Desktop Wallet and NymConnect on Windows should be considered experimental - it works fine if you’re an app developer but isn’t recommended for running nodes.
Building Nym
Nym has two main codebases:
- the Nym platform, written in Rust. This contains all of our code except for the validators.
- the Nym validators, written in Go.
This page details how to build the main Nym platform code. If you want to build and run a validator, go here instead.
Prerequisites
- Debian/Ubuntu:
pkg-config
,build-essential
,libssl-dev
,curl
,jq
,git
apt install pkg-config build-essential libssl-dev curl jq git
- Arch/Manjaro:
base-devel
pacman -S base-devel
- Mac OS X:
pkg-config
,brew
,openss1
,protobuf
,curl
,git
Running the following the script installs Homebrew and the above dependencies:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
Rust & cargo >= 1.66
We recommend using the Rust shell script installer. Installing cargo from your package manager (e.g. apt
) is not recommended as the packaged versions are usually too old.
If you really don’t want to use the shell script installer, the Rust installation docs contain instructions for many platforms.
Download and build Nym binaries
The following commands will compile binaries into the nym/target/release
directory:
rustup update
git clone https://github.com/nymtech/nym.git
cd nym
git reset --hard # in case you made any changes on your branch
git pull # in case you've checked it out before
git checkout master # master branch has the latest release version: `develop` will most likely be incompatible with deployed public networks
cargo build --release # build your binaries with **mainnet** configuration
Quite a bit of stuff gets built. The key working parts are:
- mix node:
nym-mixnode
- gateway node:
nym-gateway
- websocket client:
nym-client
- socks5 client:
nym-socks5-client
- network requester:
nym-network-requester
- nym-cli tool:
nym-cli
The repository also contains Typescript applications which aren’t built in this process. These can be built by following the instructions on their respective docs pages.
You cannot build from GitHub’s .zip or .tar.gz archive files on the releases page - the Nym build scripts automatically include the current git commit hash in the built binary during compilation, so the build will fail if you use the archive code (which isn’t a Git repository). Check the code out from github using
git clone
instead.
Node Types
This section was previously the node setup guides. These have been migrated to their own Operator Guides book.
For setup and maintenance guides, go to the Operators book linked above.
This section is a little spartan for the moment, but we will be adding detailed information about how exactly each node functions, as well as references to any literature and technical specs in the near future.
This section contains information on the different node types of the mixnet and blockchain.
Mix Nodes
The mix node setup and maintenance guide has migrated to the Operator Guides book.
Mix nodes are the backbone of the mixnet. These are the nodes that perform ‘mix mining’, otherwise known simply as ‘mixing’.
Mix nodes, after receiving a packet, decrypt its outer ‘layer’ and hold it for a variable amount of time before passing it to its next destination - either another mix node, or a gateway. In doing so, they ‘mix’ packets by sending them to their next destination in a different order than they were received.
Mix nodes are rewarded according to their quality of service, and the probability of their inclusion in the active set (i.e. the nodes that mix traffic for the next epoch) is also affected by this (as well as their delegation-based reputation - see the Mix node deepdive below for more on this).
(Coming soon) Mixing: a Step-by-Step Breakdown
Further reading
- Nym Whitepaper section 4
- Nym Blog: Mix node deepdive
- Mixnet Traffic Flow overview
- Reward Sharing for Mixnets
Gateways
The gateway setup and maintenance guide has migrated to the Operator Guides book.
Gateways are key to both the usability of the mixnet, and the operation of the mixnet’s tokenomics. They serve two main functions:
- In the future (when the mixnet is no longer running in ‘free to use’ mode), to check for zkNym credentials (previously referred to as Coconut Credentials) with which users can anonymously prove they have paid to send traffic through the mixnet. A % of the worth of these credentials will be distributed to the operator of the gateway periodically as payment for providing their service. The more credentials user clients ‘spend’ with them, the higher the rewards for the gateway operator and their delegators will be. The rest of this value will be sent to the Mixmining Pool, a pool of tokens from which
NYM
rewards are distributed to mix node operators. - Act as a mailbox for connected clients. Clients create a lasting relationship with a gateway on initialisation, binding themselves to always use a particular gateway as their ingress point for mixnet traffic (this also means that this gateway is the egress point for any traffic sent to this client - see the mixnet traffic flow page and the addressing scheme for further details). If a client is offline and the Gateway can’t deliver packets addressed to it, they will hold these packets until the client comes back online.
Further Reading
- Nym Whitepaper section 4.2
- Nym Blog: Gateways to Privacy
Network Requester
The network requester setup and maintenance guide has moved to the Operator Guides book.
Network requesters are the first instance of the catch-all term ‘service’, or ‘service providers’. In essence, think of services as being the part of the mixnet infrastructure that let you do something, such as access emails, messaging service backends, or blockchains via the mixnet.
Domain filtering
Network requesters, in essence, act as a form of proxy (somewhat analagous to a Tor exit node). If you have access to a server, you can run the network requester, which allows Nym users to send outbound requests from their local machine through the mixnet to a server, which then makes the request on their behalf, shielding them (and their metadata) from clearnet, untrusted and unknown infrastructure, such as email or message client servers.
By default the network requester is not an open proxy (although it can be used as one). It uses a whitelist for outbound requests.
Any request to a URL which is not on this local list (modified by the node operator) or Nym’s default list will be blocked.
This default whitelist is useful for knowing that the majority of Network requesters are able to support certain apps ‘out of the box’, and the local whitelist allows operators to include their own whitelisted domains.
Substantial changes are on the horizon concerning how Network Requesters manage incoming requests - if you are an operator and have experience running software such as Tor exit nodes or p2p nodes get in touch via our Matrix server.
(Coming soon) Consuming credentials for anonymous service payment
Further reading
Validators
The validator setup and maintenance guide has moved to the Operator Guides book.
Validators secure the Nyx blockchain via Proof of Stake consensus. The Nyx blockchain records the ledger of NYM
transactions and executes the smart contracts for distributing NYM
rewards. The Nyx validators are run via the nyxd
binary (codebase), maintaining a CosmWasm- and IBC-enabled blockchain.
The blockchain plays a supporting but fundamental role in the mixnet: the NYM
token used to incentivise node operators is one of two native tokens of the chain, and the chain is where the Mixnet and Vesting smart contracts are deployed.
Further Reading
- Detailed info on Nyx Validators and token flow can be found in Nym Reward Sharing for Mixnets document in section 2.3 and 2.4.
- Our quarterly update on token economics from July 2023.
- Nym Whitepaper section 3.1
Clients Overview
A large proportion of the Nym mixnet’s functionality is implemented client-side.
Clients perform the following actions on behalf of users:
- determine network topology - what mixnodes exist, what their keys are, etc.
- register with a gateway
- authenticate with a gateway
- receive and decrypt messages from the gateway
- create layer-encrypted Sphinx packets
- send Sphinx packets with real messages
- send Sphinx packet cover traffic when no real messages are being sent
- retransmit un-acknowledged packet sends - 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.
Types of Nym clients
At present, there are three Nym clients:
- the websocket (native) client
- the SOCKS5 client
- the wasm (webassembly) client
You need to choose which one you want incorporate into your app. Which one you use will depend largely on your preferred programming style and the purpose of your app.
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.
The webassembly client
If you’re working in JavaScript or Typescript in the browser, or building an edge computing app, you’ll likely want to choose the webassembly client.
It’s packaged and available on the npm registry, so you can npm install
it into your JavaScript or TypeScript application.
The webassembly client is most easily used via the Typescript SDK.
The SOCKS5 client
The nym-socks5-client
is useful for allowing existing applications to use the Nym mixnet without any code changes. All that’s necessary is that they can use one of the SOCKS5, SOCKS4a, or SOCKS4 proxy protocols (which many applications can - crypto wallets, browsers, chat applications etc).
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.
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.
Websocket Client
The Nym Websocket Client was built in the building nym section. If you haven’t yet built Nym and want to run the code on this page, go there first.
Current version
1.1.29
Client setup
Viewing command help
You can check that your binaries are properly compiled with:
./nym-client --help
Console output
Implementation of the Nym Client
Usage: nym-client [OPTIONS] <COMMAND>
Commands:
init Initialise a Nym client. Do this first!
run Run the Nym client with provided configuration client optionally overriding
set parameters
build-info Show build information of this binary
completions Generate shell completions
generate-fig-spec Generate Fig specification
help Print this message or the help of the given subcommand(s)
Options:
-c, --config-env-file <CONFIG_ENV_FILE> Path pointing to an env file that configures the client
--no-banner Flag used for disabling the printed banner in tty
-h, --help Print help
-V, --version Print version
The two most important commands you will issue to the client are:
init
- initalise a new client instance.run
- run a mixnet client process.
You can check the necessary parameters for the available commands by running:
./nym-client <command> --help
Initialising your client
Before you can use the client, you need to initalise a new instance of it. Each instance of the client has its own public/private keypair, and connects to its own gateway node. Taken together, these 3 things (public/private keypair + gateway node identity key) make up an app’s identity.
Initialising a new client instance can be done with the following command:
./nym-client init --id example-client
Console output
Version: 1.1.29
ID: example-client
Identity key: 3SW1hTnaKxK2qv7m1gCGvLnna17hri7LHtNf7NAqHDwZ
Encryption: BpnPwXaYeK4pjFL89gqexEPAyu6yUtQrucsSsxDCwfsw
Gateway ID: 9xJM74FwwHhEKKJHihD21QSZnHM2QBRMoFx9Wst6qNBS
Gateway: ws://62.171.141.32:9000
Client listening port: 1977
Address of this client: 3SW1hTnaKxK2qv7m1gCGvLnna17hri7LHtNf7NAqHDwZ.BpnPwXaYeK4pjFL89gqexEPAyu6yUtQrucsSsxDCwfsw@9xJM74FwwHhEKKJHihD21QSZnHM2QBRMoFx9Wst6qNBS
The --id
in the example above is a local identifier so that you can name your clients; it is never transmitted over the network.
There is an optional --gateway
flag that you can use if you want to use a specific gateway. The supplied argument is the Identity Key
of the gateway you wish to use, which can be found on the mainnet Network Explorer or Sandbox Testnet Explorer depending on which network you are on.
Not passing this argument will randomly select a gateway for your client.
Choosing a Gateway
By default - as in the example above - your client will choose a random gateway to connect to.
However, there are several options for choosing a gateway, if you do not want one that is randomly assigned to your client:
- If you wish to connect to a specific gateway, you can specify this with the
--gateway
flag when runninginit
. - You can also choose a gateway based on its location relative to your client. This can be done by appending the
--latency-based-routing
flag to yourinit
command. This command means that to select a gateway, your client will:- fetch a list of all availiable gateways
- send few ping messages to all of them, and measure response times.
- create a weighted distribution to randomly choose one, favouring ones with lower latency.
Note this doesn’t mean that your client will pick the closest gateway to you, but it will be far more likely to connect to gateway with a 20ms ping rather than 200ms
Running your client
You can run the initalised client by doing this:
./nym-client run --id example-client
When you run the client, it immediately starts generating (fake) cover traffic and sending it to the mixnet.
When the client is first started, it will reach out to the Nym network’s validators, and get a list of available Nym nodes (gateways, mixnodes, and validators). We call this list of nodes the network topology. The client does this so that it knows how to connect, register itself with the network, and know which mixnodes it can route Sphinx packets through.
Configuring your client
When you initalise a client instance, a configuration directory will be generated and stored in $HOME_DIR/.nym/clients/<client-name>/
.
tree $HOME/<user>/.nym/clients/example-client
├── config
│ └── config.toml
└── data
├── ack_key.pem
├── gateway_shared.pem
├── private_encryption.pem
├── private_identity.pem
├── public_encryption.pem
└── public_identity.pem
The config.toml
file contains client configuration options, while the two pem
files contain client key information.
The generated files contain the client name, public/private keypairs, and gateway address. The name <client_id>
in the example above is just a local identifier so that you can name your clients.
Configuring your client for Docker
By default, the native client listens to host 127.0.0.1
. However this can be an issue if you wish to run a client in a Dockerized environment, where it can be convenenient to listen on a different host such as 0.0.0.0
.
You can set this via the --host
flag during either the init
or run
commands.
Alternatively, a custom host can be set in the config.toml
file under the socket
section. If you do this, remember to restart your client process.
Using your client
Connecting to the local websocket
The Nym native client exposes a websocket interface that your code connects to. To program your app, choose a websocket library for whatever language you’re using. The default websocket port is 1977
, you can override that in the client config if you want.
The Nym monorepo includes websocket client example code for Rust, Go, Javacript, and Python, all of which can be found here.
Rust users can run the examples with
cargo run --example <rust_file>.rs
, as the examples are not organised in the same way as the other examples, due to already being inside a Cargo project.
All of these code examples will do the following:
- connect to a running websocket client on port
1977
- format a message to send in either JSON or Binary format. Nym messages have defined JSON formats.
- send the message into the websocket. The native client packages the message into a Sphinx packet and sends it to the mixnet
- wait for confirmation that the message hit the native client
- wait to receive messages from other Nym apps
By varying the message content, you can easily build sophisticated service provider apps. For example, instead of printing the response received from the mixnet, your service provider might take some action on behalf of the user - perhaps initiating a network request, a blockchain transaction, or writing to a local data store.
You can find an example of building both frontend and service provider code with the websocket client in the Simple Service Provider Tutorial in the Developer Portal.
Message Types
There are a small number of messages that your application sends up the websocket to interact with the native client, as follows.
Sending text
If you want to send text information through the mixnet, format a message like this one and poke it into the websocket:
{
"type": "send",
"message": "the message",
"recipient": "71od3ZAupdCdxeFNg8sdonqfZTnZZy1E86WYKEjxD4kj@FWYoUrnKuXryysptnCZgUYRTauHq4FnEFu2QGn5LZWbm"
}
In some applications, e.g. where people are chatting with friends who they know, you might want to include unencrypted reply information in the message field. This provides an easy way for the receiving chat to then turn around and send a reply message:
{
"type": "send",
"message": {
"sender": "198427b63ZAupdCdxeFNg8sdonqfZTnZZy1E86WYKEjxD4kj@FWYoUrnKuXryysptnCZgUYRTauHq4FnEFu2QGn5LZWbm",
"chatMessage": "hi julia!"
},
"recipient": "71od3ZAupdCdxeFNg8sdonqfZTnZZy1E86WYKEjxD4kj@FWYoUrnKuXryysptnCZgUYRTauHq4FnEFu2QGn5LZWbm"
}
If that fits your security model, good. However, will probably be the case that you want to send anonymous replies using Single Use Reply Blocks (SURBs).
You can read more about SURBs here but in short they are ways for the receiver of this message to anonymously reply to you - the sender - without them having to know your nym address.
Your client will send along a number of replySurbs
to the recipient of the message. These are pre-addressed Sphinx packets that the recipient can write to the payload of (i.e. write response data to), but not view the address. If the recipient is unable to fit the response data into the bucket of SURBs sent to it, it will use a SURB to request more SURBs be sent to it from your client.
{
"type": "sendAnonymous",
"message": "something you want to keep secret"
"recipient": "71od3ZAupdCdxeFNg8sdonqfZTnZZy1E86WYKEjxD4kj@FWYoUrnKuXryysptnCZgUYRTauHq4FnEFu2QGn5LZWbm"
"replySurbs": 100 // however many reply SURBs to send along with your message
}
Each bucket of replySURBs, when received as part of an incoming message, has a unique session identifier, which only identifies the bucket of pre-addressed packets. This is necessary to make sure that your app is replying to the correct people with the information meant for them! Constructing a reply with SURBs looks something like this (where senderTag
was parsed from the incoming message)
{
"type": "reply",
"message": "reply you also want to keep secret",
"senderTag": "the sender tag you parsed from the incoming message"
}
Sending binary data
You can also send bytes instead of JSON. For that you have to send a binary websocket frame containing a binary encoded
Nym ClientRequest
containing the same information.
As a response the native-client
will send a ServerResponse
to be decoded.
You can find examples of sending and receiving binary data in the Rust, Python and Go code examples, and an example project from the Nym community BTC-BC: Bitcoin transaction transmission via Nym, a client and service provider written in Rust.
Getting your own address
Sometimes, when you start your app, it can be convenient to ask the native client to tell you what your own address is (from the saved configuration files). To do this, send:
{
"type": "selfAddress"
}
You’ll get back:
{
"type": "selfAddress",
"address": "the-address" // e.g. "71od3ZAupdCdxeFNg8sdonqfZTnZZy1E86WYKEjxD4kj@FWYoUrnKuXryysptnCZgUYRTauHq4FnEFu2QGn5LZWbm"
}
Error messages
Errors from the app’s client, or from the gateway, will be sent down the websocket to your code in the following format:
{
"type": "error",
"message": "string message"
}
Socks5 Client
The Nym socks5 client was built in the building nym section. If you haven’t yet built Nym and want to run the code on this page, go there first.
Current version
1.1.29
What is this client for?
Many existing applications are able to use either the SOCKS4, SOCKS4A, or SOCKS5 proxy protocols. If you want to send such an application’s traffic through the mixnet, you can use the nym-socks5-client
to bounce network traffic through the Nym network, like this:
External Systems:
+--------------------+
|------>| Monero blockchain |
| +--------------------+
| +--------------------+
|------>| Email server |
| +--------------------+
| +--------------------+
|------>| RPC endpoint |
| +--------------------+
| +--------------------+
|------>| Website |
| +--------------------+
| +--------------------+
+----------------------------------+ |------>| etc... |
| Mixnet: | | +--------------------+
| * Gateway your client is | |
| connected to | +--------------------+ |
| * Mix nodes 1 -> 3 |<-------->| Network requester |<------+
| * Gateway that network | +--------------------+
| requester is connected to |
+----------------------------------+
^
|
|
|
|
v
+-------------------+
| +---------------+ |
| | Nym client | |
| +---------------+ |
| ^ |
| | |
| | |
| | |
| v |
| +---------------+ |
| | Your app code | |
| +---------------+ |
+-------------------+
Your Local Machine
There are 2 pieces of software that work together to send SOCKS traffic through the mixnet: the nym-socks5-client
, and the nym-network-requester
.
The nym-socks5-client
allows you to do the following from your local machine:
- Take a TCP data stream from a application that can send traffic via SOCKS5.
- Chop up the TCP stream into multiple Sphinx packets, assigning sequence numbers to them, while leaving the TCP connection open for more data
- Send the Sphinx packets through the mixnet to a network requester. Packets are shuffled and mixed as they transit the mixnet.
The nym-network-requester
then reassembles the original TCP stream using the packets’ sequence numbers, and make the intended request. It will then chop up the response into Sphinx packets and send them back through the mixnet to your nym-socks5-client
. The application will then receive its data, without even noticing that it wasn’t talking to a “normal” SOCKS5 proxy!
Client setup
Viewing command help
You can check that your binaries are properly compiled with:
./nym-socks5-client --help
Console output
A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address
Usage: nym-socks5-client [OPTIONS] <COMMAND>
Commands:
init Initialise a Nym client. Do this first!
run Run the Nym client with provided configuration client optionally overriding
set parameters
build-info Show build information of this binary
completions Generate shell completions
generate-fig-spec Generate Fig specification
help Print this message or the help of the given subcommand(s)
Options:
-c, --config-env-file <CONFIG_ENV_FILE> Path pointing to an env file that configures the client
--no-banner Flag used for disabling the printed banner in tty
-h, --help Print help
-V, --version Print version
You can check the necessary parameters for the available commands by running:
./nym-client <COMMAND> --help
Initialising a new client instance
Before you can use the client, you need to initalise a new instance of it, which can be done with the following command:
./nym-socks5-client init --id docs-example --provider Entztfv6Uaz2hpYHQJ6JKoaCTpDL5dja18SuQWVJAmmx.Cvhn9rBJw5Ay9wgHcbgCnVg89MPSV5s2muPV2YF1BXYu@Fo4f4SQLdoyoGkFae5TpVhRVoXCF8UiypLVGtGjujVPf
Console output
Version: 1.1.29
ID: docs-example
Identity key: 2tkyG97738h2cBQJinqD7XX6FX94rLsK8hYKV4M3JkTH
Encryption: BUFezqkLeC8rfqpji9q9BrAGRTnQEUiqhiHcPbmHTGi4
Gateway ID: C3VxhAq9ZLBwT7c8vRpPcMEyzwF5eZ5NNUmwu1931t48
Gateway: ws://45.86.65.186:9000
SOCKS5 listening port: 1080
Address of this client: 2tkyG97738h2cBQJinqD7XX6FX94rLsK8hYKV4M3JkTH.BUFezqkLeC8rfqpji9q9BrAGRTnQEUiqhiHcPbmHTGi4@C3VxhAq9ZLBwT7c8vRpPcMEyzwF5eZ5NNUmwu1931t48
The --id
in the example above is a local identifier so that you can name your clients and keep track of them on your local system; it is never transmitted over the network.
The --provider
field needs to be filled with the Nym address of a Network Requester that can make network requests on your behalf. If you don’t want to run your own you can select one from the mixnet explorer by copying its Client ID
and using this as the value of the --provider
flag. Alternatively, you could use this list.
Since the nodes on this list are the infrastructure for Nymconnect they will support all apps on the default whitelist: Keybase, Telegram, Electrum, Blockstream Green, and Helios.
Choosing a Gateway
By default - as in the example above - your client will choose a random gateway to connect to.
However, there are several options for choosing a gateway, if you do not want one that is randomly assigned to your client:
- If you wish to connect to a specific gateway, you can specify this with the
--gateway
flag when runninginit
. - You can also choose a gateway based on its location relative to your client. This can be done by appending the
--latency-based-selection
flag to yourinit
command. This command means that to select a gateway, your client will:- fetch a list of all availiable gateways
- send few ping messages to all of them, and measure response times.
- create a weighted distribution to randomly choose one, favouring ones with lower latency.
Note this doesn’t mean that your client will pick the closest gateway to you, but it will be far more likely to connect to gateway with a 20ms ping rather than 200ms
Configuring your client
When you initalise a client instance, a configuration directory will be generated and stored in $HOME_DIR/.nym/socks5-clients/<client-name>/
.
tree $HOME/<user>/.nym/socks5-clients/docs-example
├── config
│ └── config.toml
└── data
├── ack_key.pem
├── credentials_database.db
├── gateway_shared.pem
├── persistent_reply_store.sqlite
├── private_encryption.pem
├── private_identity.pem
├── public_encryption.pem
└── public_identity.pem
The config.toml
file contains client configuration options, while the two pem
files contain client key information.
The generated files contain the client name, public/private keypairs, and gateway address. The name <client_id>
in the example above is just a local identifier so that you can name your clients.
Configuring your client for Docker
By default, the native client listens to host 127.0.0.1
. However this can be an issue if you wish to run a client in a Dockerized environment, where it can be convenenient to listen on a different host such as 0.0.0.0
.
You can set this via the --host
flag during either the init
or run
commands.
Alternatively, a custom host can be set in the config.toml
file under the socket
section. If you do this, remember to restart your client process.
Running the socks5 client
You can run the initalised client by doing this:
./nym-socks5-client run --id docs-example
Automating your socks5 client with systemd
Create a service file for the socks5 client at /etc/systemd/system/nym-socks5-client.service
:
[Unit]
Description=Nym Socks5 Client
StartLimitInterval=350
StartLimitBurst=10
[Service]
User=nym # replace this with whatever user you wish
LimitNOFILE=65536
ExecStart=/home/nym/nym-socks5-client run --id <your_id>
KillSignal=SIGINT
Restart=on-failure
RestartSec=30
[Install]
WantedBy=multi-user.target
Now enable and start your socks5 client:
systemctl enable nym-socks5-client.service
systemctl start nym-socks5-client.service
# you can always check your socks5 client has succesfully started with:
systemctl status nym-socks5-client.service
Using your Socks5 Client
After completing the steps above, your local Socks5 Client will be listening on localhost:1080
ready to proxy traffic to the Network Requester set as the --provider
when initialising.
When trying to connect your app, generally the proxy settings are found in settings->advanced
or settings->connection
.
Here is an example of setting the proxy connecting in Blockstream Green:
Most wallets and other applications will work basically the same way: find the network proxy settings, enter the proxy url (host: localhost, port: 1080).
In some other applications, this might be written as localhost:1080 if there’s only one proxy entry field.
Useful Commands
no-banner
Adding --no-banner
startup flag will prevent Nym banner being printed even if run in tty environment.
build-info
A build-info
command prints the build information like commit hash, rust version, binary version just like what command --version
does. However, you can also specify an --output=json
flag that will format the whole output as a json, making it an order of magnitude easier to parse.
Webassembly Client
The Nym webassembly client allows any webassembly-capable runtime to build and send Sphinx packets to the Nym network, for uses in edge computing and browser-based applications.
This is currently packaged and distributed for ease of use via the Nym Typescript SDK library.
The webassembly client allows for the easy creation of Sphinx packets from within mobile apps and browser-based client-side apps (including Electron or similar).
Building apps with nym-client-wasm
Check out the examples section of the SDK docs for examples of simple application framework setups. There are also two example applications located in the clients/webassembly
directory in the main Nym platform codebase. The js-example
is a simple, bare-bones JavaScript app.
Think about what you’re sending!
Think about what information your app sends. That goes for whatever you put into your Sphinx packet messages as well as what your app’s environment may leak.
Whenever you write client PEAPPs using HTML/JavaScript, we recommend that you do not load external resources from CDNs. Webapp developers do this all the time, to save load time for common resources, or just for convenience. But when you’re writing privacy apps it’s better not to make these kinds of requests. Pack everything locally.
If you use only local resources within your Electron app or your browser extensions, explicitly encoding request data in a Sphinx packet does protect you from the normal leakage that gets sent in a browser HTTP request. There’s a lot of stuff that leaks when you make an HTTP request from a browser window. Luckily, all that metadata and request leakage doesn’t happen in Nym, because you’re choosing very explicitly what to encode into Sphinx packets, instead of sending a whole browser environment by default.
Addressing System
When a Nym client is initalised, it generates and stores its own public/private keypair locally. When the client starts, it automatically connects to the Nym network and finds out what Nym infrastructure exists. It then chooses and connects to a specific Gateway node via websocket.
All apps in the Nym network therefore have an address, in the format:
user-identity-key.user-encryption-key@gateway-identity-key
Which in practice, looks something like this:
DguTcdkWWtDyUFLvQxRdcA8qZhardhE1ZXy1YCC7Zfmq.Dxreouj5RhQqMb3ZaAxgXFdGkmfbDKwk457FdeHGKmQQ@4kjgWmFU1tcGAZYRZR57yFuVAexjLbJ5M7jvo3X5Hkcf
This is obviously not very user-friendly and the moment, and will be developed on in the coming months.
Typescript SDK
The Typescript SDK allows developers to start building browser-based mixnet applications quickly, by simply importing the SDK into their code via NPM as they would any other Typescript library.
You can find the source code here and the library on NPM here.
Currently developers can use the SDK to do the following entirely in the browser:
- Create a client
- Listen for incoming messages and reply to them
- Encrypt text and binary-encoded messages as Sphinx packets and send these through the mixnet
We will be fleshing out further mixnet-related features in the coming weeks with functionality such as importing/exporting keypairs for developing apps with a retained identity over time.
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 clients & keypairs, subscribe to Mixnet events, send & receive messages | ✔️ |
Coconut | Create & verify Coconut credentials | ❌ |
Validator | Sign & broadcast Nyx blockchain transactions, query the blockchain | ❌ |
How it works
The SDK can be thought of as a ‘wrapper’ around the compiled WebAssembly client code: it runs the client (a Wasm blob) in a web worker. This allows us to keep the work done by the client - such as the heavy lifting of creating and multiply-encrypting Sphinx packets - in a seperate thread from our UI, enabling you to build reactive frontends without worrying about the work done under the hood by the client eating your processing power.
The SDK exposes an interface that allows developers to interact with the Wasm blob inside the webworker from frontend code.
Framework Support
Currently, the SDK only works with frameworks that use either Webpack
or Parcel
as bundlers. If you want to use the SDK with a framework that isn’t on this list, such as Angular, or NodeJS, here be dragons! These frameworks will probably use a different bundler and you will probably run into problems.
Bundler | Supported |
---|---|
Webpack | ✔️ |
Packer | ✔️ |
Support for environments with different bundlers will be added in subsequent releases.
Environment | Supported |
---|---|
Browser | ✔️ |
Headless NodeJS | ❌ |
Electron Desktop | ❌ |
Using the SDK
There are multiple example projects in nym/sdk/typescript/examples/
, each for a different frontend framework.
Vanilla HTML
The best place to start if you just want to quickly get a basic frontend up and running with which to experiment is examples/plain-html
:
import { createNymMixnetClient, NymMixnetClient } from '@nymproject/sdk';
import { displayReceived, sendMessageTo, displaySenderAddress } from './dom-utils';
let nym: NymMixnetClient | null = null;
/**
* The main entry point
*/
async function main() {
nym = await createNymMixnetClient();
if (!nym) {
console.error('Oh no! Could not create client');
return;
}
const nymApiUrl = 'https://validator.nymtech.net/api';
const preferredGatewayIdentityKey = 'E3mvZTHQCdBvhfr178Swx9g4QG3kkRUun7YnToLMcMbM';
// subscribe to connect event, so that we can show the client's address
nym.events.subscribeToConnected((e) => {
if (e.args.address) {
displaySenderAddress(e.args.address);
}
});
// subscribe to message received events and show any string messages received
nym.events.subscribeToTextMessageReceivedEvent((e) => {
displayReceived(e.args.payload);
});
const sendButton = document.querySelector('#send-button');
if (sendButton) {
(sendButton as HTMLButtonElement).onclick = function () {
if (nym) {
sendMessageTo(nym);
}
};
}
// start up the client
await nym.client.start({
clientId: 'My awesome client',
nymApiUrl,
preferredGatewayIdentityKey,
});
}
// wait for the html to load
window.addEventListener('DOMContentLoaded', () => {
// let's do this!
main();
});
As you can see, all that is required to create an ephemeral keypair and connect to the mixnet is creating a client and then subscribing to the mixnet events coming down the websocket, and adding logic to deal with them.
Parcel
If you don’t want to use Webpack
as your app bundler, we have an example with Parcel
located at examples/parcel/
.
Create React App
For React developers we have an example which is a basic React app scaffold with the additional logic for creating a client and subscribing to mixnet events in examples/react-webpack-with-theme-example/
.
Developers: think about what you’re sending (and importing)!
Think about what information your app sends. That goes for whatever you put into your Sphinx packet messages as well as what your app’s environment may leak.
Whenever you write client PEApps using HTML/JavaScript, we recommend that you do not load external resources from CDNs. Webapp developers do this all the time, to save load time for common resources, or just for convenience. But when you’re writing privacy apps it’s better not to make these kinds of requests. Pack everything locally.
If you use only local resources within your Electron app or your browser extensions, explicitly encoding request data in a Sphinx packet does protect you from the normal leakage that gets sent in a browser HTTP request. There’s a lot of stuff that leaks when you make an HTTP request from a browser window. Luckily, all that metadata and request leakage doesn’t happen in Nym, because you’re choosing very explicitly what to encode into Sphinx packets, instead of sending a whole browser environment by default.
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
) or socks-client
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. As such, in order to import the crate you must specify the Nym monorepo in your Cargo.toml
file:
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, and the socks 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 in the monorepo. Just navigate tonym/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 thesdk
,tokio
, andnym_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 previouslysend_str()
). This is a nicer-to-use wrapper aroundsend_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
):
use nym_sdk::mixnet;
use nym_sdk::mixnet::MixnetMessageSender;
#[tokio::main]
async fn main() {
nym_bin_common::logging::setup_logging();
// Passing no config makes the client fire up an ephemeral session and figure shit out on its own
let mut client = mixnet::MixnetClient::connect_new().await.unwrap();
// Be able to get our client address
let our_address = client.nym_address();
println!("Our client nym address is: {our_address}");
// Send a message throught the mixnet to ourselves
client
.send_plain_message(*our_address, "hello there")
.await
.unwrap();
println!("Waiting for message (ctrl-c to exit)");
client
.on_messages(|msg| println!("Received: {}", String::from_utf8_lossy(&msg.message)))
.await;
}
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
):
use nym_sdk::mixnet;
use nym_sdk::mixnet::MixnetMessageSender;
use std::path::PathBuf;
#[tokio::main]
async fn main() {
nym_bin_common::logging::setup_logging();
// Specify some config options
let config_dir = PathBuf::from("/tmp/mixnet-client");
let storage_paths = mixnet::StoragePaths::new_from_dir(&config_dir).unwrap();
// Create the client with a storage backend, and enable it by giving it some paths. If keys
// exists at these paths, they will be loaded, otherwise they will be generated.
let client = mixnet::MixnetClientBuilder::new_with_default_storage(storage_paths)
.await
.unwrap()
.build()
.await
.unwrap();
// Now we connect to the mixnet, using keys now stored in the paths provided.
let mut client = client.connect_to_mixnet().await.unwrap();
// Be able to get our client address
let our_address = client.nym_address();
println!("Our client nym address is: {our_address}");
// Send a message throught the mixnet to ourselves
client
.send_plain_message(*our_address, "hello there")
.await
.unwrap();
println!("Waiting for message");
if let Some(received) = client.wait_for_messages().await {
for r in received {
println!("Received: {}", String::from_utf8_lossy(&r.message));
}
}
client.disconnect().await;
}
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
)
use nym_client_core::client::base_client::storage::gateway_details::{
GatewayDetailsStore, PersistedGatewayDetails,
};
use nym_sdk::mixnet::{
self, EmptyReplyStorage, EphemeralCredentialStorage, KeyManager, KeyStore, MixnetClientStorage,
MixnetMessageSender,
};
use nym_topology::provider_trait::async_trait;
#[tokio::main]
async fn main() {
nym_bin_common::logging::setup_logging();
// Just some plain data to pretend we have some external storage that the application
// implementer is using.
let mock_storage = MockClientStorage::empty();
let mut client = mixnet::MixnetClientBuilder::new_with_storage(mock_storage)
.build()
.await
.unwrap()
.connect_to_mixnet()
.await
.unwrap();
// Be able to get our client address
let our_address = client.nym_address();
println!("Our client nym address is: {our_address}");
// Send important info up the pipe to a buddy
client
.send_plain_message(*our_address, "hello there")
.await
.unwrap();
println!("Waiting for message");
if let Some(received) = client.wait_for_messages().await {
for r in received {
println!("Received: {}", String::from_utf8_lossy(&r.message));
}
}
client.disconnect().await;
}
#[allow(unused)]
struct MockClientStorage {
pub key_store: MockKeyStore,
pub gateway_details_store: MockGatewayDetailsStore,
pub reply_store: EmptyReplyStorage,
pub credential_store: EphemeralCredentialStorage,
}
impl MockClientStorage {
fn empty() -> Self {
Self {
key_store: MockKeyStore,
gateway_details_store: MockGatewayDetailsStore,
reply_store: EmptyReplyStorage::default(),
credential_store: EphemeralCredentialStorage::default(),
}
}
}
impl MixnetClientStorage for MockClientStorage {
type KeyStore = MockKeyStore;
type ReplyStore = EmptyReplyStorage;
type CredentialStore = EphemeralCredentialStorage;
type GatewayDetailsStore = MockGatewayDetailsStore;
fn into_runtime_stores(self) -> (Self::ReplyStore, Self::CredentialStore) {
(self.reply_store, self.credential_store)
}
fn key_store(&self) -> &Self::KeyStore {
&self.key_store
}
fn reply_store(&self) -> &Self::ReplyStore {
&self.reply_store
}
fn credential_store(&self) -> &Self::CredentialStore {
&self.credential_store
}
fn gateway_details_store(&self) -> &Self::GatewayDetailsStore {
&self.gateway_details_store
}
}
struct MockKeyStore;
#[async_trait]
impl KeyStore for MockKeyStore {
type StorageError = MyError;
async fn load_keys(&self) -> Result<KeyManager, Self::StorageError> {
println!("loading stored keys");
Err(MyError)
}
async fn store_keys(&self, _keys: &KeyManager) -> Result<(), Self::StorageError> {
println!("storing keys");
Ok(())
}
}
struct MockGatewayDetailsStore;
#[async_trait]
impl GatewayDetailsStore for MockGatewayDetailsStore {
type StorageError = MyError;
async fn load_gateway_details(&self) -> Result<PersistedGatewayDetails, Self::StorageError> {
println!("loading stored gateway details");
Err(MyError)
}
async fn store_gateway_details(
&self,
_details: &PersistedGatewayDetails,
) -> Result<(), Self::StorageError> {
println!("storing gateway details");
Ok(())
}
}
//
// struct MockReplyStore;
//
// #[async_trait]
// impl ReplyStorageBackend for MockReplyStore {
// type StorageError = MyError;
//
// async fn flush_surb_storage(
// &mut self,
// _storage: &CombinedReplyStorage,
// ) -> Result<(), Self::StorageError> {
// todo!()
// }
//
// async fn init_fresh(&mut self, _fresh: &CombinedReplyStorage) -> Result<(), Self::StorageError> {
// todo!()
// }
//
// async fn load_surb_storage(&self) -> Result<CombinedReplyStorage, Self::StorageError> {
// todo!()
// }
// }
//
// struct MockCredentialStore;
//
// #[async_trait]
// impl CredentialStorage for MockCredentialStore {
// type StorageError = MyError;
//
// async fn insert_coconut_credential(
// &self,
// _voucher_value: String,
// _voucher_info: String,
// _serial_number: String,
// _binding_number: String,
// _signature: String,
// _epoch_id: String,
// ) -> Result<(), Self::StorageError> {
// todo!()
// }
//
// async fn get_next_coconut_credential(&self) -> Result<CoconutCredential, Self::StorageError> {
// todo!()
// }
//
// async fn consume_coconut_credential(&self, id: i64) -> Result<(), Self::StorageError> {
// todo!()
// }
// }
#[derive(thiserror::Error, Debug)]
#[error("foobar")]
struct MyError;
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.
const DEFAULT_NUMBER_OF_SURBS: u32 = 5;
You can read more about how SURBs function under the hood here.
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:
use nym_sdk::mixnet::{
AnonymousSenderTag, MixnetClientBuilder, MixnetMessageSender, ReconstructedMessage,
StoragePaths,
};
use std::path::PathBuf;
#[tokio::main]
async fn main() {
nym_bin_common::logging::setup_logging();
// Specify some config options
let config_dir = PathBuf::from("/tmp/surb-example");
let storage_paths = StoragePaths::new_from_dir(&config_dir).unwrap();
// Create the client with a storage backend, and enable it by giving it some paths. If keys
// exists at these paths, they will be loaded, otherwise they will be generated.
let client = MixnetClientBuilder::new_with_default_storage(storage_paths)
.await
.unwrap()
.build()
.await
.unwrap();
// Now we connect to the mixnet, using keys now stored in the paths provided.
let mut client = client.connect_to_mixnet().await.unwrap();
// Be able to get our client address
let our_address = client.nym_address();
println!("\nOur client nym address is: {our_address}");
// Send a message through the mixnet to ourselves using our nym address
client
.send_plain_message(*our_address, "hello there")
.await
.unwrap();
// we're going to parse the sender_tag (AnonymousSenderTag) from the incoming message and use it to 'reply' to ourselves instead of our Nym address.
// we know there will be a sender_tag since the sdk sends SURBs along with messages by default.
println!("Waiting for message\n");
// get the actual message - discard the empty vec sent along with a potential SURB topup request
let mut message: Vec<ReconstructedMessage> = Vec::new();
while let Some(new_message) = client.wait_for_messages().await {
if new_message.is_empty() {
continue;
}
message = new_message;
break;
}
let mut parsed = String::new();
if let Some(r) = message.first() {
parsed = String::from_utf8(r.message.clone()).unwrap();
}
// parse sender_tag: we will use this to reply to sender without needing their Nym address
let return_recipient: AnonymousSenderTag = message[0].sender_tag.unwrap();
println!(
"\nReceived the following message: {} \nfrom sender with surb bucket {}",
parsed, return_recipient
);
// reply to self with it: note we use `send_str_reply` instead of `send_str`
println!("Replying with using SURBs");
client
.send_reply(return_recipient, "hi an0n!")
.await
.unwrap();
println!("Waiting for message (once you see it, ctrl-c to exit)\n");
client
.on_messages(|msg| println!("\nReceived: {}", String::from_utf8_lossy(&msg.message)))
.await;
}
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
(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):
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use nym_sdk::mixnet;
use nym_sdk::mixnet::MixnetMessageSender;
use nym_topology::provider_trait::{async_trait, TopologyProvider};
use nym_topology::{nym_topology_from_detailed, NymTopology};
use url::Url;
struct MyTopologyProvider {
validator_client: nym_validator_client::client::NymApiClient,
}
impl MyTopologyProvider {
fn new(nym_api_url: Url) -> MyTopologyProvider {
MyTopologyProvider {
validator_client: nym_validator_client::client::NymApiClient::new(nym_api_url),
}
}
async fn get_topology(&self) -> NymTopology {
let mixnodes = self
.validator_client
.get_cached_active_mixnodes()
.await
.unwrap();
// in our topology provider only use mixnodes that have mix_id divisible by 3
// and have more than 100k nym (i.e. 100'000'000'000 unym) in stake
// why? because this is just an example to showcase arbitrary uses and capabilities of this trait
let filtered_mixnodes = mixnodes
.into_iter()
.filter(|mix| {
mix.mix_id() % 3 == 0 && mix.total_stake() > "100000000000".parse().unwrap()
})
.collect::<Vec<_>>();
let gateways = self.validator_client.get_cached_gateways().await.unwrap();
nym_topology_from_detailed(filtered_mixnodes, gateways)
}
}
#[async_trait]
impl TopologyProvider for MyTopologyProvider {
// this will be manually refreshed on a timer specified inside mixnet client config
async fn get_new_topology(&mut self) -> Option<NymTopology> {
Some(self.get_topology().await)
}
}
#[tokio::main]
async fn main() {
nym_bin_common::logging::setup_logging();
let nym_api = "https://validator.nymtech.net/api/".parse().unwrap();
let my_topology_provider = MyTopologyProvider::new(nym_api);
// Passing no config makes the client fire up an ephemeral session and figure things out on its own
let mut client = mixnet::MixnetClientBuilder::new_ephemeral()
.custom_topology_provider(Box::new(my_topology_provider))
.build()
.await
.unwrap()
.connect_to_mixnet()
.await
.unwrap();
let our_address = client.nym_address();
println!("Our client nym address is: {our_address}");
// Send a message through the mixnet to ourselves
client
.send_plain_message(*our_address, "hello there")
.await
.unwrap();
println!("Waiting for message (ctrl-c to exit)");
client
.on_messages(|msg| println!("Received: {}", String::from_utf8_lossy(&msg.message)))
.await;
}
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:
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use nym_sdk::mixnet;
use nym_sdk::mixnet::MixnetMessageSender;
use nym_topology::mix::Layer;
use nym_topology::{mix, NymTopology};
use std::collections::BTreeMap;
#[tokio::main]
async fn main() {
nym_bin_common::logging::setup_logging();
// Passing no config makes the client fire up an ephemeral session and figure shit out on its own
let mut client = mixnet::MixnetClient::connect_new().await.unwrap();
let starting_topology = client.read_current_topology().await.unwrap();
// but we don't like our default topology, we want to use only those very specific, hardcoded, nodes:
let mut mixnodes = BTreeMap::new();
mixnodes.insert(
1,
vec![mix::Node {
mix_id: 63,
owner: "n1k52k5n45cqt5qpjh8tcwmgqm0wkt355yy0g5vu".to_string(),
host: "172.105.92.48".parse().unwrap(),
mix_host: "172.105.92.48:1789".parse().unwrap(),
identity_key: "GLdR2NRVZBiCoCbv4fNqt9wUJZAnNjGXHkx3TjVAUzrK"
.parse()
.unwrap(),
sphinx_key: "CBmYewWf43iarBq349KhbfYMc9ys2ebXWd4Vp4CLQ5Rq"
.parse()
.unwrap(),
layer: Layer::One,
version: "1.1.0".into(),
}],
);
mixnodes.insert(
2,
vec![mix::Node {
mix_id: 23,
owner: "n1fzv4jc7fanl9s0qj02ge2ezk3kts545kjtek47".to_string(),
host: "178.79.143.65".parse().unwrap(),
mix_host: "178.79.143.65:1789".parse().unwrap(),
identity_key: "4Yr4qmEHd9sgsuQ83191FR2hD88RfsbMmB4tzhhZWriz"
.parse()
.unwrap(),
sphinx_key: "8ndjk5oZ6HxUZNScLJJ7hk39XtUqGexdKgW7hSX6kpWG"
.parse()
.unwrap(),
layer: Layer::Two,
version: "1.1.0".into(),
}],
);
mixnodes.insert(
3,
vec![mix::Node {
mix_id: 66,
owner: "n1ae2pjd7q9p0dea65pqkvcm4x9s264v4fktpyru".to_string(),
host: "139.162.247.97".parse().unwrap(),
mix_host: "139.162.247.97:1789".parse().unwrap(),
identity_key: "66UngapebhJRni3Nj52EW1qcNsWYiuonjkWJzHFsmyYY"
.parse()
.unwrap(),
sphinx_key: "7KyZh8Z8KxuVunqytAJ2eXFuZkCS7BLTZSzujHJZsGa2"
.parse()
.unwrap(),
layer: Layer::Three,
version: "1.1.0".into(),
}],
);
// but we like the available gateways, so keep using them!
// (we like them because the author of this example is too lazy to use the same hardcoded gateway
// during client initialisation to make sure we are able to send to ourselves : ) )
let custom_topology = NymTopology::new(mixnodes, starting_topology.gateways().to_vec());
client.manually_overwrite_topology(custom_topology).await;
// and everything we send now should only ever go via those nodes
let our_address = client.nym_address();
println!("Our client nym address is: {our_address}");
// Send a message through the mixnet to ourselves
client
.send_plain_message(*our_address, "hello there")
.await
.unwrap();
println!("Waiting for message (ctrl-c to exit)");
client
.on_messages(|msg| println!("Received: {}", String::from_utf8_lossy(&msg.message)))
.await;
}
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:
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use futures::StreamExt;
use nym_sdk::mixnet;
use nym_sdk::mixnet::MixnetMessageSender;
#[tokio::main]
async fn main() {
nym_bin_common::logging::setup_logging();
// Passing no config makes the client fire up an ephemeral session and figure stuff out on its own
let mut client = mixnet::MixnetClient::connect_new().await.unwrap();
// Be able to get our client address
let our_address = *client.nym_address();
println!("Our client nym address is: {our_address}");
let sender = client.split_sender();
// receiving task
let receiving_task_handle = tokio::spawn(async move {
if let Some(received) = client.next().await {
println!("Received: {}", String::from_utf8_lossy(&received.message));
}
client.disconnect().await;
});
// sending task
let sending_task_handle = tokio::spawn(async move {
sender
.send_plain_message(our_address, "hello from a different task!")
.await
.unwrap();
});
// wait for both tasks to be done
println!("waiting for shutdown");
sending_task_handle.await.unwrap();
receiving_task_handle.await.unwrap();
}
Socks client example
There is also the option to embed the socks5-client
into your app code (examples/socks5.rs
):
use nym_sdk::mixnet;
#[tokio::main]
async fn main() {
nym_bin_common::logging::setup_logging();
println!("Connecting receiver");
let mut receiving_client = mixnet::MixnetClient::connect_new().await.unwrap();
let socks5_config = mixnet::Socks5::new(receiving_client.nym_address().to_string());
let sending_client = mixnet::MixnetClientBuilder::new_ephemeral()
.socks5_config(socks5_config)
.build()
.await
.unwrap();
println!("Connecting sender");
let mut sending_client = sending_client.connect_to_mixnet_via_socks5().await.unwrap();
let proxy = reqwest::Proxy::all(sending_client.socks5_url()).unwrap();
let reqwest_client = reqwest::Client::builder().proxy(proxy).build().unwrap();
tokio::spawn(async move {
println!("Sending socks5-wrapped http request");
// Message should be sent through the mixnet, via socks5
// We don't expect to get anything, as there is no network requester on the other end
reqwest_client.get("https://nymtech.net").send().await.ok()
});
println!("Waiting for message");
if let Some(received) = receiving_client.wait_for_messages().await {
for r in received {
println!(
"Received socks5 message requesting for endpoint: {}",
String::from_utf8_lossy(&r.message[10..27])
);
}
}
receiving_client.disconnect().await;
sending_client.disconnect().await;
}
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 representing paid bandwidth on the Sandbox testnet.
use futures::StreamExt;
use nym_network_defaults::setup_env;
use nym_sdk::mixnet;
use nym_sdk::mixnet::MixnetMessageSender;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
nym_bin_common::logging::setup_logging();
// right now, only sandbox has coconut setup
// this should be run from the `sdk/rust/nym-sdk` directory
setup_env(Some("../../../envs/sandbox.env"));
let sandbox_network = mixnet::NymNetworkDetails::new_from_env();
let mnemonic = String::from("my super secret mnemonic");
let mixnet_client = mixnet::MixnetClientBuilder::new_ephemeral()
.network_details(sandbox_network)
.enable_credentials_mode()
.build()
.await?;
let bandwidth_client = mixnet_client.create_bandwidth_client(mnemonic)?;
// Get a bandwidth credential worth 1000000 unym for the mixnet_client
bandwidth_client.acquire(1000000).await?;
// Connect using paid bandwidth credential
let mut client = mixnet_client.connect_to_mixnet().await?;
let our_address = client.nym_address();
// Send a message throughout the mixnet to ourselves
client
.send_plain_message(*our_address, "hello there")
.await?;
println!("Waiting for message");
let received = client.next().await.unwrap();
println!("Received: {}", String::from_utf8_lossy(&received.message));
client.disconnect().await;
Ok(())
}
You can read more about Coconut credentials (also referred to as zk-Nym
) here.
Desktop Wallet
The Nym Desktop Wallet lets you interact with your Nym node and to delegate stake to others, see the vesting schedule of tokens, and transfer tokens. In future releases, it will also let you access the Nym mixnet.
You can download it for Mac, Windows, or Linux.
Bypassing security warnings
On Windows you will see a security warning pop up when you attempt to run the wallet. We are in the process of getting app store keys from Microsoft so that this doesn’t happen. See the section below for details on steps to bypass these.
Linux
You will need to chmod +x
the AppImage in the terminal (or give it execute permission in your file browser) before it will run.
Windows
You will still encounter warnings when opening the wallet on Windows. This is because - although the wallet is approved by Microsoft - it has less than 10 thousand downloads at the current time. Once the wallet has passed this threshold, this warning will disappear.
Follow the steps below to bypass the warnings.
- Select more-info after clicking the msi installer app:
- Proceed to ‘run-anyway’:
- Follow the installer instructions:
For developers
If you would like to the compile the wallet yourself, follow the instructions below.
Please note that the wallet has currently only been built on the operating systems for which there are binaries as listed above. If you find an issue or any additional prerequisties, please create an issue or PR against
develop
on Github.
Software prerequisites for building the wallet
git
sudo apt update
sudo apt install git
Verify git
is installed with:
git version
# Should return: git version X.Y.Z
-
NodeJS >= v16.8.0
-
Rust & cargo >= v1.56
We recommend using the Rust shell script installer. Installing cargo from your package manager (e.g. apt
) is not recommended as the packaged versions are usually too old.
If you really don’t want to use the shell script installer, the Rust installation docs contain instructions for many platforms.
Additional prerequisites for Ubuntu/Debian systems
sudo apt update
sudo apt install pkg-config build-essential libssl-dev curl jq
Additional prerequisites for Windows
- When running on Windows you will need to install the
c++
build tools. - An easy guide to get Rust up and running can be found here.
- When installing
NodeJS
please use thecurrent features
version. - Using a package manager like Chocolatey is recommended.
Removing signing errors when building in development mode
If you’re wanting to build the wallet yourself, you will need to make a few modifications to the file located at nym-wallet/src-tauri/tauri.conf.json
before doing so. These relate to the wallet being accepted by Mac and Windows app stores, and so aren’t relevant to you when building and running the wallet yourself.
On all operating systems:
- set the value of line 49 to
false
- remove lines 50 to 54
As well as these modifications for MacOS and Windows users:
- MacOS users must also remove line 39
- Windows users must remove lines 42 to 46
Installation
Once you have made these modifications to tauri.conf.json
, inside of the nym-wallet
folder, run:
yarn install
Running in Development Mode
Make sure you copy over the contents of the provided
.env.sample
to a new.env
file before proceeding
You can run the wallet without having to install it in development mode by running the following terminal command from the nym-wallet
folder
yarn dev
This will then start the Wallet GUI and produce a binary in nym-wallet/target/debug/
named nym-wallet
.
Running in Production Mode
Make sure you copy over the contents of the provided
.env.sample
to a new.env
file before proceeding
To build and install the wallet, run the following terminal command from the nym-wallet
folder.
yarn build
This will build an executable file that you can use to install the wallet on your machine. The output will compile different types of binaries dependent on your hardware / OS system. Once the binaries are built, they can be located as follows:
Binary output directory structure
**macos**
|
└─── target/release
| |─ nym-wallet
└───target/release/bundle/dmg
│ │─ bundle_dmg.sh
│ │─ nym-wallet.*.dmg
└───target/release/bundle/macos/MacOs
│ │─ nym-wallet
|
**Linux**
└─── target/release
| │─ nym-wallet
└───target/release/bundle/appimage
│ │─ nym-wallet_*_.AppImage
│ │─ build_appimage.sh
└───target/release/bundle/deb
│ │─ nym-wallet_*_.deb
|
**Windows**
└─── target/release
| │─ nym-wallet.exe
└───target/release/bundle/msi
│ │─ nym-wallet_*_.msi
Importing or creating account(s) when you have signed in with mnemonic
To import or create a new account, first you need to create a password for your wallet:
- Log out from the wallet
- Sign in using “Sign in with mnemonic” button
- On the next screen select “Create a password“
- Type in the mnemonic you want to create a password for and follow the next steps
- Sign back in the wallet using your new password
- Come back to this page to import or create new accounts
Importing or creating account(s) when you have signed in with mnemonic but a password already exists on your machine
To import or create a new account, you need to log in with your existing password or create a new password.
Creating a new password will overwrite any old one stored on your machine. Make sure you have saved any mnemonics associated with the password before creating a new one.
- Log out
- Click on “Forgot password”
- On the next screen select “Create new password”
- Follow the instructions and create a new password
- Sign in using your new password
CLI tool for wallet encrypted file (password) recovery:
The mnemonics that are stored in the local password protected file can also be decrypted and recovered through a simple CLI tool, nym-wallet-recovery-cli
.
nym-wallet-recovery –file saved-wallet.json –password foo
The saved wallet file can be found in $XDG_DATA_HOME
or $HOME/.local/share
on Linux, $HOME/Library/Application Support
on Mac, and C:\Users\username\AppData\Local
on Windows.
CLI Wallet
If you have already read our validator setup and maintenance documentation you will have seen that we compile and use the nyxd
binary primarily for our validators. This binary can however be used for many other tasks, such as creating and using keypairs for wallets, or automated setups that require the signing and broadcasting of transactions.
Using nyxd
binary as a CLI wallet
You can use the nyxd
as a minimal CLI wallet if you want to set up an account (or multiple accounts). Just compile the binary as per the documentation, stopping after the building your validator step is complete. You can then run nyxd keys --help
to see how you can set up and store different keypairs with which to interact with the Nyx blockchain.
For more on interacting with the chain, see the Interacting with Nyx Chain and Smart Contracts page.
Mixnet Explorer
The Nym Network Explorer lets you explore the Nym network. We have open-sourced the explorer so that anyone can run an instance of it, further decentralising the network!
Prerequisites
git
sudo apt update
sudo apt install git
Verify git
is installed with:
git version
# Should return: git version X.Y.Z
- (Debian/Ubuntu)
pkg-config
,build-essential
,libssl-dev
,curl
,jq
sudo apt update
sudo apt install pkg-config build-essential libssl-dev curl jq
-
NodeJS
(usenvm install
to automatically install the correct version) andnpm
-
Rust & cargo >= 1.66
We recommend using the Rust shell script installer. Installing cargo from your package manager (e.g. apt
) is not recommended as the packaged versions are usually too old.
If you really don’t want to use the shell script installer, the Rust installation docs contain instructions for many platforms.
Local Development
Complete the steps in the building nym section, before cd
-ing into nym/explorer
.
Start a development server with hot reloading running on http://localhost:3000
with the following commands from inside the explorer
directory:
nvm install # install relevant nodejs and npm versions
npm install
npm run start
eslint
and prettier
are already configured.
You can lint the code by running:
npm run lint
This command will only show linting errors and will not fix them!
To fix all linting errors automatically run:
npm run lint:fix
Please see the development docs in explorer/docs
for more information on the structure and design of this app.
Deployment
Complete the steps in the building nym section, before cd
-ing into nym/explorer
.
The Network Explorer should be run on a machine with at least 4GB of RAM - the build process might fail if run on a less powerful machine.
Building the Explorer UI
Build the UI with these commands from within the explorer
directory:
nvm install # install relevant nodejs and npm versions
npm install
npm run build
The output will be in the dist
directory.
This can then be either served directly from the nym
directory, or from its own directory if you wish. See the template nginx config below for more on how to host this.
Building the Explorer API
The Explorer API was built in the previous step with cargo build
.
Automating the explorer with systemd
You will most likely want to automate the Explorer-API restarting if your server reboots. Below is a systemd unit file to place at /etc/systemd/system/nym-explorer-api.service
:
[Unit]
Description=Nym Explorer API (1.1.0)
StartLimitIntervalSec=350
StartLimitBurst=10
[Service]
User=nym
Type=simple
Environment="API_STATE_FILE=/home/nym/network-explorer/explorer-api-state.json"
Environment="GEO_IP_SERVICE_API_KEY=c69155d0-25f6-11ec-80bc-75e5dbd322c3"
ExecStart=explorer/api/location
Restart=on-failure
RestartSec=30
[Install]
WantedBy=multi-user.target
Proceed to start it with:
systemctl daemon-reload # to pickup the new unit file
systemctl enable nymd # to enable the service
systemctl start nymd # to actually start the service
journalctl -f # to monitor system logs showing the service start
Installing and configuring nginx for HTTPS
Setup
Nginx is an open source software used for operating high-performance web servers. It allows us to set up reverse proxying on our validator server to improve performance and security.
Install nginx
and allow the ‘Nginx Full’ rule in your firewall:
sudo ufw allow 'Nginx Full'
Check nginx is running via systemctl:
systemctl status nginx
Which should return:
● nginx.service - A high performance web server and a reverse proxy server
Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled)
Active: active (running) since Fri 2018-04-20 16:08:19 UTC; 3 days ago
Docs: man:nginx(8)
Main PID: 2369 (nginx)
Tasks: 2 (limit: 1153)
CGroup: /system.slice/nginx.service
├─2369 nginx: master process /usr/sbin/nginx -g daemon on; master_process on;
└─2380 nginx: worker process
Configuration
Replace the default nginx configuration at /etc/nginx/sites-available/
with:
server {
listen 80;
listen [::]:80;
server_name domain;
root html_location;
location / {
try_files /$uri /$uri/index.html /index.html =404;
}
location /api {
proxy_pass http://127.0.0.1:8000;
rewrite /api/(.*) /$1 break;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
Followed by:
sudo apt install certbot nginx python3
certbot --nginx -d nym-validator.yourdomain.com -m you@yourdomain.com --agree-tos --noninteractive --redirect
Configure your firewall
The following commands will allow you to set up a firewall using ufw
.
# check if you have ufw installed
ufw version
# if it is not installed, install with
sudo apt install ufw -y
# enable ufw
sudo ufw enable
# check the status of the firewall
sudo ufw status
Now open the ports:
sudo ufw allow 22,80,443/tcp
# check the status of the firewall
sudo ufw status
Interacting with Nyx Chain and Smart Contracts
There are two options for interacting with the blockchain to send tokens or interact with deployed smart contracts:
Nym-CLI
toolnyxd
binary
Nym-CLI tool (recommended in most cases)
The nym-cli
tool is a binary offering a simple interface for interacting with deployed smart contract (for instance, bonding and unbonding a mix node from the CLI), as well as creating and managing accounts and keypairs, sending tokens, and querying the blockchain.
Instructions on how to do so can be found on the nym-cli
docs page, and there are example commands in the integrations FAQ.
Nyxd binary
The nyxd
binary, although more complex to compile and use, offers the full range of commands availiable to users of CosmosSDK chains. Use this if you are (e.g.) wanting to perform more granular queries about transactions from the CLI.
You can use the instructions on how to do this on from the gaiad
docs page, and there are example commands in the integrations FAQ.
Smart Contracts
The Nyx blockchain is based on CosmWasm. It allows users to code smart contracts in a safe subset of the Rust programming language, easily export them to WebAssembly, and upload them to the blockchain. Information about the chain can be found on the Nyx blockchain explorer.
There are currently two smart contracts on the Nyx chain:
- the Mixnet contract which manages the network topology of the mixnet, tracking delegations and rewarding.
- the Vesting contract which manages
NYM
token vesting functionality.
Users will soon be able to create and upload their own CosmWasm smart contracts to Nyx and take advantage of applications such as the Coconut Credential Scheme - more to be announced regarding this very soon.
Mixnet Contract
The Mixnet smart contract is a core piece of the Nym system, functioning as the mixnet directory and keeping track of delegations and rewards: the core functionality required by an incentivised mixnet. You can find the code and build instructions here.
Functionality
The Mixnet contract has multiple functions:
- storing bonded mix node and gateway information (and removing this on unbonding).
- providing the network-topology to the (cached) validator API endpoint used by clients on startup for routing information.
- storing delegation and bond amounts.
- storing reward amounts.
The addresses of deployed smart contracts can be found in the network-defaults
directory of the codebase alongside other network default values.
Vesting Contract
The vesting contract allows for the creation of vesting accounts, allowing NYM
tokens to vest over time, and for users to minimally interact with the Mixnet using their unvested tokens. You can find the code and build instructions here.
Functionality
The Vesting contract has multiple functions:
- Creating and storing vesting
NYM
token vesting accounts. - Interacting with the Mixnet using vesting (i.e. non-transferable) tokens, allowing users to delegate their unvested tokens.
The addresses of deployed smart contracts can be found in the network-defaults
directory of the codebase alongside other network default values.
RPC Nodes
RPC Nodes (which might otherwise be referred to as ‘Lite Nodes’ or just ‘Full Nodes’) differ from Validators in that they hold a copy of the Nyx blockchain, but do not participate in consensus / block-production.
You may want to set up an RPC Node for querying the blockchain, or in order to have an endpoint that your app can use to send transactions.
In order to set up an RPC Node, simply follow the instructions to set up a Validator, but exclude the nyxd tx staking create-validator
command.
If you want to fast-sync your node, check out the Polkachu snapshot and their other resources.
Ledger Live Support
Use the following instructions to interact with the Nyx blockchain - either with deployed smart contracts, or just to send tokens - using your Ledger device to sign transactions.
Prerequisites
- Download and install Ledger Live.
- Compile the
nyxd
binary as per the instructions here. Stop after you can successfully runnyxd
and get the helptext in your console output.
Prepare your Ledger App
- Plug in your Ledger device
- Install the
Cosmos (ATOM)
app by following the instructions here. This app allows you to interact with any Cosmos SDK chain - you can manage your ATOM, OSMOSIS, NYM tokens, etc. - On the device, navigate to the Cosmos app and open it
Create a keypair
Add a reference to the ledger device on your local machine by running the following command in the same directory as your nyxd
binary:
nyxd keys add ledger_account --ledger
Command help with nyxd
More information about each command is available by consulting the help section (--help
) at each layer of nyxd
’s commands:
# logging top level command help
nyxd --help
# logging top level command help for transaction commands
nyxd tx --help
# logging top level command help for transaction commands utilising the 'bank' module
nyxd tx bank --help
Sending tokens between addresses
Perform a transaction from the CLI with nyxd
, appending the --ledger
option to the command.
As an example, the below command will send 1 NYM
from the ledger account to the $DESTINATION_ACCOUNT
:
nyxd tx bank send ledger_account $DESTINATION_ACCOOUNT 1000000unym --ledger --node https://rpc.dev.nymte.ch:443
When a command is run, the transaction will appear on the Ledger device and will require physical confirmation from the device before being signed.
Nym-specific transactions
Nym-specific commands and queries, like bonding a mix node or delegating unvested tokens, are available in the wasm
module, and follow the following pattern:
# Executing commands
nyxd tx wasm execute $CONTRACT_ADDRESS $JSON_MSG
# Querying the state of a smart contract
nyxd query wasm contract-state smart $CONTRACT_ADDRESS $JSON_MSG
You can find the value of $CONTRACT_ADDRESS
in the network defaults
file.
The value of $JSON_MSG
will be a blog of json
formatted as defined for each command and query. You can find these definitions for the mixnet smart contract here and for the vesting contract here under ExecuteMsg
and QueryMsg
.
Example command execution:
Delegate to a mix node
You can delegate to a mix node from the CLI using nyxd
and signing the transaction with your ledger by filling in the values of this example:
CONTRACT_ADDRESS=mixnet_contract_address
./nyxd tx wasm execute $CONTRACT_ADDRESS '{"delegate_to_mixnode":{"mix_identity":"MIX_NODE_IDENTITY","amount":{"amount":"100000000000","denom":"unym"}}}' --ledger --from admin --node https://rpc.dev.nymte.ch:443 --gas-prices 0.025unymt --gas auto -b block
By replacing the value of
CONTRACT_ADDRESS
with the address of the vesting contract, you could use the above command to use tokens held in the vesting contract.
Query a vesting schedule
You can query for (e.g.) seeing the current vesting period of an address by filling in the values of the following:
CONTRACT_ADDRESS=vesting_contract_address
nyxd query wasm contract-state smart $CONTRACT_ADDRESS '{"get_current_vesting_period"}:{"address": "address_to_query_for"}' --ledger --from admin --node https://rpc.dev.nymte.ch:443 --chain-id qa-net --gas-prices 0.025unymt --gas auto -b block
Coconut
Coconut is in active development - stay tuned for code and integration examples
Coconut is a cryptographic signature scheme that produces privacy-enhanced credentials. It lets application programmers who are concerned with resource access control to think and code in a new way.
Most of the time, when we build system security, we think of who questions:
- Has Alice identified herself (authentication)?
- Is Alice allowed to take a specific action (authorisation)?
Coconut fundamentally changes these questions. Rather than asking who a user is, it allows application designers to ask different questions, mostly centered around questions of rights:
- Does the entity taking this action have a right to do X?
This allows a different kind of security. Many of the computer systems we talk to every day don’t need to know who we are, they only need to know if we have a right to use the system. Coconut allows signing authorities and validators to work together to determine whether a given private key holder has a right to take an action. The credentials are generated cooperatively by decentralised, trustless systems.
Once the credentials are generated, they can be re-randomized: entirely new credentials, which no one has ever seen before, can be presented to service providers, and magically validated without being linkable back to the credential originally given out by validators.
These properties allow Coconut credentials to act as something like a decentralized and fully private version of OAuth credentials, or like cryptographic bearer tokens generated by decentralised systems. The tokens can be mutated so that they are not traceable, but still verified with the original permissions intact.
Users present cryptographic claims encoded inside the credentials to get secure access to resources despite the systems verifying credential usage not being able to know who they are.
Re-randomisation vs pseudonymity
We stand on the shoulders of giants. Ten years ago, Bitcoin showed the way forward by allowing people to control resource access without recourse to who questions. Rather, in Bitcoin and succeeding blockchains, a private key proves a right to use.
But as we can now see, private keys in blockchain systems act only as a minor barrier to finding out who is accessing resources. A Bitcoin or Ethereum private key is effectively a long-lived pseudonym which is easily traceable through successive transactions.
Coconut allows us to build truly private systems rather than pseudonymous ones.
How does Coconut work?
Just like normal credentials, Nym’s Coconut credentials can be signed with a secret key and later verified by anybody with the correct public key. But Nym credentials have additional superpowers when compared to “normal” signature schemes like RSA or DSA.
Specifically, Coconut is a blinded, re-randomizable, selective disclosure threshold credential signature scheme. That’s quite a mouthful, so let’s break it down into its component parts.
Let’s say you have a message
with the content This credential controls X
in hand. In addition to the normal sign(message, secretKey)
and verify(message, publicKey)
functions present in other signature schemes, Coconut adds the following:
-
Blind signatures - disguises message content so that the signer can’t see what they’re signing. This defends users against signers: the entity that signed can’t identify the user who created a given credential, since they’ve never seen the message they’re signing before it’s been blinded (turned into gobbledygook). Coconut uses zero-knowledge proofs so that the signer can sign confidently without seeing the unblinded content of the message.
-
Re-randomizable signatures - take a signature, and generate a brand new signature that is valid for the same underlying message
This credential controls X
. The new bitstring in the re-randomized signature is equivalent to the original signature but not linkable to it. So a user can “show” a credential multiple times, and each time it appears to be a new credential, which is unlinkable to any previous “show”. But the underlying content of the re-randomized credential is the same (including for things like double-spend protection). This once again protects the user against the signer, because the signer can’t trace the signed message that they gave back to the user when it is presented. It also protects the user against the relying party that accepts the signed credential. The user can show re-randomized credentials repeatedly, and although the underlying message is the same in all cases, there’s no way of tracking them by watching the user present the same credential multiple times. -
Selective disclosure of attributes - allows someone with the public key to verify some, but not all, parts of a message. So you could for instance selectively reveal parts of a signed message to some people, but not to others. This is a very powerful property of Coconut, potentially leading to diverse applications: voting systems, selective revelation of medical data, privacy-friendly KYC systems, etc.
-
Threshold issuance - allows signature generation to be split up across multiple nodes and decentralized, so that either all signers need to sign (n of n where n is the number of signers) or only a threshold number of signers need to sign a message (t of n where t is the threshold value).
Taken together, these properties provide privacy for applications when it comes to generating and using signatures for cryptographic claims. If you compare it to existing tech, you might think of it as a sort of supercharged decentralized privacy-friendly JWT.
A slightly expanded view of Coconut is available in this blog post.
Using Coconut for blockchain transaction privacy
In the context of a blockchain currency system, Coconut allows us to create a privacy-enhanced Coconut credential which provably represents an amount under control of a given entity. The credential can then be “spent” anonymously, as if it were the original value. Double-spending protections apply to the credential, so it can only be spent once. Nyx Validators can then unlock the value so it can be redeemed by the party holding the credential.
Although there’s still work to be done to integrate it against various blockchains, in principle Coconut can anonymise blockchain transactions in any system which provides multi-sig. We’re working on Cosmos integration at the moment. Bitcoin and Ethereum are also obvious targets here.
Coconut is simple and flexible, and can ensure privacy for more than coin transfers; it can provide privacy for more complex smart contracts as well.
Finally, it should be mentioned that Coconut can be applied to both blockchain and non-blockchain systems - it’s a general purpose technology.
Bandwidth Credentials
You can now try using Nym Bandwidth Credentials in our Sandbox testnet environment.
Create a sandbox.env
file with the following details:
CONFIGURED=true
NETWORK_NAME=sandbox
RUST_LOG=info
RUST_BACKTRACE=1
BECH32_PREFIX=nymt
MIX_DENOM=unymt
MIX_DENOM_DISPLAY=nymt
STAKE_DENOM=unyxt
STAKE_DENOM_DISPLAY=nyxt
DENOMS_EXPONENT=6
REWARDING_VALIDATOR_ADDRESS="nymt1mxuweurc066kprnngtm8zmvam7m2nw26yatpmv"
MIXNET_CONTRACT_ADDRESS="nymt1dlsvvgey26ernlj0sq2afjluh3qd4ap0k9eerekfkw5algqrwqksaf2qf7"
VESTING_CONTRACT_ADDRESS="nymt19g9xuqrvz2frv905v3fc7puryfypluhg383q9zwsmedrlqekfgys62ykm4"
MULTISIG_CONTRACT_ADDRESS="nymt142dkm8xe9f0ytyarp7ww4kvclva65705jphxsk9exn3nqdsm8jkqnp06ac"
COCONUT_BANDWIDTH_CONTRACT_ADDRESS="nymt1ty0frysegskh6ndm3v96z5xdq66qzcu0aw7xcxlgp54jg0mjwlgqplc6v0"
COCONUT_DKG_CONTRACT_ADDRESS="nymt1gwk6muhmzeuxje7df7rjvqwl2vex0kj4t2hwuzmyx5k62kfusu5qk4k5z4"
GROUP_CONTRACT_ADDRESS="nymt14ry36mwauycz08v8ndcujghxz4hmua5epxcn0mamlr3suqe0l2qsqx5ya2"
STATISTICS_SERVICE_DOMAIN_ADDRESS="http://0.0.0.0"
NYXD="https://sandbox-validator1.nymtech.net"
NYM_API="https://sandbox-validator1-api.nymtech.net/api"
Create an account on Sandbox using the nym-cli:
./nym-cli --config-env-file <path-to>sandbox.env account create
You will need nymt
funds sent to this account. Get in touch via Nym Telegram or Discord and we can send them to you.
Next, you init the nym-client with the enabled credentials mode set to true:
./nym-client --config-env-file <path-to>sandbox.env init --id <ID> --enabled-credentials-mode true
Using the new credentials binary, purchase some credentials for the client. The recovery directory is a directory where the credentials will be temporarily stored in case the request fails.
./credential --config-env-file <path-to>sandbox.env run --client-home-directory <path-to-the-client-config> --nyxd-url https://sandbox-validator1.nymtech.net --mnemonic "<mnemonic of the account created above>" --amount 50 --recovery-dir <a-path>
You can redeem this now by running the nym-client, in enabled credentials mode:
./nym-client --config-env-file <path-to>sandbox.env run --id <ID> --enabled-credentials-mode true
Run the network requester which can be downloaded here
./nym-network-requester run
You need to run this version for now, as the
nym-client
functionality was recently integrated into thenetwork-requester
binary but for the moment cannot support coconut credentials natively.
Now time to init the socks5 client:
./nym-socks5-client --config-env-file <path-to>sandbox.env init --id <ID> --provider <insert provider address which was returned when init-ing the nym-client> --enabled-credentials-mode true
Purchase credentials for this now too:
./credential --config-env-file <path-to>sandbox.env run --client-home-directory <path-to-socks5-config> --nyxd-url https://sandbox-validator1.nymtech.net --mnemonic "<any valid sandbox mnemonic>" --amount 100 --recovery-dir <a-path>
Run the socks5 client:
./nym-socks5-client --config-env-file <path-to>sandbox.env run --id <ID> --enabled-credentials-mode true
NOTE
You can check to see if credentials have been correctly purchased by installing sqlite, and proceeding to do the following:
sqlite3 ~/.nym/socks5-clients/<ID>/data/credentials_database.db
select * from coconut_credentials;
Keep in mind 1GB = 1NYM
Nym-CLI
What is this tool for?
This is a CLI tool for interacting with:
- the Nyx blockchain (account management, querying the chain state, etc)
- the smart contracts deployed on Nyx (bonding and un-bonding mixnodes, collecting rewards, etc)
It provides a convenient wrapper around the nymd
client, and has similar functionality to the nyxd
binary for querying the chain or executing smart contract methods.
Building
The nym-cli
binary can be built by running cargo build --release
in the nym/tools/nym-cli
directory.
Usage
You can see all available commands with:
./nym-cli --help
Console output
nym-cli
A client for interacting with Nym smart contracts and the Nyx blockchain
USAGE:
nym-cli [OPTIONS] <subcommand>
OPTIONS:
--config-env-file <CONFIG_ENV_FILE>
Overrides configuration as a file of environment variables. Note: individual env vars
take precedence over this file.
-h, --help
Print help information
--mixnet-contract-address <MIXNET_CONTRACT_ADDRESS>
Overrides the mixnet contract address provided either as an environment variable or in a
config file
--mnemonic <MNEMONIC>
Provide the mnemonic for your account. You can also provide this is an env var called
MNEMONIC.
--nymd-url <NYMD_URL>
Overrides the nymd URL provided either as an environment variable NYMD_VALIDATOR or in a
config file
--validator-api-url <VALIDATOR_API_URL>
Overrides the validator API URL provided either as an environment variable API_VALIDATOR
or in a config file
--vesting-contract-address <VESTING_CONTRACT_ADDRESS>
Overrides the vesting contract address provided either as an environment variable or in
a config file
subcommands:
account Query and manage Nyx blockchain accounts
block Query chain blocks
cosmwasm Manage and execute WASM smart contracts
generate-fig Generates shell completion
help Print this message or the help of the given subcommand(s)
mixnet Manage your mixnet infrastructure, delegate stake or query the directory
signature Sign and verify messages
tx Query for transactions
vesting-schedule Create and query for a vesting schedule
Example Usage
Below we have listed some example commands for some of the features listed above.
If ever in doubt what you need to type, or if you want to see alternative parameters for a command, use the nym-cli <subcommand_name> --help
to view all available options.
./nym-cli account create --help
Console output
Create a new mnemonic - note, this account does not appear on the chain until the account id is used in a transaction
USAGE:
nym-cli account create [OPTIONS]
OPTIONS:
--config-env-file <CONFIG_ENV_FILE>
Overrides configuration as a file of environment variables. Note: individual env vars take precedence over this file.
-h, --help
Print help information
--mixnet-contract-address <MIXNET_CONTRACT_ADDRESS>
Overrides the mixnet contract address provided either as an environment variable or in a
config file
--mnemonic <MNEMONIC>
Provide the mnemonic for your account. You can also provide this is an env var called
MNEMONIC.
--nymd-url <NYMD_URL>
Overrides the nymd URL provided either as an environment variable NYMD_VALIDATOR or in a
config file
--validator-api-url <VALIDATOR_API_URL>
Overrides the validator API URL provided either as an environment variable API_VALIDATOR
or in a config file
--vesting-contract-address <VESTING_CONTRACT_ADDRESS>
Overrides the vesting contract address provided either as an environment variable or in
a config file
--word-count <WORD_COUNT>
Create account
Creates an account with a random Mnemonic and a new address.
./nym-cli account create
# Result:
# 1. Mnemonic
assist jungle spoil domain saddle energy box carpet toy resist castle faith talent note outdoor inform cage lecture syrup trigger dress oppose slender museum
# 2. Address
n132tpw4kkfas7ah0vmq78dwurhxljf2f869tlf5
NEVER share your mnemonic with anyone. Keep it stored in a safe and secure location.
Check the current balance of an account
Queries the existing balance of an account.
# Using adddress below for example purposes.
./nym-cli account balance n1hzn28p2c6pzr98r85jp3h53fy8mju5w7ndd5vh
# Result:
2022-11-10T10:28:54.009Z INFO nym_cli_commands::validator::account::balance > Getting balance for n1hzn28p2c6pzr98r85jp3h53fy8mju5w7ndd5vh...
# Balance for each token will be listed here
0.264 nym
1921.995 nyx
You can also query an accounts balance by using its mnemonic:
./nym-cli account balance --mnemonic <mnemonic>
Send tokens to an account
Sends tokens to an account using an address.
./nym-cli account send <ADDRESS> <AMOUNT>
Get the current block height
Queries the specified blockchain (Nyx chain by default) for the current block height.
./nym-cli block current-height --mnemonic <mnemonic>
# Result:
Current block height:
<BLOCK_HEIGHT>
Query for a mix node
Query a mix node on the mixnet.
./nym-cli mixnet query mixnodes --mnemonic <mnemonic>
Bond a mix node
Bonding a mix node is a process that takes a few steps due to the need to sign a transaction with your nym address for replay attack protection.
- generate a signature payload:
./nym-cli mixnet operators mixnode create-mixnode-bonding-sign-payload
# returns something like
97GEhgMrPTmQVZgHqJeqWmgQ154GLKqy8xNGtLkV8xy5xc1SuwsEnqjhtZVshBYK74n53fFkKbSrS6kxkBE3vUikbU76JZmLMFmfR7aaU2NdBnfTPPHP2nwb2hJiEueq4SvvtDtQckxv7ZJzdxyXHxUeDPhzbprxTff78U3NGNk4cg6Q2K4EFqishdaqToedsXAPvVCWNbC1iWVjEq8nJ95Eb3NJyi3KmXcNDy4i8ZXgZHu4v8F4htXq2vZUdBSbizdkNr1NRvEg6PGVQdTseyuN8JxD3yuvrqprPY2kvJaT2YiYLPgWxoQtbfwcpkX4PP1PvwuMg4W8EXhitMpM2WHqLDP5vgfDGxdDCmRS44pM8ya4hcQ4g3McHWxduGWdbCzNNEsX6oQw4LVFcWn4mhbXSgqHwNQMm2TQW6LatYZSwCczdhEwV2CXe36UGCUzozmm4nj9qfUtXqDzMrHAAS8kjbKaVNaVaRRKgauQrHnK7QGg1QpVnnaxCs14wvUb62sio8XZmMzP2SjVaRJFCyJB3UwZ6L4oXMGMXSRsiKe8ZNTaa6iX69tx54CAAHBHoiReiq7E5T2VuR5v
- sign this payload:
./nym-mixnode sign --id upgrade_test --contract-msg 97GEhgMrPTmQVZgHqJeqWmgQ154GLKqy8xNGtLkV8xy5xc1SuwsEnqjhtZVshBYK74n53fFkKbSrS6kxkBE3vUikbU76JZmLMFmfR7aaU2NdBnfTPPHP2nwb2hJiEueq4SvvtDtQckxv7ZJzdxyXHxUeDPhzbprxTff78U3NGNk4cg6Q2K4EFqishdaqToedsXAPvVCWNbC1iWVjEq8nJ95Eb3NJyi3KmXcNDy4i8ZXgZHu4v8F4htXq2vZUdBSbizdkNr1NRvEg6PGVQdTseyuN8JxD3yuvrqprPY2kvJaT2YiYLPgWxoQtbfwcpkX4PP1PvwuMg4W8EXhitMpM2WHqLDP5vgfDGxdDCmRS44pM8ya4hcQ4g3McHWxduGWdbCzNNEsX6oQw4LVFcWn4mhbXSgqHwNQMm2TQW6LatYZSwCczdhEwV2CXe36UGCUzozmm4nj9qfUtXqDzMrHAAS8kjbKaVNaVaRRKgauQrHnK7QGg1QpVnnaxCs14wvUb62sio8XZmMzP2SjVaRJFCyJB3UwZ6L4oXMGMXSRsiKe8ZNTaa6iX69tx54CAAHBHoiReiq7E5T2VuR5v
- bond the node using the signature:
./nym-cli --mnemonic <mnemonic> mixnet operators mixnode bond --amount 100000000 --mix-port 1789 --version "1.1.13" --host "85.163.111.99" --identity-key "B6pWscxYb8sPAdKTci8zPy5AgMzn5Zx8KpWwQNCyUSU7" --location "nym-town" --sphinx-key "o6MmKHzRewpNzVwaV37ZX9G3BfK4AmfYvsQfyoyAFRk" --signature "2TujBZfer8r5QM639Yb8coD9xH6f5eXzjAT5dD7wMom9fH8D1u36d7UpPdVaaZrWsCynmYpobwMWqiMKr5kM6CprD"
Bond a gateway
Bonding a mix node is a process that takes a few steps due to the need to sign a transaction with your nym address for replay attack protection.
- generate a signature payload:
./nym-cli mixnet operators gateway create-gateway-bonding-sign-payload
# returns something like
97GEhgMrPTmQVZgHqJeqWmgQ154GLKqy8xNGtLkV8xy5xc1SuwsEnqjhtZVshBYK74n53fFkKbSrS6kxkBE3vUikbU76JZmLMFmfR7aaU2NdBnfTPPHP2nwb2hJiEueq4SvvtDtQckxv7ZJzdxyXHxUeDPhzbprxTff78U3NGNk4cg6Q2K4EFqishdaqToedsXAPvVCWNbC1iWVjEq8nJ95Eb3NJyi3KmXcNDy4i8ZXgZHu4v8F4htXq2vZUdBSbizdkNr1NRvEg6PGVQdTseyuN8JxD3yuvrqprPY2kvJaT2YiYLPgWxoQtbfwcpkX4PP1PvwuMg4W8EXhitMpM2WHqLDP5vgfDGxdDCmRS44pM8ya4hcQ4g3McHWxduGWdbCzNNEsX6oQw4LVFcWn4mhbXSgqHwNQMm2TQW6LatYZSwCczdhEwV2CXe36UGCUzozmm4nj9qfUtXqDzMrHAAS8kjbKaVNaVaRRKgauQrHnK7QGg1QpVnnaxCs14wvUb62sio8XZmMzP2SjVaRJFCyJB3UwZ6L4oXMGMXSRsiKe8ZNTaa6iX69tx54CAAHBHoiReiq7E5T2VuR5v
- sign this payload:
./nym-gateway sign --id upgrade_test --contract-msg 97GEhgMrPTmQVZgHqJeqWmgQ154GLKqy8xNGtLkV8xy5xc1SuwsEnqjhtZVshBYK74n53fFkKbSrS6kxkBE3vUikbU76JZmLMFmfR7aaU2NdBnfTPPHP2nwb2hJiEueq4SvvtDtQckxv7ZJzdxyXHxUeDPhzbprxTff78U3NGNk4cg6Q2K4EFqishdaqToedsXAPvVCWNbC1iWVjEq8nJ95Eb3NJyi3KmXcNDy4i8ZXgZHu4v8F4htXq2vZUdBSbizdkNr1NRvEg6PGVQdTseyuN8JxD3yuvrqprPY2kvJaT2YiYLPgWxoQtbfwcpkX4PP1PvwuMg4W8EXhitMpM2WHqLDP5vgfDGxdDCmRS44pM8ya4hcQ4g3McHWxduGWdbCzNNEsX6oQw4LVFcWn4mhbXSgqHwNQMm2TQW6LatYZSwCczdhEwV2CXe36UGCUzozmm4nj9qfUtXqDzMrHAAS8kjbKaVNaVaRRKgauQrHnK7QGg1QpVnnaxCs14wvUb62sio8XZmMzP2SjVaRJFCyJB3UwZ6L4oXMGMXSRsiKe8ZNTaa6iX69tx54CAAHBHoiReiq7E5T2VuR5v
- bond the node using this signature:
./nym-cli --mnemonic <mnemonic> mixnet operators gateway bond --amount 100000000 --mix-port 1789 --version "1.1.13" --host "85.163.111.99" --identity-key "B6pWscxYb8sPAdKTci8zPy5AgMzn5Zx8KpWwQNCyUSU7" --location "nym-town" --sphinx-key "o6MmKHzRewpNzVwaV37ZX9G3BfK4AmfYvsQfyoyAFRk" --signature "2TujBZfer8r5QM639Yb8coD9xH6f5eXzjAT5dD7wMom9fH8D1u36d7UpPdVaaZrWsCynmYpobwMWqiMKr5kM6CprD"
Un-bond a node
Un-bond a mix node or gateway.
./nym-cli mixnet operators gateway unbound --mnemonic <mnemonic>
The same command can be applied with a mix node. Just replace
gateway
withmixnode
.
Upgrade a mix node
Upgrade your node config.
./nym-cli mixnet operators mixnode settings update-config --version <new_version>
Claim a vesting reward for a mixnode
Claim rewards for a mix node bonded with locked tokens.
./nym-cli mixnet operators mixnode rewards vesting-claim --mnemonic <mnemonic>
Claim rewards
./nym-cli mixnet operators mixnode rewards --mnemonic <mnemonic>
Manage Mix node Settings
Manage your mix node settings stored in the directory.
./nym-cli mixnet operators mixnode settings update-config --version <VERSION_NUMBER>
Delegate Stake
Delegate to a mix node.
./nym-cli mixnet delegators delegate --amount <AMOUNT> –mix-id <MIX_ID> --mnemonic <mnemonic>
Un-delegate Stake
Remove stake from a mix node.
./nym-cli mixnet delegators undelegate --mix-id <MIX-ID> --mnemonic <mnemonic>
Query a reward for a delegator
Claim rewards accumulated during the delegation of unlocked tokens.
./nym-cli mixnet delegators rewards claim --mix-id <MIX-ID> --mnemonic <mnemonic>
Signature Generation: Sign a message
Sign a message.
./nym-cli signature sign --mnemonic <mnemonic> <MESSAGE>
# Result:
{"account_id":<ACCOUNT_ID>,"public_key":{"@type":"/cosmos.crypto.secp256k1.PubKey","key":<PUBLIC_KEY>},"signature":"<OUTPUT_SIGNATURE>"}
Signature Generation: Verify a signature
Verify a signature.
./nym-cli signature verify --mnemonic <mnemonic> <PUBLIC_KEY_OR_ADDRESS> <SIGNATURE_AS_HEX> <MESSAGE>
Create a Vesting Schedule
Creates a vesting schedule for an account in the vesting smart contract.
./nym-cli vesting-schedule create --mnemonic <mnemonic> --address <ADDRESS> --amount <AMOUNT>
Query a Vesting Schedule
Query for vesting schedule in the vesting smart contract.
./nym-cli vesting-schedule query --mnemonic <mnemonic>
Staking on someone’s behalf (for custodians)
There is a limitation the staking address can only perform the following actions (and are visible via the Nym Wallet:
- Bond on the gateway’s or mix node’s behalf.
- Delegate or Un-delegate (to a mix node in order to begin receiving rewards)
- Claiming the rewards on the account
The staking address has no ability to withdraw any coins from the parent’s account.
The staking address must maintain the same level of security as the parent mnemonic; while the parent mnemonic’s delegations and bonding events will be visible to the parent owner, the staking address will be the only account capable of undoing the bonding and delegating from the mix nodes or gateway.
Query for staking on behalf of someone else
./nym-cli --mnemonic <staking address mnemonic> mixnet delegators delegate --mix-id <input> --identity-key <input> --amount <input>
Code of Conduct
We are committed to providing a friendly, safe and welcoming environment for all, regardless of level of experience, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, nationality, or other similar characteristic.
Please avoid using overtly sexual aliases or other nicknames that might detract from a friendly, safe and welcoming environment for all.
Please be kind and courteous. There’s no need to be mean or rude.
Respect that people have differences of opinion and that every design or implementation choice carries a trade-off and numerous costs. There is seldom a right answer.
Please keep unstructured critique to a minimum. If you have solid ideas you want to experiment with, make a fork and see how it works.
We will exclude you from interaction if you insult, demean or harass anyone. That is not welcome behaviour. We interpret the term “harassment” as including the definition in the Citizen Code of Conduct; if you have any lack of clarity about what might be included in that concept, please read their definition. In particular, we don’t tolerate behaviour that excludes people in socially marginalized groups.
Private harassment is also unacceptable. No matter who you are, if you feel you have been or are being harassed or made uncomfortable by a community member, please contact one of the channel ops or any of the Rust moderation team immediately. Whether you’re a regular contributor or a newcomer, we care about making this community a safe place for you and we’ve got your back.
Likewise any spamming, trolling, flaming, baiting or other attention-stealing behaviour is not welcome.
Licensing
Nym is Free Software released under the Apache License V2.
All of the contributions of the Nym core developers are © Nym Technologies SA. If you are interested in talking to us about other licenses, please get in touch.