mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-15 02:42:38 +00:00
Merge branch 'unstable' of https://github.com/sigp/lighthouse into deneb-free-blobs
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
use crate::listen_addr::{ListenAddr, ListenAddress};
|
||||
use crate::rpc::config::OutboundRateLimiterConfig;
|
||||
use crate::rpc::config::{InboundRateLimiterConfig, OutboundRateLimiterConfig};
|
||||
use crate::types::GossipKind;
|
||||
use crate::{Enr, PeerIdSerialized};
|
||||
use directory::{
|
||||
@@ -148,6 +148,9 @@ pub struct Config {
|
||||
|
||||
/// Configures if/where invalid blocks should be stored.
|
||||
pub invalid_block_storage: Option<PathBuf>,
|
||||
|
||||
/// Configuration for the inbound rate limiter (requests received by this node).
|
||||
pub inbound_rate_limiter_config: Option<InboundRateLimiterConfig>,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
@@ -333,6 +336,7 @@ impl Default for Config {
|
||||
enable_light_client_server: false,
|
||||
outbound_rate_limiter_config: None,
|
||||
invalid_block_storage: None,
|
||||
inbound_rate_limiter_config: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
///! The subnet predicate used for searching for a particular subnet.
|
||||
//! The subnet predicate used for searching for a particular subnet.
|
||||
use super::*;
|
||||
use crate::types::{EnrAttestationBitfield, EnrSyncCommitteeBitfield};
|
||||
use slog::trace;
|
||||
|
||||
@@ -1278,7 +1278,7 @@ impl<TSpec: EthSpec> PeerManager<TSpec> {
|
||||
);
|
||||
}
|
||||
|
||||
let mut score_peers: &mut (f64, usize) = avg_score_per_client
|
||||
let score_peers: &mut (f64, usize) = avg_score_per_client
|
||||
.entry(peer_info.client().kind.to_string())
|
||||
.or_default();
|
||||
score_peers.0 += peer_info.score().score();
|
||||
|
||||
@@ -58,9 +58,31 @@ impl FromStr for ProtocolQuota {
|
||||
}
|
||||
}
|
||||
|
||||
/// Configurations for the rate limiter applied to outbound requests (made by the node itself).
|
||||
#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, Debug, Default)]
|
||||
pub struct OutboundRateLimiterConfig(pub RateLimiterConfig);
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, Debug, Default)]
|
||||
pub struct InboundRateLimiterConfig(pub RateLimiterConfig);
|
||||
|
||||
impl FromStr for OutboundRateLimiterConfig {
|
||||
type Err = &'static str;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
RateLimiterConfig::from_str(s).map(Self)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for InboundRateLimiterConfig {
|
||||
type Err = &'static str;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
RateLimiterConfig::from_str(s).map(Self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Configurations for the rate limiter.
|
||||
#[derive(Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct OutboundRateLimiterConfig {
|
||||
pub struct RateLimiterConfig {
|
||||
pub(super) ping_quota: Quota,
|
||||
pub(super) meta_data_quota: Quota,
|
||||
pub(super) status_quota: Quota,
|
||||
@@ -69,9 +91,10 @@ pub struct OutboundRateLimiterConfig {
|
||||
pub(super) blocks_by_root_quota: Quota,
|
||||
pub(super) blobs_by_range_quota: Quota,
|
||||
pub(super) blobs_by_root_quota: Quota,
|
||||
pub(super) light_client_bootstrap_quota: Quota,
|
||||
}
|
||||
|
||||
impl OutboundRateLimiterConfig {
|
||||
impl RateLimiterConfig {
|
||||
pub const DEFAULT_PING_QUOTA: Quota = Quota::n_every(2, 10);
|
||||
pub const DEFAULT_META_DATA_QUOTA: Quota = Quota::n_every(2, 5);
|
||||
pub const DEFAULT_STATUS_QUOTA: Quota = Quota::n_every(5, 15);
|
||||
@@ -82,11 +105,12 @@ impl OutboundRateLimiterConfig {
|
||||
pub const DEFAULT_BLOBS_BY_RANGE_QUOTA: Quota =
|
||||
Quota::n_every(methods::MAX_REQUEST_BLOB_SIDECARS, 10);
|
||||
pub const DEFAULT_BLOBS_BY_ROOT_QUOTA: Quota = Quota::n_every(128, 10);
|
||||
pub const DEFAULT_LIGHT_CLIENT_BOOTSTRAP_QUOTA: Quota = Quota::one_every(10);
|
||||
}
|
||||
|
||||
impl Default for OutboundRateLimiterConfig {
|
||||
impl Default for RateLimiterConfig {
|
||||
fn default() -> Self {
|
||||
OutboundRateLimiterConfig {
|
||||
RateLimiterConfig {
|
||||
ping_quota: Self::DEFAULT_PING_QUOTA,
|
||||
meta_data_quota: Self::DEFAULT_META_DATA_QUOTA,
|
||||
status_quota: Self::DEFAULT_STATUS_QUOTA,
|
||||
@@ -95,11 +119,12 @@ impl Default for OutboundRateLimiterConfig {
|
||||
blocks_by_root_quota: Self::DEFAULT_BLOCKS_BY_ROOT_QUOTA,
|
||||
blobs_by_range_quota: Self::DEFAULT_BLOBS_BY_RANGE_QUOTA,
|
||||
blobs_by_root_quota: Self::DEFAULT_BLOBS_BY_ROOT_QUOTA,
|
||||
light_client_bootstrap_quota: Self::DEFAULT_LIGHT_CLIENT_BOOTSTRAP_QUOTA,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for OutboundRateLimiterConfig {
|
||||
impl Debug for RateLimiterConfig {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
macro_rules! fmt_q {
|
||||
($quota:expr) => {
|
||||
@@ -111,7 +136,7 @@ impl Debug for OutboundRateLimiterConfig {
|
||||
};
|
||||
}
|
||||
|
||||
f.debug_struct("OutboundRateLimiterConfig")
|
||||
f.debug_struct("RateLimiterConfig")
|
||||
.field("ping", fmt_q!(&self.ping_quota))
|
||||
.field("metadata", fmt_q!(&self.meta_data_quota))
|
||||
.field("status", fmt_q!(&self.status_quota))
|
||||
@@ -128,7 +153,7 @@ impl Debug for OutboundRateLimiterConfig {
|
||||
/// the default values. Protocol specified more than once use only the first given Quota.
|
||||
///
|
||||
/// The expected format is a ';' separated list of [`ProtocolQuota`].
|
||||
impl FromStr for OutboundRateLimiterConfig {
|
||||
impl FromStr for RateLimiterConfig {
|
||||
type Err = &'static str;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
@@ -140,6 +165,8 @@ impl FromStr for OutboundRateLimiterConfig {
|
||||
let mut blocks_by_root_quota = None;
|
||||
let mut blobs_by_range_quota = None;
|
||||
let mut blobs_by_root_quota = None;
|
||||
let mut light_client_bootstrap_quota = None;
|
||||
|
||||
for proto_def in s.split(';') {
|
||||
let ProtocolQuota { protocol, quota } = proto_def.parse()?;
|
||||
let quota = Some(quota);
|
||||
@@ -152,10 +179,12 @@ impl FromStr for OutboundRateLimiterConfig {
|
||||
Protocol::BlobsByRoot => blobs_by_root_quota = blobs_by_root_quota.or(quota),
|
||||
Protocol::Ping => ping_quota = ping_quota.or(quota),
|
||||
Protocol::MetaData => meta_data_quota = meta_data_quota.or(quota),
|
||||
Protocol::LightClientBootstrap => return Err("Lighthouse does not send LightClientBootstrap requests. Quota should not be set."),
|
||||
Protocol::LightClientBootstrap => {
|
||||
light_client_bootstrap_quota = light_client_bootstrap_quota.or(quota)
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(OutboundRateLimiterConfig {
|
||||
Ok(RateLimiterConfig {
|
||||
ping_quota: ping_quota.unwrap_or(Self::DEFAULT_PING_QUOTA),
|
||||
meta_data_quota: meta_data_quota.unwrap_or(Self::DEFAULT_META_DATA_QUOTA),
|
||||
status_quota: status_quota.unwrap_or(Self::DEFAULT_STATUS_QUOTA),
|
||||
@@ -167,6 +196,8 @@ impl FromStr for OutboundRateLimiterConfig {
|
||||
blobs_by_range_quota: blobs_by_range_quota
|
||||
.unwrap_or(Self::DEFAULT_BLOBS_BY_RANGE_QUOTA),
|
||||
blobs_by_root_quota: blobs_by_root_quota.unwrap_or(Self::DEFAULT_BLOBS_BY_ROOT_QUOTA),
|
||||
light_client_bootstrap_quota: light_client_bootstrap_quota
|
||||
.unwrap_or(Self::DEFAULT_LIGHT_CLIENT_BOOTSTRAP_QUOTA),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ use slog::{crit, debug, o};
|
||||
use std::marker::PhantomData;
|
||||
use std::sync::Arc;
|
||||
use std::task::{Context, Poll};
|
||||
use std::time::Duration;
|
||||
use types::{EthSpec, ForkContext};
|
||||
|
||||
pub(crate) use handler::HandlerErr;
|
||||
@@ -33,7 +32,7 @@ pub use methods::{
|
||||
pub(crate) use outbound::OutboundRequest;
|
||||
pub use protocol::{max_rpc_size, Protocol, RPCError};
|
||||
|
||||
use self::config::OutboundRateLimiterConfig;
|
||||
use self::config::{InboundRateLimiterConfig, OutboundRateLimiterConfig};
|
||||
use self::self_limiter::SelfRateLimiter;
|
||||
|
||||
pub(crate) mod codec;
|
||||
@@ -113,7 +112,7 @@ type BehaviourAction<Id, TSpec> =
|
||||
/// logic.
|
||||
pub struct RPC<Id: ReqId, TSpec: EthSpec> {
|
||||
/// Rate limiter
|
||||
limiter: RateLimiter,
|
||||
limiter: Option<RateLimiter>,
|
||||
/// Rate limiter for our own requests.
|
||||
self_limiter: Option<SelfRateLimiter<Id, TSpec>>,
|
||||
/// Queue of events to be processed.
|
||||
@@ -128,38 +127,24 @@ impl<Id: ReqId, TSpec: EthSpec> RPC<Id, TSpec> {
|
||||
pub fn new(
|
||||
fork_context: Arc<ForkContext>,
|
||||
enable_light_client_server: bool,
|
||||
inbound_rate_limiter_config: Option<InboundRateLimiterConfig>,
|
||||
outbound_rate_limiter_config: Option<OutboundRateLimiterConfig>,
|
||||
log: slog::Logger,
|
||||
) -> Self {
|
||||
let log = log.new(o!("service" => "libp2p_rpc"));
|
||||
|
||||
let limiter = RateLimiter::builder()
|
||||
.n_every(Protocol::MetaData, 2, Duration::from_secs(5))
|
||||
.n_every(Protocol::Ping, 2, Duration::from_secs(10))
|
||||
.n_every(Protocol::Status, 5, Duration::from_secs(15))
|
||||
.one_every(Protocol::Goodbye, Duration::from_secs(10))
|
||||
.one_every(Protocol::LightClientBootstrap, Duration::from_secs(10))
|
||||
.n_every(
|
||||
Protocol::BlocksByRange,
|
||||
methods::MAX_REQUEST_BLOCKS,
|
||||
Duration::from_secs(10),
|
||||
)
|
||||
.n_every(Protocol::BlocksByRoot, 128, Duration::from_secs(10))
|
||||
.n_every(Protocol::BlobsByRoot, 128, Duration::from_secs(10))
|
||||
.n_every(
|
||||
Protocol::BlobsByRange,
|
||||
MAX_REQUEST_BLOB_SIDECARS,
|
||||
Duration::from_secs(10),
|
||||
)
|
||||
.build()
|
||||
.expect("Configuration parameters are valid");
|
||||
let inbound_limiter = inbound_rate_limiter_config.map(|config| {
|
||||
debug!(log, "Using inbound rate limiting params"; "config" => ?config);
|
||||
RateLimiter::new_with_config(config.0)
|
||||
.expect("Inbound limiter configuration parameters are valid")
|
||||
});
|
||||
|
||||
let self_limiter = outbound_rate_limiter_config.map(|config| {
|
||||
SelfRateLimiter::new(config, log.clone()).expect("Configuration parameters are valid")
|
||||
});
|
||||
|
||||
RPC {
|
||||
limiter,
|
||||
limiter: inbound_limiter,
|
||||
self_limiter,
|
||||
events: Vec::new(),
|
||||
fork_context,
|
||||
@@ -249,50 +234,60 @@ where
|
||||
event: <Self::ConnectionHandler as ConnectionHandler>::OutEvent,
|
||||
) {
|
||||
if let Ok(RPCReceived::Request(ref id, ref req)) = event {
|
||||
// check if the request is conformant to the quota
|
||||
match self.limiter.allows(&peer_id, req) {
|
||||
Ok(()) => {
|
||||
// send the event to the user
|
||||
self.events
|
||||
.push(NetworkBehaviourAction::GenerateEvent(RPCMessage {
|
||||
peer_id,
|
||||
conn_id,
|
||||
event,
|
||||
}))
|
||||
}
|
||||
Err(RateLimitedErr::TooLarge) => {
|
||||
// we set the batch sizes, so this is a coding/config err for most protocols
|
||||
let protocol = req.protocol();
|
||||
if matches!(protocol, Protocol::BlocksByRange) {
|
||||
debug!(self.log, "Blocks by range request will never be processed"; "request" => %req);
|
||||
} else {
|
||||
crit!(self.log, "Request size too large to ever be processed"; "protocol" => %protocol);
|
||||
if let Some(limiter) = self.limiter.as_mut() {
|
||||
// check if the request is conformant to the quota
|
||||
match limiter.allows(&peer_id, req) {
|
||||
Ok(()) => {
|
||||
// send the event to the user
|
||||
self.events
|
||||
.push(NetworkBehaviourAction::GenerateEvent(RPCMessage {
|
||||
peer_id,
|
||||
conn_id,
|
||||
event,
|
||||
}))
|
||||
}
|
||||
// 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,
|
||||
"Rate limited. Request too large".into(),
|
||||
),
|
||||
);
|
||||
}
|
||||
Err(RateLimitedErr::TooSoon(wait_time)) => {
|
||||
debug!(self.log, "Request exceeds the rate limit";
|
||||
Err(RateLimitedErr::TooLarge) => {
|
||||
// we set the batch sizes, so this is a coding/config err for most protocols
|
||||
let protocol = req.protocol();
|
||||
if matches!(protocol, Protocol::BlocksByRange) {
|
||||
debug!(self.log, "Blocks by range request will never be processed"; "request" => %req);
|
||||
} else {
|
||||
crit!(self.log, "Request size too large to ever be processed"; "protocol" => %protocol);
|
||||
}
|
||||
// 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,
|
||||
"Rate limited. Request too large".into(),
|
||||
),
|
||||
);
|
||||
}
|
||||
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());
|
||||
// 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,
|
||||
format!("Wait {:?}", wait_time).into(),
|
||||
),
|
||||
);
|
||||
// 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,
|
||||
format!("Wait {:?}", wait_time).into(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// No rate limiting, send the event to the user
|
||||
self.events
|
||||
.push(NetworkBehaviourAction::GenerateEvent(RPCMessage {
|
||||
peer_id,
|
||||
conn_id,
|
||||
event,
|
||||
}))
|
||||
}
|
||||
} else {
|
||||
self.events
|
||||
@@ -310,7 +305,9 @@ where
|
||||
_: &mut impl PollParameters,
|
||||
) -> Poll<NetworkBehaviourAction<Self::OutEvent, Self::ConnectionHandler>> {
|
||||
// let the rate limiter prune.
|
||||
let _ = self.limiter.poll_unpin(cx);
|
||||
if let Some(limiter) = self.limiter.as_mut() {
|
||||
let _ = limiter.poll_unpin(cx);
|
||||
}
|
||||
|
||||
if let Some(self_limiter) = self.self_limiter.as_mut() {
|
||||
if let Poll::Ready(event) = self_limiter.poll_ready(cx) {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use super::config::RateLimiterConfig;
|
||||
use crate::rpc::Protocol;
|
||||
use fnv::FnvHashMap;
|
||||
use libp2p::PeerId;
|
||||
@@ -151,29 +152,6 @@ impl RPCRateLimiterBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
/// Allow one token every `time_period` to be used for this `protocol`.
|
||||
/// This produces a hard limit.
|
||||
pub fn one_every(self, protocol: Protocol, time_period: Duration) -> Self {
|
||||
self.set_quota(
|
||||
protocol,
|
||||
Quota {
|
||||
replenish_all_every: time_period,
|
||||
max_tokens: 1,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// Allow `n` tokens to be use used every `time_period` for this `protocol`.
|
||||
pub fn n_every(self, protocol: Protocol, n: u64, time_period: Duration) -> Self {
|
||||
self.set_quota(
|
||||
protocol,
|
||||
Quota {
|
||||
max_tokens: n,
|
||||
replenish_all_every: time_period,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub fn build(self) -> Result<RPCRateLimiter, &'static str> {
|
||||
// get our quotas
|
||||
let ping_quota = self.ping_quota.ok_or("Ping quota not specified")?;
|
||||
@@ -254,6 +232,29 @@ impl<T: EthSpec> RateLimiterItem for super::OutboundRequest<T> {
|
||||
}
|
||||
}
|
||||
impl RPCRateLimiter {
|
||||
pub fn new_with_config(config: RateLimiterConfig) -> Result<Self, &'static str> {
|
||||
// Destructure to make sure every configuration value is used.
|
||||
let RateLimiterConfig {
|
||||
ping_quota,
|
||||
meta_data_quota,
|
||||
status_quota,
|
||||
goodbye_quota,
|
||||
blocks_by_range_quota,
|
||||
blocks_by_root_quota,
|
||||
light_client_bootstrap_quota,
|
||||
} = config;
|
||||
|
||||
Self::builder()
|
||||
.set_quota(Protocol::Ping, ping_quota)
|
||||
.set_quota(Protocol::MetaData, meta_data_quota)
|
||||
.set_quota(Protocol::Status, status_quota)
|
||||
.set_quota(Protocol::Goodbye, goodbye_quota)
|
||||
.set_quota(Protocol::BlocksByRange, blocks_by_range_quota)
|
||||
.set_quota(Protocol::BlocksByRoot, blocks_by_root_quota)
|
||||
.set_quota(Protocol::LightClientBootstrap, light_client_bootstrap_quota)
|
||||
.build()
|
||||
}
|
||||
|
||||
/// Get a builder instance.
|
||||
pub fn builder() -> RPCRateLimiterBuilder {
|
||||
RPCRateLimiterBuilder::default()
|
||||
|
||||
@@ -52,32 +52,7 @@ impl<Id: ReqId, TSpec: EthSpec> SelfRateLimiter<Id, TSpec> {
|
||||
/// Creates a new [`SelfRateLimiter`] based on configration values.
|
||||
pub fn new(config: OutboundRateLimiterConfig, log: Logger) -> Result<Self, &'static str> {
|
||||
debug!(log, "Using self rate limiting params"; "config" => ?config);
|
||||
// Destructure to make sure every configuration value is used.
|
||||
let OutboundRateLimiterConfig {
|
||||
ping_quota,
|
||||
meta_data_quota,
|
||||
status_quota,
|
||||
goodbye_quota,
|
||||
blocks_by_range_quota,
|
||||
blocks_by_root_quota,
|
||||
blobs_by_range_quota,
|
||||
blobs_by_root_quota,
|
||||
} = config;
|
||||
|
||||
let limiter = RateLimiter::builder()
|
||||
.set_quota(Protocol::Ping, ping_quota)
|
||||
.set_quota(Protocol::MetaData, meta_data_quota)
|
||||
.set_quota(Protocol::Status, status_quota)
|
||||
.set_quota(Protocol::Goodbye, goodbye_quota)
|
||||
.set_quota(Protocol::BlocksByRange, blocks_by_range_quota)
|
||||
.set_quota(Protocol::BlocksByRoot, blocks_by_root_quota)
|
||||
.set_quota(Protocol::BlobsByRange, blobs_by_range_quota)
|
||||
.set_quota(Protocol::BlobsByRoot, blobs_by_root_quota)
|
||||
// Manually set the LightClientBootstrap quota, since we use the same rate limiter for
|
||||
// inbound and outbound requests, and the LightClientBootstrap is an only inbound
|
||||
// protocol.
|
||||
.one_every(Protocol::LightClientBootstrap, Duration::from_secs(10))
|
||||
.build()?;
|
||||
let limiter = RateLimiter::new_with_config(config.0)?;
|
||||
|
||||
Ok(SelfRateLimiter {
|
||||
delayed_requests: Default::default(),
|
||||
|
||||
@@ -269,6 +269,7 @@ impl<AppReqId: ReqId, TSpec: EthSpec> Network<AppReqId, TSpec> {
|
||||
let eth2_rpc = RPC::new(
|
||||
ctx.fork_context.clone(),
|
||||
config.enable_light_client_server,
|
||||
config.inbound_rate_limiter_config.clone(),
|
||||
config.outbound_rate_limiter_config.clone(),
|
||||
log.clone(),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user