feat(discv4): serve both IP families on the shared discovery UDP port#26402
Open
MysticRyuujin wants to merge 8 commits into
Open
feat(discv4): serve both IP families on the shared discovery UDP port#26402MysticRyuujin wants to merge 8 commits into
MysticRyuujin wants to merge 8 commits into
Conversation
discv4 egress now routes each packet to the socket matching the destination family; the --v5 sibling socket doubles as discv4's secondary-family egress and the EIP-868 ENR advertises both families. Previously opposite-family pings were received via the unrecognized-frame forwarder but every reply died in send_to with an address-family error. Also adds --bootnodes (enode URLs and signed ENRs) to seed both discovery protocols; ENR entries seed discv5 signed-only so bootstrap doesn't block on a redundant unsigned request_enr, and entries whose family has no bound socket warn at startup.
The test helpers bound 0.0.0.0 and services then dialed each other at that address, which only Linux delivers (aliased to loopback); macOS and Windows reject the sends with EHOSTUNREACH, so every test doing a real UDP exchange hung through its timeout or failed off-Linux. Also renames test_bootnode_not_in_update_stream: it has asserted the bootnode DOES appear since it was introduced in paradigmxyz#14787.
Review findings on the unreleased dual-stack/bootnodes work: - discv5's bootstrap aborts on an ENR it cannot contact (AddNodeFailed): filter --bootnodes seeds by the families the listen config serves, so an unservable entry warns and is skipped instead of killing the node - derive an ENR seed's discv4 endpoint from a served family instead of the ip4-first conversion default - normalize v4-mapped enode hosts and reject seeds with a zero discovery port or a node id off the secp256k1 curve (discv5 dropped those silently) - pin the record's IP family once a secondary is advertised so a periodic external-ip re-resolution can't flip it, and normalize the same-family filter once at service construction - leave unspecified addresses out of the EIP-868 ENR rather than advertise 0.0.0.0 next to a valid opposite-family endpoint - pass the primary socket's family into send_loop instead of a fallible re-query that fails open
In shared-port dual-stack mode the discv5 forwarder feeds both sockets' unrecognized frames into discv4, so IPv6 discv4 pings were received and processed but every pong died on the IPv4-only egress socket. Bind the dual-stack sibling before discv4 starts and pass it as the secondary egress — replies are source-addressed, so no advertisement changes are needed.
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.
Follow-up to #26089.
In shared-port mode discv4 could only send through its primary-family socket, while discv5's forwarder fed it packets from both family sockets — opposite-family pings were received and answered, then every reply died in
send_towith an address-family error. Outgoing discv4 packets are now routed to the socket matching the destination family, and both the bootnode command and the node'sDiscoverybind the dual-stack sibling socket before discv4 starts so it doubles as the secondary egress. Replies are source-addressed, so the node side needs no advertisement changes; the bootnode's EIP-868 ENR additionally advertises both families.Also adds
--bootnodes(enode URLs and signed ENRs) to the bootnode command — both discovery configs already support bootstrap nodes, there was just no CLI exposure. Seeds are validated and filtered against the families the node serves: an unservable ENR warns and is skipped instead of aborting startup via discv5'sAddNodeFailed, ENR endpoints prefer a served family over the ip4-first conversion default, v4-mapped hosts are normalized, and zero-port or off-curve-id entries error instead of failing silently.The test commit binds the discv4 test helpers to loopback: services dialed each other at
0.0.0.0, which only Linux delivers — on macOS/Windows every real UDP exchange failed withEHOSTUNREACHand hung through its timeout (the suite now runs in ~1s off-Linux). Also renamestest_bootnode_not_in_update_stream, which has asserted the bootnode does appear since #14787.Two behavior notes for review:
ip 0.0.0.0. This also applies to a full node before external-IP resolution completes; EIP-778 keys are optional, and resolution re-adds the endpoint once known.enr_update), and wiringsecondary_advertised_ipintoNetworkConfigis left for a follow-up.