Files
nym/src/node/mod.rs
T

208 lines
6.7 KiB
Rust

use std::net::SocketAddr;
use std::sync::{Arc, RwLock};
use std::time::Duration;
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 tokio::time::{interval_at, Instant, Interval};
use curve25519_dalek::scalar::Scalar;
use sphinx::header::delays::Delay as SphinxDelay;
use sphinx::{ProcessedPacket, SphinxPacket};
use tokio::prelude::*;
use tokio::runtime::Runtime;
use crate::mix_peer::MixPeer;
pub mod runner;
// TODO: this will probably need to be moved elsewhere I imagine
#[derive(Debug)]
pub enum MixProcessingError {
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,
}
// 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) -> Self {
ForwardingData {
packet,
delay,
recipient,
}
}
}
// ProcessingData defines all data required to correctly unwrap sphinx packets
struct ProcessingData {
secret_key: Scalar,
}
impl ProcessingData {
fn new(secret_key: Scalar) -> Self {
ProcessingData { secret_key }
}
fn add_arc_rwlock(self) -> Arc<RwLock<Self>> {
Arc::new(RwLock::new(self))
}
}
struct PacketProcessor {}
impl PacketProcessor {
pub fn process_sphinx_data_packet(
packet_data: &[u8],
processing_data: Arc<RwLock<ProcessingData>>,
) -> Result<ForwardingData, MixProcessingError> {
let packet = SphinxPacket::from_bytes(packet_data.to_vec())?;
let (next_packet, next_hop_address, delay) =
match packet.process(processing_data.read().unwrap().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);
Ok(fwd_data)
}
async fn wait_and_forward(forwarding_data: ForwardingData) {
let delay_duration = Duration::from_nanos(forwarding_data.delay.get_value());
println!("client says to wait for {:?}", delay_duration);
tokio::time::delay_for(delay_duration).await;
println!("waited {:?} - time to forward the packet!", delay_duration);
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 {
network_address: SocketAddr,
secret_key: Scalar,
layer: usize,
}
impl MixNode {
pub fn new(network_address: SocketAddr, secret_key: Scalar, layer: usize) -> Self {
MixNode {
network_address,
secret_key,
layer,
}
}
async fn process_socket_connection(
mut socket: tokio::net::TcpStream,
processing_data: Arc<RwLock<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(),
)
.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_listening(&self) -> Result<(), Box<dyn std::error::Error>> {
// Create the runtime, probably later move it to MixNode itself?
let mut rt = Runtime::new()?;
// 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).add_arc_rwlock();
let (directory, presence, mut presence_timer) = self.setup_presence_timer();
loop {
presence_timer.tick().await;
let response = directory.presence_mix_nodes_post.post(&presence);
println!("response: {:?}", response.unwrap().text());
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;
});
}
})
}
fn setup_presence_timer(&self) -> (directory::Client, MixNodePresence, Interval) {
let presence_notifications_start = Instant::now() + Duration::from_nanos(5000);
let presence_notifications_interval =
interval_at(presence_notifications_start, Duration::from_millis(5000));
let config = directory::Config {
base_url: "https://directory.nymtech.net/".to_string(),
};
let directory = directory::Client::new(config);
let presence = MixNodePresence {
host: "halpin.org:6666".to_string(),
pub_key: "superkey".to_string(),
layer: 666,
last_seen: 666,
};
(directory, presence, presence_notifications_interval)
}
}