Feature/multi surbs invalidation (#1858)
* Cleaned up RealMessagesController constructor * introduced config field for maximum_reply_surb_age * Handling edge-case reply-surb failures * invalidating old reply surbs * Removing old reply keys from cache * Invalidating old reply keys * missing config changes
This commit is contained in:
committed by
GitHub
parent
11fe5cfbf8
commit
2c4611044c
@@ -19,8 +19,9 @@ CREATE TABLE sender_tag
|
||||
|
||||
CREATE TABLE reply_key
|
||||
(
|
||||
key_digest BLOB NOT NULL UNIQUE,
|
||||
reply_key BLOB NOT NULL UNIQUE
|
||||
key_digest BLOB NOT NULL UNIQUE,
|
||||
reply_key BLOB NOT NULL UNIQUE,
|
||||
sent_at_timestamp INTEGER NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE reply_surb_sender
|
||||
|
||||
@@ -16,9 +16,11 @@ use nymsphinx::anonymous_replies::requests::{AnonymousSenderTag, RepliableMessag
|
||||
use nymsphinx::anonymous_replies::{ReplySurb, SurbEncryptionKey};
|
||||
use nymsphinx::chunking::fragment::Fragment;
|
||||
use nymsphinx::message::NymMessage;
|
||||
use nymsphinx::params::{PacketSize, DEFAULT_NUM_MIX_HOPS};
|
||||
use nymsphinx::preparer::{MessagePreparer, PreparedFragment};
|
||||
use rand::{CryptoRng, Rng};
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use thiserror::Error;
|
||||
use topology::{NymTopology, NymTopologyError};
|
||||
|
||||
@@ -88,10 +90,63 @@ impl SurbWrappedPreparationError {
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct MessageHandler<R> {
|
||||
rng: R,
|
||||
pub(crate) struct Config {
|
||||
/// Key used to decrypt contents of received SURBAcks
|
||||
ack_key: Arc<AckKey>,
|
||||
self_address: Recipient,
|
||||
|
||||
/// Address of this client which also represent an address to which all acknowledgements
|
||||
/// and surb-based are going to be sent.
|
||||
sender_address: Recipient,
|
||||
|
||||
/// 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,
|
||||
|
||||
/// Predefined packet size used for the encapsulated messages.
|
||||
packet_size: PacketSize,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn new(
|
||||
ack_key: Arc<AckKey>,
|
||||
sender_address: Recipient,
|
||||
average_packet_delay: Duration,
|
||||
average_ack_delay: Duration,
|
||||
) -> Self {
|
||||
Config {
|
||||
ack_key,
|
||||
sender_address,
|
||||
average_packet_delay,
|
||||
average_ack_delay,
|
||||
num_mix_hops: DEFAULT_NUM_MIX_HOPS,
|
||||
packet_size: PacketSize::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// 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
|
||||
}
|
||||
|
||||
/// Allows setting non-default size of the sphinx packets sent out.
|
||||
pub fn with_custom_packet_size(mut self, packet_size: PacketSize) -> Self {
|
||||
self.packet_size = packet_size;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct MessageHandler<R> {
|
||||
config: Config,
|
||||
rng: R,
|
||||
message_preparer: MessagePreparer<R>,
|
||||
action_sender: AckActionSender,
|
||||
real_message_sender: BatchRealMessageSender,
|
||||
@@ -104,22 +159,30 @@ impl<R> MessageHandler<R>
|
||||
where
|
||||
R: CryptoRng + Rng,
|
||||
{
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) fn new(
|
||||
config: Config,
|
||||
rng: R,
|
||||
ack_key: Arc<AckKey>,
|
||||
self_address: Recipient,
|
||||
message_preparer: MessagePreparer<R>,
|
||||
action_sender: AckActionSender,
|
||||
real_message_sender: BatchRealMessageSender,
|
||||
topology_access: TopologyAccessor,
|
||||
reply_key_storage: SentReplyKeys,
|
||||
tag_storage: UsedSenderTags,
|
||||
) -> Self {
|
||||
MessageHandler {
|
||||
) -> Self
|
||||
where
|
||||
R: Copy,
|
||||
{
|
||||
let message_preparer = MessagePreparer::new(
|
||||
rng,
|
||||
config.sender_address,
|
||||
config.average_packet_delay,
|
||||
config.average_ack_delay,
|
||||
)
|
||||
.with_custom_real_message_packet_size(config.packet_size)
|
||||
.with_mix_hops(config.num_mix_hops);
|
||||
|
||||
MessageHandler {
|
||||
config,
|
||||
rng,
|
||||
ack_key,
|
||||
self_address,
|
||||
message_preparer,
|
||||
action_sender,
|
||||
real_message_sender,
|
||||
@@ -145,7 +208,7 @@ where
|
||||
&self,
|
||||
permit: &'a TopologyReadPermit<'a>,
|
||||
) -> Result<&'a NymTopology, PreparationError> {
|
||||
match permit.try_get_valid_topology_ref(&self.self_address, None) {
|
||||
match permit.try_get_valid_topology_ref(&self.config.sender_address, None) {
|
||||
Ok(topology_ref) => Ok(topology_ref),
|
||||
Err(err) => {
|
||||
warn!("Could not process the packet - the network topology is invalid - {err}");
|
||||
@@ -224,7 +287,8 @@ where
|
||||
) -> Result<(), SurbWrappedPreparationError> {
|
||||
debug!("requesting {amount} reply SURBs from {from:?}");
|
||||
|
||||
let surbs_request = ReplyMessage::new_surb_request_message(self.self_address, amount);
|
||||
let surbs_request =
|
||||
ReplyMessage::new_surb_request_message(self.config.sender_address, amount);
|
||||
self.try_send_single_surb_message(from, surbs_request, reply_surb, true)
|
||||
.await
|
||||
}
|
||||
@@ -267,7 +331,12 @@ where
|
||||
let chunk_clone = fragment.clone();
|
||||
let prepared_fragment = self
|
||||
.message_preparer
|
||||
.prepare_reply_chunk_for_sending(chunk_clone, topology, &self.ack_key, reply_surb)
|
||||
.prepare_reply_chunk_for_sending(
|
||||
chunk_clone,
|
||||
topology,
|
||||
&self.config.ack_key,
|
||||
reply_surb,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let real_message =
|
||||
@@ -319,7 +388,7 @@ where
|
||||
let prepared_fragment = self.message_preparer.prepare_chunk_for_sending(
|
||||
chunk_clone,
|
||||
topology,
|
||||
&self.ack_key,
|
||||
&self.config.ack_key,
|
||||
&recipient,
|
||||
)?;
|
||||
|
||||
@@ -399,7 +468,7 @@ where
|
||||
|
||||
let prepared_fragment = self
|
||||
.message_preparer
|
||||
.prepare_chunk_for_sending(chunk, topology, &self.ack_key, &recipient)
|
||||
.prepare_chunk_for_sending(chunk, topology, &self.config.ack_key, &recipient)
|
||||
.unwrap();
|
||||
|
||||
Ok(prepared_fragment)
|
||||
@@ -418,7 +487,7 @@ where
|
||||
|
||||
let prepared_fragment = self
|
||||
.message_preparer
|
||||
.prepare_reply_chunk_for_sending(chunk, topology, &self.ack_key, reply_surb)
|
||||
.prepare_reply_chunk_for_sending(chunk, topology, &self.config.ack_key, reply_surb)
|
||||
.unwrap();
|
||||
|
||||
Ok(prepared_fragment)
|
||||
|
||||
@@ -28,11 +28,11 @@ use log::*;
|
||||
use nymsphinx::acknowledgements::AckKey;
|
||||
use nymsphinx::addressing::clients::Recipient;
|
||||
use nymsphinx::params::PacketSize;
|
||||
use nymsphinx::preparer::MessagePreparer;
|
||||
use rand::{rngs::OsRng, CryptoRng, Rng};
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::client::replies::reply_controller;
|
||||
use crate::config;
|
||||
pub(crate) use acknowledgement_control::{AckActionSender, Action};
|
||||
|
||||
@@ -85,6 +85,64 @@ pub struct Config {
|
||||
/// Defines maximum amount of time the client is going to wait for reply surbs before explicitly asking
|
||||
/// for more even though in theory they wouldn't need to.
|
||||
maximum_reply_surb_waiting_period: Duration,
|
||||
|
||||
/// Defines maximum amount of time given reply surb is going to be valid for.
|
||||
/// This is going to be superseded by key rotation once implemented.
|
||||
maximum_reply_surb_age: Duration,
|
||||
|
||||
/// Defines maximum amount of time given reply key is going to be valid for.
|
||||
/// This is going to be superseded by key rotation once implemented.
|
||||
maximum_reply_key_age: Duration,
|
||||
}
|
||||
|
||||
impl<'a> From<&'a Config> for acknowledgement_control::Config {
|
||||
fn from(cfg: &'a Config) -> Self {
|
||||
acknowledgement_control::Config::new(
|
||||
cfg.ack_wait_addition,
|
||||
cfg.ack_wait_multiplier,
|
||||
cfg.retransmission_reply_surb_request_size,
|
||||
)
|
||||
.with_custom_packet_size(cfg.packet_size)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a Config> for real_traffic_stream::Config {
|
||||
fn from(cfg: &'a Config) -> Self {
|
||||
real_traffic_stream::Config::new(
|
||||
Arc::clone(&cfg.ack_key),
|
||||
cfg.self_recipient,
|
||||
cfg.average_ack_delay_duration,
|
||||
cfg.average_packet_delay_duration,
|
||||
cfg.average_message_sending_delay,
|
||||
cfg.disable_main_poisson_packet_distribution,
|
||||
)
|
||||
.with_custom_cover_packet_size(cfg.packet_size)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a Config> for reply_controller::Config {
|
||||
fn from(cfg: &'a Config) -> Self {
|
||||
reply_controller::Config::new(
|
||||
cfg.minimum_reply_surb_request_size,
|
||||
cfg.maximum_reply_surb_request_size,
|
||||
cfg.maximum_allowed_reply_surb_request_size,
|
||||
cfg.maximum_reply_surb_waiting_period,
|
||||
cfg.maximum_reply_surb_age,
|
||||
cfg.maximum_reply_key_age,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a Config> for message_handler::Config {
|
||||
fn from(cfg: &'a Config) -> Self {
|
||||
message_handler::Config::new(
|
||||
Arc::clone(&cfg.ack_key),
|
||||
cfg.self_recipient,
|
||||
cfg.average_packet_delay_duration,
|
||||
cfg.average_ack_delay_duration,
|
||||
)
|
||||
.with_custom_packet_size(cfg.packet_size)
|
||||
}
|
||||
}
|
||||
|
||||
impl Config {
|
||||
@@ -114,6 +172,8 @@ impl Config {
|
||||
.retransmission_reply_surb_request_size,
|
||||
maximum_reply_surb_waiting_period: base_client_debug_config
|
||||
.maximum_reply_surb_waiting_period,
|
||||
maximum_reply_surb_age: base_client_debug_config.maximum_reply_surb_age,
|
||||
maximum_reply_key_age: base_client_debug_config.maximum_reply_key_age,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,10 +210,10 @@ impl RealMessagesController<OsRng> {
|
||||
) -> Self {
|
||||
let rng = OsRng;
|
||||
|
||||
// create channels for inter-task communication
|
||||
let (real_message_sender, real_message_receiver) = tokio::sync::mpsc::channel(1);
|
||||
let (sent_notifier_tx, sent_notifier_rx) = mpsc::unbounded();
|
||||
let (ack_action_tx, ack_action_rx) = mpsc::unbounded();
|
||||
|
||||
let ack_controller_connectors = AcknowledgementControllerConnectors::new(
|
||||
input_receiver,
|
||||
sent_notifier_rx,
|
||||
@@ -162,26 +222,16 @@ impl RealMessagesController<OsRng> {
|
||||
ack_action_rx,
|
||||
);
|
||||
|
||||
let ack_control_config = acknowledgement_control::Config::new(
|
||||
config.ack_wait_addition,
|
||||
config.ack_wait_multiplier,
|
||||
config.retransmission_reply_surb_request_size,
|
||||
)
|
||||
.with_custom_packet_size(config.packet_size);
|
||||
// create all configs for the components
|
||||
let ack_control_config = (&config).into();
|
||||
let out_queue_config = (&config).into();
|
||||
let reply_controller_config = (&config).into();
|
||||
let message_handler_config = (&config).into();
|
||||
|
||||
// TODO: construct MessagePreparer itself inside the MessageHandler
|
||||
let message_preparer = MessagePreparer::new(
|
||||
rng,
|
||||
config.self_recipient,
|
||||
config.average_packet_delay_duration,
|
||||
config.average_ack_delay_duration,
|
||||
)
|
||||
.with_custom_real_message_packet_size(config.packet_size);
|
||||
// create the actual components
|
||||
let message_handler = MessageHandler::new(
|
||||
message_handler_config,
|
||||
rng,
|
||||
Arc::clone(&config.ack_key),
|
||||
config.self_recipient,
|
||||
message_preparer,
|
||||
ack_action_tx,
|
||||
real_message_sender,
|
||||
topology_access.clone(),
|
||||
@@ -189,42 +239,28 @@ impl RealMessagesController<OsRng> {
|
||||
reply_storage.tags_storage(),
|
||||
);
|
||||
|
||||
let reply_control = ReplyController::new(
|
||||
message_handler.clone(),
|
||||
reply_storage.surbs_storage(),
|
||||
reply_storage.tags_storage(),
|
||||
reply_controller_receiver,
|
||||
config.minimum_reply_surb_request_size,
|
||||
config.maximum_reply_surb_request_size,
|
||||
config.maximum_allowed_reply_surb_request_size,
|
||||
config.maximum_reply_surb_waiting_period,
|
||||
);
|
||||
|
||||
let ack_control = AcknowledgementController::new(
|
||||
ack_control_config,
|
||||
Arc::clone(&config.ack_key),
|
||||
ack_controller_connectors,
|
||||
message_handler,
|
||||
message_handler.clone(),
|
||||
reply_controller_sender,
|
||||
reply_storage.surbs_storage(),
|
||||
);
|
||||
|
||||
let out_queue_config = real_traffic_stream::Config::new(
|
||||
config.average_ack_delay_duration,
|
||||
config.average_packet_delay_duration,
|
||||
config.average_message_sending_delay,
|
||||
config.disable_main_poisson_packet_distribution,
|
||||
)
|
||||
.with_custom_cover_packet_size(config.packet_size);
|
||||
let reply_control = ReplyController::new(
|
||||
reply_controller_config,
|
||||
message_handler,
|
||||
reply_storage,
|
||||
reply_controller_receiver,
|
||||
);
|
||||
|
||||
let out_queue_control = OutQueueControl::new(
|
||||
out_queue_config,
|
||||
config.ack_key,
|
||||
rng,
|
||||
sent_notifier_tx,
|
||||
mix_sender,
|
||||
real_message_receiver,
|
||||
rng,
|
||||
config.self_recipient,
|
||||
topology_access,
|
||||
lane_queue_lengths,
|
||||
client_connection_rx,
|
||||
|
||||
@@ -48,6 +48,12 @@ fn get_time_now() -> wasm_timer::Instant {
|
||||
|
||||
/// Configurable parameters of the `OutQueueControl`
|
||||
pub(crate) struct Config {
|
||||
/// Key used to encrypt and decrypt content of an ACK packet.
|
||||
ack_key: Arc<AckKey>,
|
||||
|
||||
/// Represents full address of this client.
|
||||
our_full_destination: Recipient,
|
||||
|
||||
/// Average delay an acknowledgement packet is going to get delay at a single mixnode.
|
||||
average_ack_delay: Duration,
|
||||
|
||||
@@ -67,12 +73,16 @@ pub(crate) struct Config {
|
||||
|
||||
impl Config {
|
||||
pub(crate) fn new(
|
||||
ack_key: Arc<AckKey>,
|
||||
our_full_destination: Recipient,
|
||||
average_ack_delay: Duration,
|
||||
average_packet_delay: Duration,
|
||||
average_message_sending_delay: Duration,
|
||||
disable_poisson_packet_distribution: bool,
|
||||
) -> Self {
|
||||
Config {
|
||||
ack_key,
|
||||
our_full_destination,
|
||||
average_ack_delay,
|
||||
average_packet_delay,
|
||||
average_message_sending_delay,
|
||||
@@ -94,9 +104,6 @@ where
|
||||
/// Configurable parameters of the `ActionController`
|
||||
config: Config,
|
||||
|
||||
/// Key used to encrypt and decrypt content of an ACK packet.
|
||||
ack_key: Arc<AckKey>,
|
||||
|
||||
/// Channel used for notifying of a real packet being sent out. Used to start up retransmission timer.
|
||||
sent_notifier: SentPacketNotificationSender,
|
||||
|
||||
@@ -120,9 +127,6 @@ where
|
||||
/// before being sent out into the network.
|
||||
real_receiver: BatchRealMessageReceiver,
|
||||
|
||||
/// Represents full address of this client.
|
||||
our_full_destination: Recipient,
|
||||
|
||||
/// Instance of a cryptographically secure random number generator.
|
||||
rng: R,
|
||||
|
||||
@@ -186,25 +190,21 @@ where
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) fn new(
|
||||
config: Config,
|
||||
ack_key: Arc<AckKey>,
|
||||
rng: R,
|
||||
sent_notifier: SentPacketNotificationSender,
|
||||
mix_tx: BatchMixMessageSender,
|
||||
real_receiver: BatchRealMessageReceiver,
|
||||
rng: R,
|
||||
our_full_destination: Recipient,
|
||||
topology_access: TopologyAccessor,
|
||||
lane_queue_lengths: LaneQueueLengths,
|
||||
client_connection_rx: ConnectionCommandReceiver,
|
||||
) -> Self {
|
||||
OutQueueControl {
|
||||
config,
|
||||
ack_key,
|
||||
sent_notifier,
|
||||
next_delay: None,
|
||||
sending_delay_controller: Default::default(),
|
||||
mix_tx,
|
||||
real_receiver,
|
||||
our_full_destination,
|
||||
rng,
|
||||
topology_access,
|
||||
transmission_buffer: Default::default(),
|
||||
@@ -232,8 +232,8 @@ where
|
||||
let topology_permit = self.topology_access.get_read_permit().await;
|
||||
// the ack is sent back to ourselves (and then ignored)
|
||||
let topology_ref = match topology_permit.try_get_valid_topology_ref(
|
||||
&self.our_full_destination,
|
||||
Some(&self.our_full_destination),
|
||||
&self.config.our_full_destination,
|
||||
Some(&self.config.our_full_destination),
|
||||
) {
|
||||
Ok(topology) => topology,
|
||||
Err(err) => {
|
||||
@@ -246,8 +246,8 @@ where
|
||||
generate_loop_cover_packet(
|
||||
&mut self.rng,
|
||||
topology_ref,
|
||||
&self.ack_key,
|
||||
&self.our_full_destination,
|
||||
&self.config.ack_key,
|
||||
&self.config.our_full_destination,
|
||||
self.config.average_ack_delay,
|
||||
self.config.average_packet_delay,
|
||||
self.config.cover_packet_size,
|
||||
|
||||
@@ -328,7 +328,7 @@ impl ReceivedMessagesBuffer {
|
||||
.try_pop(possible_key_digest)
|
||||
.map(|reply_encryption_key| {
|
||||
(
|
||||
reply_encryption_key,
|
||||
*reply_encryption_key,
|
||||
&mut raw_message[reply_surb_digest_size..],
|
||||
)
|
||||
})
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::client::real_messages_control::message_handler::{MessageHandler, PreparationError};
|
||||
use crate::client::replies::reply_storage::{ReceivedReplySurbsMap, UsedSenderTags};
|
||||
use crate::client::replies::reply_storage::CombinedReplyStorage;
|
||||
use client_connections::TransmissionLane;
|
||||
use futures::channel::mpsc;
|
||||
use futures::StreamExt;
|
||||
use log::{debug, trace, warn};
|
||||
use log::{debug, error, info, trace, warn};
|
||||
use nymsphinx::addressing::clients::Recipient;
|
||||
use nymsphinx::anonymous_replies::requests::AnonymousSenderTag;
|
||||
use nymsphinx::anonymous_replies::ReplySurb;
|
||||
@@ -15,12 +15,13 @@ use rand::{CryptoRng, Rng};
|
||||
use std::cmp::{max, min};
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
use std::time::Duration;
|
||||
use time::OffsetDateTime;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use tokio::time::Instant;
|
||||
type IntervalStream = tokio_stream::wrappers::IntervalStream;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use wasm_timer::Instant;
|
||||
type IntervalStream = gloo_timers::future::IntervalStream;
|
||||
|
||||
pub fn new_control_channels() -> (ReplyControllerSender, ReplyControllerReceiver) {
|
||||
let (tx, rx) = mpsc::unbounded();
|
||||
@@ -101,6 +102,35 @@ pub enum ReplyControllerMessage {
|
||||
},
|
||||
}
|
||||
|
||||
pub struct Config {
|
||||
min_surb_request_size: u32,
|
||||
max_surb_request_size: u32,
|
||||
maximum_allowed_reply_surb_request_size: u32,
|
||||
max_surb_waiting_period: Duration,
|
||||
max_reply_surb_age: Duration,
|
||||
max_reply_key_age: Duration,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub(crate) fn new(
|
||||
min_surb_request_size: u32,
|
||||
max_surb_request_size: u32,
|
||||
maximum_allowed_reply_surb_request_size: u32,
|
||||
max_surb_waiting_period: Duration,
|
||||
max_reply_surb_age: Duration,
|
||||
max_reply_key_age: Duration,
|
||||
) -> Self {
|
||||
Self {
|
||||
min_surb_request_size,
|
||||
max_surb_request_size,
|
||||
maximum_allowed_reply_surb_request_size,
|
||||
max_surb_waiting_period,
|
||||
max_reply_surb_age,
|
||||
max_reply_key_age,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// the purpose of this task:
|
||||
// - buffers split messages from input message listener if there were insufficient surbs to send them
|
||||
// - upon getting extra surbs, resends them
|
||||
@@ -111,6 +141,8 @@ pub enum ReplyControllerMessage {
|
||||
// TODO: this should be split into ingress and egress controllers
|
||||
// because currently its trying to perform two distinct jobs
|
||||
pub struct ReplyController<R> {
|
||||
config: Config,
|
||||
|
||||
// TODO: incorporate that field at some point
|
||||
// and use binomial distribution to determine the expected required number
|
||||
// of surbs required to send the message through
|
||||
@@ -118,44 +150,33 @@ pub struct ReplyController<R> {
|
||||
request_receiver: ReplyControllerReceiver,
|
||||
pending_replies: HashMap<AnonymousSenderTag, VecDeque<Fragment>>,
|
||||
message_handler: MessageHandler<R>,
|
||||
received_reply_surbs: ReceivedReplySurbsMap,
|
||||
tag_storage: UsedSenderTags,
|
||||
|
||||
min_surb_request_size: u32,
|
||||
max_surb_request_size: u32,
|
||||
maximum_allowed_reply_surb_request_size: u32,
|
||||
max_surb_waiting_period: Duration,
|
||||
full_reply_storage: CombinedReplyStorage,
|
||||
}
|
||||
|
||||
impl<R> ReplyController<R>
|
||||
where
|
||||
R: CryptoRng + Rng,
|
||||
{
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) fn new(
|
||||
config: Config,
|
||||
message_handler: MessageHandler<R>,
|
||||
received_reply_surbs: ReceivedReplySurbsMap,
|
||||
tag_storage: UsedSenderTags,
|
||||
full_reply_storage: CombinedReplyStorage,
|
||||
request_receiver: ReplyControllerReceiver,
|
||||
min_surb_request_size: u32,
|
||||
max_surb_request_size: u32,
|
||||
maximum_allowed_reply_surb_request_size: u32,
|
||||
max_surb_waiting_period: Duration,
|
||||
) -> Self {
|
||||
ReplyController {
|
||||
config,
|
||||
request_receiver,
|
||||
pending_replies: Default::default(),
|
||||
pending_replies: HashMap::new(),
|
||||
message_handler,
|
||||
received_reply_surbs,
|
||||
tag_storage,
|
||||
min_surb_request_size,
|
||||
max_surb_request_size,
|
||||
maximum_allowed_reply_surb_request_size,
|
||||
max_surb_waiting_period,
|
||||
full_reply_storage,
|
||||
}
|
||||
}
|
||||
|
||||
fn insert_pending_replies(&mut self, recipient: &AnonymousSenderTag, fragments: Vec<Fragment>) {
|
||||
fn insert_pending_replies<V: Into<VecDeque<Fragment>>>(
|
||||
&mut self,
|
||||
recipient: &AnonymousSenderTag,
|
||||
fragments: V,
|
||||
) {
|
||||
if let Some(existing) = self.pending_replies.get_mut(recipient) {
|
||||
existing.append(&mut fragments.into())
|
||||
} else {
|
||||
@@ -173,10 +194,22 @@ where
|
||||
None => return false,
|
||||
};
|
||||
|
||||
let available_surbs = self.received_reply_surbs.available_surbs(target);
|
||||
let pending_surbs = self.received_reply_surbs.pending_reception(target) as usize;
|
||||
let min_surbs_threshold = self.received_reply_surbs.min_surb_threshold();
|
||||
let max_surbs_threshold = self.received_reply_surbs.max_surb_threshold();
|
||||
let available_surbs = self
|
||||
.full_reply_storage
|
||||
.surbs_storage_ref()
|
||||
.available_surbs(target);
|
||||
let pending_surbs = self
|
||||
.full_reply_storage
|
||||
.surbs_storage_ref()
|
||||
.pending_reception(target) as usize;
|
||||
let min_surbs_threshold = self
|
||||
.full_reply_storage
|
||||
.surbs_storage_ref()
|
||||
.min_surb_threshold();
|
||||
let max_surbs_threshold = self
|
||||
.full_reply_storage
|
||||
.surbs_storage_ref()
|
||||
.max_surb_threshold();
|
||||
|
||||
debug!("queue size: {queue_size}, available surbs: {available_surbs} pending surbs: {pending_surbs} threshold range: {min_surbs_threshold}..{max_surbs_threshold}");
|
||||
|
||||
@@ -190,7 +223,11 @@ where
|
||||
data: Vec<u8>,
|
||||
lane: TransmissionLane,
|
||||
) {
|
||||
if !self.received_reply_surbs.contains_surbs_for(&recipient_tag) {
|
||||
if !self
|
||||
.full_reply_storage
|
||||
.surbs_storage_ref()
|
||||
.contains_surbs_for(&recipient_tag)
|
||||
{
|
||||
warn!("received reply request for {:?} but we don't have any surbs stored for that recipient!", recipient_tag);
|
||||
return;
|
||||
}
|
||||
@@ -206,7 +243,8 @@ where
|
||||
// (but at some point we run out of surbs for surb requests)
|
||||
|
||||
let (surbs, _surbs_left) = self
|
||||
.received_reply_surbs
|
||||
.full_reply_storage
|
||||
.surbs_storage_ref()
|
||||
.get_reply_surbs(&recipient_tag, required_surbs);
|
||||
|
||||
if let Some(reply_surbs) = surbs {
|
||||
@@ -215,8 +253,10 @@ where
|
||||
.try_send_reply_chunks(recipient_tag, fragments, reply_surbs, lane)
|
||||
.await
|
||||
{
|
||||
// TODO: perhaps there should be some timer here to repeat the request once topology recovers
|
||||
let err = err.return_unused_surbs(&self.received_reply_surbs, &recipient_tag);
|
||||
let err = err.return_unused_surbs(
|
||||
self.full_reply_storage.surbs_storage_ref(),
|
||||
&recipient_tag,
|
||||
);
|
||||
warn!("failed to send reply to {:?} - {err}", recipient_tag);
|
||||
}
|
||||
} else {
|
||||
@@ -236,7 +276,8 @@ where
|
||||
amount: u32,
|
||||
) -> Result<(), PreparationError> {
|
||||
let reply_surb = self
|
||||
.received_reply_surbs
|
||||
.full_reply_storage
|
||||
.surbs_storage_ref()
|
||||
.get_reply_surb_ignoring_threshold(&target)
|
||||
.and_then(|(reply_surb, _)| reply_surb)
|
||||
.ok_or(PreparationError::NotEnoughSurbs {
|
||||
@@ -249,15 +290,15 @@ where
|
||||
.try_request_additional_reply_surbs(target, reply_surb, amount)
|
||||
.await
|
||||
{
|
||||
let err = err.return_unused_surbs(&self.received_reply_surbs, &target);
|
||||
let err = err.return_unused_surbs(self.full_reply_storage.surbs_storage_ref(), &target);
|
||||
warn!(
|
||||
"failed to request additional surbs from {:?} - {err}",
|
||||
target
|
||||
);
|
||||
// TODO: perhaps there should be some timer here to repeat the request once topology recovers
|
||||
return Err(err);
|
||||
} else {
|
||||
self.received_reply_surbs
|
||||
self.full_reply_storage
|
||||
.surbs_storage_ref()
|
||||
.increment_pending_reception(&target, amount);
|
||||
}
|
||||
|
||||
@@ -289,8 +330,14 @@ where
|
||||
|
||||
async fn try_clear_pending_queue(&mut self, target: AnonymousSenderTag) {
|
||||
trace!("trying to clear pending queue");
|
||||
let available_surbs = self.received_reply_surbs.available_surbs(&target);
|
||||
let min_surbs_threshold = self.received_reply_surbs.min_surb_threshold();
|
||||
let available_surbs = self
|
||||
.full_reply_storage
|
||||
.surbs_storage_ref()
|
||||
.available_surbs(&target);
|
||||
let min_surbs_threshold = self
|
||||
.full_reply_storage
|
||||
.surbs_storage_ref()
|
||||
.min_surb_threshold();
|
||||
|
||||
let max_to_clear = if available_surbs > min_surbs_threshold {
|
||||
available_surbs - min_surbs_threshold
|
||||
@@ -302,8 +349,7 @@ where
|
||||
|
||||
// we're guaranteed to not get more entries than we have reply surbs for
|
||||
if let Some(to_send) = self.pop_at_most_pending_replies(&target, max_to_clear) {
|
||||
// TODO: optimise: we're cloning the fragments every time to re-insert them into the buffer in case of failure
|
||||
let to_send_vec = to_send.into_iter().collect::<Vec<_>>();
|
||||
let to_send_vec = to_send.iter().cloned().collect::<Vec<_>>();
|
||||
|
||||
if to_send_vec.is_empty() {
|
||||
panic!(
|
||||
@@ -312,10 +358,16 @@ where
|
||||
}
|
||||
|
||||
let (surbs_for_reply, _) = self
|
||||
.received_reply_surbs
|
||||
.full_reply_storage
|
||||
.surbs_storage_ref()
|
||||
.get_reply_surbs(&target, to_send_vec.len());
|
||||
let surbs_for_reply =
|
||||
surbs_for_reply.expect("is is possible for this to ever show up?");
|
||||
|
||||
let Some(surbs_for_reply) = surbs_for_reply else {
|
||||
// probably retransmission
|
||||
debug!("somehow different task has stolen our reply surbs!");
|
||||
self.insert_pending_replies(&target, to_send);
|
||||
return
|
||||
};
|
||||
|
||||
if let Err(err) = self
|
||||
.message_handler
|
||||
@@ -327,9 +379,10 @@ where
|
||||
)
|
||||
.await
|
||||
{
|
||||
let err = err.return_unused_surbs(&self.received_reply_surbs, &target);
|
||||
let err =
|
||||
err.return_unused_surbs(self.full_reply_storage.surbs_storage_ref(), &target);
|
||||
self.insert_pending_replies(&target, to_send);
|
||||
warn!("failed to clear pending queue for {:?} - {err}", target);
|
||||
// TODO: perhaps there should be some timer here to repeat the request once topology recovers
|
||||
}
|
||||
} else {
|
||||
trace!("the pending queue is empty");
|
||||
@@ -345,15 +398,19 @@ where
|
||||
trace!("handling received surbs");
|
||||
|
||||
// clear the requesting flag since we should have been asking for surbs
|
||||
self.received_reply_surbs
|
||||
self.full_reply_storage
|
||||
.surbs_storage_ref()
|
||||
.reset_surbs_last_received_at(&from);
|
||||
if from_surb_request {
|
||||
self.received_reply_surbs
|
||||
self.full_reply_storage
|
||||
.surbs_storage_ref()
|
||||
.decrement_pending_reception(&from, reply_surbs.len() as u32);
|
||||
}
|
||||
|
||||
// store received surbs
|
||||
self.received_reply_surbs.insert_surbs(&from, reply_surbs);
|
||||
self.full_reply_storage
|
||||
.surbs_storage_ref()
|
||||
.insert_surbs(&from, reply_surbs);
|
||||
|
||||
// use as many as we can for clearing pending queue
|
||||
self.try_clear_pending_queue(from).await;
|
||||
@@ -367,15 +424,19 @@ where
|
||||
async fn handle_surb_request(&mut self, recipient: Recipient, mut amount: u32) {
|
||||
// 1. check whether we sent any surbs in the past to this recipient, otherwise
|
||||
// they have no business in asking for more
|
||||
if !self.tag_storage.exists(&recipient) {
|
||||
if !self
|
||||
.full_reply_storage
|
||||
.tags_storage_ref()
|
||||
.exists(&recipient)
|
||||
{
|
||||
warn!("{recipient} asked us for reply SURBs even though we never sent them any anonymous messages before!");
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. check whether the requested amount is within sane range
|
||||
if amount > self.maximum_allowed_reply_surb_request_size {
|
||||
warn!("The requested reply surb amount is larger than our maximum allowed ({amount} > {}). Lowering it to a more sane value...", self.maximum_allowed_reply_surb_request_size);
|
||||
amount = self.maximum_allowed_reply_surb_request_size;
|
||||
if amount > self.config.maximum_allowed_reply_surb_request_size {
|
||||
warn!("The requested reply surb amount is larger than our maximum allowed ({amount} > {}). Lowering it to a more sane value...", self.config.maximum_allowed_reply_surb_request_size);
|
||||
amount = self.config.maximum_allowed_reply_surb_request_size;
|
||||
}
|
||||
|
||||
// 3. construct and send the surbs away
|
||||
@@ -435,8 +496,8 @@ where
|
||||
}
|
||||
|
||||
let request_size = min(
|
||||
self.max_surb_request_size,
|
||||
max(queue_size, self.min_surb_request_size),
|
||||
self.config.max_surb_request_size,
|
||||
max(queue_size, self.config.min_surb_request_size),
|
||||
);
|
||||
|
||||
if let Err(err) = self
|
||||
@@ -449,23 +510,33 @@ where
|
||||
|
||||
async fn inspect_stale_entries(&mut self) {
|
||||
let mut to_request = Vec::new();
|
||||
let mut to_remove = Vec::new();
|
||||
|
||||
let now = Instant::now();
|
||||
let now = OffsetDateTime::now_utc();
|
||||
for (pending_reply_target, vals) in &self.pending_replies {
|
||||
if vals.is_empty() {
|
||||
// TODO: remove it from the map before getting here
|
||||
continue;
|
||||
}
|
||||
|
||||
let last_received = self
|
||||
.received_reply_surbs
|
||||
.surbs_last_received_at(pending_reply_target)
|
||||
.expect("I think this shouldnt fail? to be verified.");
|
||||
let Some(last_received) = self
|
||||
.full_reply_storage.surbs_storage_ref()
|
||||
.surbs_last_received_at(pending_reply_target) else {
|
||||
error!("we have {} pending replies for {pending_reply_target}, but we somehow never received any reply surbs from them!", vals.len());
|
||||
to_remove.push(*pending_reply_target);
|
||||
continue
|
||||
};
|
||||
|
||||
let diff = now - last_received;
|
||||
// this should never ever happen (famous last words, eh?), but in case it DOES happen eventually
|
||||
// purge that malformed data
|
||||
let Ok(last_received_time) = OffsetDateTime::from_unix_timestamp(last_received) else {
|
||||
error!("somehow our stored timestamp ({last_received}) for surbs from {pending_reply_target} is corrupted!. Going to remove all the associated entries");
|
||||
to_remove.push(*pending_reply_target);
|
||||
continue
|
||||
};
|
||||
let diff = now - last_received_time;
|
||||
|
||||
if diff > self.max_surb_waiting_period {
|
||||
warn!("We haven't received any surbs in {:?} from {:?}. Going to explicitly ask for more", diff, pending_reply_target);
|
||||
if diff > self.config.max_surb_waiting_period {
|
||||
warn!("We haven't received any surbs in {:?} from {pending_reply_target}. Going to explicitly ask for more", diff);
|
||||
to_request.push(*pending_reply_target);
|
||||
}
|
||||
}
|
||||
@@ -473,23 +544,96 @@ where
|
||||
for pending_reply_target in to_request {
|
||||
self.request_reply_surbs_for_queue_clearing(pending_reply_target)
|
||||
.await;
|
||||
self.received_reply_surbs
|
||||
self.full_reply_storage
|
||||
.surbs_storage_ref()
|
||||
.reset_pending_reception(&pending_reply_target)
|
||||
}
|
||||
|
||||
for to_remove in to_remove {
|
||||
self.pending_replies.remove(&to_remove);
|
||||
}
|
||||
}
|
||||
|
||||
async fn invalidate_old_data(&self) {
|
||||
let now = OffsetDateTime::now_utc();
|
||||
|
||||
let mut to_remove_surbs = Vec::new();
|
||||
let mut to_remove_keys = Vec::new();
|
||||
for map_ref in self.full_reply_storage.surbs_storage_ref().as_raw_iter() {
|
||||
let (sender, received) = map_ref.pair();
|
||||
// TODO: handle the following edge case:
|
||||
// there's a malicious client sending us exactly one reply surb just before we should have invalidated
|
||||
// the data thus making us keep everything in memory
|
||||
// possible solution: keep timestamp PER reply surb (but that seems like an overkill)
|
||||
// but I doubt this is ever going to be a problem...
|
||||
// ...
|
||||
// However, if you're reading this message, it probably became a legit problem,
|
||||
// so I guess add timestamp per surb then? chop-chop.
|
||||
|
||||
let last_received = received.surbs_last_received_at();
|
||||
// this should never ever happen (famous last words, eh?), but in case it DOES happen eventually
|
||||
// purge that malformed data
|
||||
let Ok(last_received_time) = OffsetDateTime::from_unix_timestamp(last_received) else {
|
||||
error!("somehow our stored timestamp ({last_received}) for surbs from {sender} is corrupted!. Going to remove all the associated entries");
|
||||
to_remove_surbs.push(*sender);
|
||||
continue
|
||||
};
|
||||
let diff = now - last_received_time;
|
||||
|
||||
if diff > self.config.max_reply_surb_age {
|
||||
info!("it's been {diff:?} since we last received any reply surb from {sender}. Going to remove all stored entries...");
|
||||
|
||||
to_remove_surbs.push(*sender);
|
||||
}
|
||||
}
|
||||
|
||||
for map_ref in self.full_reply_storage.key_storage_ref().as_raw_iter() {
|
||||
let (digest, reply_key) = map_ref.pair();
|
||||
|
||||
// this should never ever happen (famous last words, eh?), but in case it DOES happen eventually
|
||||
// purge that malformed data
|
||||
let Ok(sent_at) = OffsetDateTime::from_unix_timestamp(reply_key.sent_at_timestamp) else {
|
||||
error!("somehow our stored timestamp ({}) for one of our reply key is corrupted!. Going to remove all the entry", reply_key.sent_at_timestamp);
|
||||
to_remove_keys.push(*digest);
|
||||
continue
|
||||
};
|
||||
|
||||
let diff = now - sent_at;
|
||||
|
||||
if diff > self.config.max_reply_key_age {
|
||||
debug!("it's been {diff:?} since we created this reply key. it's probably never going to get used, so we're going to purge it...");
|
||||
to_remove_keys.push(*digest);
|
||||
}
|
||||
}
|
||||
|
||||
for to_remove in to_remove_surbs {
|
||||
self.full_reply_storage
|
||||
.surbs_storage_ref()
|
||||
.remove(&to_remove);
|
||||
}
|
||||
|
||||
for to_remove in to_remove_keys {
|
||||
self.full_reply_storage.key_storage().remove(to_remove)
|
||||
}
|
||||
}
|
||||
|
||||
fn create_interval_stream(polling_rate: Duration) -> IntervalStream {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
return tokio_stream::wrappers::IntervalStream::new(tokio::time::interval(polling_rate));
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
return gloo_timers::future::IntervalStream::new(polling_rate.as_millis() as u32);
|
||||
}
|
||||
|
||||
pub(crate) async fn run_with_shutdown(&mut self, mut shutdown: task::ShutdownListener) {
|
||||
debug!("Started ReplyController with graceful shutdown support");
|
||||
|
||||
let polling_rate = Duration::from_secs(5);
|
||||
let mut stale_inspection = Self::create_interval_stream(polling_rate);
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let mut interval =
|
||||
tokio_stream::wrappers::IntervalStream::new(tokio::time::interval(polling_rate));
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
let mut interval =
|
||||
gloo_timers::future::IntervalStream::new(polling_rate.as_millis() as u32);
|
||||
// this is in the order of hours/days so we don't have to poll it that often
|
||||
let polling_rate = Duration::from_secs(self.config.max_reply_surb_age.as_secs() / 10);
|
||||
let mut invalidation_inspection = Self::create_interval_stream(polling_rate);
|
||||
|
||||
while !shutdown.is_shutdown() {
|
||||
tokio::select! {
|
||||
@@ -504,9 +648,12 @@ where
|
||||
break;
|
||||
}
|
||||
},
|
||||
_ = interval.next() => {
|
||||
_ = stale_inspection.next() => {
|
||||
self.inspect_stale_entries().await
|
||||
},
|
||||
_ = invalidation_inspection.next() => {
|
||||
self.invalidate_old_data().await
|
||||
}
|
||||
}
|
||||
}
|
||||
assert!(shutdown.is_shutdown_poll());
|
||||
@@ -518,12 +665,12 @@ where
|
||||
debug!("Started ReplyController without graceful shutdown support");
|
||||
|
||||
let polling_rate = Duration::from_secs(5);
|
||||
let mut interval_timer = tokio::time::interval(polling_rate);
|
||||
let mut interval = Self::create_interval_stream(polling_rate);
|
||||
|
||||
loop {
|
||||
tokio::select! {
|
||||
req = self.request_receiver.next() => self.handle_request(req.unwrap()).await,
|
||||
_ = interval_timer.tick() => self.inspect_stale_entries().await
|
||||
_ = interval.next() => self.inspect_stale_entries().await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ use nymsphinx::anonymous_replies::requests::AnonymousSenderTag;
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
use time::OffsetDateTime;
|
||||
use tokio::time::Instant;
|
||||
|
||||
mod error;
|
||||
mod manager;
|
||||
@@ -221,7 +220,7 @@ impl Backend {
|
||||
let mut received_surbs = Vec::with_capacity(surb_senders.len());
|
||||
for sender in surb_senders {
|
||||
let sender_id = sender.id;
|
||||
let (sender_tag, surbs_last_received_at): (AnonymousSenderTag, Instant) =
|
||||
let (sender_tag, surbs_last_received_at_timestamp): (AnonymousSenderTag, i64) =
|
||||
sender.try_into()?;
|
||||
let stored_surbs = self
|
||||
.manager
|
||||
@@ -233,7 +232,7 @@ impl Backend {
|
||||
|
||||
received_surbs.push((
|
||||
sender_tag,
|
||||
ReceivedReplySurbs::new_retrieved(stored_surbs, surbs_last_received_at),
|
||||
ReceivedReplySurbs::new_retrieved(stored_surbs, surbs_last_received_at_timestamp),
|
||||
))
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::client::replies::reply_storage::backend::fs_backend::error::StorageError;
|
||||
use crate::client::replies::reply_storage::key_storage::UsedReplyKey;
|
||||
use crypto::generic_array::typenum::Unsigned;
|
||||
use crypto::Digest;
|
||||
use nymsphinx::addressing::clients::{Recipient, RecipientBytes};
|
||||
@@ -9,9 +10,6 @@ use nymsphinx::anonymous_replies::encryption_key::EncryptionKeyDigest;
|
||||
use nymsphinx::anonymous_replies::requests::{AnonymousSenderTag, SENDER_TAG_SIZE};
|
||||
use nymsphinx::anonymous_replies::{ReplySurb, SurbEncryptionKey, SurbEncryptionKeySize};
|
||||
use nymsphinx::params::ReplySurbKeyDigestAlgorithm;
|
||||
use std::time::Duration;
|
||||
use time::OffsetDateTime;
|
||||
use tokio::time::Instant;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct StoredSenderTag {
|
||||
@@ -63,21 +61,20 @@ impl TryFrom<StoredSenderTag> for (RecipientBytes, AnonymousSenderTag) {
|
||||
pub(crate) struct StoredReplyKey {
|
||||
pub(crate) key_digest: Vec<u8>,
|
||||
pub(crate) reply_key: Vec<u8>,
|
||||
pub(crate) sent_at_timestamp: i64,
|
||||
}
|
||||
|
||||
impl StoredReplyKey {
|
||||
pub(crate) fn new(
|
||||
key_digest: EncryptionKeyDigest,
|
||||
reply_key: SurbEncryptionKey,
|
||||
) -> StoredReplyKey {
|
||||
pub(crate) fn new(key_digest: EncryptionKeyDigest, reply_key: UsedReplyKey) -> StoredReplyKey {
|
||||
StoredReplyKey {
|
||||
key_digest: key_digest.to_vec(),
|
||||
reply_key: reply_key.to_bytes(),
|
||||
reply_key: (*reply_key).to_bytes(),
|
||||
sent_at_timestamp: reply_key.sent_at_timestamp,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<StoredReplyKey> for (EncryptionKeyDigest, SurbEncryptionKey) {
|
||||
impl TryFrom<StoredReplyKey> for (EncryptionKeyDigest, UsedReplyKey) {
|
||||
type Error = StorageError;
|
||||
|
||||
fn try_from(value: StoredReplyKey) -> Result<Self, Self::Error> {
|
||||
@@ -102,7 +99,10 @@ impl TryFrom<StoredReplyKey> for (EncryptionKeyDigest, SurbEncryptionKey) {
|
||||
});
|
||||
};
|
||||
|
||||
Ok((digest, reply_key))
|
||||
Ok((
|
||||
digest,
|
||||
UsedReplyKey::new(reply_key, value.sent_at_timestamp),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,25 +113,18 @@ pub(crate) struct StoredSurbSender {
|
||||
}
|
||||
|
||||
impl StoredSurbSender {
|
||||
pub(crate) fn new(tag: AnonymousSenderTag, last_sent: Instant) -> Self {
|
||||
// this doesn't have to be sub-second accurate
|
||||
// as a matter of fact even if it's off by few minutes or even hours,
|
||||
// it would still be good enough
|
||||
let elapsed = last_sent.elapsed();
|
||||
let now = OffsetDateTime::now_utc();
|
||||
let last_sent = now - elapsed;
|
||||
|
||||
pub(crate) fn new(tag: AnonymousSenderTag, last_sent_timestamp: i64) -> Self {
|
||||
StoredSurbSender {
|
||||
// for the purposes of STORING data,
|
||||
// we ignore that field anyway
|
||||
id: 0,
|
||||
tag: tag.to_bytes().to_vec(),
|
||||
last_sent_timestamp: last_sent.unix_timestamp(),
|
||||
last_sent_timestamp,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<StoredSurbSender> for (AnonymousSenderTag, Instant) {
|
||||
impl TryFrom<StoredSurbSender> for (AnonymousSenderTag, i64) {
|
||||
type Error = StorageError;
|
||||
|
||||
fn try_from(value: StoredSurbSender) -> Result<Self, Self::Error> {
|
||||
@@ -145,26 +138,10 @@ impl TryFrom<StoredSurbSender> for (AnonymousSenderTag, Instant) {
|
||||
});
|
||||
};
|
||||
|
||||
let datetime =
|
||||
OffsetDateTime::from_unix_timestamp(value.last_sent_timestamp).map_err(|err| {
|
||||
StorageError::CorruptedData {
|
||||
details: format!("failed to parse stored timestamp - {err}"),
|
||||
}
|
||||
})?;
|
||||
|
||||
let duration_since: Duration =
|
||||
(OffsetDateTime::now_utc() - datetime)
|
||||
.try_into()
|
||||
.map_err(|err| StorageError::CorruptedData {
|
||||
details: format!(
|
||||
"failed to extract valid duration from the stored timestamp - {err}"
|
||||
),
|
||||
})?;
|
||||
|
||||
let now = Instant::now();
|
||||
let instant = now.checked_sub(duration_since).unwrap_or(now);
|
||||
|
||||
Ok((AnonymousSenderTag::from_bytes(sender_tag_bytes), instant))
|
||||
Ok((
|
||||
AnonymousSenderTag::from_bytes(sender_tag_bytes),
|
||||
value.last_sent_timestamp,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -46,17 +46,14 @@ impl CombinedReplyStorage {
|
||||
self.used_tags.clone()
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub fn key_storage_ref(&self) -> &SentReplyKeys {
|
||||
&self.sent_reply_keys
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub fn surbs_storage_ref(&self) -> &ReceivedReplySurbsMap {
|
||||
&self.received_reply_surbs
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub fn tags_storage_ref(&self) -> &UsedSenderTags {
|
||||
&self.used_tags
|
||||
}
|
||||
|
||||
@@ -1,24 +1,22 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use dashmap::iter::Iter;
|
||||
use dashmap::DashMap;
|
||||
use nymsphinx::anonymous_replies::encryption_key::EncryptionKeyDigest;
|
||||
use nymsphinx::anonymous_replies::SurbEncryptionKey;
|
||||
use std::ops::Deref;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
|
||||
use dashmap::iter::Iter;
|
||||
use time::OffsetDateTime;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
// TODO: we might have to also put the tag here
|
||||
// TODO2: some timestamp to indicate when entries should get purged if we expect to never get the reply back
|
||||
pub struct SentReplyKeys {
|
||||
inner: Arc<SentReplyKeysInner>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct SentReplyKeysInner {
|
||||
data: DashMap<EncryptionKeyDigest, SurbEncryptionKey>,
|
||||
data: DashMap<EncryptionKeyDigest, UsedReplyKey>,
|
||||
}
|
||||
|
||||
impl SentReplyKeys {
|
||||
@@ -31,7 +29,7 @@ impl SentReplyKeys {
|
||||
}
|
||||
|
||||
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
|
||||
pub(crate) fn from_raw(raw: Vec<(EncryptionKeyDigest, SurbEncryptionKey)>) -> SentReplyKeys {
|
||||
pub(crate) fn from_raw(raw: Vec<(EncryptionKeyDigest, UsedReplyKey)>) -> SentReplyKeys {
|
||||
SentReplyKeys {
|
||||
inner: Arc::new(SentReplyKeysInner {
|
||||
data: raw.into_iter().collect(),
|
||||
@@ -39,22 +37,50 @@ impl SentReplyKeys {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
|
||||
pub(crate) fn as_raw_iter(&self) -> Iter<'_, EncryptionKeyDigest, SurbEncryptionKey> {
|
||||
pub(crate) fn as_raw_iter(&self) -> Iter<'_, EncryptionKeyDigest, UsedReplyKey> {
|
||||
self.inner.data.iter()
|
||||
}
|
||||
|
||||
pub(crate) fn insert_multiple(&self, keys: Vec<SurbEncryptionKey>) {
|
||||
let now = OffsetDateTime::now_utc().unix_timestamp();
|
||||
for key in keys {
|
||||
self.insert(key)
|
||||
self.insert(UsedReplyKey::new(key, now))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn insert(&self, key: SurbEncryptionKey) {
|
||||
pub(crate) fn insert(&self, key: UsedReplyKey) {
|
||||
self.inner.data.insert(key.compute_digest(), key);
|
||||
}
|
||||
|
||||
pub(crate) fn try_pop(&self, digest: EncryptionKeyDigest) -> Option<SurbEncryptionKey> {
|
||||
pub(crate) fn try_pop(&self, digest: EncryptionKeyDigest) -> Option<UsedReplyKey> {
|
||||
self.inner.data.remove(&digest).map(|(_k, v)| v)
|
||||
}
|
||||
|
||||
pub(crate) fn remove(&self, digest: EncryptionKeyDigest) {
|
||||
self.inner.data.remove(&digest);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub(crate) struct UsedReplyKey {
|
||||
key: SurbEncryptionKey,
|
||||
// the purpose of this field is to perform invalidation at relatively very long intervals
|
||||
pub(crate) sent_at_timestamp: i64,
|
||||
}
|
||||
|
||||
impl UsedReplyKey {
|
||||
pub(crate) fn new(key: SurbEncryptionKey, sent_at_timestamp: i64) -> Self {
|
||||
UsedReplyKey {
|
||||
key,
|
||||
sent_at_timestamp,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for UsedReplyKey {
|
||||
type Target = SurbEncryptionKey;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.key
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use dashmap::iter::Iter;
|
||||
use dashmap::DashMap;
|
||||
use log::trace;
|
||||
use nymsphinx::anonymous_replies::requests::AnonymousSenderTag;
|
||||
@@ -8,15 +9,7 @@ use nymsphinx::anonymous_replies::ReplySurb;
|
||||
use std::collections::VecDeque;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use tokio::time::Instant;
|
||||
|
||||
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
|
||||
use dashmap::iter::Iter;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use wasm_timer::Instant;
|
||||
use time::OffsetDateTime;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ReceivedReplySurbsMap {
|
||||
@@ -63,18 +56,21 @@ impl ReceivedReplySurbsMap {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
|
||||
pub(crate) fn as_raw_iter(&self) -> Iter<'_, AnonymousSenderTag, ReceivedReplySurbs> {
|
||||
self.inner.data.iter()
|
||||
}
|
||||
|
||||
pub(crate) fn remove(&self, target: &AnonymousSenderTag) {
|
||||
self.inner.data.remove(target);
|
||||
}
|
||||
|
||||
pub(crate) fn reset_surbs_last_received_at(&self, target: &AnonymousSenderTag) {
|
||||
if let Some(mut entry) = self.inner.data.get_mut(target) {
|
||||
entry.surbs_last_received_at = Instant::now();
|
||||
entry.surbs_last_received_at_timestamp = OffsetDateTime::now_utc().unix_timestamp();
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn surbs_last_received_at(&self, target: &AnonymousSenderTag) -> Option<Instant> {
|
||||
pub(crate) fn surbs_last_received_at(&self, target: &AnonymousSenderTag) -> Option<i64> {
|
||||
self.inner
|
||||
.data
|
||||
.get(target)
|
||||
@@ -199,7 +195,7 @@ pub(crate) struct ReceivedReplySurbs {
|
||||
data: VecDeque<ReplySurb>,
|
||||
|
||||
pending_reception: u32,
|
||||
surbs_last_received_at: Instant,
|
||||
surbs_last_received_at_timestamp: i64,
|
||||
}
|
||||
|
||||
impl ReceivedReplySurbs {
|
||||
@@ -207,19 +203,19 @@ impl ReceivedReplySurbs {
|
||||
ReceivedReplySurbs {
|
||||
data: initial_surbs,
|
||||
pending_reception: 0,
|
||||
surbs_last_received_at: Instant::now(),
|
||||
surbs_last_received_at_timestamp: OffsetDateTime::now_utc().unix_timestamp(),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
|
||||
pub(crate) fn new_retrieved(
|
||||
surbs: Vec<ReplySurb>,
|
||||
surbs_last_received_at: Instant,
|
||||
surbs_last_received_at_timestamp: i64,
|
||||
) -> ReceivedReplySurbs {
|
||||
ReceivedReplySurbs {
|
||||
data: surbs.into(),
|
||||
pending_reception: 0,
|
||||
surbs_last_received_at,
|
||||
surbs_last_received_at_timestamp,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,8 +224,8 @@ impl ReceivedReplySurbs {
|
||||
&self.data
|
||||
}
|
||||
|
||||
pub(crate) fn surbs_last_received_at(&self) -> Instant {
|
||||
self.surbs_last_received_at
|
||||
pub(crate) fn surbs_last_received_at(&self) -> i64 {
|
||||
self.surbs_last_received_at_timestamp
|
||||
}
|
||||
|
||||
pub(crate) fn pending_reception(&self) -> u32 {
|
||||
@@ -276,7 +272,7 @@ impl ReceivedReplySurbs {
|
||||
let mut v = surbs.into_iter().collect::<VecDeque<_>>();
|
||||
trace!("storing {} surbs in the storage", v.len());
|
||||
self.data.append(&mut v);
|
||||
self.surbs_last_received_at = Instant::now();
|
||||
self.surbs_last_received_at_timestamp = OffsetDateTime::now_utc().unix_timestamp();
|
||||
trace!("we now have {} surbs!", self.data.len());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,12 @@ const DEFAULT_MAXIMUM_ALLOWED_SURB_REQUEST_SIZE: u32 = 500;
|
||||
const DEFAULT_RETRANSMISSION_REPLY_SURB_REQUEST_SIZE: u32 = 10;
|
||||
const DEFAULT_MAXIMUM_REPLY_SURB_WAITING_PERIOD: Duration = Duration::from_secs(10);
|
||||
|
||||
// 12 hours
|
||||
const DEFAULT_MAXIMUM_REPLY_SURB_AGE: Duration = Duration::from_secs(12 * 60 * 60);
|
||||
|
||||
// 24 hours
|
||||
const DEFAULT_MAXIMUM_REPLY_KEY_AGE: Duration = Duration::from_secs(24 * 60 * 60);
|
||||
|
||||
pub fn missing_string_value() -> String {
|
||||
MISSING_VALUE.to_string()
|
||||
}
|
||||
@@ -323,6 +329,14 @@ impl<T> Config<T> {
|
||||
pub fn get_maximum_reply_surb_waiting_period(&self) -> Duration {
|
||||
self.debug.maximum_reply_surb_waiting_period
|
||||
}
|
||||
|
||||
pub fn get_maximum_reply_surb_age(&self) -> Duration {
|
||||
self.debug.maximum_reply_surb_age
|
||||
}
|
||||
|
||||
pub fn get_maximum_reply_key_age(&self) -> Duration {
|
||||
self.debug.maximum_reply_key_age
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: NymConfig> Default for Config<T> {
|
||||
@@ -570,6 +584,16 @@ pub struct DebugConfig {
|
||||
/// for more even though in theory they wouldn't need to.
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub maximum_reply_surb_waiting_period: Duration,
|
||||
|
||||
/// Defines maximum amount of time given reply surb is going to be valid for.
|
||||
/// This is going to be superseded by key rotation once implemented.
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub maximum_reply_surb_age: Duration,
|
||||
|
||||
/// Defines maximum amount of time given reply key is going to be valid for.
|
||||
/// This is going to be superseded by key rotation once implemented.
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub maximum_reply_key_age: Duration,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
@@ -602,6 +626,8 @@ impl Default for DebugConfig {
|
||||
maximum_allowed_reply_surb_request_size: DEFAULT_MAXIMUM_ALLOWED_SURB_REQUEST_SIZE,
|
||||
retransmission_reply_surb_request_size: DEFAULT_RETRANSMISSION_REPLY_SURB_REQUEST_SIZE,
|
||||
maximum_reply_surb_waiting_period: DEFAULT_MAXIMUM_REPLY_SURB_WAITING_PERIOD,
|
||||
maximum_reply_surb_age: DEFAULT_MAXIMUM_REPLY_SURB_AGE,
|
||||
maximum_reply_key_age: DEFAULT_MAXIMUM_REPLY_KEY_AGE,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,6 +129,14 @@ pub struct Debug {
|
||||
/// Defines maximum amount of time the client is going to wait for reply surbs before explicitly asking
|
||||
/// for more even though in theory they wouldn't need to.
|
||||
pub maximum_reply_surb_waiting_period_ms: u64,
|
||||
|
||||
/// Defines maximum amount of time given reply surb is going to be valid for.
|
||||
/// This is going to be superseded by key rotation once implemented.
|
||||
pub maximum_reply_surb_age_ms: u64,
|
||||
|
||||
/// Defines maximum amount of time given reply key is going to be valid for.
|
||||
/// This is going to be superseded by key rotation once implemented.
|
||||
pub maximum_reply_key_age_ms: u64,
|
||||
}
|
||||
|
||||
impl From<Debug> for ConfigDebug {
|
||||
@@ -167,6 +175,8 @@ impl From<Debug> for ConfigDebug {
|
||||
maximum_reply_surb_waiting_period: Duration::from_millis(
|
||||
debug.maximum_reply_surb_waiting_period_ms,
|
||||
),
|
||||
maximum_reply_surb_age: Duration::from_millis(debug.maximum_reply_surb_age_ms),
|
||||
maximum_reply_key_age: Duration::from_millis(debug.maximum_reply_key_age_ms),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -198,6 +208,8 @@ impl From<ConfigDebug> for Debug {
|
||||
maximum_reply_surb_waiting_period_ms: debug
|
||||
.maximum_reply_surb_waiting_period
|
||||
.as_millis() as u64,
|
||||
maximum_reply_surb_age_ms: debug.maximum_reply_surb_age.as_millis() as u64,
|
||||
maximum_reply_key_age_ms: debug.maximum_reply_key_age.as_millis() as u64,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user