feat(p2p): add opt-in discv5 peer discovery - #579
Draft
MegaRedHand wants to merge 1 commit into
Draft
Conversation
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`.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds opt-in discv5 peer discovery, so a lean node can find peers instead of
being handed them. Off by default;
--discovery.enableturns it on and--discovery.portgives it its own UDP socket (it must differ from--gossipsub-port, and the node refuses to start otherwise rather thanfailing later with an opaque
EADDRINUSE). Static bootnode dialing isuntouched.
Built on ethrex's discovery stack:
DiscoveryServerruns discv5-only andwrites what it finds into a
PeerTable, whichP2PServerpolls, filters anddials over libp2p QUIC.
How peers are judged
Admission follows the beacon phase0 p2p spec, mirroring lighthouse's
eth2_fork_predicate:eth2entryfork_digestnext_fork_version/next_fork_epochquicportsecp256k1,ip/ip6These live in a
LeanFilterhanded to the peer table as itsPeerFilter, soeach record is judged the moment it arrives rather than at dial time. No
rejection is final: the peer table re-runs the filter as soon as the peer
publishes a higher-
seqENR, so a node that adds aquicentry or gains anaddress through discv5's IP voting is reconsidered without a restart.
Admitted peers are ranked by how many attestation subnets they advertise that
no connected peer covers, so discovery fills coverage gaps first.
attnetsisself-reported and unauthenticated, so subnet ids at or beyond the local
committee count are dropped before ranking sees them: otherwise an ENR padding
its bitfield with a few hundred bytes of
0xFFwould outrank every honest peerforever.
Also here
--discovery.advertise-ipseparates the bound address from the advertisedone, for a node behind NAT or on a host whose public IP is not what it binds.
GET /lean/v0/node/identityreports the local ENR alongside the peer id,grouped into a
NodeIdentitystruct.quicport: one with only audpentryis kept as a discv5 seed even though it cannot be dialed over libp2p.
docs/discovery.mdcovers the ENR layout, the admission rules, the operatorflags and the known limitations.
Dependency
ethrex-p2pis pinned to the unmergedfeat/discovery-peer-requirementsbranch, which carries the unified
DiscoveryServer, the peer table and thePeerFilterseam.Cargo.lockpins the exact commit, so builds arereproducible. This should be repointed at a main revision before merge.
Note that ethrex still uses libssz 0.2.2 while ethlambda is on 0.3.0, so the
dependency graph now carries both. Nothing SSZ-typed crosses the boundary
(lean's
EnrForkIdis its own type), but it is worth knowing.Known limitation: discovery is one-sided
DiscoveryServer::spawnbuilds its own local record from the ethrexNodeandoffers no way to seed the consensus entries, so the ENR ethrex answers queries
with carries
ip,udpandsecp256k1but noteth2,attnetsorquic. The ENR this node reports is complete; the one it serves is not.We therefore find and admit lean peers, but a lean peer applying these same
rules to what ethrex serves would refuse us for a missing
quicentry. Closingthis needs a way to hand
spawna prepared record.Separately, lean's
fork_digestis the hardcoded cross-client dummy0x12345678, so theeth2check separates lean from non-lean but not onelean devnet from another. Two devnets running this code will peer with each
other.
Testing
make lintclean.cargo test --workspace --profile release-fast --no-fail-fast: 601 passed,0 failed, including the forkchoice, signature, STF and SSZ spec tests.
oversized-
attnetsranking attack, subnet ranking, andspawn_discoverybinding a real socket (including
--discovery.advertise-ipand a busy port).Draft because the ethrex dependency is still an unmerged branch.