Files
Jędrzej Stuczyński 4a1ce8154a chore: resolve 1.87 clippy warnings (#5802)
* Clippy in wallet & sdk

* Clippy in wallet

* Pin rust to 1.86 in builder

* apply changes from b7da75a18c

* missing nym-node features

* Box all the things

* additional boxes in the wallet

* post rebasing clippy

---------

Co-authored-by: dynco-nym <173912580+dynco-nym@users.noreply.github.com>
2025-05-27 11:08:36 +01:00

413 lines
14 KiB
Rust

use super::message::{
ConnectionId, Message, OutboundMessage, SubstreamId, SubstreamMessage, TransportMessage,
};
use futures::{
io::{Error as IoError, ErrorKind},
AsyncRead, AsyncWrite,
};
use log::debug;
use nym_sphinx::addressing::clients::Recipient;
use parking_lot::Mutex;
use std::{
pin::Pin,
sync::{
atomic::{AtomicU64, Ordering},
Arc,
},
task::{Context, Poll},
};
use tokio::sync::{
mpsc::{UnboundedReceiver, UnboundedSender},
oneshot::Receiver,
};
#[derive(Debug)]
pub struct Substream {
remote_recipient: Recipient,
connection_id: ConnectionId,
pub(crate) substream_id: SubstreamId,
/// inbound messages; inbound_tx is in the corresponding Connection
pub(crate) inbound_rx: UnboundedReceiver<Vec<u8>>,
/// outbound messages; go directly to the mixnet
outbound_tx: UnboundedSender<OutboundMessage>,
/// used to signal when the substream is closed
close_rx: Receiver<()>,
closed: Mutex<bool>,
// buffer of data that's been written to the stream,
// but not yet read by the application.
unread_data: Mutex<Vec<u8>>,
message_nonce: Arc<AtomicU64>,
}
impl Substream {
pub(crate) fn new(
remote_recipient: Recipient,
connection_id: ConnectionId,
substream_id: SubstreamId,
inbound_rx: UnboundedReceiver<Vec<u8>>,
outbound_tx: UnboundedSender<OutboundMessage>,
close_rx: Receiver<()>,
message_nonce: Arc<AtomicU64>,
) -> Self {
Substream {
remote_recipient,
connection_id,
substream_id,
inbound_rx,
outbound_tx,
close_rx,
closed: Mutex::new(false),
unread_data: Mutex::new(vec![]),
message_nonce,
}
}
fn check_closed(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Result<(), IoError> {
let closed_err = IoError::other("stream closed");
// close_rx will return an error if the channel is closed (ie. sender was dropped),
// or if it's empty
let received_closed = self.close_rx.try_recv();
let mut closed = self.closed.lock();
if *closed {
return Err(closed_err);
}
if received_closed.is_ok() {
*closed = true;
return Err(closed_err);
}
Ok(())
}
}
impl AsyncRead for Substream {
fn poll_read(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut [u8],
) -> Poll<Result<usize, IoError>> {
let closed_result = self.as_mut().check_closed(cx);
if let Err(e) = closed_result {
return Poll::Ready(Err(e));
}
let inbound_rx_data = self.inbound_rx.poll_recv(cx);
// first, write any previously unread data to the buf
let mut unread_data = self.unread_data.lock();
let filled_len = if unread_data.len() > 0 {
let unread_len = unread_data.len();
let buf_len = buf.len();
let copy_len = std::cmp::min(unread_len, buf_len);
buf[..copy_len].copy_from_slice(&unread_data[..copy_len]);
*unread_data = unread_data[copy_len..].to_vec();
copy_len
} else {
0
};
if let Poll::Ready(Some(data)) = inbound_rx_data {
if filled_len == buf.len() {
// we've filled the buffer, so we'll have to save the rest for later
let mut new = vec![];
new.extend(unread_data.drain(..));
new.extend(data.iter());
*unread_data = new;
return Poll::Ready(Ok(filled_len));
}
// otherwise, there's still room in the buffer, so we'll copy the rest of the data
let remaining_len = buf.len() - filled_len;
let data_len = data.len();
// we have more data than buffer room remaining, save the extra for later
if remaining_len < data_len {
unread_data.extend_from_slice(&data[remaining_len..]);
}
let copied = std::cmp::min(remaining_len, data_len);
buf[filled_len..filled_len + copied].copy_from_slice(&data[..copied]);
debug!("poll_read copied {} bytes: data {:?}", copied, buf);
return Poll::Ready(Ok(copied));
}
if filled_len > 0 {
debug!("poll_read copied {} bytes: data {:?}", filled_len, buf);
return Poll::Ready(Ok(filled_len));
}
Poll::Pending
}
}
impl AsyncWrite for Substream {
fn poll_write(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<Result<usize, IoError>> {
if let Err(e) = self.as_mut().check_closed(cx) {
return Poll::Ready(Err(e));
}
let nonce = self.message_nonce.fetch_add(1, Ordering::SeqCst);
self.outbound_tx
.send(OutboundMessage {
recipient: self.remote_recipient,
message: Message::TransportMessage(TransportMessage {
nonce,
id: self.connection_id.clone(),
message: SubstreamMessage::new_with_data(
self.substream_id.clone(),
buf.to_vec(),
),
}),
})
.map_err(|e| IoError::other(format!("poll_write outbound_tx error: {}", e)))?;
Poll::Ready(Ok(buf.len()))
}
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), IoError>> {
if let Err(e) = self.check_closed(cx) {
return Poll::Ready(Err(e));
}
Poll::Ready(Ok(()))
}
fn poll_close(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<(), IoError>> {
let nonce = self.message_nonce.fetch_add(1, Ordering::SeqCst);
let mut closed = self.closed.lock();
if *closed {
return Poll::Ready(Err(IoError::other("stream closed")));
}
*closed = true;
// send a close message to the mixnet
self.outbound_tx
.send(OutboundMessage {
recipient: self.remote_recipient,
message: Message::TransportMessage(TransportMessage {
nonce,
id: self.connection_id.clone(),
message: SubstreamMessage::new_close(self.substream_id.clone()),
}),
})
.map_err(|e| IoError::other(format!("poll_close outbound_rx error: {}", e)))?;
Poll::Ready(Ok(()))
}
}
#[cfg(test)]
mod test {
use super::super::message::{
ConnectionId, Message, SubstreamId, SubstreamMessage, TransportMessage,
};
use super::super::mixnet::initialize_mixnet;
use super::Substream;
use futures::{AsyncReadExt, AsyncWriteExt};
use nym_sdk::mixnet::MixnetClient;
use nym_sphinx::addressing::clients::Recipient;
use std::sync::atomic::AtomicU64;
use std::sync::Arc;
#[tokio::test]
async fn test_substream_poll_read_unread_data() {
let (outbound_tx, _) = tokio::sync::mpsc::unbounded_channel();
let connection_id = ConnectionId::generate();
let substream_id = SubstreamId::generate();
let (inbound_tx, inbound_rx) = tokio::sync::mpsc::unbounded_channel();
let (_, close_rx) = tokio::sync::oneshot::channel();
let mut substream = Substream::new(
Recipient::try_from_base58_string("D1rrpsysCGCYXy9saP8y3kmNpGtJZUXN9SvFoUcqAsM9.9Ssso1ea5NfkbMASdiseDSjTN1fSWda5SgEVjdSN4CvV@GJqd3ZxpXWSNxTfx7B1pPtswpetH4LnJdFeLeuY5KUuN").unwrap(),
connection_id,
substream_id,
inbound_rx,
outbound_tx,
close_rx,
Arc::new(AtomicU64::new(1)),
);
// test writing and reading w/ same length data
let data = b"hello".to_vec();
inbound_tx.send(data.clone()).unwrap();
let mut buf = [0u8; 5];
let read_len = substream.read(&mut buf).await.unwrap();
assert_eq!(read_len, data.len());
assert_eq!(buf.to_vec(), data);
// test writing data longer than read buffer
let data = b"nootwashere".to_vec();
inbound_tx.send(data.clone()).unwrap();
let mut buf = [0u8; 4];
let read_len = substream.read(&mut buf).await.unwrap();
assert_eq!(read_len, buf.len());
assert_eq!(buf.to_vec(), b"noot".to_vec());
let mut buf = [0u8; 7];
let read_len = substream.read(&mut buf).await.unwrap();
assert_eq!(read_len, buf.len());
assert_eq!(buf.to_vec(), b"washere".to_vec());
// test read buffer larger than written data
let data = b"nootwashere".to_vec();
inbound_tx.send(data.clone()).unwrap();
let mut buf = [0u8; 16];
let read_len = substream.read(&mut buf).await.unwrap();
assert_eq!(read_len, data.len());
assert_eq!(buf[..data.len()], data);
assert_eq!(buf[data.len()..].to_vec(), vec![0u8; 16 - data.len()]);
// test writing data longer than read buffer multiple times
let data = b"nootwashere".to_vec();
inbound_tx.send(data.clone()).unwrap();
let mut buf = [0u8; 4];
let read_len = substream.read(&mut buf).await.unwrap();
assert_eq!(read_len, buf.len());
assert_eq!(buf.to_vec(), b"noot".to_vec());
let data = b"asdf".to_vec();
inbound_tx.send(data.clone()).unwrap();
let mut buf = [0u8; 4];
let read_len = substream.read(&mut buf).await.unwrap();
assert_eq!(read_len, buf.len());
assert_eq!(buf.to_vec(), b"wash".to_vec());
let mut buf = [0u8; 8];
let read_len = substream.read(&mut buf).await.unwrap();
assert_eq!(read_len, 7);
assert_eq!(buf[..7], b"ereasdf".to_vec());
}
#[tokio::test]
async fn test_substream_read_write() {
let client = MixnetClient::connect_new().await.unwrap();
let (self_address, mut mixnet_inbound_rx, outbound_tx) =
initialize_mixnet(client, None).await.unwrap();
const MSG_INNER: &[u8] = "hello".as_bytes();
let connection_id = ConnectionId::generate();
let substream_id = SubstreamId::generate();
let (inbound_tx, inbound_rx) = tokio::sync::mpsc::unbounded_channel();
let (_, close_rx) = tokio::sync::oneshot::channel();
let mut substream = Substream::new(
self_address,
connection_id,
substream_id,
inbound_rx,
outbound_tx,
close_rx,
Arc::new(AtomicU64::new(1)),
);
// send message to ourselves over the mixnet
substream.write_all(MSG_INNER).await.unwrap();
// receive full message over the mixnet
let recv_msg = mixnet_inbound_rx.recv().await.unwrap();
match recv_msg.0 {
Message::TransportMessage(TransportMessage {
nonce,
id: _,
message:
SubstreamMessage {
substream_id: _,
message_type: msg,
},
}) => {
assert_eq!(nonce, 1);
match msg {
super::super::message::SubstreamMessageType::Data(data) => {
assert_eq!(data, MSG_INNER);
// send message to substream inbound channel
inbound_tx.send(data).unwrap();
}
_ => panic!("unexpected message type"),
}
}
_ => panic!("unexpected message"),
}
// read message from substream
let mut buf = [0u8; MSG_INNER.len()];
substream.read_exact(&mut buf).await.unwrap();
assert_eq!(buf, MSG_INNER);
// close substream
substream.close().await.unwrap();
// try to read/write to closed substream; should error
substream.write_all(MSG_INNER).await.unwrap_err();
substream.read_exact(&mut buf).await.unwrap_err();
// assert a close message was sent over the mixnet
let recv_msg = mixnet_inbound_rx.recv().await.unwrap();
match recv_msg.0 {
Message::TransportMessage(TransportMessage {
nonce: _,
id: _,
message:
SubstreamMessage {
substream_id: _,
message_type: msg,
},
}) => match msg {
super::super::message::SubstreamMessageType::Close => {}
_ => panic!("unexpected message type"),
},
_ => panic!("unexpected message: {:?}", recv_msg.0),
}
}
#[tokio::test]
async fn test_substream_recv_close() {
let client = MixnetClient::connect_new().await.unwrap();
let (self_address, _, outbound_tx) = initialize_mixnet(client, None).await.unwrap();
const MSG_INNER: &[u8] = "hello".as_bytes();
let connection_id = ConnectionId::generate();
let substream_id = SubstreamId::generate();
let (_, inbound_rx) = tokio::sync::mpsc::unbounded_channel();
let (close_tx, close_rx) = tokio::sync::oneshot::channel();
let mut substream = Substream::new(
self_address,
connection_id,
substream_id,
inbound_rx,
outbound_tx,
close_rx,
Arc::new(AtomicU64::new(1)),
);
// close substream
close_tx.send(()).unwrap();
// try to read/write to closed substream; should error
substream.write_all(MSG_INNER).await.unwrap_err();
let mut buf = [0u8; MSG_INNER.len()];
substream.read_exact(&mut buf).await.unwrap_err();
}
}