mirror of
https://github.com/sigp/lighthouse.git
synced 2026-05-30 20:57:10 +00:00
add a unique integer id to Rpc requests (#6444)
* add id to rpc requests * rename rpc request and response types for more accurate meaning * remove unrequired build_request function * remove unirequired Request wrapper types and unify Outbound and Inbound Request * add RequestId to NetworkMessage::SendResponse ,NetworkMessage::SendErrorResponse to be passed to Rpc::send_response
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -1,11 +1,12 @@
|
||||
#![allow(clippy::type_complexity)]
|
||||
#![allow(clippy::cognitive_complexity)]
|
||||
|
||||
use super::methods::{GoodbyeReason, RPCCodedResponse, RPCResponseErrorCode};
|
||||
use super::methods::{GoodbyeReason, RpcErrorResponse, RpcResponse};
|
||||
use super::outbound::OutboundRequestContainer;
|
||||
use super::protocol::{InboundOutput, InboundRequest, Protocol, RPCError, RPCProtocol};
|
||||
use super::{RPCReceived, RPCSend, ReqId};
|
||||
use crate::rpc::outbound::{OutboundFramed, OutboundRequest};
|
||||
use super::protocol::{InboundOutput, Protocol, RPCError, RPCProtocol, RequestType};
|
||||
use super::RequestId;
|
||||
use super::{RPCReceived, RPCSend, ReqId, Request};
|
||||
use crate::rpc::outbound::OutboundFramed;
|
||||
use crate::rpc::protocol::InboundFramed;
|
||||
use fnv::FnvHashMap;
|
||||
use futures::prelude::*;
|
||||
@@ -95,7 +96,7 @@ where
|
||||
events_out: SmallVec<[HandlerEvent<Id, E>; 4]>,
|
||||
|
||||
/// Queue of outbound substreams to open.
|
||||
dial_queue: SmallVec<[(Id, OutboundRequest<E>); 4]>,
|
||||
dial_queue: SmallVec<[(Id, RequestType<E>); 4]>,
|
||||
|
||||
/// Current number of concurrent outbound substreams being opened.
|
||||
dial_negotiated: u32,
|
||||
@@ -159,7 +160,7 @@ struct InboundInfo<E: EthSpec> {
|
||||
/// State of the substream.
|
||||
state: InboundState<E>,
|
||||
/// Responses queued for sending.
|
||||
pending_items: VecDeque<RPCCodedResponse<E>>,
|
||||
pending_items: VecDeque<RpcResponse<E>>,
|
||||
/// Protocol of the original request we received from the peer.
|
||||
protocol: Protocol,
|
||||
/// Responses that the peer is still expecting from us.
|
||||
@@ -205,7 +206,7 @@ pub enum OutboundSubstreamState<E: EthSpec> {
|
||||
/// The framed negotiated substream.
|
||||
substream: Box<OutboundFramed<Stream, E>>,
|
||||
/// Keeps track of the actual request sent.
|
||||
request: OutboundRequest<E>,
|
||||
request: RequestType<E>,
|
||||
},
|
||||
/// Closing an outbound substream>
|
||||
Closing(Box<OutboundFramed<Stream, E>>),
|
||||
@@ -263,7 +264,7 @@ where
|
||||
|
||||
// Queue our goodbye message.
|
||||
if let Some((id, reason)) = goodbye_reason {
|
||||
self.dial_queue.push((id, OutboundRequest::Goodbye(reason)));
|
||||
self.dial_queue.push((id, RequestType::Goodbye(reason)));
|
||||
}
|
||||
|
||||
self.state = HandlerState::ShuttingDown(Box::pin(sleep(Duration::from_secs(
|
||||
@@ -273,7 +274,7 @@ where
|
||||
}
|
||||
|
||||
/// Opens an outbound substream with a request.
|
||||
fn send_request(&mut self, id: Id, req: OutboundRequest<E>) {
|
||||
fn send_request(&mut self, id: Id, req: RequestType<E>) {
|
||||
match self.state {
|
||||
HandlerState::Active => {
|
||||
self.dial_queue.push((id, req));
|
||||
@@ -291,10 +292,10 @@ where
|
||||
/// Sends a response to a peer's request.
|
||||
// NOTE: If the substream has closed due to inactivity, or the substream is in the
|
||||
// wrong state a response will fail silently.
|
||||
fn send_response(&mut self, inbound_id: SubstreamId, response: RPCCodedResponse<E>) {
|
||||
fn send_response(&mut self, inbound_id: SubstreamId, response: RpcResponse<E>) {
|
||||
// check if the stream matching the response still exists
|
||||
let Some(inbound_info) = self.inbound_substreams.get_mut(&inbound_id) else {
|
||||
if !matches!(response, RPCCodedResponse::StreamTermination(..)) {
|
||||
if !matches!(response, RpcResponse::StreamTermination(..)) {
|
||||
// the stream is closed after sending the expected number of responses
|
||||
trace!(self.log, "Inbound stream has expired. Response not sent";
|
||||
"response" => %response, "id" => inbound_id);
|
||||
@@ -302,7 +303,7 @@ where
|
||||
return;
|
||||
};
|
||||
// If the response we are sending is an error, report back for handling
|
||||
if let RPCCodedResponse::Error(ref code, ref reason) = response {
|
||||
if let RpcResponse::Error(ref code, ref reason) = response {
|
||||
self.events_out.push(HandlerEvent::Err(HandlerErr::Inbound {
|
||||
error: RPCError::ErrorResponse(*code, reason.to_string()),
|
||||
proto: inbound_info.protocol,
|
||||
@@ -329,7 +330,7 @@ where
|
||||
type ToBehaviour = HandlerEvent<Id, E>;
|
||||
type InboundProtocol = RPCProtocol<E>;
|
||||
type OutboundProtocol = OutboundRequestContainer<E>;
|
||||
type OutboundOpenInfo = (Id, OutboundRequest<E>); // Keep track of the id and the request
|
||||
type OutboundOpenInfo = (Id, RequestType<E>); // Keep track of the id and the request
|
||||
type InboundOpenInfo = ();
|
||||
|
||||
fn listen_protocol(&self) -> SubstreamProtocol<Self::InboundProtocol, ()> {
|
||||
@@ -403,8 +404,8 @@ where
|
||||
|
||||
if info.pending_items.back().map(|l| l.close_after()) == Some(false) {
|
||||
// if the last chunk does not close the stream, append an error
|
||||
info.pending_items.push_back(RPCCodedResponse::Error(
|
||||
RPCResponseErrorCode::ServerError,
|
||||
info.pending_items.push_back(RpcResponse::Error(
|
||||
RpcErrorResponse::ServerError,
|
||||
"Request timed out".into(),
|
||||
));
|
||||
}
|
||||
@@ -672,13 +673,13 @@ where
|
||||
let proto = entry.get().proto;
|
||||
|
||||
let received = match response {
|
||||
RPCCodedResponse::StreamTermination(t) => {
|
||||
RpcResponse::StreamTermination(t) => {
|
||||
HandlerEvent::Ok(RPCReceived::EndOfStream(id, t))
|
||||
}
|
||||
RPCCodedResponse::Success(resp) => {
|
||||
RpcResponse::Success(resp) => {
|
||||
HandlerEvent::Ok(RPCReceived::Response(id, resp))
|
||||
}
|
||||
RPCCodedResponse::Error(ref code, ref r) => {
|
||||
RpcResponse::Error(ref code, ref r) => {
|
||||
HandlerEvent::Err(HandlerErr::Outbound {
|
||||
id,
|
||||
proto,
|
||||
@@ -888,21 +889,23 @@ where
|
||||
}
|
||||
|
||||
// If we received a goodbye, shutdown the connection.
|
||||
if let InboundRequest::Goodbye(_) = req {
|
||||
if let RequestType::Goodbye(_) = req {
|
||||
self.shutdown(None);
|
||||
}
|
||||
|
||||
self.events_out.push(HandlerEvent::Ok(RPCReceived::Request(
|
||||
self.current_inbound_substream_id,
|
||||
req,
|
||||
)));
|
||||
self.events_out
|
||||
.push(HandlerEvent::Ok(RPCReceived::Request(Request {
|
||||
id: RequestId::next(),
|
||||
substream_id: self.current_inbound_substream_id,
|
||||
r#type: req,
|
||||
})));
|
||||
self.current_inbound_substream_id.0 += 1;
|
||||
}
|
||||
|
||||
fn on_fully_negotiated_outbound(
|
||||
&mut self,
|
||||
substream: OutboundFramed<Stream, E>,
|
||||
(id, request): (Id, OutboundRequest<E>),
|
||||
(id, request): (Id, RequestType<E>),
|
||||
) {
|
||||
self.dial_negotiated -= 1;
|
||||
// Reset any io-retries counter.
|
||||
@@ -958,7 +961,7 @@ where
|
||||
}
|
||||
fn on_dial_upgrade_error(
|
||||
&mut self,
|
||||
request_info: (Id, OutboundRequest<E>),
|
||||
request_info: (Id, RequestType<E>),
|
||||
error: StreamUpgradeError<RPCError>,
|
||||
) {
|
||||
let (id, req) = request_info;
|
||||
@@ -1016,15 +1019,15 @@ impl slog::Value for SubstreamId {
|
||||
/// error that occurred with sending a message is reported also.
|
||||
async fn send_message_to_inbound_substream<E: EthSpec>(
|
||||
mut substream: InboundSubstream<E>,
|
||||
message: RPCCodedResponse<E>,
|
||||
message: RpcResponse<E>,
|
||||
last_chunk: bool,
|
||||
) -> Result<(InboundSubstream<E>, bool), RPCError> {
|
||||
if matches!(message, RPCCodedResponse::StreamTermination(_)) {
|
||||
if matches!(message, RpcResponse::StreamTermination(_)) {
|
||||
substream.close().await.map(|_| (substream, true))
|
||||
} else {
|
||||
// chunks that are not stream terminations get sent, and the stream is closed if
|
||||
// the response is an error
|
||||
let is_error = matches!(message, RPCCodedResponse::Error(..));
|
||||
let is_error = matches!(message, RpcResponse::Error(..));
|
||||
|
||||
let send_result = substream.send(message).await;
|
||||
|
||||
|
||||
@@ -481,7 +481,7 @@ impl DataColumnsByRootRequest {
|
||||
// Collection of enums and structs used by the Codecs to encode/decode RPC messages
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum RPCResponse<E: EthSpec> {
|
||||
pub enum RpcSuccessResponse<E: EthSpec> {
|
||||
/// A HELLO message.
|
||||
Status(StatusMessage),
|
||||
|
||||
@@ -545,11 +545,11 @@ pub enum ResponseTermination {
|
||||
/// The structured response containing a result/code indicating success or failure
|
||||
/// and the contents of the response
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum RPCCodedResponse<E: EthSpec> {
|
||||
pub enum RpcResponse<E: EthSpec> {
|
||||
/// The response is a successful.
|
||||
Success(RPCResponse<E>),
|
||||
Success(RpcSuccessResponse<E>),
|
||||
|
||||
Error(RPCResponseErrorCode, ErrorType),
|
||||
Error(RpcErrorResponse, ErrorType),
|
||||
|
||||
/// Received a stream termination indicating which response is being terminated.
|
||||
StreamTermination(ResponseTermination),
|
||||
@@ -564,7 +564,7 @@ pub struct LightClientBootstrapRequest {
|
||||
/// The code assigned to an erroneous `RPCResponse`.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, IntoStaticStr)]
|
||||
#[strum(serialize_all = "snake_case")]
|
||||
pub enum RPCResponseErrorCode {
|
||||
pub enum RpcErrorResponse {
|
||||
RateLimited,
|
||||
BlobsNotFoundForBlock,
|
||||
InvalidRequest,
|
||||
@@ -574,13 +574,13 @@ pub enum RPCResponseErrorCode {
|
||||
Unknown,
|
||||
}
|
||||
|
||||
impl<E: EthSpec> RPCCodedResponse<E> {
|
||||
impl<E: EthSpec> RpcResponse<E> {
|
||||
/// Used to encode the response in the codec.
|
||||
pub fn as_u8(&self) -> Option<u8> {
|
||||
match self {
|
||||
RPCCodedResponse::Success(_) => Some(0),
|
||||
RPCCodedResponse::Error(code, _) => Some(code.as_u8()),
|
||||
RPCCodedResponse::StreamTermination(_) => None,
|
||||
RpcResponse::Success(_) => Some(0),
|
||||
RpcResponse::Error(code, _) => Some(code.as_u8()),
|
||||
RpcResponse::StreamTermination(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -592,64 +592,66 @@ impl<E: EthSpec> RPCCodedResponse<E> {
|
||||
/// Builds an RPCCodedResponse from a response code and an ErrorMessage
|
||||
pub fn from_error(response_code: u8, err: ErrorType) -> Self {
|
||||
let code = match response_code {
|
||||
1 => RPCResponseErrorCode::InvalidRequest,
|
||||
2 => RPCResponseErrorCode::ServerError,
|
||||
3 => RPCResponseErrorCode::ResourceUnavailable,
|
||||
139 => RPCResponseErrorCode::RateLimited,
|
||||
140 => RPCResponseErrorCode::BlobsNotFoundForBlock,
|
||||
_ => RPCResponseErrorCode::Unknown,
|
||||
1 => RpcErrorResponse::InvalidRequest,
|
||||
2 => RpcErrorResponse::ServerError,
|
||||
3 => RpcErrorResponse::ResourceUnavailable,
|
||||
139 => RpcErrorResponse::RateLimited,
|
||||
140 => RpcErrorResponse::BlobsNotFoundForBlock,
|
||||
_ => RpcErrorResponse::Unknown,
|
||||
};
|
||||
RPCCodedResponse::Error(code, err)
|
||||
RpcResponse::Error(code, err)
|
||||
}
|
||||
|
||||
/// Returns true if this response always terminates the stream.
|
||||
pub fn close_after(&self) -> bool {
|
||||
!matches!(self, RPCCodedResponse::Success(_))
|
||||
!matches!(self, RpcResponse::Success(_))
|
||||
}
|
||||
}
|
||||
|
||||
impl RPCResponseErrorCode {
|
||||
impl RpcErrorResponse {
|
||||
fn as_u8(&self) -> u8 {
|
||||
match self {
|
||||
RPCResponseErrorCode::InvalidRequest => 1,
|
||||
RPCResponseErrorCode::ServerError => 2,
|
||||
RPCResponseErrorCode::ResourceUnavailable => 3,
|
||||
RPCResponseErrorCode::Unknown => 255,
|
||||
RPCResponseErrorCode::RateLimited => 139,
|
||||
RPCResponseErrorCode::BlobsNotFoundForBlock => 140,
|
||||
RpcErrorResponse::InvalidRequest => 1,
|
||||
RpcErrorResponse::ServerError => 2,
|
||||
RpcErrorResponse::ResourceUnavailable => 3,
|
||||
RpcErrorResponse::Unknown => 255,
|
||||
RpcErrorResponse::RateLimited => 139,
|
||||
RpcErrorResponse::BlobsNotFoundForBlock => 140,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
use super::Protocol;
|
||||
impl<E: EthSpec> RPCResponse<E> {
|
||||
impl<E: EthSpec> RpcSuccessResponse<E> {
|
||||
pub fn protocol(&self) -> Protocol {
|
||||
match self {
|
||||
RPCResponse::Status(_) => Protocol::Status,
|
||||
RPCResponse::BlocksByRange(_) => Protocol::BlocksByRange,
|
||||
RPCResponse::BlocksByRoot(_) => Protocol::BlocksByRoot,
|
||||
RPCResponse::BlobsByRange(_) => Protocol::BlobsByRange,
|
||||
RPCResponse::BlobsByRoot(_) => Protocol::BlobsByRoot,
|
||||
RPCResponse::DataColumnsByRoot(_) => Protocol::DataColumnsByRoot,
|
||||
RPCResponse::DataColumnsByRange(_) => Protocol::DataColumnsByRange,
|
||||
RPCResponse::Pong(_) => Protocol::Ping,
|
||||
RPCResponse::MetaData(_) => Protocol::MetaData,
|
||||
RPCResponse::LightClientBootstrap(_) => Protocol::LightClientBootstrap,
|
||||
RPCResponse::LightClientOptimisticUpdate(_) => Protocol::LightClientOptimisticUpdate,
|
||||
RPCResponse::LightClientFinalityUpdate(_) => Protocol::LightClientFinalityUpdate,
|
||||
RpcSuccessResponse::Status(_) => Protocol::Status,
|
||||
RpcSuccessResponse::BlocksByRange(_) => Protocol::BlocksByRange,
|
||||
RpcSuccessResponse::BlocksByRoot(_) => Protocol::BlocksByRoot,
|
||||
RpcSuccessResponse::BlobsByRange(_) => Protocol::BlobsByRange,
|
||||
RpcSuccessResponse::BlobsByRoot(_) => Protocol::BlobsByRoot,
|
||||
RpcSuccessResponse::DataColumnsByRoot(_) => Protocol::DataColumnsByRoot,
|
||||
RpcSuccessResponse::DataColumnsByRange(_) => Protocol::DataColumnsByRange,
|
||||
RpcSuccessResponse::Pong(_) => Protocol::Ping,
|
||||
RpcSuccessResponse::MetaData(_) => Protocol::MetaData,
|
||||
RpcSuccessResponse::LightClientBootstrap(_) => Protocol::LightClientBootstrap,
|
||||
RpcSuccessResponse::LightClientOptimisticUpdate(_) => {
|
||||
Protocol::LightClientOptimisticUpdate
|
||||
}
|
||||
RpcSuccessResponse::LightClientFinalityUpdate(_) => Protocol::LightClientFinalityUpdate,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for RPCResponseErrorCode {
|
||||
impl std::fmt::Display for RpcErrorResponse {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let repr = match self {
|
||||
RPCResponseErrorCode::InvalidRequest => "The request was invalid",
|
||||
RPCResponseErrorCode::ResourceUnavailable => "Resource unavailable",
|
||||
RPCResponseErrorCode::ServerError => "Server error occurred",
|
||||
RPCResponseErrorCode::Unknown => "Unknown error occurred",
|
||||
RPCResponseErrorCode::RateLimited => "Rate limited",
|
||||
RPCResponseErrorCode::BlobsNotFoundForBlock => "No blobs for the given root",
|
||||
RpcErrorResponse::InvalidRequest => "The request was invalid",
|
||||
RpcErrorResponse::ResourceUnavailable => "Resource unavailable",
|
||||
RpcErrorResponse::ServerError => "Server error occurred",
|
||||
RpcErrorResponse::Unknown => "Unknown error occurred",
|
||||
RpcErrorResponse::RateLimited => "Rate limited",
|
||||
RpcErrorResponse::BlobsNotFoundForBlock => "No blobs for the given root",
|
||||
};
|
||||
f.write_str(repr)
|
||||
}
|
||||
@@ -661,45 +663,47 @@ impl std::fmt::Display for StatusMessage {
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: EthSpec> std::fmt::Display for RPCResponse<E> {
|
||||
impl<E: EthSpec> std::fmt::Display for RpcSuccessResponse<E> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
RPCResponse::Status(status) => write!(f, "{}", status),
|
||||
RPCResponse::BlocksByRange(block) => {
|
||||
RpcSuccessResponse::Status(status) => write!(f, "{}", status),
|
||||
RpcSuccessResponse::BlocksByRange(block) => {
|
||||
write!(f, "BlocksByRange: Block slot: {}", block.slot())
|
||||
}
|
||||
RPCResponse::BlocksByRoot(block) => {
|
||||
RpcSuccessResponse::BlocksByRoot(block) => {
|
||||
write!(f, "BlocksByRoot: Block slot: {}", block.slot())
|
||||
}
|
||||
RPCResponse::BlobsByRange(blob) => {
|
||||
RpcSuccessResponse::BlobsByRange(blob) => {
|
||||
write!(f, "BlobsByRange: Blob slot: {}", blob.slot())
|
||||
}
|
||||
RPCResponse::BlobsByRoot(sidecar) => {
|
||||
RpcSuccessResponse::BlobsByRoot(sidecar) => {
|
||||
write!(f, "BlobsByRoot: Blob slot: {}", sidecar.slot())
|
||||
}
|
||||
RPCResponse::DataColumnsByRoot(sidecar) => {
|
||||
RpcSuccessResponse::DataColumnsByRoot(sidecar) => {
|
||||
write!(f, "DataColumnsByRoot: Data column slot: {}", sidecar.slot())
|
||||
}
|
||||
RPCResponse::DataColumnsByRange(sidecar) => {
|
||||
RpcSuccessResponse::DataColumnsByRange(sidecar) => {
|
||||
write!(
|
||||
f,
|
||||
"DataColumnsByRange: Data column slot: {}",
|
||||
sidecar.slot()
|
||||
)
|
||||
}
|
||||
RPCResponse::Pong(ping) => write!(f, "Pong: {}", ping.data),
|
||||
RPCResponse::MetaData(metadata) => write!(f, "Metadata: {}", metadata.seq_number()),
|
||||
RPCResponse::LightClientBootstrap(bootstrap) => {
|
||||
RpcSuccessResponse::Pong(ping) => write!(f, "Pong: {}", ping.data),
|
||||
RpcSuccessResponse::MetaData(metadata) => {
|
||||
write!(f, "Metadata: {}", metadata.seq_number())
|
||||
}
|
||||
RpcSuccessResponse::LightClientBootstrap(bootstrap) => {
|
||||
write!(f, "LightClientBootstrap Slot: {}", bootstrap.get_slot())
|
||||
}
|
||||
RPCResponse::LightClientOptimisticUpdate(update) => {
|
||||
RpcSuccessResponse::LightClientOptimisticUpdate(update) => {
|
||||
write!(
|
||||
f,
|
||||
"LightClientOptimisticUpdate Slot: {}",
|
||||
update.signature_slot()
|
||||
)
|
||||
}
|
||||
RPCResponse::LightClientFinalityUpdate(update) => {
|
||||
RpcSuccessResponse::LightClientFinalityUpdate(update) => {
|
||||
write!(
|
||||
f,
|
||||
"LightClientFinalityUpdate Slot: {}",
|
||||
@@ -710,12 +714,12 @@ impl<E: EthSpec> std::fmt::Display for RPCResponse<E> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: EthSpec> std::fmt::Display for RPCCodedResponse<E> {
|
||||
impl<E: EthSpec> std::fmt::Display for RpcResponse<E> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
RPCCodedResponse::Success(res) => write!(f, "{}", res),
|
||||
RPCCodedResponse::Error(code, err) => write!(f, "{}: {}", code, err),
|
||||
RPCCodedResponse::StreamTermination(_) => write!(f, "Stream Termination"),
|
||||
RpcResponse::Success(res) => write!(f, "{}", res),
|
||||
RpcResponse::Error(code, err) => write!(f, "{}: {}", code, err),
|
||||
RpcResponse::StreamTermination(_) => write!(f, "Stream Termination"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ use libp2p::PeerId;
|
||||
use rate_limiter::{RPCRateLimiter as RateLimiter, RateLimitedErr};
|
||||
use slog::{crit, debug, o, trace};
|
||||
use std::marker::PhantomData;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::task::{Context, Poll};
|
||||
use std::time::Duration;
|
||||
@@ -23,16 +24,15 @@ use types::{EthSpec, ForkContext};
|
||||
|
||||
pub(crate) use handler::{HandlerErr, HandlerEvent};
|
||||
pub(crate) use methods::{
|
||||
MetaData, MetaDataV1, MetaDataV2, MetaDataV3, Ping, RPCCodedResponse, RPCResponse,
|
||||
MetaData, MetaDataV1, MetaDataV2, MetaDataV3, Ping, RpcResponse, RpcSuccessResponse,
|
||||
};
|
||||
pub(crate) use protocol::InboundRequest;
|
||||
pub use protocol::RequestType;
|
||||
|
||||
pub use handler::SubstreamId;
|
||||
pub use methods::{
|
||||
BlocksByRangeRequest, BlocksByRootRequest, GoodbyeReason, LightClientBootstrapRequest,
|
||||
RPCResponseErrorCode, ResponseTermination, StatusMessage,
|
||||
ResponseTermination, RpcErrorResponse, StatusMessage,
|
||||
};
|
||||
pub(crate) use outbound::OutboundRequest;
|
||||
pub use protocol::{max_rpc_size, Protocol, RPCError};
|
||||
|
||||
use self::config::{InboundRateLimiterConfig, OutboundRateLimiterConfig};
|
||||
@@ -48,6 +48,8 @@ mod protocol;
|
||||
mod rate_limiter;
|
||||
mod self_limiter;
|
||||
|
||||
static NEXT_REQUEST_ID: AtomicUsize = AtomicUsize::new(1);
|
||||
|
||||
/// Composite trait for a request id.
|
||||
pub trait ReqId: Send + 'static + std::fmt::Debug + Copy + Clone {}
|
||||
impl<T> ReqId for T where T: Send + 'static + std::fmt::Debug + Copy + Clone {}
|
||||
@@ -59,13 +61,13 @@ pub enum RPCSend<Id, E: EthSpec> {
|
||||
///
|
||||
/// The `Id` is given by the application making the request. These
|
||||
/// go over *outbound* connections.
|
||||
Request(Id, OutboundRequest<E>),
|
||||
Request(Id, RequestType<E>),
|
||||
/// A response sent from Lighthouse.
|
||||
///
|
||||
/// The `SubstreamId` must correspond to the RPC-given ID of the original request received from the
|
||||
/// peer. The second parameter is a single chunk of a response. These go over *inbound*
|
||||
/// connections.
|
||||
Response(SubstreamId, RPCCodedResponse<E>),
|
||||
Response(SubstreamId, RpcResponse<E>),
|
||||
/// Lighthouse has requested to terminate the connection with a goodbye message.
|
||||
Shutdown(Id, GoodbyeReason),
|
||||
}
|
||||
@@ -77,17 +79,46 @@ pub enum RPCReceived<Id, E: EthSpec> {
|
||||
///
|
||||
/// The `SubstreamId` is given by the `RPCHandler` as it identifies this request with the
|
||||
/// *inbound* substream over which it is managed.
|
||||
Request(SubstreamId, InboundRequest<E>),
|
||||
Request(Request<E>),
|
||||
/// A response received from the outside.
|
||||
///
|
||||
/// The `Id` corresponds to the application given ID of the original request sent to the
|
||||
/// peer. The second parameter is a single chunk of a response. These go over *outbound*
|
||||
/// connections.
|
||||
Response(Id, RPCResponse<E>),
|
||||
Response(Id, RpcSuccessResponse<E>),
|
||||
/// Marks a request as completed
|
||||
EndOfStream(Id, ResponseTermination),
|
||||
}
|
||||
|
||||
/// Rpc `Request` identifier.
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct RequestId(usize);
|
||||
|
||||
impl RequestId {
|
||||
/// Returns the next available [`RequestId`].
|
||||
pub fn next() -> Self {
|
||||
Self(NEXT_REQUEST_ID.fetch_add(1, Ordering::SeqCst))
|
||||
}
|
||||
|
||||
/// Creates an _unchecked_ [`RequestId`].
|
||||
///
|
||||
/// [`Rpc`] enforces that [`RequestId`]s are unique and not reused.
|
||||
/// This constructor does not, hence the _unchecked_.
|
||||
///
|
||||
/// It is primarily meant for allowing manual tests.
|
||||
pub fn new_unchecked(id: usize) -> Self {
|
||||
Self(id)
|
||||
}
|
||||
}
|
||||
|
||||
/// An Rpc Request.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Request<E: EthSpec> {
|
||||
pub id: RequestId,
|
||||
pub substream_id: SubstreamId,
|
||||
pub r#type: RequestType<E>,
|
||||
}
|
||||
|
||||
impl<E: EthSpec, Id: std::fmt::Debug> std::fmt::Display for RPCSend<Id, E> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
@@ -177,7 +208,8 @@ impl<Id: ReqId, E: EthSpec> RPC<Id, E> {
|
||||
&mut self,
|
||||
peer_id: PeerId,
|
||||
id: (ConnectionId, SubstreamId),
|
||||
event: RPCCodedResponse<E>,
|
||||
_request_id: RequestId,
|
||||
event: RpcResponse<E>,
|
||||
) {
|
||||
self.events.push(ToSwarm::NotifyHandler {
|
||||
peer_id,
|
||||
@@ -189,7 +221,7 @@ impl<Id: ReqId, E: EthSpec> RPC<Id, E> {
|
||||
/// Submits an RPC request.
|
||||
///
|
||||
/// The peer must be connected for this to succeed.
|
||||
pub fn send_request(&mut self, peer_id: PeerId, request_id: Id, req: OutboundRequest<E>) {
|
||||
pub fn send_request(&mut self, peer_id: PeerId, request_id: Id, req: RequestType<E>) {
|
||||
let event = if let Some(self_limiter) = self.self_limiter.as_mut() {
|
||||
match self_limiter.allows(peer_id, request_id, req) {
|
||||
Ok(event) => event,
|
||||
@@ -229,7 +261,7 @@ impl<Id: ReqId, E: EthSpec> RPC<Id, E> {
|
||||
data: self.seq_number,
|
||||
};
|
||||
trace!(self.log, "Sending Ping"; "peer_id" => %peer_id);
|
||||
self.send_request(peer_id, id, OutboundRequest::Ping(ping));
|
||||
self.send_request(peer_id, id, RequestType::Ping(ping));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -368,13 +400,17 @@ where
|
||||
event: <Self::ConnectionHandler as ConnectionHandler>::ToBehaviour,
|
||||
) {
|
||||
match event {
|
||||
HandlerEvent::Ok(RPCReceived::Request(id, req)) => {
|
||||
HandlerEvent::Ok(RPCReceived::Request(Request {
|
||||
id,
|
||||
substream_id,
|
||||
r#type,
|
||||
})) => {
|
||||
if let Some(limiter) = self.limiter.as_mut() {
|
||||
// check if the request is conformant to the quota
|
||||
match limiter.allows(&peer_id, &req) {
|
||||
match limiter.allows(&peer_id, &r#type) {
|
||||
Err(RateLimitedErr::TooLarge) => {
|
||||
// we set the batch sizes, so this is a coding/config err for most protocols
|
||||
let protocol = req.versioned_protocol().protocol();
|
||||
let protocol = r#type.versioned_protocol().protocol();
|
||||
if matches!(
|
||||
protocol,
|
||||
Protocol::BlocksByRange
|
||||
@@ -384,7 +420,7 @@ where
|
||||
| Protocol::BlobsByRoot
|
||||
| Protocol::DataColumnsByRoot
|
||||
) {
|
||||
debug!(self.log, "Request too large to process"; "request" => %req, "protocol" => %protocol);
|
||||
debug!(self.log, "Request too large to process"; "request" => %r#type, "protocol" => %protocol);
|
||||
} else {
|
||||
// Other protocols shouldn't be sending large messages, we should flag the peer kind
|
||||
crit!(self.log, "Request size too large to ever be processed"; "protocol" => %protocol);
|
||||
@@ -393,9 +429,10 @@ where
|
||||
// the handler upon receiving the error code will send it back to the behaviour
|
||||
self.send_response(
|
||||
peer_id,
|
||||
(conn_id, id),
|
||||
RPCCodedResponse::Error(
|
||||
RPCResponseErrorCode::RateLimited,
|
||||
(conn_id, substream_id),
|
||||
id,
|
||||
RpcResponse::Error(
|
||||
RpcErrorResponse::RateLimited,
|
||||
"Rate limited. Request too large".into(),
|
||||
),
|
||||
);
|
||||
@@ -403,30 +440,33 @@ where
|
||||
}
|
||||
Err(RateLimitedErr::TooSoon(wait_time)) => {
|
||||
debug!(self.log, "Request exceeds the rate limit";
|
||||
"request" => %req, "peer_id" => %peer_id, "wait_time_ms" => wait_time.as_millis());
|
||||
"request" => %r#type, "peer_id" => %peer_id, "wait_time_ms" => wait_time.as_millis());
|
||||
// send an error code to the peer.
|
||||
// the handler upon receiving the error code will send it back to the behaviour
|
||||
self.send_response(
|
||||
peer_id,
|
||||
(conn_id, id),
|
||||
RPCCodedResponse::Error(
|
||||
RPCResponseErrorCode::RateLimited,
|
||||
(conn_id, substream_id),
|
||||
id,
|
||||
RpcResponse::Error(
|
||||
RpcErrorResponse::RateLimited,
|
||||
format!("Wait {:?}", wait_time).into(),
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
// No rate limiting, continue.
|
||||
Ok(_) => {}
|
||||
Ok(()) => {}
|
||||
}
|
||||
}
|
||||
|
||||
// If we received a Ping, we queue a Pong response.
|
||||
if let InboundRequest::Ping(_) = req {
|
||||
if let RequestType::Ping(_) = r#type {
|
||||
trace!(self.log, "Received Ping, queueing Pong";"connection_id" => %conn_id, "peer_id" => %peer_id);
|
||||
self.send_response(
|
||||
peer_id,
|
||||
(conn_id, id),
|
||||
RPCCodedResponse::Success(RPCResponse::Pong(Ping {
|
||||
(conn_id, substream_id),
|
||||
id,
|
||||
RpcResponse::Success(RpcSuccessResponse::Pong(Ping {
|
||||
data: self.seq_number,
|
||||
})),
|
||||
);
|
||||
@@ -435,7 +475,11 @@ where
|
||||
self.events.push(ToSwarm::GenerateEvent(RPCMessage {
|
||||
peer_id,
|
||||
conn_id,
|
||||
message: Ok(RPCReceived::Request(id, req)),
|
||||
message: Ok(RPCReceived::Request(Request {
|
||||
id,
|
||||
substream_id,
|
||||
r#type,
|
||||
})),
|
||||
}));
|
||||
}
|
||||
HandlerEvent::Ok(rpc) => {
|
||||
@@ -496,8 +540,8 @@ where
|
||||
match &self.message {
|
||||
Ok(received) => {
|
||||
let (msg_kind, protocol) = match received {
|
||||
RPCReceived::Request(_, req) => {
|
||||
("request", req.versioned_protocol().protocol())
|
||||
RPCReceived::Request(Request { r#type, .. }) => {
|
||||
("request", r#type.versioned_protocol().protocol())
|
||||
}
|
||||
RPCReceived::Response(_, res) => ("response", res.protocol()),
|
||||
RPCReceived::EndOfStream(_, end) => (
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use super::methods::*;
|
||||
use super::protocol::ProtocolId;
|
||||
use super::protocol::SupportedProtocol;
|
||||
use super::RPCError;
|
||||
use super::RequestType;
|
||||
use crate::rpc::codec::SSZSnappyOutboundCodec;
|
||||
use crate::rpc::protocol::Encoding;
|
||||
use futures::future::BoxFuture;
|
||||
@@ -21,25 +20,11 @@ use types::{EthSpec, ForkContext};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct OutboundRequestContainer<E: EthSpec> {
|
||||
pub req: OutboundRequest<E>,
|
||||
pub req: RequestType<E>,
|
||||
pub fork_context: Arc<ForkContext>,
|
||||
pub max_rpc_size: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum OutboundRequest<E: EthSpec> {
|
||||
Status(StatusMessage),
|
||||
Goodbye(GoodbyeReason),
|
||||
BlocksByRange(OldBlocksByRangeRequest),
|
||||
BlocksByRoot(BlocksByRootRequest),
|
||||
BlobsByRange(BlobsByRangeRequest),
|
||||
BlobsByRoot(BlobsByRootRequest),
|
||||
DataColumnsByRoot(DataColumnsByRootRequest),
|
||||
DataColumnsByRange(DataColumnsByRangeRequest),
|
||||
Ping(Ping),
|
||||
MetaData(MetadataRequest<E>),
|
||||
}
|
||||
|
||||
impl<E: EthSpec> UpgradeInfo for OutboundRequestContainer<E> {
|
||||
type Info = ProtocolId;
|
||||
type InfoIter = Vec<Self::Info>;
|
||||
@@ -50,133 +35,6 @@ impl<E: EthSpec> UpgradeInfo for OutboundRequestContainer<E> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Implements the encoding per supported protocol for `RPCRequest`.
|
||||
impl<E: EthSpec> OutboundRequest<E> {
|
||||
pub fn supported_protocols(&self) -> Vec<ProtocolId> {
|
||||
match self {
|
||||
// add more protocols when versions/encodings are supported
|
||||
OutboundRequest::Status(_) => vec![ProtocolId::new(
|
||||
SupportedProtocol::StatusV1,
|
||||
Encoding::SSZSnappy,
|
||||
)],
|
||||
OutboundRequest::Goodbye(_) => vec![ProtocolId::new(
|
||||
SupportedProtocol::GoodbyeV1,
|
||||
Encoding::SSZSnappy,
|
||||
)],
|
||||
OutboundRequest::BlocksByRange(_) => vec![
|
||||
ProtocolId::new(SupportedProtocol::BlocksByRangeV2, Encoding::SSZSnappy),
|
||||
ProtocolId::new(SupportedProtocol::BlocksByRangeV1, Encoding::SSZSnappy),
|
||||
],
|
||||
OutboundRequest::BlocksByRoot(_) => vec![
|
||||
ProtocolId::new(SupportedProtocol::BlocksByRootV2, Encoding::SSZSnappy),
|
||||
ProtocolId::new(SupportedProtocol::BlocksByRootV1, Encoding::SSZSnappy),
|
||||
],
|
||||
OutboundRequest::BlobsByRange(_) => vec![ProtocolId::new(
|
||||
SupportedProtocol::BlobsByRangeV1,
|
||||
Encoding::SSZSnappy,
|
||||
)],
|
||||
OutboundRequest::BlobsByRoot(_) => vec![ProtocolId::new(
|
||||
SupportedProtocol::BlobsByRootV1,
|
||||
Encoding::SSZSnappy,
|
||||
)],
|
||||
OutboundRequest::DataColumnsByRoot(_) => vec![ProtocolId::new(
|
||||
SupportedProtocol::DataColumnsByRootV1,
|
||||
Encoding::SSZSnappy,
|
||||
)],
|
||||
OutboundRequest::DataColumnsByRange(_) => vec![ProtocolId::new(
|
||||
SupportedProtocol::DataColumnsByRangeV1,
|
||||
Encoding::SSZSnappy,
|
||||
)],
|
||||
OutboundRequest::Ping(_) => vec![ProtocolId::new(
|
||||
SupportedProtocol::PingV1,
|
||||
Encoding::SSZSnappy,
|
||||
)],
|
||||
OutboundRequest::MetaData(_) => vec![
|
||||
ProtocolId::new(SupportedProtocol::MetaDataV3, Encoding::SSZSnappy),
|
||||
ProtocolId::new(SupportedProtocol::MetaDataV2, Encoding::SSZSnappy),
|
||||
ProtocolId::new(SupportedProtocol::MetaDataV1, Encoding::SSZSnappy),
|
||||
],
|
||||
}
|
||||
}
|
||||
/* These functions are used in the handler for stream management */
|
||||
|
||||
/// Maximum number of responses expected for this request.
|
||||
pub fn max_responses(&self) -> u64 {
|
||||
match self {
|
||||
OutboundRequest::Status(_) => 1,
|
||||
OutboundRequest::Goodbye(_) => 0,
|
||||
OutboundRequest::BlocksByRange(req) => *req.count(),
|
||||
OutboundRequest::BlocksByRoot(req) => req.block_roots().len() as u64,
|
||||
OutboundRequest::BlobsByRange(req) => req.max_blobs_requested::<E>(),
|
||||
OutboundRequest::BlobsByRoot(req) => req.blob_ids.len() as u64,
|
||||
OutboundRequest::DataColumnsByRoot(req) => req.data_column_ids.len() as u64,
|
||||
OutboundRequest::DataColumnsByRange(req) => req.max_requested::<E>(),
|
||||
OutboundRequest::Ping(_) => 1,
|
||||
OutboundRequest::MetaData(_) => 1,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expect_exactly_one_response(&self) -> bool {
|
||||
match self {
|
||||
OutboundRequest::Status(_) => true,
|
||||
OutboundRequest::Goodbye(_) => false,
|
||||
OutboundRequest::BlocksByRange(_) => false,
|
||||
OutboundRequest::BlocksByRoot(_) => false,
|
||||
OutboundRequest::BlobsByRange(_) => false,
|
||||
OutboundRequest::BlobsByRoot(_) => false,
|
||||
OutboundRequest::DataColumnsByRoot(_) => false,
|
||||
OutboundRequest::DataColumnsByRange(_) => false,
|
||||
OutboundRequest::Ping(_) => true,
|
||||
OutboundRequest::MetaData(_) => true,
|
||||
}
|
||||
}
|
||||
|
||||
/// Gives the corresponding `SupportedProtocol` to this request.
|
||||
pub fn versioned_protocol(&self) -> SupportedProtocol {
|
||||
match self {
|
||||
OutboundRequest::Status(_) => SupportedProtocol::StatusV1,
|
||||
OutboundRequest::Goodbye(_) => SupportedProtocol::GoodbyeV1,
|
||||
OutboundRequest::BlocksByRange(req) => match req {
|
||||
OldBlocksByRangeRequest::V1(_) => SupportedProtocol::BlocksByRangeV1,
|
||||
OldBlocksByRangeRequest::V2(_) => SupportedProtocol::BlocksByRangeV2,
|
||||
},
|
||||
OutboundRequest::BlocksByRoot(req) => match req {
|
||||
BlocksByRootRequest::V1(_) => SupportedProtocol::BlocksByRootV1,
|
||||
BlocksByRootRequest::V2(_) => SupportedProtocol::BlocksByRootV2,
|
||||
},
|
||||
OutboundRequest::BlobsByRange(_) => SupportedProtocol::BlobsByRangeV1,
|
||||
OutboundRequest::BlobsByRoot(_) => SupportedProtocol::BlobsByRootV1,
|
||||
OutboundRequest::DataColumnsByRoot(_) => SupportedProtocol::DataColumnsByRootV1,
|
||||
OutboundRequest::DataColumnsByRange(_) => SupportedProtocol::DataColumnsByRangeV1,
|
||||
OutboundRequest::Ping(_) => SupportedProtocol::PingV1,
|
||||
OutboundRequest::MetaData(req) => match req {
|
||||
MetadataRequest::V1(_) => SupportedProtocol::MetaDataV1,
|
||||
MetadataRequest::V2(_) => SupportedProtocol::MetaDataV2,
|
||||
MetadataRequest::V3(_) => SupportedProtocol::MetaDataV3,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the `ResponseTermination` type associated with the request if a stream gets
|
||||
/// terminated.
|
||||
pub fn stream_termination(&self) -> ResponseTermination {
|
||||
match self {
|
||||
// this only gets called after `multiple_responses()` returns true. Therefore, only
|
||||
// variants that have `multiple_responses()` can have values.
|
||||
OutboundRequest::BlocksByRange(_) => ResponseTermination::BlocksByRange,
|
||||
OutboundRequest::BlocksByRoot(_) => ResponseTermination::BlocksByRoot,
|
||||
OutboundRequest::BlobsByRange(_) => ResponseTermination::BlobsByRange,
|
||||
OutboundRequest::BlobsByRoot(_) => ResponseTermination::BlobsByRoot,
|
||||
OutboundRequest::DataColumnsByRoot(_) => ResponseTermination::DataColumnsByRoot,
|
||||
OutboundRequest::DataColumnsByRange(_) => ResponseTermination::DataColumnsByRange,
|
||||
OutboundRequest::Status(_) => unreachable!(),
|
||||
OutboundRequest::Goodbye(_) => unreachable!(),
|
||||
OutboundRequest::Ping(_) => unreachable!(),
|
||||
OutboundRequest::MetaData(_) => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* RPC Response type - used for outbound upgrades */
|
||||
|
||||
/* Outbound upgrades */
|
||||
@@ -211,22 +69,3 @@ where
|
||||
.boxed()
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: EthSpec> std::fmt::Display for OutboundRequest<E> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
OutboundRequest::Status(status) => write!(f, "Status Message: {}", status),
|
||||
OutboundRequest::Goodbye(reason) => write!(f, "Goodbye: {}", reason),
|
||||
OutboundRequest::BlocksByRange(req) => write!(f, "Blocks by range: {}", req),
|
||||
OutboundRequest::BlocksByRoot(req) => write!(f, "Blocks by root: {:?}", req),
|
||||
OutboundRequest::BlobsByRange(req) => write!(f, "Blobs by range: {:?}", req),
|
||||
OutboundRequest::BlobsByRoot(req) => write!(f, "Blobs by root: {:?}", req),
|
||||
OutboundRequest::DataColumnsByRoot(req) => write!(f, "Data columns by root: {:?}", req),
|
||||
OutboundRequest::DataColumnsByRange(req) => {
|
||||
write!(f, "Data columns by range: {:?}", req)
|
||||
}
|
||||
OutboundRequest::Ping(ping) => write!(f, "Ping: {}", ping.data),
|
||||
OutboundRequest::MetaData(_) => write!(f, "MetaData request"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -645,7 +645,7 @@ pub fn rpc_data_column_limits<E: EthSpec>() -> RpcLimits {
|
||||
// The inbound protocol reads the request, decodes it and returns the stream to the protocol
|
||||
// handler to respond to once ready.
|
||||
|
||||
pub type InboundOutput<TSocket, E> = (InboundRequest<E>, InboundFramed<TSocket, E>);
|
||||
pub type InboundOutput<TSocket, E> = (RequestType<E>, InboundFramed<TSocket, E>);
|
||||
pub type InboundFramed<TSocket, E> =
|
||||
Framed<std::pin::Pin<Box<TimeoutStream<Compat<TSocket>>>>, SSZSnappyInboundCodec<E>>;
|
||||
|
||||
@@ -679,19 +679,19 @@ where
|
||||
// MetaData requests should be empty, return the stream
|
||||
match versioned_protocol {
|
||||
SupportedProtocol::MetaDataV1 => {
|
||||
Ok((InboundRequest::MetaData(MetadataRequest::new_v1()), socket))
|
||||
Ok((RequestType::MetaData(MetadataRequest::new_v1()), socket))
|
||||
}
|
||||
SupportedProtocol::MetaDataV2 => {
|
||||
Ok((InboundRequest::MetaData(MetadataRequest::new_v2()), socket))
|
||||
Ok((RequestType::MetaData(MetadataRequest::new_v2()), socket))
|
||||
}
|
||||
SupportedProtocol::MetaDataV3 => {
|
||||
Ok((InboundRequest::MetaData(MetadataRequest::new_v3()), socket))
|
||||
Ok((RequestType::MetaData(MetadataRequest::new_v3()), socket))
|
||||
}
|
||||
SupportedProtocol::LightClientOptimisticUpdateV1 => {
|
||||
Ok((InboundRequest::LightClientOptimisticUpdate, socket))
|
||||
Ok((RequestType::LightClientOptimisticUpdate, socket))
|
||||
}
|
||||
SupportedProtocol::LightClientFinalityUpdateV1 => {
|
||||
Ok((InboundRequest::LightClientFinalityUpdate, socket))
|
||||
Ok((RequestType::LightClientFinalityUpdate, socket))
|
||||
}
|
||||
_ => {
|
||||
match tokio::time::timeout(
|
||||
@@ -713,7 +713,7 @@ where
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum InboundRequest<E: EthSpec> {
|
||||
pub enum RequestType<E: EthSpec> {
|
||||
Status(StatusMessage),
|
||||
Goodbye(GoodbyeReason),
|
||||
BlocksByRange(OldBlocksByRangeRequest),
|
||||
@@ -730,56 +730,56 @@ pub enum InboundRequest<E: EthSpec> {
|
||||
}
|
||||
|
||||
/// Implements the encoding per supported protocol for `RPCRequest`.
|
||||
impl<E: EthSpec> InboundRequest<E> {
|
||||
impl<E: EthSpec> RequestType<E> {
|
||||
/* These functions are used in the handler for stream management */
|
||||
|
||||
/// Maximum number of responses expected for this request.
|
||||
pub fn max_responses(&self) -> u64 {
|
||||
match self {
|
||||
InboundRequest::Status(_) => 1,
|
||||
InboundRequest::Goodbye(_) => 0,
|
||||
InboundRequest::BlocksByRange(req) => *req.count(),
|
||||
InboundRequest::BlocksByRoot(req) => req.block_roots().len() as u64,
|
||||
InboundRequest::BlobsByRange(req) => req.max_blobs_requested::<E>(),
|
||||
InboundRequest::BlobsByRoot(req) => req.blob_ids.len() as u64,
|
||||
InboundRequest::DataColumnsByRoot(req) => req.data_column_ids.len() as u64,
|
||||
InboundRequest::DataColumnsByRange(req) => req.max_requested::<E>(),
|
||||
InboundRequest::Ping(_) => 1,
|
||||
InboundRequest::MetaData(_) => 1,
|
||||
InboundRequest::LightClientBootstrap(_) => 1,
|
||||
InboundRequest::LightClientOptimisticUpdate => 1,
|
||||
InboundRequest::LightClientFinalityUpdate => 1,
|
||||
RequestType::Status(_) => 1,
|
||||
RequestType::Goodbye(_) => 0,
|
||||
RequestType::BlocksByRange(req) => *req.count(),
|
||||
RequestType::BlocksByRoot(req) => req.block_roots().len() as u64,
|
||||
RequestType::BlobsByRange(req) => req.max_blobs_requested::<E>(),
|
||||
RequestType::BlobsByRoot(req) => req.blob_ids.len() as u64,
|
||||
RequestType::DataColumnsByRoot(req) => req.data_column_ids.len() as u64,
|
||||
RequestType::DataColumnsByRange(req) => req.max_requested::<E>(),
|
||||
RequestType::Ping(_) => 1,
|
||||
RequestType::MetaData(_) => 1,
|
||||
RequestType::LightClientBootstrap(_) => 1,
|
||||
RequestType::LightClientOptimisticUpdate => 1,
|
||||
RequestType::LightClientFinalityUpdate => 1,
|
||||
}
|
||||
}
|
||||
|
||||
/// Gives the corresponding `SupportedProtocol` to this request.
|
||||
pub fn versioned_protocol(&self) -> SupportedProtocol {
|
||||
match self {
|
||||
InboundRequest::Status(_) => SupportedProtocol::StatusV1,
|
||||
InboundRequest::Goodbye(_) => SupportedProtocol::GoodbyeV1,
|
||||
InboundRequest::BlocksByRange(req) => match req {
|
||||
RequestType::Status(_) => SupportedProtocol::StatusV1,
|
||||
RequestType::Goodbye(_) => SupportedProtocol::GoodbyeV1,
|
||||
RequestType::BlocksByRange(req) => match req {
|
||||
OldBlocksByRangeRequest::V1(_) => SupportedProtocol::BlocksByRangeV1,
|
||||
OldBlocksByRangeRequest::V2(_) => SupportedProtocol::BlocksByRangeV2,
|
||||
},
|
||||
InboundRequest::BlocksByRoot(req) => match req {
|
||||
RequestType::BlocksByRoot(req) => match req {
|
||||
BlocksByRootRequest::V1(_) => SupportedProtocol::BlocksByRootV1,
|
||||
BlocksByRootRequest::V2(_) => SupportedProtocol::BlocksByRootV2,
|
||||
},
|
||||
InboundRequest::BlobsByRange(_) => SupportedProtocol::BlobsByRangeV1,
|
||||
InboundRequest::BlobsByRoot(_) => SupportedProtocol::BlobsByRootV1,
|
||||
InboundRequest::DataColumnsByRoot(_) => SupportedProtocol::DataColumnsByRootV1,
|
||||
InboundRequest::DataColumnsByRange(_) => SupportedProtocol::DataColumnsByRangeV1,
|
||||
InboundRequest::Ping(_) => SupportedProtocol::PingV1,
|
||||
InboundRequest::MetaData(req) => match req {
|
||||
RequestType::BlobsByRange(_) => SupportedProtocol::BlobsByRangeV1,
|
||||
RequestType::BlobsByRoot(_) => SupportedProtocol::BlobsByRootV1,
|
||||
RequestType::DataColumnsByRoot(_) => SupportedProtocol::DataColumnsByRootV1,
|
||||
RequestType::DataColumnsByRange(_) => SupportedProtocol::DataColumnsByRangeV1,
|
||||
RequestType::Ping(_) => SupportedProtocol::PingV1,
|
||||
RequestType::MetaData(req) => match req {
|
||||
MetadataRequest::V1(_) => SupportedProtocol::MetaDataV1,
|
||||
MetadataRequest::V2(_) => SupportedProtocol::MetaDataV2,
|
||||
MetadataRequest::V3(_) => SupportedProtocol::MetaDataV3,
|
||||
},
|
||||
InboundRequest::LightClientBootstrap(_) => SupportedProtocol::LightClientBootstrapV1,
|
||||
InboundRequest::LightClientOptimisticUpdate => {
|
||||
RequestType::LightClientBootstrap(_) => SupportedProtocol::LightClientBootstrapV1,
|
||||
RequestType::LightClientOptimisticUpdate => {
|
||||
SupportedProtocol::LightClientOptimisticUpdateV1
|
||||
}
|
||||
InboundRequest::LightClientFinalityUpdate => {
|
||||
RequestType::LightClientFinalityUpdate => {
|
||||
SupportedProtocol::LightClientFinalityUpdateV1
|
||||
}
|
||||
}
|
||||
@@ -791,19 +791,96 @@ impl<E: EthSpec> InboundRequest<E> {
|
||||
match self {
|
||||
// this only gets called after `multiple_responses()` returns true. Therefore, only
|
||||
// variants that have `multiple_responses()` can have values.
|
||||
InboundRequest::BlocksByRange(_) => ResponseTermination::BlocksByRange,
|
||||
InboundRequest::BlocksByRoot(_) => ResponseTermination::BlocksByRoot,
|
||||
InboundRequest::BlobsByRange(_) => ResponseTermination::BlobsByRange,
|
||||
InboundRequest::BlobsByRoot(_) => ResponseTermination::BlobsByRoot,
|
||||
InboundRequest::DataColumnsByRoot(_) => ResponseTermination::DataColumnsByRoot,
|
||||
InboundRequest::DataColumnsByRange(_) => ResponseTermination::DataColumnsByRange,
|
||||
InboundRequest::Status(_) => unreachable!(),
|
||||
InboundRequest::Goodbye(_) => unreachable!(),
|
||||
InboundRequest::Ping(_) => unreachable!(),
|
||||
InboundRequest::MetaData(_) => unreachable!(),
|
||||
InboundRequest::LightClientBootstrap(_) => unreachable!(),
|
||||
InboundRequest::LightClientFinalityUpdate => unreachable!(),
|
||||
InboundRequest::LightClientOptimisticUpdate => unreachable!(),
|
||||
RequestType::BlocksByRange(_) => ResponseTermination::BlocksByRange,
|
||||
RequestType::BlocksByRoot(_) => ResponseTermination::BlocksByRoot,
|
||||
RequestType::BlobsByRange(_) => ResponseTermination::BlobsByRange,
|
||||
RequestType::BlobsByRoot(_) => ResponseTermination::BlobsByRoot,
|
||||
RequestType::DataColumnsByRoot(_) => ResponseTermination::DataColumnsByRoot,
|
||||
RequestType::DataColumnsByRange(_) => ResponseTermination::DataColumnsByRange,
|
||||
RequestType::Status(_) => unreachable!(),
|
||||
RequestType::Goodbye(_) => unreachable!(),
|
||||
RequestType::Ping(_) => unreachable!(),
|
||||
RequestType::MetaData(_) => unreachable!(),
|
||||
RequestType::LightClientBootstrap(_) => unreachable!(),
|
||||
RequestType::LightClientFinalityUpdate => unreachable!(),
|
||||
RequestType::LightClientOptimisticUpdate => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn supported_protocols(&self) -> Vec<ProtocolId> {
|
||||
match self {
|
||||
// add more protocols when versions/encodings are supported
|
||||
RequestType::Status(_) => vec![ProtocolId::new(
|
||||
SupportedProtocol::StatusV1,
|
||||
Encoding::SSZSnappy,
|
||||
)],
|
||||
RequestType::Goodbye(_) => vec![ProtocolId::new(
|
||||
SupportedProtocol::GoodbyeV1,
|
||||
Encoding::SSZSnappy,
|
||||
)],
|
||||
RequestType::BlocksByRange(_) => vec![
|
||||
ProtocolId::new(SupportedProtocol::BlocksByRangeV2, Encoding::SSZSnappy),
|
||||
ProtocolId::new(SupportedProtocol::BlocksByRangeV1, Encoding::SSZSnappy),
|
||||
],
|
||||
RequestType::BlocksByRoot(_) => vec![
|
||||
ProtocolId::new(SupportedProtocol::BlocksByRootV2, Encoding::SSZSnappy),
|
||||
ProtocolId::new(SupportedProtocol::BlocksByRootV1, Encoding::SSZSnappy),
|
||||
],
|
||||
RequestType::BlobsByRange(_) => vec![ProtocolId::new(
|
||||
SupportedProtocol::BlobsByRangeV1,
|
||||
Encoding::SSZSnappy,
|
||||
)],
|
||||
RequestType::BlobsByRoot(_) => vec![ProtocolId::new(
|
||||
SupportedProtocol::BlobsByRootV1,
|
||||
Encoding::SSZSnappy,
|
||||
)],
|
||||
RequestType::DataColumnsByRoot(_) => vec![ProtocolId::new(
|
||||
SupportedProtocol::DataColumnsByRootV1,
|
||||
Encoding::SSZSnappy,
|
||||
)],
|
||||
RequestType::DataColumnsByRange(_) => vec![ProtocolId::new(
|
||||
SupportedProtocol::DataColumnsByRangeV1,
|
||||
Encoding::SSZSnappy,
|
||||
)],
|
||||
RequestType::Ping(_) => vec![ProtocolId::new(
|
||||
SupportedProtocol::PingV1,
|
||||
Encoding::SSZSnappy,
|
||||
)],
|
||||
RequestType::MetaData(_) => vec![
|
||||
ProtocolId::new(SupportedProtocol::MetaDataV3, Encoding::SSZSnappy),
|
||||
ProtocolId::new(SupportedProtocol::MetaDataV2, Encoding::SSZSnappy),
|
||||
ProtocolId::new(SupportedProtocol::MetaDataV1, Encoding::SSZSnappy),
|
||||
],
|
||||
RequestType::LightClientBootstrap(_) => vec![ProtocolId::new(
|
||||
SupportedProtocol::LightClientBootstrapV1,
|
||||
Encoding::SSZSnappy,
|
||||
)],
|
||||
RequestType::LightClientOptimisticUpdate => vec![ProtocolId::new(
|
||||
SupportedProtocol::LightClientOptimisticUpdateV1,
|
||||
Encoding::SSZSnappy,
|
||||
)],
|
||||
RequestType::LightClientFinalityUpdate => vec![ProtocolId::new(
|
||||
SupportedProtocol::LightClientFinalityUpdateV1,
|
||||
Encoding::SSZSnappy,
|
||||
)],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expect_exactly_one_response(&self) -> bool {
|
||||
match self {
|
||||
RequestType::Status(_) => true,
|
||||
RequestType::Goodbye(_) => false,
|
||||
RequestType::BlocksByRange(_) => false,
|
||||
RequestType::BlocksByRoot(_) => false,
|
||||
RequestType::BlobsByRange(_) => false,
|
||||
RequestType::BlobsByRoot(_) => false,
|
||||
RequestType::DataColumnsByRoot(_) => false,
|
||||
RequestType::DataColumnsByRange(_) => false,
|
||||
RequestType::Ping(_) => true,
|
||||
RequestType::MetaData(_) => true,
|
||||
RequestType::LightClientBootstrap(_) => true,
|
||||
RequestType::LightClientOptimisticUpdate => true,
|
||||
RequestType::LightClientFinalityUpdate => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -819,7 +896,7 @@ pub enum RPCError {
|
||||
/// IO Error.
|
||||
IoError(String),
|
||||
/// The peer returned a valid response but the response indicated an error.
|
||||
ErrorResponse(RPCResponseErrorCode, String),
|
||||
ErrorResponse(RpcErrorResponse, String),
|
||||
/// Timed out waiting for a response.
|
||||
StreamTimeout,
|
||||
/// Peer does not support the protocol.
|
||||
@@ -898,28 +975,28 @@ impl std::error::Error for RPCError {
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: EthSpec> std::fmt::Display for InboundRequest<E> {
|
||||
impl<E: EthSpec> std::fmt::Display for RequestType<E> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
InboundRequest::Status(status) => write!(f, "Status Message: {}", status),
|
||||
InboundRequest::Goodbye(reason) => write!(f, "Goodbye: {}", reason),
|
||||
InboundRequest::BlocksByRange(req) => write!(f, "Blocks by range: {}", req),
|
||||
InboundRequest::BlocksByRoot(req) => write!(f, "Blocks by root: {:?}", req),
|
||||
InboundRequest::BlobsByRange(req) => write!(f, "Blobs by range: {:?}", req),
|
||||
InboundRequest::BlobsByRoot(req) => write!(f, "Blobs by root: {:?}", req),
|
||||
InboundRequest::DataColumnsByRoot(req) => write!(f, "Data columns by root: {:?}", req),
|
||||
InboundRequest::DataColumnsByRange(req) => {
|
||||
RequestType::Status(status) => write!(f, "Status Message: {}", status),
|
||||
RequestType::Goodbye(reason) => write!(f, "Goodbye: {}", reason),
|
||||
RequestType::BlocksByRange(req) => write!(f, "Blocks by range: {}", req),
|
||||
RequestType::BlocksByRoot(req) => write!(f, "Blocks by root: {:?}", req),
|
||||
RequestType::BlobsByRange(req) => write!(f, "Blobs by range: {:?}", req),
|
||||
RequestType::BlobsByRoot(req) => write!(f, "Blobs by root: {:?}", req),
|
||||
RequestType::DataColumnsByRoot(req) => write!(f, "Data columns by root: {:?}", req),
|
||||
RequestType::DataColumnsByRange(req) => {
|
||||
write!(f, "Data columns by range: {:?}", req)
|
||||
}
|
||||
InboundRequest::Ping(ping) => write!(f, "Ping: {}", ping.data),
|
||||
InboundRequest::MetaData(_) => write!(f, "MetaData request"),
|
||||
InboundRequest::LightClientBootstrap(bootstrap) => {
|
||||
RequestType::Ping(ping) => write!(f, "Ping: {}", ping.data),
|
||||
RequestType::MetaData(_) => write!(f, "MetaData request"),
|
||||
RequestType::LightClientBootstrap(bootstrap) => {
|
||||
write!(f, "Light client boostrap: {}", bootstrap.root)
|
||||
}
|
||||
InboundRequest::LightClientOptimisticUpdate => {
|
||||
RequestType::LightClientOptimisticUpdate => {
|
||||
write!(f, "Light client optimistic update request")
|
||||
}
|
||||
InboundRequest::LightClientFinalityUpdate => {
|
||||
RequestType::LightClientFinalityUpdate => {
|
||||
write!(f, "Light client finality update request")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,7 +252,7 @@ pub trait RateLimiterItem {
|
||||
fn max_responses(&self) -> u64;
|
||||
}
|
||||
|
||||
impl<E: EthSpec> RateLimiterItem for super::InboundRequest<E> {
|
||||
impl<E: EthSpec> RateLimiterItem for super::RequestType<E> {
|
||||
fn protocol(&self) -> Protocol {
|
||||
self.versioned_protocol().protocol()
|
||||
}
|
||||
@@ -262,15 +262,6 @@ impl<E: EthSpec> RateLimiterItem for super::InboundRequest<E> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: EthSpec> RateLimiterItem for super::OutboundRequest<E> {
|
||||
fn protocol(&self) -> Protocol {
|
||||
self.versioned_protocol().protocol()
|
||||
}
|
||||
|
||||
fn max_responses(&self) -> u64 {
|
||||
self.max_responses()
|
||||
}
|
||||
}
|
||||
impl RPCRateLimiter {
|
||||
pub fn new_with_config(config: RateLimiterConfig) -> Result<Self, &'static str> {
|
||||
// Destructure to make sure every configuration value is used.
|
||||
|
||||
@@ -14,13 +14,13 @@ use types::EthSpec;
|
||||
use super::{
|
||||
config::OutboundRateLimiterConfig,
|
||||
rate_limiter::{RPCRateLimiter as RateLimiter, RateLimitedErr},
|
||||
BehaviourAction, OutboundRequest, Protocol, RPCSend, ReqId,
|
||||
BehaviourAction, Protocol, RPCSend, ReqId, RequestType,
|
||||
};
|
||||
|
||||
/// A request that was rate limited or waiting on rate limited requests for the same peer and
|
||||
/// protocol.
|
||||
struct QueuedRequest<Id: ReqId, E: EthSpec> {
|
||||
req: OutboundRequest<E>,
|
||||
req: RequestType<E>,
|
||||
request_id: Id,
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ impl<Id: ReqId, E: EthSpec> SelfRateLimiter<Id, E> {
|
||||
&mut self,
|
||||
peer_id: PeerId,
|
||||
request_id: Id,
|
||||
req: OutboundRequest<E>,
|
||||
req: RequestType<E>,
|
||||
) -> Result<BehaviourAction<Id, E>, Error> {
|
||||
let protocol = req.versioned_protocol().protocol();
|
||||
// First check that there are not already other requests waiting to be sent.
|
||||
@@ -101,7 +101,7 @@ impl<Id: ReqId, E: EthSpec> SelfRateLimiter<Id, E> {
|
||||
limiter: &mut RateLimiter,
|
||||
peer_id: PeerId,
|
||||
request_id: Id,
|
||||
req: OutboundRequest<E>,
|
||||
req: RequestType<E>,
|
||||
log: &Logger,
|
||||
) -> Result<BehaviourAction<Id, E>, (QueuedRequest<Id, E>, Duration)> {
|
||||
match limiter.allows(&peer_id, &req) {
|
||||
@@ -211,7 +211,7 @@ mod tests {
|
||||
use crate::rpc::config::{OutboundRateLimiterConfig, RateLimiterConfig};
|
||||
use crate::rpc::rate_limiter::Quota;
|
||||
use crate::rpc::self_limiter::SelfRateLimiter;
|
||||
use crate::rpc::{OutboundRequest, Ping, Protocol};
|
||||
use crate::rpc::{Ping, Protocol, RequestType};
|
||||
use crate::service::api_types::{AppRequestId, RequestId, SyncRequestId};
|
||||
use libp2p::PeerId;
|
||||
use std::time::Duration;
|
||||
@@ -235,7 +235,7 @@ mod tests {
|
||||
RequestId::Application(AppRequestId::Sync(SyncRequestId::RangeBlockAndBlobs {
|
||||
id: i,
|
||||
})),
|
||||
OutboundRequest::Ping(Ping { data: i as u64 }),
|
||||
RequestType::Ping(Ping { data: i as u64 }),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user