Merge remote-tracking branch 'nym-mixnode/master'
This commit is contained in:
+1
-1
@@ -1,3 +1,3 @@
|
||||
.idea
|
||||
/target
|
||||
**/*.rs.bk
|
||||
/.idea/
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
# nym-mixnode Changelog
|
||||
|
||||
## 0.2.0
|
||||
|
||||
* removed the `--local` flag
|
||||
* introduced `--directory` argument to support arbitrary directory servers. Leaving it out will point the node at the "https://directory.nymtech.net" alpha testnet server
|
||||
* the `host` argument is now required
|
||||
* IPv6 support
|
||||
* mixnode version number is now shown at node start
|
||||
* directory server location is now shown at node start
|
||||
|
||||
## 0.1.0 - Initial Release
|
||||
|
||||
* The bare minimum set of features required by a Nym Mixnode
|
||||
Generated
+2371
File diff suppressed because it is too large
Load Diff
+21
@@ -0,0 +1,21 @@
|
||||
[package]
|
||||
build = "build.rs"
|
||||
name = "nym-mixnode"
|
||||
version = "0.2.0"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
base64 = "0.11.0"
|
||||
clap = "2.33.0"
|
||||
curve25519-dalek = "1.2.3"
|
||||
futures = "0.3.1"
|
||||
nym-client = { path = "../nym-client" }
|
||||
sphinx = { path = "../sphinx" }
|
||||
tokio = { version = "0.2", features = ["full"] }
|
||||
|
||||
|
||||
[build-dependencies]
|
||||
built = "0.3.2"
|
||||
@@ -0,0 +1,27 @@
|
||||
# Nym Mixnode
|
||||
|
||||
A Rust mixnode implementation.
|
||||
|
||||
## Building
|
||||
|
||||
* check out the code
|
||||
* [install rust](https://www.rust-lang.org/tools/install) (stable)
|
||||
* `cargo build --release` (for a production build)
|
||||
|
||||
The built binary can be found at `target/release/nym-mixnode`
|
||||
|
||||
## Usage
|
||||
|
||||
* `nym-mixnode` prints a help message showing usage options
|
||||
* `nym-mixnode run --help` prints a help message showing usage options for the run command
|
||||
* `nym-mixnode run --layer 1` will start the mixnode in layer 1 (coordinate with other people to find out which layer you need to start yours in)
|
||||
|
||||
By default, the Nym Mixnode will start on port 1789. If desired, you can change the port using the `--port` option.
|
||||
|
||||
### Dependencies
|
||||
|
||||
It's important to download the following repositories before building the mixnode:
|
||||
|
||||
* https://github.com/nymtech/nym-client
|
||||
* https://github.com/nymtech/nym-sfw-provider
|
||||
* https://github.com/nymtech/sphinx
|
||||
@@ -0,0 +1,5 @@
|
||||
use built;
|
||||
|
||||
fn main() {
|
||||
built::write_built_file().expect("Failed to acquire build-time information");
|
||||
}
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
use clap::{App, Arg, ArgMatches, SubCommand};
|
||||
use std::process;
|
||||
|
||||
mod mix_peer;
|
||||
mod node;
|
||||
|
||||
fn main() {
|
||||
let arg_matches = App::new("Nym Mixnode")
|
||||
.version("0.1.0")
|
||||
.author("Nymtech")
|
||||
.about("Implementation of the Loopix-based Mixnode")
|
||||
.subcommand(
|
||||
SubCommand::with_name("run")
|
||||
.about("Starts the mixnode")
|
||||
.arg(
|
||||
Arg::with_name("host")
|
||||
.long("host")
|
||||
.help("The custom host on which the mixnode will be running")
|
||||
.takes_value(true)
|
||||
.required(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("port")
|
||||
.long("port")
|
||||
.help("The port on which the mixnode will be listening")
|
||||
.takes_value(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("layer")
|
||||
.long("layer")
|
||||
.help("The mixnet layer of this particular node")
|
||||
.takes_value(true)
|
||||
.required(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("directory")
|
||||
.long("directory")
|
||||
.help("Address of the directory server the node is sending presence and metrics to")
|
||||
.takes_value(true),
|
||||
),
|
||||
)
|
||||
.get_matches();
|
||||
|
||||
if let Err(e) = execute(arg_matches) {
|
||||
println!("{}", e);
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
pub mod built_info {
|
||||
// The file has been placed there by the build script.
|
||||
include!(concat!(env!("OUT_DIR"), "/built.rs"));
|
||||
}
|
||||
|
||||
fn execute(matches: ArgMatches) -> Result<(), String> {
|
||||
match matches.subcommand() {
|
||||
("run", Some(m)) => Ok(node::runner::start(m)),
|
||||
_ => Err(usage()),
|
||||
}
|
||||
}
|
||||
|
||||
fn usage() -> String {
|
||||
banner() + "usage: --help to see available options.\n\n"
|
||||
}
|
||||
|
||||
fn banner() -> String {
|
||||
format!(
|
||||
r#"
|
||||
|
||||
_ __ _ _ _ __ ___
|
||||
| '_ \| | | | '_ \ _ \
|
||||
| | | | |_| | | | | | |
|
||||
|_| |_|\__, |_| |_| |_|
|
||||
|___/
|
||||
|
||||
(mixnode - version {:})
|
||||
|
||||
"#,
|
||||
built_info::PKG_VERSION
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
use curve25519_dalek::digest::Digest;
|
||||
use nym_client::utils::addressing;
|
||||
use std::convert::TryInto;
|
||||
use std::error::Error;
|
||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
|
||||
use tokio::prelude::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MixPeer {
|
||||
connection: SocketAddr,
|
||||
}
|
||||
|
||||
impl MixPeer {
|
||||
// note that very soon `next_hop_address` will be changed to `next_hop_metadata`
|
||||
pub fn new(next_hop_address: [u8; 32]) -> MixPeer {
|
||||
let next_hop_socket_address =
|
||||
addressing::socket_address_from_encoded_bytes(next_hop_address);
|
||||
MixPeer {
|
||||
connection: next_hop_socket_address,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn send(&self, bytes: Vec<u8>) -> Result<(), Box<dyn Error>> {
|
||||
let next_hop_address = self.connection.clone();
|
||||
let mut stream = tokio::net::TcpStream::connect(next_hop_address).await?;
|
||||
stream.write_all(&bytes).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn to_string(&self) -> String {
|
||||
self.connection.to_string()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
use futures::channel::mpsc;
|
||||
use futures::lock::Mutex;
|
||||
use futures::StreamExt;
|
||||
use nym_client::clients::directory;
|
||||
use nym_client::clients::directory::metrics::MixMetric;
|
||||
use nym_client::clients::directory::requests::metrics_mixes_post::MetricsMixPoster;
|
||||
use nym_client::clients::directory::DirectoryClient;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
const METRICS_INTERVAL: u64 = 3;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MetricsReporter {
|
||||
received: u64,
|
||||
sent: HashMap<String, u64>,
|
||||
}
|
||||
|
||||
impl MetricsReporter {
|
||||
pub(crate) fn new() -> Self {
|
||||
MetricsReporter {
|
||||
received: 0,
|
||||
sent: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn add_arc_mutex(self) -> Arc<Mutex<Self>> {
|
||||
Arc::new(Mutex::new(self))
|
||||
}
|
||||
|
||||
async fn increment_received_metrics(metrics: Arc<Mutex<MetricsReporter>>) {
|
||||
let mut unlocked = metrics.lock().await;
|
||||
unlocked.received += 1;
|
||||
}
|
||||
|
||||
pub(crate) async fn run_received_metrics_control(
|
||||
metrics: Arc<Mutex<MetricsReporter>>,
|
||||
mut rx: mpsc::Receiver<()>,
|
||||
) {
|
||||
while let Some(_) = rx.next().await {
|
||||
MetricsReporter::increment_received_metrics(metrics.clone()).await;
|
||||
}
|
||||
}
|
||||
|
||||
async fn increment_sent_metrics(metrics: Arc<Mutex<MetricsReporter>>, sent_to: String) {
|
||||
let mut unlocked = metrics.lock().await;
|
||||
let receiver_count = unlocked.sent.entry(sent_to).or_insert(0);
|
||||
*receiver_count += 1;
|
||||
}
|
||||
|
||||
pub(crate) async fn run_sent_metrics_control(
|
||||
metrics: Arc<Mutex<MetricsReporter>>,
|
||||
mut rx: mpsc::Receiver<String>,
|
||||
) {
|
||||
while let Some(sent_metric) = rx.next().await {
|
||||
MetricsReporter::increment_sent_metrics(metrics.clone(), sent_metric).await;
|
||||
}
|
||||
}
|
||||
|
||||
async fn acquire_and_reset_metrics(
|
||||
metrics: Arc<Mutex<MetricsReporter>>,
|
||||
) -> (u64, HashMap<String, u64>) {
|
||||
let mut unlocked = metrics.lock().await;
|
||||
let received = unlocked.received;
|
||||
|
||||
let sent = std::mem::replace(&mut unlocked.sent, HashMap::new());
|
||||
unlocked.received = 0;
|
||||
|
||||
(received, sent)
|
||||
}
|
||||
|
||||
pub(crate) async fn run_metrics_sender(
|
||||
metrics: Arc<Mutex<MetricsReporter>>,
|
||||
cfg: directory::Config,
|
||||
pub_key_str: String,
|
||||
) {
|
||||
let delay_duration = Duration::from_secs(METRICS_INTERVAL);
|
||||
let directory_client = directory::Client::new(cfg);
|
||||
loop {
|
||||
tokio::time::delay_for(delay_duration).await;
|
||||
let (received, sent) =
|
||||
MetricsReporter::acquire_and_reset_metrics(metrics.clone()).await;
|
||||
directory_client
|
||||
.metrics_post
|
||||
.post(&MixMetric {
|
||||
pub_key: pub_key_str.clone(),
|
||||
received,
|
||||
sent,
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
+265
@@ -0,0 +1,265 @@
|
||||
use curve25519_dalek::montgomery::MontgomeryPoint;
|
||||
use curve25519_dalek::scalar::Scalar;
|
||||
use futures::channel::mpsc;
|
||||
use futures::lock::Mutex;
|
||||
use sphinx::header::delays::Delay as SphinxDelay;
|
||||
use sphinx::{ProcessedPacket, SphinxPacket};
|
||||
use std::net::SocketAddr;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use tokio::prelude::*;
|
||||
use tokio::runtime::Runtime;
|
||||
|
||||
use crate::mix_peer;
|
||||
use crate::mix_peer::MixPeer;
|
||||
use crate::node::metrics::MetricsReporter;
|
||||
use futures::SinkExt;
|
||||
use nym_client::clients::directory;
|
||||
|
||||
mod metrics;
|
||||
mod presence;
|
||||
pub mod runner;
|
||||
|
||||
pub struct Config {
|
||||
directory_server: String,
|
||||
layer: usize,
|
||||
public_key: MontgomeryPoint,
|
||||
secret_key: Scalar,
|
||||
socket_address: SocketAddr,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn public_key_string(&self) -> String {
|
||||
let key_bytes = self.public_key.to_bytes().to_vec();
|
||||
base64::encode_config(&key_bytes, base64::URL_SAFE)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum MixProcessingError {
|
||||
InvalidPeerAddressError,
|
||||
SphinxRecoveryError,
|
||||
ReceivedFinalHopError,
|
||||
}
|
||||
|
||||
impl From<sphinx::ProcessingError> for MixProcessingError {
|
||||
// for time being just have a single error instance for all possible results of sphinx::ProcessingError
|
||||
fn from(_: sphinx::ProcessingError) -> Self {
|
||||
use MixProcessingError::*;
|
||||
|
||||
SphinxRecoveryError
|
||||
}
|
||||
}
|
||||
|
||||
struct ForwardingData {
|
||||
packet: SphinxPacket,
|
||||
delay: SphinxDelay,
|
||||
recipient: MixPeer,
|
||||
sent_metrics_tx: mpsc::Sender<String>,
|
||||
}
|
||||
|
||||
// TODO: this will need to be changed if MixPeer will live longer than our Forwarding Data
|
||||
impl ForwardingData {
|
||||
fn new(
|
||||
packet: SphinxPacket,
|
||||
delay: SphinxDelay,
|
||||
recipient: MixPeer,
|
||||
sent_metrics_tx: mpsc::Sender<String>,
|
||||
) -> Self {
|
||||
ForwardingData {
|
||||
packet,
|
||||
delay,
|
||||
recipient,
|
||||
sent_metrics_tx,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ProcessingData defines all data required to correctly unwrap sphinx packets
|
||||
struct ProcessingData {
|
||||
secret_key: Scalar,
|
||||
received_metrics_tx: mpsc::Sender<()>,
|
||||
sent_metrics_tx: mpsc::Sender<String>,
|
||||
}
|
||||
|
||||
impl ProcessingData {
|
||||
fn new(
|
||||
secret_key: Scalar,
|
||||
received_metrics_tx: mpsc::Sender<()>,
|
||||
sent_metrics_tx: mpsc::Sender<String>,
|
||||
) -> Self {
|
||||
ProcessingData {
|
||||
secret_key,
|
||||
received_metrics_tx,
|
||||
sent_metrics_tx,
|
||||
}
|
||||
}
|
||||
|
||||
fn add_arc_mutex(self) -> Arc<Mutex<Self>> {
|
||||
Arc::new(Mutex::new(self))
|
||||
}
|
||||
}
|
||||
|
||||
struct PacketProcessor;
|
||||
|
||||
impl PacketProcessor {
|
||||
pub async fn process_sphinx_data_packet(
|
||||
packet_data: &[u8],
|
||||
processing_data: Arc<Mutex<ProcessingData>>,
|
||||
) -> Result<ForwardingData, MixProcessingError> {
|
||||
// we received something resembling a sphinx packet, report it!
|
||||
let processing_data = processing_data.lock().await;
|
||||
let mut received_sender = processing_data.received_metrics_tx.clone();
|
||||
|
||||
received_sender.send(()).await.unwrap();
|
||||
|
||||
let packet = SphinxPacket::from_bytes(packet_data.to_vec())?;
|
||||
let (next_packet, next_hop_address, delay) =
|
||||
match packet.process(processing_data.secret_key) {
|
||||
ProcessedPacket::ProcessedPacketForwardHop(packet, address, delay) => {
|
||||
(packet, address, delay)
|
||||
}
|
||||
_ => return Err(MixProcessingError::ReceivedFinalHopError),
|
||||
};
|
||||
|
||||
let next_mix = MixPeer::new(next_hop_address);
|
||||
|
||||
let fwd_data = ForwardingData::new(
|
||||
next_packet,
|
||||
delay,
|
||||
next_mix,
|
||||
processing_data.sent_metrics_tx.clone(),
|
||||
);
|
||||
Ok(fwd_data)
|
||||
}
|
||||
|
||||
async fn wait_and_forward(mut forwarding_data: ForwardingData) {
|
||||
let delay_duration = Duration::from_nanos(forwarding_data.delay.get_value());
|
||||
tokio::time::delay_for(delay_duration).await;
|
||||
forwarding_data
|
||||
.sent_metrics_tx
|
||||
.send(forwarding_data.recipient.to_string())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
println!("RECIPIENT: {:?}", forwarding_data.recipient);
|
||||
match forwarding_data
|
||||
.recipient
|
||||
.send(forwarding_data.packet.to_bytes())
|
||||
.await
|
||||
{
|
||||
Ok(()) => (),
|
||||
Err(e) => {
|
||||
println!(
|
||||
"failed to write bytes to next mix peer. err = {:?}",
|
||||
e.to_string()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// the MixNode will live for whole duration of this program
|
||||
pub struct MixNode {
|
||||
directory_server: String,
|
||||
network_address: SocketAddr,
|
||||
public_key: MontgomeryPoint,
|
||||
secret_key: Scalar,
|
||||
// TODO: use it later to enforce forward travel
|
||||
// layer: usize,
|
||||
}
|
||||
|
||||
impl MixNode {
|
||||
pub fn new(config: &Config) -> Self {
|
||||
MixNode {
|
||||
directory_server: config.directory_server.clone(),
|
||||
network_address: config.socket_address,
|
||||
secret_key: config.secret_key,
|
||||
public_key: config.public_key,
|
||||
// layer: config.layer,
|
||||
}
|
||||
}
|
||||
|
||||
async fn process_socket_connection(
|
||||
mut socket: tokio::net::TcpStream,
|
||||
processing_data: Arc<Mutex<ProcessingData>>,
|
||||
) {
|
||||
// NOTE: processing_data is copied here!!
|
||||
let mut buf = [0u8; sphinx::PACKET_SIZE];
|
||||
|
||||
// In a loop, read data from the socket and write the data back.
|
||||
loop {
|
||||
match socket.read(&mut buf).await {
|
||||
// socket closed
|
||||
Ok(n) if n == 0 => {
|
||||
println!("Remote connection closed.");
|
||||
return;
|
||||
}
|
||||
Ok(_) => {
|
||||
let fwd_data = PacketProcessor::process_sphinx_data_packet(
|
||||
buf.as_ref(),
|
||||
processing_data.clone(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
PacketProcessor::wait_and_forward(fwd_data).await;
|
||||
}
|
||||
Err(e) => {
|
||||
println!("failed to read from socket; err = {:?}", e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// Write the some data back
|
||||
if let Err(e) = socket.write_all(b"foomp").await {
|
||||
println!("failed to write reply to socket; err = {:?}", e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start(&self) -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Create the runtime, probably later move it to MixNode itself?
|
||||
let mut rt = Runtime::new()?;
|
||||
|
||||
let (received_tx, received_rx) = mpsc::channel(1024);
|
||||
let (sent_tx, sent_rx) = mpsc::channel(1024);
|
||||
|
||||
let directory_cfg = directory::Config {
|
||||
base_url: self.directory_server.clone(),
|
||||
};
|
||||
let pub_key_str =
|
||||
base64::encode_config(&self.public_key.to_bytes().to_vec(), base64::URL_SAFE);
|
||||
|
||||
let metrics = MetricsReporter::new().add_arc_mutex();
|
||||
rt.spawn(MetricsReporter::run_received_metrics_control(
|
||||
metrics.clone(),
|
||||
received_rx,
|
||||
));
|
||||
rt.spawn(MetricsReporter::run_sent_metrics_control(
|
||||
metrics.clone(),
|
||||
sent_rx,
|
||||
));
|
||||
rt.spawn(MetricsReporter::run_metrics_sender(
|
||||
metrics,
|
||||
directory_cfg,
|
||||
pub_key_str,
|
||||
));
|
||||
|
||||
// Spawn the root task
|
||||
rt.block_on(async {
|
||||
let mut listener = tokio::net::TcpListener::bind(self.network_address).await?;
|
||||
let processing_data =
|
||||
ProcessingData::new(self.secret_key, received_tx, sent_tx).add_arc_mutex();
|
||||
|
||||
loop {
|
||||
let (socket, _) = listener.accept().await?;
|
||||
|
||||
let thread_processing_data = processing_data.clone();
|
||||
tokio::spawn(async move {
|
||||
MixNode::process_socket_connection(socket, thread_processing_data).await;
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
use crate::node;
|
||||
use nym_client::clients::directory;
|
||||
use nym_client::clients::directory::presence::MixNodePresence;
|
||||
use nym_client::clients::directory::requests::presence_mixnodes_post::PresenceMixNodesPoster;
|
||||
use nym_client::clients::directory::DirectoryClient;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
pub struct Notifier {
|
||||
pub net_client: directory::Client,
|
||||
presence: MixNodePresence,
|
||||
}
|
||||
|
||||
impl Notifier {
|
||||
pub fn new(node_config: &node::Config) -> Notifier {
|
||||
let config = directory::Config {
|
||||
base_url: node_config.directory_server.clone(),
|
||||
};
|
||||
let net_client = directory::Client::new(config);
|
||||
let presence = MixNodePresence {
|
||||
host: node_config.socket_address.to_string(), // note: the directory server determines the real incoming IP itself, but uses the socket. Host here is just a placeholder.
|
||||
pub_key: node_config.public_key_string(),
|
||||
layer: node_config.layer as u64,
|
||||
last_seen: 0,
|
||||
};
|
||||
Notifier {
|
||||
net_client,
|
||||
presence,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn notify(&self) {
|
||||
self.net_client
|
||||
.presence_mix_nodes_post
|
||||
.post(&self.presence)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub fn run(&self) {
|
||||
loop {
|
||||
self.notify();
|
||||
thread::sleep(Duration::from_secs(5));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
use crate::banner;
|
||||
use crate::node;
|
||||
use crate::node::presence;
|
||||
use crate::node::MixNode;
|
||||
use clap::ArgMatches;
|
||||
|
||||
use std::net::ToSocketAddrs;
|
||||
use std::thread;
|
||||
|
||||
pub fn start(matches: &ArgMatches) {
|
||||
println!("{}", banner());
|
||||
println!("Starting mixnode...");
|
||||
|
||||
let config = new_config(matches);
|
||||
println!("Public key: {}", config.public_key_string());
|
||||
println!("Directory server: {}", config.directory_server);
|
||||
println!(
|
||||
"Listening for incoming packets on {}",
|
||||
config.socket_address
|
||||
);
|
||||
|
||||
let mix = MixNode::new(&config);
|
||||
thread::spawn(move || {
|
||||
let notifier = presence::Notifier::new(&config);
|
||||
notifier.run();
|
||||
});
|
||||
mix.start().unwrap();
|
||||
}
|
||||
|
||||
fn new_config(matches: &ArgMatches) -> node::Config {
|
||||
let host = matches.value_of("host").unwrap();
|
||||
|
||||
let port = match matches.value_of("port").unwrap_or("1789").parse::<u16>() {
|
||||
Ok(n) => n,
|
||||
Err(err) => panic!("Invalid port value provided - {:?}", err),
|
||||
};
|
||||
|
||||
let layer = match matches.value_of("layer").unwrap().parse::<usize>() {
|
||||
Ok(n) => n,
|
||||
Err(err) => panic!("Invalid layer value provided - {:?}", err),
|
||||
};
|
||||
|
||||
let socket_address = (host, port)
|
||||
.to_socket_addrs()
|
||||
.expect("Failed to combine host and port")
|
||||
.next()
|
||||
.expect("Failed to extract the socket address from the iterator");
|
||||
|
||||
let (secret_key, public_key) = sphinx::crypto::keygen();
|
||||
|
||||
let directory_server = matches
|
||||
.value_of("directory")
|
||||
.unwrap_or("https://directory.nymtech.net")
|
||||
.to_string();
|
||||
|
||||
node::Config {
|
||||
directory_server,
|
||||
layer,
|
||||
public_key,
|
||||
socket_address,
|
||||
secret_key,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user