Compare commits

...

4 Commits

Author SHA1 Message Date
aniampio 5f758f47ba Add extended packet sizes 2023-02-15 09:17:30 +00:00
aniampio e0274bb394 Increase sampling time 2023-02-14 21:15:33 +00:00
aniampio 607d4dc743 Change benchmark name 2023-02-14 21:13:13 +00:00
aniampio 25f7b7a083 Add benchmarks 2023-02-14 17:44:19 +00:00
6 changed files with 326 additions and 44 deletions
Generated
+1
View File
@@ -3924,6 +3924,7 @@ dependencies = [
name = "nymsphinx"
version = "0.1.0"
dependencies = [
"criterion 0.3.6",
"crypto",
"mixnet-contract-common",
"nymsphinx-acknowledgements",
+5
View File
@@ -27,6 +27,11 @@ topology = { path = "../topology" }
[dev-dependencies]
mixnet-contract-common = { path = "../cosmwasm-smart-contracts/mixnet-contract" }
criterion = "0.3"
[[bench]]
name = "benchmarks"
harness = false
# do not include this when compiling into wasm as it somehow when combined together with reqwest, it will require
# net2 via tokio-util -> tokio -> mio -> net2
+269
View File
@@ -0,0 +1,269 @@
use std::borrow::Borrow;
use std::collections::HashMap;
use std::sync::Arc;
use std::time::Duration;
use criterion::{black_box, Criterion, criterion_group, criterion_main};
use crypto::asymmetric::{encryption, identity};
use crypto::asymmetric::encryption::{KeyPair, PrivateKey};
use crypto::asymmetric::identity::PublicKey;
use mixnet_contract_common::Layer;
use nymsphinx::{delays, Node, NODE_ADDRESS_LENGTH, NodeAddressBytes, NymsphinxPayloadBuilder, PAYLOAD_OVERHEAD_SIZE, SphinxPacket};
use nymsphinx::acknowledgements::AckKey;
use nymsphinx::acknowledgements::surb_ack::SurbAck;
use nymsphinx::addressing::clients::Recipient;
use nymsphinx::builder::SphinxPacketBuilder;
use nymsphinx::chunking::fragment::{Fragment, FragmentHeader, FragmentIdentifier};
use nymsphinx::cover::generate_loop_cover_packet;
use nymsphinx::crypto::keygen;
use nymsphinx::params::packet_sizes::PacketSize::{ExtendedPacket16, ExtendedPacket32, ExtendedPacket8, RegularPacket};
use nymsphinx::params::PacketSize;
use topology::{gateway, mix, MixLayer, NymTopology};
const REGULAR_PACKET_SIZE: usize = PAYLOAD_OVERHEAD_SIZE + 2 * 1024;
const EXTENDED_PACKET_SIZE_8: usize = PAYLOAD_OVERHEAD_SIZE + 8 * 1024;
const EXTENDED_PACKET_SIZE_16: usize = PAYLOAD_OVERHEAD_SIZE + 16 * 1024;
const EXTENDED_PACKET_SIZE_32: usize = PAYLOAD_OVERHEAD_SIZE + 32 * 1024;
struct BenchCase {
packet_size: PacketSize,
}
fn feature_topology(sender_gateway_id: PublicKey, recipient_gateway_id: PublicKey) -> (NymTopology, KeyPair) {
let mut rng = rand::thread_rng();
let gateway1 = gateway::Node {
owner: "N/A".to_string(),
stake: 1000,
location: "N/A".to_string(),
host: "1.1.1.1".parse().unwrap(),
mix_host: "1.1.1.1:1789".parse().unwrap(),
clients_port: 8888,
identity_key: sender_gateway_id,
sphinx_key: encryption::PublicKey::from_base58_string(
"C7cown6dYCLZpLiMFC1PaBmhvLvmJmLDJGeRTbPD45bX",
)
.unwrap(),
version: "0.x.0".to_string(),
};
let gateway2 = gateway::Node {
identity_key: recipient_gateway_id,
..gateway1.clone()
};
let node1_enc_keys = KeyPair::new(&mut rng);
let node1 = mix::Node {
mix_id: 42,
owner: "N/A".to_string(),
host: "3.3.3.3".parse().unwrap(),
mix_host: "3.3.3.3:1789".parse().unwrap(),
identity_key: identity::PublicKey::from_base58_string(
"3ebjp1Fb9hdcS1AR6AZihgeJiMHkB5jjJUsvqNnfQwU7",
)
.unwrap(),
sphinx_key: *node1_enc_keys.public_key(),
layer: Layer::One,
version: "0.x.0".to_string(),
};
let node2 = mix::Node {
owner: "Alice".to_string(),
..node1.clone()
};
let node3 = mix::Node {
owner: "Bob".to_string(),
..node1.clone()
};
let mut mixes: HashMap<MixLayer, Vec<mix::Node>> = HashMap::new();
mixes.insert(1, vec![node1]);
mixes.insert(2, vec![node2]);
mixes.insert(3, vec![node3]);
let topology = NymTopology::new(mixes, vec![gateway1, gateway2]);
(topology, node1_enc_keys)
}
fn make_packet_copy(packet: &SphinxPacket) -> SphinxPacket {
SphinxPacket::from_bytes(&packet.to_bytes()).unwrap()
}
fn bench_loop_packet_create(c: &mut Criterion) {
let mut group = c.benchmark_group("benchmark-sphinx");
// group.sample_size(200);
group.measurement_time(Duration::from_secs(500));
let mut rng = rand::thread_rng();
let case = BenchCase {
packet_size: RegularPacket,
};
// create sender
let sender_client_id_pair = identity::KeyPair::new(&mut rng);
let sender_client_enc_pair = encryption::KeyPair::new(&mut rng);
let sender_gateway_id_pair = identity::KeyPair::new(&mut rng);
let packet_sender = Recipient::new(
*sender_client_id_pair.public_key(),
*sender_client_enc_pair.public_key(),
*sender_gateway_id_pair.public_key(),
);
// build topology
let (topology, node_keypair) = feature_topology(*sender_gateway_id_pair.public_key(), *sender_gateway_id_pair.public_key());
// generate the encryption key for the ack
let ack_key = AckKey::new(&mut rng);
group.bench_function(
&format!(
"[Sphinx] create_loop_cover_packet_with_payload_size_{}",
case.packet_size.payload_size(),
),
|b| {
b.iter(|| {
generate_loop_cover_packet(
&mut rng,
&topology,
&ack_key,
&packet_sender,
Duration::from_millis(50),
Duration::from_millis(50),
case.packet_size)
})
},
);
// let's create the packet to later benchmark the processing
let packet = generate_loop_cover_packet(
&mut rng,
&topology,
&ack_key,
&packet_sender,
Duration::from_millis(50),
Duration::from_millis(50),
case.packet_size).unwrap();
group.bench_function(
&format!(
"[Sphinx] process_loop_cover_packet_with_payload_size_{}",
case.packet_size.payload_size(),
),
|b| {
b.iter(|| {
make_packet_copy(&packet.sphinx_packet).process(&node_keypair.private_key().into())
})
},
);
// let new_packet = packet.sphinx_packet.process(&node_keypair.private_key().into());
}
fn bench_new_no_surb(c: &mut Criterion) {
let mut group = c.benchmark_group("benchmark-sphinx");
// group.sample_size(200);
group.measurement_time(Duration::from_secs(500));
let mut rng = rand::thread_rng();
let case = BenchCase {
packet_size: ExtendedPacket8,
};
// create sender
let sender_client_id_pair = identity::KeyPair::new(&mut rng);
let sender_client_enc_pair = encryption::KeyPair::new(&mut rng);
let sender_gateway_id_pair = identity::KeyPair::new(&mut rng);
let packet_sender = Recipient::new(
*sender_client_id_pair.public_key(),
*sender_client_enc_pair.public_key(),
*sender_gateway_id_pair.public_key(),
);
// create recipient
let recipient_client_id_pair = identity::KeyPair::new(&mut rng);
let recipient_client_enc_pair = encryption::KeyPair::new(&mut rng);
let recipient_gateway_id_pair = identity::KeyPair::new(&mut rng);
let packet_recipient = Recipient::new(
*recipient_client_id_pair.public_key(),
*recipient_client_enc_pair.public_key(),
*recipient_gateway_id_pair.public_key(),
);
// build topology
let (topology, node_keypair) = feature_topology(*sender_gateway_id_pair.public_key(), *recipient_gateway_id_pair.public_key());
// generate pseudorandom route for the packet
let route = topology.random_route_to_gateway(
&mut rng,
3,
packet_recipient.gateway(),
).unwrap();
// generate some payload
let mlen = 40;
let mut msg = vec![0u8; mlen];
let fragment = Fragment {
header: FragmentHeader::try_new(
12345,
u8::max_value(),
u8::max_value(),
None,
Some(1234),
)
.unwrap(),
payload: msg,
};
let ack_key = AckKey::new(&mut rng);
let surb_ack = SurbAck::construct(
&mut rng,
&packet_sender,
&ack_key,
fragment.fragment_identifier().to_bytes(),
Duration::from_millis(50),
&topology,
).unwrap();
let packet_payload = NymsphinxPayloadBuilder::new(fragment, surb_ack)
.build_regular(&mut rng, packet_recipient.encryption_key());
let delays = delays::generate_from_average_duration(route.len(), Duration::from_millis(50));
let destination = packet_recipient.as_sphinx_destination();
group.bench_function(
&format!(
"[Sphinx] create_packet_no_reply_surbs_with_payload_size_{}",
case.packet_size.payload_size(),
),
|b| {
b.iter(|| {
SphinxPacketBuilder::new()
.with_payload_size(case.packet_size.payload_size())
.build_packet(packet_payload.clone(), &route, &destination, &delays)
})
},
);
// let's create the packet to later benchmark the processing
let sphinx_packet = SphinxPacketBuilder::new()
.with_payload_size(case.packet_size.payload_size())
.build_packet(packet_payload.clone(), &route, &destination, &delays)
.unwrap();
group.bench_function(
&format!(
"[Sphinx] process_packet_with_payload_size_{}",
case.packet_size.payload_size(),
),
|b| {
b.iter(|| {
make_packet_copy(&sphinx_packet).process(&node_keypair.private_key().into())
})
},
);
}
criterion_group!(sphinx, bench_loop_packet_create, bench_new_no_surb);
criterion_main!(sphinx);
+39 -35
View File
@@ -1,11 +1,13 @@
// Copyright 2021-2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::ChunkingError;
use nymsphinx_params::{SerializedFragmentIdentifier, FRAG_ID_LEN};
use std::convert::TryInto;
use std::fmt::{self, Debug, Formatter};
use nymsphinx_params::{FRAG_ID_LEN, SerializedFragmentIdentifier};
use crate::ChunkingError;
// Personal reflection: In hindsight I've spent too much time on relatively too little
// gain here, as even though I might have saved couple of bytes per packet, the gain
// is negligible in the context of having to include SURB-ACKs and reply-SURBs in the packets.
@@ -110,8 +112,8 @@ impl FragmentIdentifier {
/// header used to reconstruct the message after being received.
#[derive(PartialEq, Clone)]
pub struct Fragment {
header: FragmentHeader,
payload: Vec<u8>,
pub header: FragmentHeader,
pub payload: Vec<u8>,
}
// manual implementation to hide detailed payload that we don't care about
@@ -290,7 +292,7 @@ impl Fragment {
/// and for the longest messages, without upper bound, there is usually also only 7 bytes
/// of overhead apart from first and last fragments in each set that instead have 10 bytes of overhead.
#[derive(PartialEq, Clone, Debug)]
pub(crate) struct FragmentHeader {
pub struct FragmentHeader {
/// ID associated with `FragmentSet` to which this particular `Fragment` belongs.
/// Its value is restricted to (0, i32::max_value()].
/// Note that it *excludes* 0, but *includes* i32::max_value().
@@ -319,7 +321,7 @@ impl FragmentHeader {
/// Tries to create a new `FragmentHeader` using provided metadata. Bunch of logical
/// checks are performed to see if the data is not self-contradictory,
/// for example if current_fragment > total_fragments.
fn try_new(
pub fn try_new(
id: i32,
total_fragments: u8,
current_fragment: u8,
@@ -460,9 +462,11 @@ impl FragmentHeader {
#[cfg(test)]
mod fragment_tests {
use super::*;
use rand::{RngCore, thread_rng};
use nymsphinx_params::packet_sizes::PacketSize;
use rand::{thread_rng, RngCore};
use super::*;
fn max_plaintext_size() -> usize {
PacketSize::default().plaintext_size() - PacketSize::AckPacket.size()
@@ -585,7 +589,7 @@ mod fragment_tests {
None,
Some(1234),
)
.unwrap(),
.unwrap(),
payload: msg,
};
let packet_bytes = fragment.clone().into_bytes();
@@ -602,7 +606,7 @@ mod fragment_tests {
None,
Some(1234),
)
.unwrap(),
.unwrap(),
payload: msg,
};
let packet_bytes = fragment.clone().into_bytes();
@@ -644,7 +648,7 @@ mod fragment_tests {
None,
max_plaintext_size(),
)
.is_ok());
.is_ok());
assert!(Fragment::try_new(
&non_full_payload,
id,
@@ -654,7 +658,7 @@ mod fragment_tests {
None,
max_plaintext_size(),
)
.is_ok());
.is_ok());
assert!(Fragment::try_new(
&non_full_payload2,
@@ -665,7 +669,7 @@ mod fragment_tests {
None,
max_plaintext_size(),
)
.is_ok());
.is_ok());
assert!(Fragment::try_new(
&non_full_payload2,
id,
@@ -675,7 +679,7 @@ mod fragment_tests {
None,
max_plaintext_size(),
)
.is_ok());
.is_ok());
}
#[test]
@@ -697,7 +701,7 @@ mod fragment_tests {
None,
max_plaintext_size(),
)
.is_err());
.is_err());
assert!(Fragment::try_new(
&non_full_payload,
id,
@@ -707,7 +711,7 @@ mod fragment_tests {
None,
max_plaintext_size(),
)
.is_err());
.is_err());
assert!(Fragment::try_new(
&too_much_payload,
@@ -718,7 +722,7 @@ mod fragment_tests {
None,
max_plaintext_size(),
)
.is_err());
.is_err());
assert!(Fragment::try_new(
&too_much_payload,
id,
@@ -728,7 +732,7 @@ mod fragment_tests {
None,
max_plaintext_size(),
)
.is_err());
.is_err());
assert!(Fragment::try_new(
&too_much_payload,
id,
@@ -738,7 +742,7 @@ mod fragment_tests {
None,
max_plaintext_size(),
)
.is_err());
.is_err());
assert!(Fragment::try_new(
&non_full_payload2,
@@ -749,7 +753,7 @@ mod fragment_tests {
None,
max_plaintext_size(),
)
.is_err());
.is_err());
assert!(Fragment::try_new(
&non_full_payload2,
id,
@@ -759,7 +763,7 @@ mod fragment_tests {
None,
max_plaintext_size(),
)
.is_err());
.is_err());
}
#[test]
@@ -780,7 +784,7 @@ mod fragment_tests {
None,
max_plaintext_size(),
)
.is_ok());
.is_ok());
assert!(Fragment::try_new(
&full_payload,
id,
@@ -790,7 +794,7 @@ mod fragment_tests {
None,
max_plaintext_size(),
)
.is_ok());
.is_ok());
assert!(Fragment::try_new(
&non_full_payload,
id,
@@ -800,7 +804,7 @@ mod fragment_tests {
None,
max_plaintext_size(),
)
.is_ok());
.is_ok());
assert!(Fragment::try_new(
&non_full_payload2,
id,
@@ -810,7 +814,7 @@ mod fragment_tests {
None,
max_plaintext_size(),
)
.is_ok());
.is_ok());
assert!(Fragment::try_new(
&full_payload,
@@ -821,7 +825,7 @@ mod fragment_tests {
Some(link_id),
max_plaintext_size(),
)
.is_ok());
.is_ok());
}
#[test]
@@ -842,7 +846,7 @@ mod fragment_tests {
None,
max_plaintext_size(),
)
.is_err());
.is_err());
assert!(Fragment::try_new(
&non_full_payload2,
id,
@@ -852,7 +856,7 @@ mod fragment_tests {
None,
max_plaintext_size(),
)
.is_err());
.is_err());
assert!(Fragment::try_new(
&too_much_payload,
id,
@@ -862,7 +866,7 @@ mod fragment_tests {
None,
max_plaintext_size(),
)
.is_err());
.is_err());
assert!(Fragment::try_new(
&too_much_payload,
id,
@@ -872,7 +876,7 @@ mod fragment_tests {
None,
max_plaintext_size(),
)
.is_err());
.is_err());
assert!(Fragment::try_new(
&non_full_payload,
@@ -883,7 +887,7 @@ mod fragment_tests {
Some(link_id),
max_plaintext_size(),
)
.is_err());
.is_err());
assert!(Fragment::try_new(
&non_full_payload2,
id,
@@ -893,7 +897,7 @@ mod fragment_tests {
Some(link_id),
max_plaintext_size(),
)
.is_err());
.is_err());
assert!(Fragment::try_new(
&too_much_payload,
@@ -904,7 +908,7 @@ mod fragment_tests {
Some(link_id),
max_plaintext_size(),
)
.is_err());
.is_err());
}
}
@@ -1008,7 +1012,7 @@ mod fragment_header {
None,
Some(0),
)
.is_err());
.is_err());
}
#[test]
@@ -1066,7 +1070,7 @@ mod fragment_header {
None,
Some(1234),
)
.is_ok());
.is_ok());
assert!(FragmentHeader::try_new(12345, 10, 2, Some(1234), None).is_err());
}
+4 -3
View File
@@ -1,11 +1,12 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::convert::TryFrom;
use std::fmt::{self, Debug, Display, Formatter};
use nymsphinx_addressing::nodes::{NymNodeRoutingAddress, NymNodeRoutingAddressError};
use nymsphinx_params::{PacketMode, PacketSize};
use nymsphinx_types::SphinxPacket;
use std::convert::TryFrom;
use std::fmt::{self, Debug, Display, Formatter};
#[derive(Debug)]
pub enum MixPacketFormattingError {
@@ -46,7 +47,7 @@ impl From<NymNodeRoutingAddressError> for MixPacketFormattingError {
pub struct MixPacket {
next_hop: NymNodeRoutingAddress,
sphinx_packet: SphinxPacket,
pub sphinx_packet: SphinxPacket,
packet_mode: PacketMode,
}
+8 -6
View File
@@ -1,6 +1,8 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use rand::{CryptoRng, RngCore};
use crypto::aes::cipher::{KeyIvInit, StreamCipher};
use crypto::asymmetric::encryption;
use crypto::shared_key::new_ephemeral_shared_key;
@@ -12,7 +14,6 @@ use nymsphinx_chunking::fragment::Fragment;
use nymsphinx_params::{
PacketEncryptionAlgorithm, PacketHkdfAlgorithm, ReplySurbEncryptionAlgorithm,
};
use rand::{CryptoRng, RngCore};
pub struct NymsphinxPayloadBuilder {
fragment: Fragment,
@@ -27,10 +28,10 @@ impl NymsphinxPayloadBuilder {
fn build<C>(
self,
packet_encryption_key: &CipherKey<C>,
variant_data: impl IntoIterator<Item = u8>,
variant_data: impl IntoIterator<Item=u8>,
) -> NymsphinxPayload
where
C: StreamCipher + KeyIvInit,
where
C: StreamCipher + KeyIvInit,
{
let (_, surb_ack_bytes) = self.surb_ack.prepare_for_sending();
@@ -68,8 +69,8 @@ impl NymsphinxPayloadBuilder {
rng: &mut R,
recipient_encryption_key: &encryption::PublicKey,
) -> NymsphinxPayload
where
R: RngCore + CryptoRng,
where
R: RngCore + CryptoRng,
{
// create keys for 'payload' encryption
let (ephemeral_keypair, shared_key) = new_ephemeral_shared_key::<
@@ -88,6 +89,7 @@ impl NymsphinxPayloadBuilder {
// the actual byte data that will be put into the sphinx packet paylaod.
// no more transformations are going to happen to it
// TODO: use that fact for some better compile time assertions
#[derive(Clone)]
pub struct NymsphinxPayload(Vec<u8>);
impl AsRef<[u8]> for NymsphinxPayload {