mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-06 10:11:44 +00:00
Delayed RPC Send Using Tokens (#5923)
closes https://github.com/sigp/lighthouse/issues/5785 The diagram below shows the differences in how the receiver (responder) behaves before and after this PR. The following sentences will detail the changes. ```mermaid flowchart TD subgraph "*** After ***" Start2([START]) --> AA[Receive request] AA --> COND1{Is there already an active request <br> with the same protocol?} COND1 --> |Yes| CC[Send error response] CC --> End2([END]) %% COND1 --> |No| COND2{Request is too large?} %% COND2 --> |Yes| CC COND1 --> |No| DD[Process request] DD --> EE{Rate limit reached?} EE --> |Yes| FF[Wait until tokens are regenerated] FF --> EE EE --> |No| GG[Send response] GG --> End2 end subgraph "*** Before ***" Start([START]) --> A[Receive request] A --> B{Rate limit reached <br> or <br> request is too large?} B -->|Yes| C[Send error response] C --> End([END]) B -->|No| E[Process request] E --> F[Send response] F --> End end ``` ### `Is there already an active request with the same protocol?` This check is not performed in `Before`. This is taken from the PR in the consensus-spec, which proposes updates regarding rate limiting and response timeout. https://github.com/ethereum/consensus-specs/pull/3767/files > The requester MUST NOT make more than two concurrent requests with the same ID. The PR mentions the requester side. In this PR, I introduced the `ActiveRequestsLimiter` for the `responder` side to restrict more than two requests from running simultaneously on the same protocol per peer. If the limiter disallows a request, the responder sends a rate-limited error and penalizes the requester. ### `Rate limit reached?` and `Wait until tokens are regenerated` UPDATE: I moved the limiter logic to the behaviour side. https://github.com/sigp/lighthouse/pull/5923#issuecomment-2379535927 ~~The rate limiter is shared between the behaviour and the handler. (`Arc<Mutex<RateLimiter>>>`) The handler checks the rate limit and queues the response if the limit is reached. The behaviour handles pruning.~~ ~~I considered not sharing the rate limiter between the behaviour and the handler, and performing all of these either within the behaviour or handler. However, I decided against this for the following reasons:~~ - ~~Regarding performing everything within the behaviour: The behaviour is unable to recognize the response protocol when `RPC::send_response()` is called, especially when the response is `RPCCodedResponse::Error`. Therefore, the behaviour can't rate limit responses based on the response protocol.~~ - ~~Regarding performing everything within the handler: When multiple connections are established with a peer, there could be multiple handlers interacting with that peer. Thus, we cannot enforce rate limiting per peer solely within the handler. (Any ideas? 🤔 )~~
This commit is contained in:
@@ -16,6 +16,7 @@ use types::{
|
||||
|
||||
type E = MinimalEthSpec;
|
||||
|
||||
use lighthouse_network::rpc::config::InboundRateLimiterConfig;
|
||||
use tempfile::Builder as TempBuilder;
|
||||
|
||||
/// Returns a dummy fork context
|
||||
@@ -77,7 +78,11 @@ pub fn build_tracing_subscriber(level: &str, enabled: bool) {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_config(mut boot_nodes: Vec<Enr>) -> Arc<NetworkConfig> {
|
||||
pub fn build_config(
|
||||
mut boot_nodes: Vec<Enr>,
|
||||
disable_peer_scoring: bool,
|
||||
inbound_rate_limiter: Option<InboundRateLimiterConfig>,
|
||||
) -> Arc<NetworkConfig> {
|
||||
let mut config = NetworkConfig::default();
|
||||
|
||||
// Find unused ports by using the 0 port.
|
||||
@@ -93,6 +98,8 @@ pub fn build_config(mut boot_nodes: Vec<Enr>) -> Arc<NetworkConfig> {
|
||||
config.enr_address = (Some(std::net::Ipv4Addr::LOCALHOST), None);
|
||||
config.boot_nodes_enr.append(&mut boot_nodes);
|
||||
config.network_dir = path.into_path();
|
||||
config.disable_peer_scoring = disable_peer_scoring;
|
||||
config.inbound_rate_limiter_config = inbound_rate_limiter;
|
||||
Arc::new(config)
|
||||
}
|
||||
|
||||
@@ -102,8 +109,10 @@ pub async fn build_libp2p_instance(
|
||||
fork_name: ForkName,
|
||||
chain_spec: Arc<ChainSpec>,
|
||||
service_name: String,
|
||||
disable_peer_scoring: bool,
|
||||
inbound_rate_limiter: Option<InboundRateLimiterConfig>,
|
||||
) -> Libp2pInstance {
|
||||
let config = build_config(boot_nodes);
|
||||
let config = build_config(boot_nodes, disable_peer_scoring, inbound_rate_limiter);
|
||||
// launch libp2p service
|
||||
|
||||
let (signal, exit) = async_channel::bounded(1);
|
||||
@@ -144,6 +153,8 @@ pub async fn build_node_pair(
|
||||
fork_name: ForkName,
|
||||
spec: Arc<ChainSpec>,
|
||||
protocol: Protocol,
|
||||
disable_peer_scoring: bool,
|
||||
inbound_rate_limiter: Option<InboundRateLimiterConfig>,
|
||||
) -> (Libp2pInstance, Libp2pInstance) {
|
||||
let mut sender = build_libp2p_instance(
|
||||
rt.clone(),
|
||||
@@ -151,10 +162,20 @@ pub async fn build_node_pair(
|
||||
fork_name,
|
||||
spec.clone(),
|
||||
"sender".to_string(),
|
||||
disable_peer_scoring,
|
||||
inbound_rate_limiter.clone(),
|
||||
)
|
||||
.await;
|
||||
let mut receiver = build_libp2p_instance(
|
||||
rt,
|
||||
vec![],
|
||||
fork_name,
|
||||
spec.clone(),
|
||||
"receiver".to_string(),
|
||||
disable_peer_scoring,
|
||||
inbound_rate_limiter,
|
||||
)
|
||||
.await;
|
||||
let mut receiver =
|
||||
build_libp2p_instance(rt, vec![], fork_name, spec.clone(), "receiver".to_string()).await;
|
||||
|
||||
// let the two nodes set up listeners
|
||||
let sender_fut = async {
|
||||
@@ -235,6 +256,8 @@ pub async fn build_linear(
|
||||
fork_name,
|
||||
spec.clone(),
|
||||
"linear".to_string(),
|
||||
false,
|
||||
None,
|
||||
)
|
||||
.await,
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user