ChainSafe / ChainSafe/lodestar
Enable discv5 Inbound Rate Limiting in Lodestar
- Dominant language
- TypeScript
- Stars
- 1.4k
- Forks
- 483
- Avg merge
- 1d 16h
- Merged PRs (30d)
- 150
Description
### Describe the bug
## Summary
The discv5 implementation supports an optional inbound rate limiter (two-layer
GCRA: global + per-IP), but the beacon node never turns it on. In the default
configuration, a Lodestar node's discv5 UDP port accepts an unbounded inbound
packet rate.
To pre-empt a possible confusion: Lodestar *does* rate-limit ReqResp methods
(`beacon-node/src/network/reqresp/rateLimit.ts`, per-RPC quotas) — that is a different layer
and unaffected. This issue is only about discv5 UDP.
## Evidence (verified at the analyzed revision `lodestar@98e4089`)
**1. The limiter is conditional in the receive path**
(`@chainsafe/discv5`, `src/transport/udp.ts`, `handleIncoming`):
```ts
private handleIncoming = (data, rinfo) => {
if (this.rateLimiter && !this.rateLimiter.allowEncodedPacket(rinfo.address)) {
return;
}
...decodePacket(this.srcId, data)...
}
```
**2. The limiter is only constructed when the option is provided**
(`@chainsafe/discv5`, `src/service/service.ts`):
```ts
const rateLimiter = opts.rateLimiterOpts && new RateLimiter(opts.rateLimiterOpts, ...);
```
**3. The beacon node never passes it — and the worker type cannot carry it**
(`packages/beacon-node/src/network/discv5/worker.ts`):
```ts
const discv5 = Discv5.create({
enr, privateKey, bindAddrs, config, metricsRegistry,
// no rateLimiterOpts
})
```
Moreover, `Discv5WorkerData` (`packages/beacon-node/src/network/discv5/types.ts`)
has **no `rateLimiterOpts` field at all**, so enabling the limiter requires
adding the field to the worker data type and plumbing it through the network
config — it is an integration gap, not a one-liner.
## Cross client comparison
How the other consensus clients handle discv5 receive-side rate limiting by
default:
| Client | Default receive-side limiting on the discv5 UDP port |
|---|---|
| Lighthouse | Token bucket via `discv5::RateLimiterBuilder`: total 10 pkt/s, per-IP 9/s, per-node 8/s, 1 h IP ban on abuse (`lighthouse_network/src/config.rs`) |
| Grandine | Same as Lighthouse (inherited through its `eth2_libp2p` fork) |
| Teku | Netty `ChannelTrafficShapingHandler`, 250,000 bytes/s read limit on the discovery socket |
| Prysm | None (reported separately) |
| **Lodestar** | **Limiter implemented in the discv5 library but not enabled by the node** |
So in the current default landscape Lodestar is one of two clients with no
active discv5 rate limiter, and the only one whose limiter exists but is
unwired.
## Impact
With no bound on the inbound packet rate, CPU and bandwidth spent on discv5
packet parsing is attacker-controllable. Because discv5 runs over UDP and
source addresses can be spoofed, a per-IP-only limiter would not be sufficient
anyway — the global layer is what carries the defense, and the library already
implements the global+per-IP combination. The gap is integration, not missing
functionality.
### Expected behavior
Lodestar should enable the discv5 library's inbound rate limiter by default
with conservative global and per-IP limits, while allowing operators to tune
the limits when needed. Abuse-related IP bans should also expire after a
bounded period rather than remain permanent.
### Steps to reproduce
_No response_
### Additional context
_No response_
### Operating system
Linux
### Lodestar version or commit hash
v1.39.0
Contributor guide
Research direction
Start by reading packages/beacon-node/src/network/discv5/worker.ts and types.ts, then trace how network configuration reaches Discv5.create and the @chainsafe/discv5 rateLimiterOpts option. Done means the limiter is enabled by default with configurable global and per-IP limits, bounded abuse-ban expiry, and coverage for the worker-data plumbing.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- networking, security
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100