use crate::{
counters::{
self, network_application_inbound_traffic, network_application_outbound_traffic,
RECEIVED_LABEL, SENT_LABEL,
},
logging::NetworkSchema,
peer_manager::{PeerManagerError, TransportNotification},
protocols::{
direct_send::Message,
rpc::{InboundRpcRequest, InboundRpcs, OutboundRpcRequest, OutboundRpcs},
wire::messaging::v1::{
DirectSendMsg, ErrorCode, NetworkMessage, NetworkMessageSink, NetworkMessageStream,
Priority, ReadError, WriteError,
},
},
transport::{self, Connection, ConnectionMetadata},
ProtocolId,
};
use aptos_config::network_id::NetworkContext;
use aptos_logger::prelude::*;
use aptos_rate_limiter::rate_limit::SharedBucket;
use aptos_time_service::{TimeService, TimeServiceTrait};
use aptos_types::PeerId;
use bytes::Bytes;
use channel::aptos_channel;
use futures::{
self,
channel::oneshot,
io::{AsyncRead, AsyncWrite},
stream::StreamExt,
FutureExt, SinkExt, TryFutureExt,
};
use serde::Serialize;
use short_hex_str::AsShortHexStr;
use std::{fmt, panic, time::Duration};
use tokio::runtime::Handle;
use tokio_util::compat::{
FuturesAsyncReadCompatExt, TokioAsyncReadCompatExt, TokioAsyncWriteCompatExt,
};
#[cfg(test)]
mod test;
#[cfg(any(test, feature = "fuzzing"))]
pub mod fuzzing;
#[derive(Debug)]
pub enum PeerRequest {
SendRpc(OutboundRpcRequest),
SendDirectSend(Message),
}
#[derive(Debug, PartialEq)]
pub enum PeerNotification {
RecvRpc(InboundRpcRequest),
RecvMessage(Message),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)]
pub enum DisconnectReason {
Requested,
ConnectionLost,
}
impl fmt::Display for DisconnectReason {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
DisconnectReason::Requested => "Requested",
DisconnectReason::ConnectionLost => "ConnectionLost",
};
write!(f, "{}", s)
}
}
enum State {
Connected,
ShuttingDown(DisconnectReason),
}
pub struct Peer<TSocket> {
network_context: NetworkContext,
executor: Handle,
time_service: TimeService,
connection_metadata: ConnectionMetadata,
connection: Option<TSocket>,
connection_notifs_tx: channel::Sender<TransportNotification<TSocket>>,
peer_reqs_rx: aptos_channel::Receiver<ProtocolId, PeerRequest>,
peer_notifs_tx: aptos_channel::Sender<ProtocolId, PeerNotification>,
inbound_rpcs: InboundRpcs,
outbound_rpcs: OutboundRpcs,
state: State,
max_frame_size: usize,
inbound_rate_limiter: Option<SharedBucket>,
outbound_rate_limiter: Option<SharedBucket>,
}
impl<TSocket> Peer<TSocket>
where
TSocket: AsyncRead + AsyncWrite + Send + 'static,
{
pub fn new(
network_context: NetworkContext,
executor: Handle,
time_service: TimeService,
connection: Connection<TSocket>,
connection_notifs_tx: channel::Sender<TransportNotification<TSocket>>,
peer_reqs_rx: aptos_channel::Receiver<ProtocolId, PeerRequest>,
peer_notifs_tx: aptos_channel::Sender<ProtocolId, PeerNotification>,
inbound_rpc_timeout: Duration,
max_concurrent_inbound_rpcs: u32,
max_concurrent_outbound_rpcs: u32,
max_frame_size: usize,
inbound_rate_limiter: Option<SharedBucket>,
outbound_rate_limiter: Option<SharedBucket>,
) -> Self {
let Connection {
metadata: connection_metadata,
socket,
} = connection;
let remote_peer_id = connection_metadata.remote_peer_id;
Self {
network_context,
executor,
time_service: time_service.clone(),
connection_metadata,
connection: Some(socket),
connection_notifs_tx,
peer_reqs_rx,
peer_notifs_tx,
inbound_rpcs: InboundRpcs::new(
network_context,
time_service.clone(),
remote_peer_id,
inbound_rpc_timeout,
max_concurrent_inbound_rpcs,
),
outbound_rpcs: OutboundRpcs::new(
network_context,
time_service,
remote_peer_id,
max_concurrent_outbound_rpcs,
),
state: State::Connected,
max_frame_size,
inbound_rate_limiter,
outbound_rate_limiter,
}
}
fn remote_peer_id(&self) -> PeerId {
self.connection_metadata.remote_peer_id
}
pub async fn start(mut self) {
let remote_peer_id = self.remote_peer_id();
trace!(
NetworkSchema::new(&self.network_context)
.connection_metadata(&self.connection_metadata),
"{} Starting Peer actor for peer: {}",
self.network_context,
remote_peer_id.short_str()
);
let (read_socket, write_socket) =
tokio::io::split(self.connection.take().unwrap().compat());
let mut reader = NetworkMessageStream::new(
read_socket.compat(),
self.max_frame_size,
self.inbound_rate_limiter.clone(),
)
.fuse();
let writer = NetworkMessageSink::new(
write_socket.compat_write(),
self.max_frame_size,
self.outbound_rate_limiter.clone(),
);
let (mut write_reqs_tx, writer_close_tx) = Self::start_writer_task(
&self.executor,
self.time_service.clone(),
self.connection_metadata.clone(),
self.network_context,
writer,
);
let reason = loop {
if let State::ShuttingDown(reason) = self.state {
break reason;
}
futures::select! {
maybe_request = self.peer_reqs_rx.next() => {
match maybe_request {
Some(request) => self.handle_outbound_request(request, &mut write_reqs_tx).await,
None => self.shutdown(DisconnectReason::Requested),
}
},
maybe_message = reader.next() => {
match maybe_message {
Some(message) => {
if let Err(err) = self.handle_inbound_message(message, &mut write_reqs_tx).await {
warn!(
NetworkSchema::new(&self.network_context)
.connection_metadata(&self.connection_metadata),
error = %err,
"{} Error in handling inbound message from peer: {}, error: {}",
self.network_context,
remote_peer_id.short_str(),
err
);
}
},
None => self.shutdown(DisconnectReason::ConnectionLost),
}
},
maybe_response = self.inbound_rpcs.next_completed_response() => {
if let Err(err) = self.inbound_rpcs.send_outbound_response(&mut write_reqs_tx, maybe_response).await {
warn!(
NetworkSchema::new(&self.network_context).connection_metadata(&self.connection_metadata),
error = %err,
"{} Error in handling inbound rpc request, error: {}", self.network_context, err,
);
}
},
(request_id, maybe_completed_request) = self.outbound_rpcs.next_completed_request() => {
self.outbound_rpcs.handle_completed_request(request_id, maybe_completed_request);
}
}
};
self.do_shutdown(writer_close_tx, reason).await;
}
fn start_writer_task(
executor: &Handle,
time_service: TimeService,
connection_metadata: ConnectionMetadata,
network_context: NetworkContext,
mut writer: NetworkMessageSink<impl AsyncWrite + Unpin + Send + 'static>,
) -> (
channel::Sender<(
NetworkMessage,
oneshot::Sender<Result<(), PeerManagerError>>,
)>,
oneshot::Sender<()>,
) {
let remote_peer_id = connection_metadata.remote_peer_id;
let (write_reqs_tx, mut write_reqs_rx): (
channel::Sender<(
NetworkMessage,
oneshot::Sender<Result<(), PeerManagerError>>,
)>,
_,
) = channel::new(1024, &counters::PENDING_WIRE_MESSAGES);
let (close_tx, close_rx) = oneshot::channel();
let writer_task = async move {
let mut close_rx = close_rx.into_stream();
loop {
futures::select! {
(message, ack_ch) = write_reqs_rx.select_next_some() => {
if let Err(err) = writer
.send(&message)
.map_ok(|_| ack_ch.send(Ok(())))
.await
{
warn!(
NetworkSchema::new(&network_context)
.connection_metadata(&connection_metadata),
error = %err,
"{} Error in sending message to peer: {}, error: {}",
network_context,
remote_peer_id.short_str(),
err
);
break;
}
},
_ = close_rx.select_next_some() => {
break;
}
}
}
info!(
NetworkSchema::new(&network_context).connection_metadata(&connection_metadata),
"{} Closing connection to peer: {}",
network_context,
remote_peer_id.short_str()
);
let flush_and_close = async {
writer.flush().await?;
writer.close().await?;
Ok(()) as Result<(), WriteError>
};
match time_service
.timeout(transport::TRANSPORT_TIMEOUT, flush_and_close)
.await
{
Err(_) => {
info!(
NetworkSchema::new(&network_context)
.connection_metadata(&connection_metadata),
"{} Timeout in flush/close of connection to peer: {}",
network_context,
remote_peer_id.short_str()
);
}
Ok(Err(err)) => {
info!(
NetworkSchema::new(&network_context)
.connection_metadata(&connection_metadata),
error = %err,
"{} Failure in flush/close of connection to peer: {}, error: {}",
network_context,
remote_peer_id.short_str(),
err
);
}
Ok(Ok(())) => {
info!(
NetworkSchema::new(&network_context)
.connection_metadata(&connection_metadata),
"{} Closed connection to peer: {}",
network_context,
remote_peer_id.short_str()
);
}
}
};
executor.spawn(writer_task);
(write_reqs_tx, close_tx)
}
async fn handle_inbound_message(
&mut self,
message: Result<NetworkMessage, ReadError>,
write_reqs_tx: &mut channel::Sender<(
NetworkMessage,
oneshot::Sender<Result<(), PeerManagerError>>,
)>,
) -> Result<(), PeerManagerError> {
trace!(
NetworkSchema::new(&self.network_context)
.connection_metadata(&self.connection_metadata),
"{} Received message from peer {}",
self.network_context,
self.remote_peer_id().short_str()
);
let message = match message {
Ok(message) => message,
Err(err) => match err {
ReadError::DeserializeError(_, _, ref frame_prefix) => {
let message_type = frame_prefix.as_ref().get(0).unwrap_or(&0);
let protocol_id = frame_prefix.as_ref().get(1).unwrap_or(&0);
let error_code = ErrorCode::parsing_error(*message_type, *protocol_id);
let message = NetworkMessage::Error(error_code);
let (ack_tx, _) = oneshot::channel();
write_reqs_tx.send((message, ack_tx)).await?;
return Err(err.into());
}
ReadError::IoError(_) => {
self.shutdown(DisconnectReason::ConnectionLost);
return Err(err.into());
}
},
};
match message {
NetworkMessage::DirectSendMsg(message) => self.handle_inbound_direct_send(message),
NetworkMessage::Error(error_msg) => {
warn!(
NetworkSchema::new(&self.network_context)
.connection_metadata(&self.connection_metadata),
error_msg = ?error_msg,
"{} Peer {} sent an error message: {:?}",
self.network_context,
self.remote_peer_id().short_str(),
error_msg,
);
}
NetworkMessage::RpcRequest(request) => {
if let Err(err) = self
.inbound_rpcs
.handle_inbound_request(&mut self.peer_notifs_tx, request)
{
warn!(
NetworkSchema::new(&self.network_context)
.connection_metadata(&self.connection_metadata),
error = %err,
"{} Error handling inbound rpc request: {}",
self.network_context,
err
);
}
}
NetworkMessage::RpcResponse(response) => {
self.outbound_rpcs.handle_inbound_response(response)
}
};
Ok(())
}
fn handle_inbound_direct_send(&mut self, message: DirectSendMsg) {
let peer_id = self.remote_peer_id();
let protocol_id = message.protocol_id;
let data = message.raw_msg;
trace!(
NetworkSchema::new(&self.network_context).remote_peer(&peer_id),
protocol_id = protocol_id,
"{} DirectSend: Received inbound message from peer {} for protocol {:?}",
self.network_context,
peer_id.short_str(),
protocol_id
);
let data_len = data.len() as u64;
counters::direct_send_messages(&self.network_context, RECEIVED_LABEL).inc();
counters::direct_send_bytes(&self.network_context, RECEIVED_LABEL).inc_by(data_len);
network_application_inbound_traffic(self.network_context, message.protocol_id, data_len);
let notif = PeerNotification::RecvMessage(Message {
protocol_id,
mdata: Bytes::from(data),
});
if let Err(err) = self.peer_notifs_tx.push(protocol_id, notif) {
warn!(
NetworkSchema::new(&self.network_context),
error = ?err,
"{} Failed to notify PeerManager about inbound DirectSend message. Error: {:?}",
self.network_context,
err
);
}
}
async fn handle_outbound_request(
&mut self,
request: PeerRequest,
write_reqs_tx: &mut channel::Sender<(
NetworkMessage,
oneshot::Sender<Result<(), PeerManagerError>>,
)>,
) {
trace!(
"Peer {} PeerRequest::{:?}",
self.remote_peer_id().short_str(),
request
);
match request {
PeerRequest::SendDirectSend(message) => {
let message_len = message.mdata.len();
let protocol_id = message.protocol_id;
network_application_outbound_traffic(
self.network_context,
protocol_id,
message_len as u64,
);
let message = NetworkMessage::DirectSendMsg(DirectSendMsg {
protocol_id,
priority: Priority::default(),
raw_msg: Vec::from(message.mdata.as_ref()),
});
let (ack_tx, _ack_rx) = oneshot::channel();
match write_reqs_tx.send((message, ack_tx)).await {
Ok(_) => {
counters::direct_send_messages(&self.network_context, SENT_LABEL).inc();
counters::direct_send_bytes(&self.network_context, SENT_LABEL)
.inc_by(message_len as u64);
}
Err(e) => {
warn!(
NetworkSchema::new(&self.network_context)
.connection_metadata(&self.connection_metadata),
error = ?e,
"Failed to send direct send message for protocol {} to peer: {}. Error: {:?}",
protocol_id,
self.remote_peer_id().short_str(),
e,
);
}
}
}
PeerRequest::SendRpc(request) => {
let protocol_id = request.protocol_id;
network_application_outbound_traffic(
self.network_context,
protocol_id,
request.data.len() as u64,
);
if let Err(e) = self
.outbound_rpcs
.handle_outbound_request(request, write_reqs_tx)
.await
{
warn!(
NetworkSchema::new(&self.network_context)
.connection_metadata(&self.connection_metadata),
error = %e,
"Failed to send outbound rpc request for protocol {} to peer: {}. Error: {}",
protocol_id,
self.remote_peer_id().short_str(),
e,
);
}
}
}
}
fn shutdown(&mut self, reason: DisconnectReason) {
self.state = State::ShuttingDown(reason);
}
async fn do_shutdown(mut self, writer_close_tx: oneshot::Sender<()>, reason: DisconnectReason) {
let remote_peer_id = self.remote_peer_id();
if let Err(e) = self
.connection_notifs_tx
.send(TransportNotification::Disconnected(
self.connection_metadata.clone(),
reason,
))
.await
{
warn!(
NetworkSchema::new(&self.network_context)
.connection_metadata(&self.connection_metadata),
error = ?e,
"{} Failed to notify upstream about disconnection of peer: {}; error: {:?}",
self.network_context,
remote_peer_id.short_str(),
e
);
}
if let Err(e) = writer_close_tx.send(()) {
info!(
NetworkSchema::new(&self.network_context)
.connection_metadata(&self.connection_metadata),
error = ?e,
"{} Failed to send close instruction to writer task. It must already be terminating/terminated. Error: {:?}",
self.network_context,
e
);
}
trace!(
NetworkSchema::new(&self.network_context)
.connection_metadata(&self.connection_metadata),
"{} Peer actor for '{}' terminated",
self.network_context,
remote_peer_id.short_str()
);
}
}