use crate::protocols::wire::handshake::v1::ProtocolId;
use aptos_rate_limiter::{async_lib::AsyncRateLimiter, rate_limit::SharedBucket};
use bytes::Bytes;
use futures::{
io::{AsyncRead, AsyncWrite},
sink::Sink,
stream::Stream,
};
use pin_project::pin_project;
#[cfg(any(test, feature = "fuzzing"))]
use proptest_derive::Arbitrary;
use serde::{Deserialize, Serialize};
use std::{
io,
pin::Pin,
task::{Context, Poll},
};
use thiserror::Error;
use tokio_util::{
codec::{FramedRead, FramedWrite, LengthDelimitedCodec},
compat::{Compat, FuturesAsyncReadCompatExt, FuturesAsyncWriteCompatExt},
};
#[cfg(test)]
mod test;
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
pub enum NetworkMessage {
Error(ErrorCode),
RpcRequest(RpcRequest),
RpcResponse(RpcResponse),
DirectSendMsg(DirectSendMsg),
}
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
pub enum ErrorCode {
ParsingError(ParsingErrorType),
NotSupported(NotSupportedType),
}
impl ErrorCode {
pub fn parsing_error(message: u8, protocol: u8) -> Self {
ErrorCode::ParsingError(ParsingErrorType { message, protocol })
}
}
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
pub struct ParsingErrorType {
message: u8,
protocol: u8,
}
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
pub enum NotSupportedType {
RpcRequest(ProtocolId),
DirectSendMsg(ProtocolId),
}
pub type RequestId = u32;
pub type Priority = u8;
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
pub struct RpcRequest {
pub protocol_id: ProtocolId,
pub request_id: RequestId,
pub priority: Priority,
#[serde(with = "serde_bytes")]
pub raw_request: Vec<u8>,
}
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
pub struct RpcResponse {
pub request_id: RequestId,
pub priority: Priority,
#[serde(with = "serde_bytes")]
pub raw_response: Vec<u8>,
}
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
pub struct DirectSendMsg {
pub protocol_id: ProtocolId,
pub priority: Priority,
#[serde(with = "serde_bytes")]
pub raw_msg: Vec<u8>,
}
#[derive(Debug, Error)]
pub enum ReadError {
#[error("network message stream: failed to deserialize network message frame: {0}, frame length: {1}, frame prefix: {2:?}")]
DeserializeError(#[source] bcs::Error, usize, Bytes),
#[error("network message stream: IO error while reading message: {0}")]
IoError(#[from] io::Error),
}
#[derive(Debug, Error)]
pub enum WriteError {
#[error("network message sink: failed to serialize network message: {0}")]
SerializeError(#[source] bcs::Error),
#[error("network message sink: IO error while sending message: {0}")]
IoError(#[from] io::Error),
}
pub fn network_message_frame_codec(max_frame_size: usize) -> LengthDelimitedCodec {
LengthDelimitedCodec::builder()
.max_frame_length(max_frame_size)
.length_field_length(4)
.big_endian()
.new_codec()
}
#[pin_project]
pub struct NetworkMessageStream<TReadSocket: AsyncRead + Unpin> {
#[pin]
framed_read: FramedRead<Compat<AsyncRateLimiter<TReadSocket>>, LengthDelimitedCodec>,
}
impl<TReadSocket: AsyncRead + Unpin> NetworkMessageStream<TReadSocket> {
pub fn new(socket: TReadSocket, max_frame_size: usize, bucket: Option<SharedBucket>) -> Self {
let frame_codec = network_message_frame_codec(max_frame_size);
let rate_limited_socket = AsyncRateLimiter::new(socket, bucket);
let compat_socket = rate_limited_socket.compat();
let framed_read = FramedRead::new(compat_socket, frame_codec);
Self { framed_read }
}
}
impl<TReadSocket: AsyncRead + Unpin> Stream for NetworkMessageStream<TReadSocket> {
type Item = Result<NetworkMessage, ReadError>;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
match self.project().framed_read.poll_next(cx) {
Poll::Ready(Some(Ok(frame))) => {
let frame = frame.freeze();
match bcs::from_bytes(&frame) {
Ok(message) => Poll::Ready(Some(Ok(message))),
Err(err) => {
let mut frame = frame;
let frame_len = frame.len();
frame.truncate(8);
let err = ReadError::DeserializeError(err, frame_len, frame);
Poll::Ready(Some(Err(err)))
}
}
}
Poll::Ready(Some(Err(err))) => Poll::Ready(Some(Err(ReadError::IoError(err)))),
Poll::Ready(None) => Poll::Ready(None),
Poll::Pending => Poll::Pending,
}
}
}
#[pin_project]
pub struct NetworkMessageSink<TWriteSocket: AsyncWrite> {
#[pin]
framed_write: FramedWrite<Compat<AsyncRateLimiter<TWriteSocket>>, LengthDelimitedCodec>,
}
impl<TWriteSocket: AsyncWrite> NetworkMessageSink<TWriteSocket> {
pub fn new(socket: TWriteSocket, max_frame_size: usize, bucket: Option<SharedBucket>) -> Self {
let frame_codec = network_message_frame_codec(max_frame_size);
let rate_limited_socket = AsyncRateLimiter::new(socket, bucket);
let compat_socket = rate_limited_socket.compat_write();
let framed_write = FramedWrite::new(compat_socket, frame_codec);
Self { framed_write }
}
}
#[cfg(test)]
impl<TWriteSocket: AsyncWrite + Unpin> NetworkMessageSink<TWriteSocket> {
pub async fn send_raw_frame(&mut self, frame: Bytes) -> Result<(), WriteError> {
use futures::sink::SinkExt;
self.framed_write
.send(frame)
.await
.map_err(WriteError::IoError)
}
}
impl<TWriteSocket: AsyncWrite> Sink<&NetworkMessage> for NetworkMessageSink<TWriteSocket> {
type Error = WriteError;
fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.project()
.framed_write
.poll_ready(cx)
.map_err(WriteError::IoError)
}
fn start_send(self: Pin<&mut Self>, message: &NetworkMessage) -> Result<(), Self::Error> {
let frame = bcs::to_bytes(message).map_err(WriteError::SerializeError)?;
let frame = Bytes::from(frame);
self.project()
.framed_write
.start_send(frame)
.map_err(WriteError::IoError)
}
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.project()
.framed_write
.poll_flush(cx)
.map_err(WriteError::IoError)
}
fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.project()
.framed_write
.poll_close(cx)
.map_err(WriteError::IoError)
}
}