diff --git a/nym-sphinx-wasm/src/lib.rs b/nym-sphinx-wasm/src/lib.rs index 0546a5558c..9b5ca20a0e 100644 --- a/nym-sphinx-wasm/src/lib.rs +++ b/nym-sphinx-wasm/src/lib.rs @@ -14,15 +14,18 @@ use crypto::identity::MixIdentityPublicKey; use curve25519_dalek::montgomery::MontgomeryPoint; use nymsphinx::addressing::nodes::NymNodeRoutingAddress; +use nymsphinx::chunking::split_and_prepare_payloads; use serde::{Deserialize, Serialize}; use serde_json; use sphinx::header::delays; use sphinx::route::DestinationAddressBytes; +use sphinx::route::NodeAddressBytes; use sphinx::route::{Destination, Node}; use sphinx::SphinxPacket; +use std::convert::TryFrom; use std::convert::TryInto; +use std::net::SocketAddr; use std::time::Duration; - use wasm_bindgen::prelude::*; mod utils; @@ -36,12 +39,12 @@ const IDENTIFIER_LENGTH: usize = 16; static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; #[derive(Serialize, Deserialize)] -pub struct Route { +pub struct JsonRoute { nodes: Vec, } #[wasm_bindgen] -#[derive(Serialize, Deserialize)] +#[derive(Clone, Serialize, Deserialize)] pub struct NodeData { address: String, public_key: String, @@ -54,36 +57,73 @@ pub struct NodeData { /// Message chunking is currently not implemented. If the message exceeds the /// capacity of a single Sphinx packet, the extra information will be discarded. #[wasm_bindgen] -pub fn create_sphinx_packet(rout: String, msg: &str, destination: &str) -> Vec { +pub fn create_sphinx_packet(raw_route: &str, msg: &str, destination: &str) -> Vec { utils::set_panic_hook(); // nicer js errors. - let r: Route = serde_json::from_str(&rout).unwrap(); - let mut route: Vec = vec![]; - for node in r.nodes.iter() { - let address = node.address.parse().unwrap(); - let public_key = public_key_from_str(&node.public_key); - let n = NymNodeRoutingAddress(address); - let x = Node::new(n.try_into().unwrap(), public_key); - route.push(x); - } + let route = sphinx_route_from(raw_route); - let average_delay = Duration::from_secs_f64(1.0); + let average_delay = Duration::from_secs_f64(0.1); let delays = delays::generate_from_average_duration(route.len(), average_delay); let dest_bytes = DestinationAddressBytes::from_base58_string(destination.to_owned()); let dest = Destination::new(dest_bytes, [4u8; IDENTIFIER_LENGTH]); + let message = split_and_prepare_payloads(&msg.as_bytes()).pop().unwrap(); + let sphinx_packet = match SphinxPacket::new(message, &route, &dest, &delays).unwrap() { + SphinxPacket { header, payload } => SphinxPacket { header, payload }, + }; - let mut message = nymsphinx::chunking::split_and_prepare_payloads(&msg.as_bytes()); - let sphinx_packet = - match SphinxPacket::new(message.pop().unwrap(), &route, &dest, &delays).unwrap() { - SphinxPacket { header, payload } => SphinxPacket { header, payload }, - }; - - sphinx_packet.to_bytes() + payload(sphinx_packet, route) } -fn public_key_from_str(s: &str) -> MontgomeryPoint { +/// Concatenate the first mix address bytes with the Sphinx packet. +/// +/// The Nym gateway node has no idea what is inside the Sphinx packet, or where +/// it should send a packet it receives. So we prepend the packet with the +/// address bytes of the first mix inside the packet, so that the gateway can +/// forward the packet to it. +fn payload(sphinx_packet: SphinxPacket, route: Vec) -> Vec { + let mut packet = sphinx_packet.to_bytes(); + let mut first_mix_address = route.first().unwrap().clone().address.to_bytes().to_vec(); + let mut bytes: Vec = vec![]; + bytes.append(&mut first_mix_address); + bytes.append(&mut packet); + bytes +} + +fn sphinx_route_from(raw_route: &str) -> Vec { + let json_route: JsonRoute = serde_json::from_str(raw_route).unwrap(); + + assert!( + json_route.nodes.len() > 0, + "Sphinx packet must route to at least one mixnode." + ); + + let mut sphinx_route: Vec = vec![]; + for node_data in json_route.nodes.iter() { + let x = Node::try_from(node_data.clone()).expect("Malformed NodeData"); + sphinx_route.push(x); + } + sphinx_route +} + +// Converts a base58 &str into a public key (MontgomeryPoint) +// +// A TryInto would be nice here but this crate doesn't own MontgomeryPoint. +fn public_key_from(s: &str) -> MontgomeryPoint { let src = MixIdentityPublicKey::from_base58_string(s.to_owned()).to_bytes(); let mut dest: [u8; 32] = [0; 32]; dest.copy_from_slice(&src); MontgomeryPoint(dest) } + +impl TryFrom for Node { + type Error = (); + + fn try_from(node_data: NodeData) -> Result { + let parsed: SocketAddr = node_data.address.parse().unwrap(); + let address: NodeAddressBytes = NymNodeRoutingAddress(parsed).try_into().unwrap(); + + let pub_key = public_key_from(&node_data.public_key); + + Ok(Node { address, pub_key }) + } +}