Files
nym/common/node-tester-utils/src/tester.rs
T
Drazen Urch b5c8b69547 Outfox integration (#3331)
* Experiment with serde

* Framed encoding serde POC

* Outfox framing

* Outfox rest compat (#3333)

* Outfox forwarding compat

* Tidy up interface

* PacketSize compat

* Address PR comments

* Rebase on develop

commit 342883fcbe
Author: durch <durch@users.noreply.github.com>
Date:   Thu Apr 27 09:17:18 2023 +0200

    Put back PacketType 1

commit 61a0ee5a19
Author: Tommy Verrall <tommyvez@protonmail.com>
Date:   Wed Apr 26 16:37:29 2023 +0100

    change output for cpu-cycle management logs

commit 3956109c7e
Author: Tommy Verrall <tommy@nymtech.net>
Date:   Wed Apr 26 12:13:22 2023 +0100

    change the workflow file to build with cpucycles

commit 8d725b13c5
Author: durch <durch@users.noreply.github.com>
Date:   Mon Apr 24 13:14:58 2023 +0200

    Outfox client compat

commit 4d166c389b
Author: durch <durch@users.noreply.github.com>
Date:   Fri Apr 21 00:30:46 2023 +0200

    Address PR comments

commit 145c3c1223
Author: durch <durch@users.noreply.github.com>
Date:   Fri Apr 21 00:12:35 2023 +0200

    Rename PacketMode

commit cbd654d6fd
Author: Drazen Urch <drazen@urch.eu>
Date:   Thu Apr 20 23:59:40 2023 +0200

    Outfox rest compat (#3333)

    * Outfox forwarding compat

    * Tidy up interface

    * PacketSize compat

commit e7be91a94c
Author: durch <durch@users.noreply.github.com>
Date:   Wed Apr 19 16:36:48 2023 +0200

    Remove serde cruft

commit 582e7d566a
Author: durch <durch@users.noreply.github.com>
Date:   Wed Apr 19 16:24:09 2023 +0200

    Outfox framing

commit 6464da5f01
Author: durch <durch@users.noreply.github.com>
Date:   Tue Apr 18 22:23:02 2023 +0200

    Framing compat

commit d5e77e499b
Author: durch <durch@users.noreply.github.com>
Date:   Tue Apr 18 18:18:54 2023 +0200

    Framed encoding serde POC

commit f086f9c35a
Author: durch <durch@users.noreply.github.com>
Date:   Tue Apr 18 16:54:21 2023 +0200

    Experiment with serde

* Client tweaks

* Speed up from_plaintext

* SurbAcks

* More work on the reciever end, and outfox format

* Cleanup and fmt

* Wrap up rebase

* Happy clippy

* Fix lock files

* Final cleanup
2023-05-29 10:05:11 +02:00

290 lines
8.6 KiB
Rust

// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::error::NetworkTestingError;
use crate::Empty;
use crate::MixId;
use crate::TestMessage;
use nym_sphinx::acknowledgements::AckKey;
use nym_sphinx::addressing::clients::Recipient;
use nym_sphinx::message::NymMessage;
use nym_sphinx::params::{PacketSize, DEFAULT_NUM_MIX_HOPS};
use nym_sphinx::preparer::{FragmentPreparer, PreparedFragment};
use nym_sphinx_params::PacketType;
use nym_topology::{gateway, mix, NymTopology};
use rand::{CryptoRng, Rng};
use serde::Serialize;
use std::sync::Arc;
use std::time::Duration;
pub struct NodeTester<R> {
rng: R,
base_topology: NymTopology,
/// Generally test packets are designed to be sent from ourselves to ourselves,
/// However, one might want to customise this behaviour.
/// In that case an explicit `Recipient` has to be provided when constructing test packets.
self_address: Option<Recipient>,
packet_size: PacketSize,
/// Average delay a data packet is going to get delay at a single mixnode.
average_packet_delay: Duration,
/// Average delay an acknowledgement packet is going to get delay at a single mixnode.
average_ack_delay: Duration,
/// Number of mix hops each packet ('real' message, ack, reply) is expected to take.
/// Note that it does not include gateway hops.
num_mix_hops: u8,
// while acks are going to be ignored they still need to be constructed
// so that the gateway would be able to correctly process and forward the message
ack_key: Arc<AckKey>,
}
impl<R> NodeTester<R>
where
R: Rng + CryptoRng,
{
pub fn new(
rng: R,
base_topology: NymTopology,
self_address: Option<Recipient>,
packet_size: PacketSize,
average_packet_delay: Duration,
average_ack_delay: Duration,
ack_key: Arc<AckKey>,
) -> Self {
Self {
rng,
base_topology,
self_address,
packet_size,
average_packet_delay,
average_ack_delay,
num_mix_hops: DEFAULT_NUM_MIX_HOPS,
ack_key,
}
}
/// Allows setting non-default number of expected mix hops in the network.
#[allow(dead_code)]
pub fn with_mix_hops(mut self, hops: u8) -> Self {
self.num_mix_hops = hops;
self
}
pub fn testable_mix_topology(&self, node: &mix::Node) -> NymTopology {
let mut topology = self.base_topology.clone();
topology.set_mixes_in_layer(node.layer as u8, vec![node.clone()]);
topology
}
pub fn testable_gateway_topology(&self, gateway: &gateway::Node) -> NymTopology {
let mut topology = self.base_topology.clone();
topology.set_gateways(vec![gateway.clone()]);
topology
}
pub fn simple_mixnode_test_packets(
&mut self,
mix: &mix::Node,
test_packets: u32,
) -> Result<Vec<PreparedFragment>, NetworkTestingError> {
self.mixnode_test_packets(mix, Empty, test_packets, None)
}
pub fn mixnode_test_packets<T>(
&mut self,
mix: &mix::Node,
msg_ext: T,
test_packets: u32,
custom_recipient: Option<Recipient>,
) -> Result<Vec<PreparedFragment>, NetworkTestingError>
where
T: Serialize + Clone,
{
let ephemeral_topology = self.testable_mix_topology(mix);
let mut packets = Vec::with_capacity(test_packets as usize);
for plaintext in TestMessage::mix_plaintexts(mix, test_packets, msg_ext)? {
packets.push(self.wrap_plaintext_data(
plaintext,
&ephemeral_topology,
custom_recipient,
)?);
}
Ok(packets)
}
pub fn mixnodes_test_packets<T>(
&mut self,
nodes: &[mix::Node],
msg_ext: T,
test_packets: u32,
custom_recipient: Option<Recipient>,
) -> Result<Vec<PreparedFragment>, NetworkTestingError>
where
T: Serialize + Clone,
{
let mut packets = Vec::new();
for node in nodes {
packets.append(&mut self.mixnode_test_packets(
node,
msg_ext.clone(),
test_packets,
custom_recipient,
)?)
}
Ok(packets)
}
pub fn existing_mixnode_test_packets<T>(
&mut self,
mix_id: MixId,
msg_ext: T,
test_packets: u32,
custom_recipient: Option<Recipient>,
) -> Result<Vec<PreparedFragment>, NetworkTestingError>
where
T: Serialize + Clone,
{
let Some(node) = self.base_topology.find_mix(mix_id) else {
return Err(NetworkTestingError::NonExistentMixnode {mix_id})
};
self.mixnode_test_packets(&node.clone(), msg_ext, test_packets, custom_recipient)
}
pub fn existing_identity_mixnode_test_packets<T>(
&mut self,
encoded_mix_identity: String,
msg_ext: T,
test_packets: u32,
custom_recipient: Option<Recipient>,
) -> Result<Vec<PreparedFragment>, NetworkTestingError>
where
T: Serialize + Clone,
{
let Some(node) = self.base_topology.find_mix_by_identity(&encoded_mix_identity) else {
return Err(NetworkTestingError::NonExistentMixnodeIdentity { mix_identity: encoded_mix_identity })
};
self.mixnode_test_packets(&node.clone(), msg_ext, test_packets, custom_recipient)
}
pub fn gateway_test_packets<T>(
&mut self,
gateway: &gateway::Node,
msg_ext: T,
test_packets: u32,
custom_recipient: Option<Recipient>,
) -> Result<Vec<PreparedFragment>, NetworkTestingError>
where
T: Serialize + Clone,
{
let ephemeral_topology = self.testable_gateway_topology(gateway);
let mut packets = Vec::with_capacity(test_packets as usize);
for plaintext in TestMessage::gateway_plaintexts(gateway, test_packets, msg_ext)? {
packets.push(self.wrap_plaintext_data(
plaintext,
&ephemeral_topology,
custom_recipient,
)?);
}
Ok(packets)
}
pub fn existing_gateway_test_packets<T>(
&mut self,
encoded_gateway_identity: String,
msg_ext: T,
test_packets: u32,
custom_recipient: Option<Recipient>,
) -> Result<Vec<PreparedFragment>, NetworkTestingError>
where
T: Serialize + Clone,
{
let Some(node) = self.base_topology.find_gateway(&encoded_gateway_identity) else {
return Err(NetworkTestingError::NonExistentGateway { gateway_identity: encoded_gateway_identity })
};
self.gateway_test_packets(&node.clone(), msg_ext, test_packets, custom_recipient)
}
pub fn wrap_plaintext_data(
&mut self,
plaintext: Vec<u8>,
topology: &NymTopology,
custom_recipient: Option<Recipient>,
) -> Result<PreparedFragment, NetworkTestingError> {
let message = NymMessage::new_plain(plaintext);
let mut fragments = self.pad_and_split_message(message, self.packet_size);
if fragments.len() != 1 {
return Err(NetworkTestingError::TestMessageTooLong);
}
// SAFETY: the unwrap here is fine as if the vec was somehow empty
// we would have returned the error when checking for its length
let fragment = fragments.pop().unwrap();
// either `self_address` or `custom_recipient` has to be specified.
let address = custom_recipient.unwrap_or(
self.self_address
.ok_or(NetworkTestingError::UnknownPacketRecipient)?,
);
// TODO: can we avoid this arc clone?
let ack_key = Arc::clone(&self.ack_key);
Ok(self.prepare_chunk_for_sending(
fragment,
topology,
&ack_key,
&address,
&address,
PacketType::Mix,
)?)
}
pub fn create_test_packet<T>(
&mut self,
message: &TestMessage<T>,
topology: &NymTopology,
custom_recipient: Option<Recipient>,
) -> Result<PreparedFragment, NetworkTestingError>
where
T: Serialize,
{
let serialized = message.as_bytes()?;
self.wrap_plaintext_data(serialized, topology, custom_recipient)
}
}
impl<R: CryptoRng + Rng> FragmentPreparer for NodeTester<R> {
type Rng = R;
fn rng(&mut self) -> &mut Self::Rng {
&mut self.rng
}
fn num_mix_hops(&self) -> u8 {
self.num_mix_hops
}
fn average_packet_delay(&self) -> Duration {
self.average_packet_delay
}
fn average_ack_delay(&self) -> Duration {
self.average_ack_delay
}
}