use crate::mixnet::client::MixnetClientBuilder;
use crate::mixnet::client::DEFAULT_NUMBER_OF_SURBS;
use crate::mixnet::stream::{MixnetListener, MixnetStream};
use crate::mixnet::traits::MixnetMessageSender;
use crate::{Error, Result};
use async_trait::async_trait;
use futures::{ready, Stream, StreamExt};
use log::{debug, error};
use nym_client_core::client::base_client::GatewayConnection;
use nym_client_core::client::mix_traffic::ClientRequestSender;
use nym_client_core::client::{
base_client::{ClientInput, ClientOutput, ClientState},
inbound_messages::InputMessage,
received_buffer::ReconstructedMessagesReceiver,
};
use nym_client_core::config::{ForgetMe, RememberMe};
use nym_crypto::asymmetric::ed25519;
use nym_gateway_requests::ClientRequest;
use nym_sphinx::addressing::clients::Recipient;
use nym_sphinx::{params::PacketType, receiver::ReconstructedMessage};
use nym_statistics_common::clients::{ClientStatsEvents, ClientStatsSender};
use nym_task::connections::{ConnectionCommandSender, LaneQueueLengths};
use nym_task::ShutdownTracker;
use nym_topology::{NymRouteProvider, NymTopology};
use std::pin::Pin;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::task::{Context, Poll};
use std::time::Duration;
use tokio::sync::RwLockReadGuard;
use tokio_util::sync::CancellationToken;
pub struct MixnetClient {
pub(crate) nym_address: Recipient,
pub(crate) identity_keys: Arc<ed25519::KeyPair>,
pub(crate) client_input: ClientInput,
#[allow(dead_code)]
pub(crate) client_output: ClientOutput,
pub(crate) client_state: ClientState,
pub(crate) reconstructed_receiver: Option<ReconstructedMessagesReceiver>,
pub(crate) stats_events_reporter: ClientStatsSender,
pub(crate) shutdown_handle: ShutdownTracker,
pub(crate) packet_type: Option<PacketType>,
_buffered: Vec<ReconstructedMessage>,
pub(crate) forget_me: ForgetMe,
pub(crate) remember_me: RememberMe,
pub(crate) stream_mode: Arc<AtomicBool>,
pub(crate) streams: Option<super::stream::StreamState>,
pub(crate) stream_idle_timeout: Duration,
}
impl MixnetClient {
#[allow(clippy::too_many_arguments)]
pub(crate) fn new(
nym_address: Recipient,
identity_keys: Arc<ed25519::KeyPair>,
client_input: ClientInput,
client_output: ClientOutput,
client_state: ClientState,
reconstructed_receiver: ReconstructedMessagesReceiver,
stats_events_reporter: ClientStatsSender,
task_handle: ShutdownTracker,
packet_type: Option<PacketType>,
forget_me: ForgetMe,
remember_me: RememberMe,
) -> Self {
Self {
nym_address,
identity_keys,
client_input,
client_output,
client_state,
reconstructed_receiver: Some(reconstructed_receiver),
stats_events_reporter,
shutdown_handle: task_handle,
packet_type,
_buffered: Vec::new(),
forget_me,
remember_me,
stream_mode: Arc::new(AtomicBool::new(false)),
streams: None,
stream_idle_timeout: super::stream::DEFAULT_STREAM_IDLE_TIMEOUT,
}
}
pub async fn connect_new() -> Result<Self> {
MixnetClientBuilder::new_ephemeral()
.build()?
.connect_to_mixnet()
.await
}
pub fn nym_address(&self) -> &Recipient {
&self.nym_address
}
pub fn cancellation_token(&self) -> CancellationToken {
self.shutdown_handle.child_shutdown_token().inner().clone()
}
pub fn client_request_sender(&self) -> ClientRequestSender {
self.client_input.client_request_sender.clone()
}
pub fn identity_keypair(&self) -> Arc<ed25519::KeyPair> {
self.identity_keys.clone()
}
pub fn sign(&self, data: &[u8]) -> ed25519::Signature {
self.identity_keys.private_key().sign(data)
}
pub fn sign_text(&self, text: &str) -> String {
self.identity_keys.private_key().sign_text(text)
}
pub fn gateway_connection(&self) -> GatewayConnection {
self.client_state.gateway_connection
}
pub fn split_sender(&self) -> MixnetClientSender {
MixnetClientSender {
client_input: self.client_input.clone(),
packet_type: self.packet_type,
stream_mode: self.stream_mode.clone(),
}
}
pub fn connection_command_sender(&self) -> ConnectionCommandSender {
self.client_input.connection_command_sender.clone()
}
pub fn shared_lane_queue_lengths(&self) -> LaneQueueLengths {
self.client_state.shared_lane_queue_lengths.clone()
}
pub async fn manually_overwrite_topology(&self, new_topology: NymTopology) {
self.client_state
.topology_accessor
.manually_change_topology(new_topology)
.await
}
pub async fn read_current_route_provider(
&self,
) -> Option<RwLockReadGuard<'_, NymRouteProvider>> {
self.client_state
.topology_accessor
.current_route_provider()
.await
}
pub fn restore_automatic_topology_refreshing(&self) {
self.client_state.topology_accessor.release_manual_control()
}
pub async fn wait_for_messages(&mut self) -> Option<Vec<ReconstructedMessage>> {
if self.stream_mode.load(Ordering::SeqCst) {
tracing::warn!("wait_for_messages() called after stream mode activated");
return None;
}
self.reconstructed_receiver.as_mut()?.next().await
}
pub async fn on_messages<F>(&mut self, fun: F)
where
F: Fn(ReconstructedMessage),
{
while let Some(msgs) = self.wait_for_messages().await {
for msg in msgs {
fun(msg)
}
}
}
pub fn send_stats_event(&self, event: ClientStatsEvents) {
self.stats_events_reporter.report(event);
}
pub fn stats_events_reporter(&self) -> ClientStatsSender {
self.stats_events_reporter.clone()
}
pub async fn disconnect(self) {
if self.forget_me.any() {
log::debug!("Sending forget me request: {:?}", self.forget_me);
match self.send_forget_me().await {
Ok(_) => (),
Err(e) => error!("Failed to send forget me request: {e}"),
};
tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;
} else if self.remember_me.stats() {
log::debug!("Sending remember me request: {:?}", self.remember_me);
match self.send_remember_me().await {
Ok(_) => (),
Err(e) => error!("Failed to send remember me request: {e}"),
};
tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;
}
self.shutdown_handle.shutdown().await;
}
pub async fn send_forget_me(&self) -> Result<()> {
let client_request = ClientRequest::ForgetMe {
client: self.forget_me.client(),
stats: self.forget_me.stats(),
};
match self
.client_input
.client_request_sender
.send(client_request)
.await
{
Ok(_) => Ok(()),
Err(e) => {
error!("Failed to send forget me request: {e}");
Err(Error::MessageSendingFailure)
}
}
}
pub async fn send_remember_me(&self) -> Result<()> {
let client_request = ClientRequest::RememberMe {
session_type: self.remember_me.session_type(),
};
match self
.client_input
.client_request_sender
.send(client_request)
.await
{
Ok(_) => Ok(()),
Err(e) => {
error!("Failed to send remember me request: {e}");
Err(Error::MessageSendingFailure)
}
}
}
pub async fn open_stream(
&mut self,
recipient: Recipient,
reply_surbs: Option<u32>,
) -> Result<MixnetStream> {
super::stream::open_stream(
self,
recipient,
reply_surbs.unwrap_or(DEFAULT_NUMBER_OF_SURBS),
)
.await
}
pub fn listener(&mut self) -> Result<MixnetListener> {
super::stream::listener(self)
}
}
pub struct MixnetClientSender {
client_input: ClientInput,
packet_type: Option<PacketType>,
stream_mode: Arc<AtomicBool>,
}
impl Clone for MixnetClientSender {
fn clone(&self) -> Self {
Self {
client_input: self.client_input.clone(),
packet_type: self.packet_type,
stream_mode: self.stream_mode.clone(),
}
}
}
impl Stream for MixnetClient {
type Item = ReconstructedMessage;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
if self.stream_mode.load(Ordering::SeqCst) {
tracing::warn!("Stream::poll_next() called after stream mode activated");
return Poll::Ready(None);
}
if let Some(next) = self._buffered.pop() {
cx.waker().wake_by_ref();
return Poll::Ready(Some(next));
}
let receiver = match self.reconstructed_receiver.as_mut() {
Some(rx) => rx,
None => return Poll::Ready(None),
};
match ready!(Pin::new(receiver).poll_next(cx)) {
None => Poll::Ready(None),
Some(mut msgs) => {
if let Some(next) = msgs.pop() {
if !msgs.is_empty() {
self._buffered = msgs;
cx.waker().wake_by_ref();
}
Poll::Ready(Some(next))
} else {
debug!("the reconstructed messages vector is empty");
cx.waker().wake_by_ref();
Poll::Pending
}
}
}
}
}
#[async_trait]
impl MixnetMessageSender for MixnetClient {
fn packet_type(&self) -> Option<PacketType> {
self.packet_type
}
async fn send(&self, message: InputMessage) -> Result<()> {
if self.stream_mode.load(Ordering::SeqCst) {
tracing::warn!("send() called after stream mode activated");
return Err(Error::StreamModeActive);
}
self.client_input
.send(message)
.await
.map_err(|_| Error::MessageSendingFailure)
}
}
#[async_trait]
impl MixnetMessageSender for MixnetClientSender {
fn packet_type(&self) -> Option<PacketType> {
self.packet_type
}
async fn send(&self, message: InputMessage) -> Result<()> {
if self.stream_mode.load(Ordering::SeqCst) {
tracing::warn!("send() called after stream mode activated");
return Err(Error::StreamModeActive);
}
self.client_input
.send(message)
.await
.map_err(|_| Error::MessageSendingFailure)
}
}