Consensus discovery requirements
The discovery domain: discv5
Discovery Version 5 (discv5) (Protocol version v5.1) is used for peer discovery.
discv5 is a standalone protocol, running on UDP on a dedicated port, meant for
peer discovery only. discv5 supports self-certified, flexible peer records
(ENRs) and topic-based advertisement, both of which are (or will be)
requirements in this context.
Integration into libp2p stacks
discv5 SHOULD be integrated into the client’s libp2p stack by implementing an
adaptor to make it conform to the
service discovery
and
peer routing
abstractions and interfaces (go-libp2p links provided).
Inputs to operations include peer IDs (when locating a specific peer) or capabilities (when searching for peers with a specific capability), and the outputs will be multiaddrs converted from the ENR records returned by the discv5 backend.
This integration enables the libp2p stack to subsequently form connections and streams with discovered peers.
ENR structure
The Ethereum Node Record (ENR) for an Ethereum consensus client MUST contain the following entries (exclusive of the sequence number and signature, which MUST be present in an ENR):
- The compressed secp256k1 publickey, 33 bytes (
secp256k1field).
The ENR MAY contain the following entries:
- An IPv4 address (
ipfield) and/or IPv6 address (ip6field). - An IPv4 QUIC port (
quicfield) representing the local libp2p QUIC (UDP) listening port and/or the corresponding IPv6 port (quic6field). - An IPv4 TCP port (
tcpfield) representing the local libp2p TCP listening port and/or the corresponding IPv6 port (tcp6field). - An IPv4 UDP port (
udpfield) representing the local discv5 listening port and/or the corresponding IPv6 port (udp6field).
Specifications of these parameters can be found in the ENR Specification.
eth2 field
[Modified in Fulu:EIP7892]
Note: The structure of ENRForkID has not changed but the field value
computations have changed. Unless explicitly mentioned here, all specifications
from phase0/p2p-interface.md#eth2-field
carry over.
ENRs MUST carry a generic eth2 key with an 16-byte value of the node’s current
fork digest, next fork version, and next fork epoch to ensure connections are
made with peers on the intended Ethereum network.
| Key | Value |
|---|---|
eth2 | SSZ ENRForkID |
Specifically, the value of the eth2 key MUST be the following SSZ encoded
object (ENRForkID):
(
fork_digest: ForkDigest
next_fork_version: Version
next_fork_epoch: Epoch
)The fields of ENRForkID are defined as:
fork_digestiscompute_fork_digest(genesis_validators_root, epoch)where:genesis_validators_rootis the staticRootfound instate.genesis_validators_root.epochis the node’s current epoch defined by the wall-clock time (not necessarily the epoch to which the node is sync).
next_fork_versionis the fork version corresponding to the next planned fork at a future epoch. The fork version will only change for regular forks, not BPO forks. Note that it is possible for the blob schedule to define a change at the same epoch as a regular fork; this situation would be considered a regular fork. If no future fork is planned, setnext_fork_version = current_fork_versionto signal this fact.next_fork_epochis the epoch at which the next fork (whether a regular fork or a BPO fork) is planned. If no future fork is planned, setnext_fork_epoch = FAR_FUTURE_EPOCHto signal this fact.
Genesis and peer compatibility
Note: fork_digest is composed of values that are not known until the genesis
block/state are available. Due to this, clients SHOULD NOT form ENRs and begin
peer discovery until genesis values are known. One notable exception to this
rule is the distribution of bootnode ENRs prior to genesis. In this case,
bootnode ENRs SHOULD be initially distributed with eth2 field set as
ENRForkID(fork_digest=compute_fork_digest(b'\x00'*32, GENESIS_EPOCH), next_fork_version=GENESIS_FORK_VERSION, next_fork_epoch=FAR_FUTURE_EPOCH).
After genesis values are known, the bootnodes SHOULD update ENRs to participate
in normal discovery operations.
Clients SHOULD connect to peers with fork_digest, next_fork_version, and
next_fork_epoch that match local values.
Clients MAY connect to peers with the same fork_digest but a different
next_fork_version/next_fork_epoch. Unless ENRForkID is manually updated to
matching prior to the earlier next_fork_epoch of the two clients, these
connecting clients will be unable to successfully interact starting at the
earlier next_fork_epoch.
Attestation subnet bitfield
The ENR attnets entry signifies the attestation subnet bitfield with the
following form to more easily discover peers participating in particular
attestation gossip subnets.
| Key | Value |
|---|---|
attnets | SSZ BitVector[ATTESTATION_SUBNET_COUNT] |
If a node’s MetaData.attnets has any non-zero bit, the ENR MUST include the
attnets entry with the same value as MetaData.attnets.
If a node’s MetaData.attnets is composed of all zeros, the ENR MAY optionally
include the attnets entry or leave it out entirely.
Sync committee bitfield
An additional bitfield is added to the ENR under the key syncnets to
facilitate sync committee subnet discovery. The length of this bitfield is
SYNC_COMMITTEE_SUBNET_COUNT where each bit corresponds to a distinct
subnet_id for a specific sync committee subnet. The ith bit is set in this
bitfield if the validator is currently subscribed to the sync_committee_{i}
topic.
| Key | Value |
|---|---|
syncnets | SSZ BitVector[SYNC_COMMITTEE_SUBNET_COUNT] |
See the validator document for further details on how the new bits are used.
Custody group count and next fork digest once Fulu is scheduled
A new field is added to the ENR under the key cgc to facilitate custody data
column discovery. This new field MUST be added once FULU_FORK_EPOCH is
assigned any value other than FAR_FUTURE_EPOCH.
| Key | Value |
|---|---|
cgc | Custody group count, Uint64 big endian integer with no leading zero bytes (0 is encoded as empty byte string) |
Next fork digest
A new entry is added to the ENR under the key nfd, short for next fork
digest. This entry communicates the digest of the next scheduled fork,
regardless of whether it is a regular or a Blob-Parameters-Only fork. This new
entry MUST be added once FULU_FORK_EPOCH is assigned any value other than
FAR_FUTURE_EPOCH. Adding this entry prior to the Fulu upgrade will not impact
peering as nodes will ignore unknown ENR entries and nfd mismatches do not
cause disconnects.
If no next fork is scheduled, the nfd entry contains the default value for the
type (i.e., the SSZ representation of a zero-filled array).
| Key | Value |
|---|---|
nfd | SSZ Bytes4 ForkDigest |
When discovering and interfacing with peers, nodes MUST evaluate nfd alongside
their existing consideration of the ENRForkID::next_* fields under the eth2
key, to form a more accurate view of the peer’s intended next fork for the
purposes of sustained peering. If there is a mismatch, the node MUST NOT
disconnect before the fork boundary, but it MAY disconnect at/after the fork
boundary.
Nodes unprepared to follow the Fulu upgrade will be unaware of nfd entries.
However, their existing comparison of eth2 entries (concretely
next_fork_epoch) is sufficient to detect upcoming divergence.
Finding attestation subnet peers for upcoming duties
The beacon-chain shufflings are designed to provide a minimum of 1 epoch lookahead on the validator’s upcoming committee assignments for attesting dictated by the shuffling and slot. Note that this lookahead does not apply to proposing, which must be checked during the epoch in question.
get_committee_assignment should be called at the start of each epoch to get
the assignment for the next epoch (current_epoch + 1). A validator should plan
for future assignments by noting their assigned attestation slot and joining the
committee index attestation subnet related to their committee assignment.
Specifically a validator should:
- Call
_, committee_index, _ = get_committee_assignment(state, next_epoch, validator_index)when checking for next epoch assignments. - Calculate the committees per slot for the next epoch:
committees_per_slot = get_committee_count_per_slot(state, next_epoch) - Calculate the subnet index:
subnet_id = compute_subnet_for_attestation(committees_per_slot, slot, committee_index) - Find peers of the pubsub topic
beacon_attestation_{subnet_id}.- If an insufficient number of current peers are subscribed to the topic,
the validator must discover new peers on this topic. Via the discovery
protocol, find peers with an ENR containing the
attnetsentry such thatENR["attnets"][subnet_id] == True. Then validate that the peers are still persisted on the desired topic by requestingGetMetaDataand checking the resultingattnetsfield. - If the validator is assigned to be an aggregator for the slot (see
is_aggregator()), then subscribe to the topic.
- If an insufficient number of current peers are subscribed to the topic,
the validator must discover new peers on this topic. Via the discovery
protocol, find peers with an ENR containing the
Note: If the validator is not assigned to be an aggregator, the validator only needs sufficient number of peers on the topic to be able to publish messages. The validator does not need to subscribe and listen to all messages on the topic.