Skip to content

Commit 818f54a

Browse files
committed
feat(p2p): add opt-in discv5 peer discovery
Lean nodes could only meet through a static bootnode list, so every new node needed an operator to hand it peers. This wires ethrex's discv5 stack in behind `--discovery.enable`: the node builds and signs its own ENR, joins the DHT on its own UDP socket, and dials what it finds over libp2p QUIC. Static bootnode dialing is untouched and discovery is off by default, so nothing changes for an operator who does not ask for it. Admission follows the beacon phase0 p2p spec, mirroring lighthouse's `eth2_fork_predicate`: the `eth2` fork digest must match, a differing `next_fork_version`/`next_fork_epoch` is explicitly tolerated, and the peer must advertise a `quic` port. The checks live in a `LeanFilter` that ethrex's peer table runs as each ENR arrives, so a record is judged where it lands rather than at dial time, and is judged afresh whenever the peer publishes a higher-`seq` record. Survivors are ranked by how many attestation subnets they cover that no connected peer does, so discovery fills subnet gaps first. A peer's `attnets` is self-reported, so subnet ids at or beyond the local committee count are dropped before ranking sees them. `ethrex-p2p` is pinned to the unmerged `feat/discovery-peer-requirements` branch, which carries the unified `DiscoveryServer`, the peer table, and the `PeerFilter` seam. Repoint it at a main revision once that merges. Known gap: `DiscoveryServer::spawn` builds its own local record and offers no way to seed the consensus entries, so the ENR ethrex answers queries with carries `ip`/`udp`/`secp256k1` but not `eth2`, `attnets` or `quic`. Discovery is one-sided until `spawn` can take a prepared record: we find and admit lean peers, but a lean peer applying these same rules to what ethrex serves would refuse us. See `docs/discovery.md`.
1 parent 72e5b71 commit 818f54a

18 files changed

Lines changed: 2486 additions & 894 deletions

File tree

‎CLAUDE.md‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,13 @@ actual_slot = finalized_slot + 1 + relative_index
280280
- Mesh size: 8 (6-12 bounds), heartbeat: 700ms
281281
- **Req/Resp**: Status, BlocksByRoot, BlocksByRange (snappy frame compression + varint length)
282282

283+
### Peer Discovery (discv5, opt-in)
284+
- Off by default; `--discovery.enable` plus `--discovery.port` (own UDP socket, must differ from `--gossipsub-port`)
285+
- Reuses ethrex's `DiscoveryServer` + `PeerTable` with discv4 disabled and an empty in-memory ethrex `Store` (`spawn` requires one; lean has no execution chain)
286+
- ENR follows the beacon phase0 spec: `ip`/`udp`/`quic`/`secp256k1`/`eth2`/`attnets`, deliberately **no** `tcp`
287+
- Admission mirrors lighthouse: `eth2.fork_digest` must match, `next_fork_*` may differ, `quic` entry required. Handed to the peer table as `LeanFilter: PeerFilter`, so records are judged on arrival, not at dial time; a reject is re-judged on a higher-`seq` ENR
288+
- Candidates ranked by uncovered attestation subnets. See [`docs/discovery.md`](docs/discovery.md)
289+
283290
### Retry Strategy on Block Requests
284291
- Exponential backoff: doubling from `INITIAL_BACKOFF_MS` (5ms → 2560ms)
285292
- Max `MAX_FETCH_RETRIES` (10) attempts, random peer selection on retry

0 commit comments

Comments
 (0)