Fulu -- Networking¶
- Introduction
- Modifications in Fulu
- Preset
- Configuration
- Containers
- Helpers
- Modified
compute_fork_version - New
compute_max_request_data_column_sidecars verify_data_column_sidecarverify_data_column_sidecar_kzg_proofsverify_data_column_sidecar_inclusion_proofverify_partial_data_column_header_inclusion_proofverify_partial_data_column_sidecar_kzg_proofscompute_subnet_for_data_column_sidecar
- Modified
- MetaData
- The gossip domain: gossipsub
- The Req/Resp domain
- The discovery domain: discv5
- Peer scoring
- Supernodes
Introduction¶
This document contains the consensus-layer networking specifications for Fulu.
The specification of these changes continues in the same format as the network specifications of previous upgrades, and assumes them as pre-requisite.
Modifications in Fulu¶
Preset¶
| Name | Value | Description |
|---|---|---|
KZG_COMMITMENTS_INCLUSION_PROOF_DEPTH |
uint64(floorlog2(get_generalized_index(BeaconBlockBody, 'blob_kzg_commitments'))) (= 4) |
Merkle proof index for blob_kzg_commitments |
Configuration¶
[New in Fulu:EIP7594]
| Name | Value | Description |
|---|---|---|
DATA_COLUMN_SIDECAR_SUBNET_COUNT |
128 |
The number of data column sidecar subnets used in the gossipsub protocol |
MIN_EPOCHS_FOR_DATA_COLUMN_SIDECARS_REQUESTS |
2**12 (= 4,096 epochs) |
The minimum epoch range over which a node must serve data column sidecars |
Containers¶
DataColumnsByRootIdentifier¶
PartialDataColumnSidecar¶
The PartialDataColumnSidecar is similar to the DataColumnSidecar container,
except that only the cells and proofs identified by the bitmap are present.
Note: The column index is inferred from the gossipsub topic subnet.
PartialDataColumnPartsMetadata¶
Peers communicate the cells available with a bitmap. A set bit (1) at index
i means that the peer has the cell at index i. Peers explicitly request
cells with a second request bitmap of the same length that is set to 1 if the
peer would like to receive or provide this cell.
If a cell is available, its corresponding proof MUST be available.
This is encoded as the following SSZ container:
This means that for each cell there are two bits of state. Where the first bit represents the bit from the available bitlist, and the second bit represents the bit from the requests bit.
| Bits | Description |
|---|---|
| 00 | The peer does not have the cell and does not want it |
| 01 | The peer does not have the cell and does want it |
| 1X | The peer has the cell and is willing to provide it |
Having a cell but not willing to provide it is functionally the same as not having the cell and not wanting it, so it does not need a separate state.
PartialDataColumnHeader¶
The PartialDataColumnHeader is the header that is common to all columns for a
given block. It lets a peer identify which blobs are included in a block, as
well as validating cells and proofs. This header is only sent on eager pushes
because a peer can only make a request after having the data in this header.
This header can be derived from a beacon block or a DataColumnSidecar.
Helpers¶
Modified compute_fork_version¶
New compute_max_request_data_column_sidecars¶
verify_data_column_sidecar¶
verify_data_column_sidecar_kzg_proofs¶
verify_data_column_sidecar_inclusion_proof¶
verify_partial_data_column_header_inclusion_proof¶
verify_partial_data_column_sidecar_kzg_proofs¶
compute_subnet_for_data_column_sidecar¶
MetaData¶
The MetaData stored locally by clients is updated with an additional field to
communicate the custody group count.
Where
seq_number,attnets, andsyncnetshave the same meaning defined in the Altair document.custody_group_countrepresents the node's custody group count. Clients MAY reject peers with a value less thanCUSTODY_REQUIREMENT.
The gossip domain: gossipsub¶
Some gossip meshes are upgraded in Fulu to support upgraded types.
Topics and messages¶
Global topics¶
beacon_block¶
Updated validation
- [REJECT] The length of KZG commitments is less than or equal to the
limitation defined in the consensus layer -- i.e. validate that
len(signed_beacon_block.message.body.blob_kzg_commitments) <= get_blob_parameters(get_current_epoch(state)).max_blobs_per_block
Blob subnets¶
Deprecated blob_sidecar_{subnet_id}¶
blob_sidecar_{subnet_id} is deprecated.
data_column_sidecar_{subnet_id}¶
This topic is used to propagate column sidecars, where each column maps to some
subnet_id.
The type of the payload of this topic is DataColumnSidecar.
The following validations MUST pass before forwarding the
sidecar: DataColumnSidecar on the network, assuming the alias
block_header = sidecar.signed_block_header.message:
- [REJECT] The sidecar is valid as verified by
verify_data_column_sidecar(sidecar). - [REJECT] The sidecar is for the correct subnet -- i.e.
compute_subnet_for_data_column_sidecar(sidecar.index) == subnet_id. - [IGNORE] The sidecar is not from a future slot (with a
MAXIMUM_GOSSIP_CLOCK_DISPARITYallowance) -- i.e. validate thatblock_header.slot <= current_slot(a client MAY queue future sidecars for processing at the appropriate slot). - [IGNORE] The sidecar is from a slot greater than the latest finalized slot
-- i.e. validate that
block_header.slot > compute_start_slot_at_epoch(state.finalized_checkpoint.epoch) - [REJECT] The proposer signature of
sidecar.signed_block_header, is valid with respect to theblock_header.proposer_indexpubkey. - [IGNORE] The sidecar's block's parent (defined by
block_header.parent_root) has been seen (via gossip or non-gossip sources) (a client MAY queue sidecars for processing once the parent block is retrieved). - [REJECT] The sidecar's block's parent (defined by
block_header.parent_root) passes validation. - [REJECT] The sidecar is from a higher slot than the sidecar's block's parent
(defined by
block_header.parent_root). - [REJECT] The current finalized_checkpoint is an ancestor of the sidecar's
block -- i.e.
get_checkpoint_block(store, block_header.parent_root, store.finalized_checkpoint.epoch) == store.finalized_checkpoint.root. - [REJECT] The sidecar's
kzg_commitmentsfield inclusion proof is valid as verified byverify_data_column_sidecar_inclusion_proof(sidecar). - [REJECT] The sidecar's column data is valid as verified by
verify_data_column_sidecar_kzg_proofs(sidecar). - [IGNORE] The sidecar is the first sidecar for the tuple
(block_header.slot, block_header.proposer_index, sidecar.index)with valid header signature, sidecar inclusion proof, and kzg proof. - [REJECT] The sidecar is proposed by the expected
proposer_indexfor the block's slot in the context of the current shuffling (defined byblock_header.parent_root/block_header.slot). If theproposer_indexcannot immediately be verified against the expected shuffling, the sidecar MAY be queued for later processing while proposers for the block's branch are calculated -- in such a case do notREJECT, insteadIGNOREthis message.
Note: In the verify_data_column_sidecar_inclusion_proof(sidecar) check, for
all the sidecars of the same block, it verifies against the same set of
kzg_commitments of the given beacon block. Client can choose to cache the
result of the arguments tuple
(sidecar.kzg_commitments, sidecar.kzg_commitments_inclusion_proof, sidecar.signed_block_header).
Distributed blob publishing using blobs retrieved from local execution-layer client¶
Honest nodes SHOULD query engine_getBlobsV2 as soon as they receive a valid
beacon_block or data_column_sidecar from gossip. If ALL blobs matching
kzg_commitments are retrieved, they should convert the response to data
columns, and import the result.
Implementers are encouraged to leverage this method to increase the likelihood of incorporating and attesting to the last block when its proposer is not able to publish data columns on time.
When clients use the local execution layer to retrieve blobs, they SHOULD skip
verification of those blobs. When subsequently importing the blobs as data
columns, they MUST behave as if the data_column_sidecar had been received via
gossip. In particular, clients MUST:
- Publish the corresponding
data_column_sidecaron thedata_column_sidecar_{subnet_id}topic if and only if they are subscribed to it, either due to custody requirements or additional sampling. - Update gossip rule related data structures (i.e. update the anti-equivocation cache).
Partial Messages on data_column_sidecar_{subnet_id}¶
Validating partial messages happens in two parts. First, the
PartialDataColumnHeader needs to be validated, then the cell and proof data.
Once a PartialDataColumnHeader is validated for a corresponding block on any
subnet (gossipsub topic), it can be used for all subnets.
Due to the nature of partial messages, it is possible to get the
PartialDataColumnHeader with no cells, and get cells in a future response.
For all partial messages:
- [REJECT] A header and/or cells are present in the message (it is not semantically empty).
- [REJECT] There are the same number of cells and proofs in the message and this number is equal the number of 1s in the cells_present_bitmap.
For verifying the PartialDataColumnHeader in a partial message:
- [REJECT] If a valid header was previously received, the received header MUST equal the previously valid header.
- [REJECT] The hash of the block header in
signed_block_headerMUST be the same one identified by the partial message's group id. - [REJECT] The header's
kzg_commitmentslist is non-empty. - [IGNORE] The header is not from a future slot (with a
MAXIMUM_GOSSIP_CLOCK_DISPARITYallowance) -- i.e. validate thatblock_header.slot <= current_slot(a client MAY queue future headers for processing at the appropriate slot). - [IGNORE] The header is from a slot greater than the latest finalized slot --
i.e. validate that
block_header.slot > compute_start_slot_at_epoch(state.finalized_checkpoint.epoch).j - [REJECT] The proposer signature of
signed_block_headeris valid with respect to theblock_header.proposer_indexpubkey. - [IGNORE] The header's block's parent (defined by
block_header.parent_root) has been seen (via gossip or non-gossip sources) (a client MAY queue header for processing once the parent block is retrieved). - [REJECT] The header's block's parent (defined by
block_header.parent_root) passes validation. - [REJECT] The header is from a higher slot than the header's block's parent
(defined by
block_header.parent_root). - [REJECT] The current
finalized_checkpointis an ancestor of the header's block -- i.e.get_checkpoint_block(store, block_header.parent_root, store.finalized_checkpoint.epoch) == store.finalized_checkpoint.root. - [REJECT] The header's
kzg_commitmentsfield inclusion proof is valid as verified byverify_partial_data_column_header_inclusion_proof. - [REJECT] The header is proposed by the expected
proposer_indexfor the block's slot in the context of the current shuffling (defined byblock_header.parent_root/block_header.slot). If theproposer_indexcannot immediately be verified against the expected shuffling, the header MAY be queued for later processing while proposers for the block's branch are calculated -- in such a case do notREJECT, insteadIGNOREthis message.
For verifying the cells in a partial message:
- [IGNORE] If the received partial message contains only cell and proof data,
the node has seen a valid corresponding
PartialDataColumnHeader. - [IGNORE] The corresponding header is not from a future slot. See related header check above for more details.
- [IGNORE] The corresponding header is from a slot greater than the latest finalized slot. See related header check above for more details.
- [REJECT] The cells present bitmap length is equal to the number of KZG
commitments in the
PartialDataColumnHeader. - [REJECT] For cells the receiver already has, The sidecar's cell and proof data are equal to the local copy. This an optional check for the receiver, but the sender MUST always send valid cell and proof data.
- [REJECT] The sidecar's cell and proof data is valid as verified by
verify_partial_data_column_sidecar_kzg_proofs(sidecar, header.kzg_commitments, column_index).
Partial columns for Cell Dissemination¶
Gossipsub's Partial Message Extension enables exchanging selective parts of a message rather than the whole. The specification here describes how consensus-layer clients use Partial Messages to disseminate cells along with their proofs.
Partial message group ID¶
When sending a partial message, the gossipsub group ID MUST be the block root prefixed by a single byte used for versioning. The version byte MUST be zero. Other versions may be defined later.
Parts metadata¶
The parts metadata is encoded with the PartialDataColumnPartsMetadata
container.
Encoding and decoding responses¶
All responses MUST be encoded and decoded with the PartialDataColumnSidecar
container.
Eager pushing¶
In contrast to standard gossipsub, a client explicitly requests missing parts from a peer. A client can send its request before receiving a peer's parts metadata. This registers interest in certain parts, even if the peer does not have these parts yet.
This request can introduce extra latency compared to a peer unconditionally pushing messages, especially in the first hop of dissemination.
To address this tradeoff, a client MAY choose to eagerly push some (or all) of the cells it has. Clients SHOULD only do this when they are reasonably confident that a peer does not have the provided cells. For example, a proposer including private blobs SHOULD eagerly push the cells corresponding to the private blobs.
Clients, by default, SHOULD NOT eagerly push cells when proposing a block. Clients SHOULD expose a flag to opt-in to eagerly pushing all cells when proposing a block.
Clients SHOULD eagerly push the PartialDataColumnHeader to inform peers as to
which blobs are included in this block, and therefore which cells they are
missing. Clients SHOULD NOT send a PartialDataColumnHeader to a peer that has
already sent the client a message, as that peer already has the header (it is a
prerequisite to sending a message).
Clients MAY choose to not eagerly push the PartialDataColumnHeader if it has
previously sent the header to the peer on another topic.
Clients SHOULD request cells from peers after validating a
PartialDataColumnHeader, even if the corresponding block has not been seen
yet.
Interaction with standard gossipsub¶
Requesting partial messages¶
A peer requests partial messages for a topic by setting the partial field in
gossipsub's SubOpts RPC message to true.
Mesh¶
The Partial Message Extension uses the same mesh peers for a given topic as the
standard gossipsub topics for DataColumnSidecars.
Fanout¶
The Partial Message Extension uses the same fanout peers for a given topic as
the standard gossipsub topics for DataColumnSidecars.
Scoring¶
On receiving useful novel data from a peer, the client should report to gossipsub a positive first message delivery.
Clients SHOULD limit the rate at which a peer gets the first message delivery reward to prevent a peer from scoring better by providing cells one at a time rather than many cells at once.
On receiving invalid data, the client should report to gossipsub an invalid message delivery.
Forwarding¶
Once clients can construct the full DataColumnSidecar after receiving missing
cells, they should forward the full DataColumnSidecar over standard gossipsub
to peers that do not support partial messages. This provides backwards
compatibility with nodes that do not yet support partial messages.
Avoid forwarding the full DataColumnSidecar message to peers that requested
partial messages for that given topic. It is purely redundant information.
The Req/Resp domain¶
Messages¶
Status v2¶
Protocol ID: /eth2/beacon_chain/req/status/2/
Request, Response Content:
As seen by the client at the time of sending the message:
earliest_available_slot: The slot of earliest available block (SignedBeaconBlock).
Note: According the the definition of earliest_available_slot:
- If the node is able to serve all blocks throughout the entire sidecars
retention period (as defined by both
MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTSandMIN_EPOCHS_FOR_DATA_COLUMN_SIDECARS_REQUESTS), but is NOT able to serve all sidecars during this period, it should advertise the earliest slot from which it can serve all sidecars. - If the node is able to serve all sidecars throughout the entire sidecars
retention period (as defined by both
MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTSandMIN_EPOCHS_FOR_DATA_COLUMN_SIDECARS_REQUESTS), it should advertise the earliest slot from which it can serve all blocks.
BlobSidecarsByRange v1¶
Protocol ID: /eth2/beacon_chain/req/blob_sidecars_by_range/1/
Deprecated as of FULU_FORK_EPOCH + MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS.
During the deprecation transition period:
- Clients MUST respond with a list of blob sidecars from the range
[min(current_epoch - MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS, FULU_FORK_EPOCH), FULU_FORK_EPOCH)if the requested range includes any epochs in this interval. - Clients MAY respond with an empty list if the requested range lies entirely at
or after
FULU_FORK_EPOCH. - Clients SHOULD NOT penalize peers for requesting blob sidecars from
FULU_FORK_EPOCH.
BlobSidecarsByRoot v1¶
Protocol ID: /eth2/beacon_chain/req/blob_sidecars_by_root/1/
Deprecated as of FULU_FORK_EPOCH + MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS.
During the deprecation transition period:
- Clients MUST respond with blob sidecars corresponding to block roots from the
range
[min(current_epoch - MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS, FULU_FORK_EPOCH), FULU_FORK_EPOCH)if any of the requested roots correspond to blocks in this interval. - Clients MAY respond with an empty list if all requested roots correspond to
blocks at or after
FULU_FORK_EPOCH. - Clients SHOULD NOT penalize peers for requesting blob sidecars from
FULU_FORK_EPOCH.
DataColumnSidecarsByRange v1¶
Protocol ID: /eth2/beacon_chain/req/data_column_sidecars_by_range/1/
Request Content:
Response Content:
Requests data column sidecars in the slot range
[start_slot, start_slot + count) of the given columns, leading up to the
current head block as selected by fork choice.
Before consuming the next response chunk, the response reader SHOULD verify the
data column sidecar is well-formatted through verify_data_column_sidecar, has
valid inclusion proof through verify_data_column_sidecar_inclusion_proof, and
is correct w.r.t. the expected KZG commitments through
verify_data_column_sidecar_kzg_proofs.
DataColumnSidecarsByRange is primarily used to sync data columns that may have
been missed on gossip and to sync within the
MIN_EPOCHS_FOR_DATA_COLUMN_SIDECARS_REQUESTS window.
The request MUST be encoded as an SSZ-container.
The response MUST consist of zero or more response_chunk. Each successful
response_chunk MUST contain a single DataColumnSidecar payload.
Let data_column_serve_range be
[max(current_epoch - MIN_EPOCHS_FOR_DATA_COLUMN_SIDECARS_REQUESTS, FULU_FORK_EPOCH), current_epoch].
Clients MUST keep a record of data column sidecars seen on the epoch range
data_column_serve_range where current_epoch is defined by the current
wall-clock time, and clients MUST support serving requests of data columns on
this range.
Peers that are unable to reply to data column sidecar requests within the range
data_column_serve_range SHOULD respond with error code
3: ResourceUnavailable. Such peers that are unable to successfully reply to
this range of requests MAY get descored or disconnected at any time.
Note: The above requirement implies that nodes that start from a recent weak
subjectivity checkpoint MUST backfill the local data columns database to at
least the range data_column_serve_range to be fully compliant with
DataColumnSidecarsByRange requests.
Note: Although clients that bootstrap from a weak subjectivity checkpoint can begin participating in the networking immediately, other peers MAY disconnect and/or temporarily ban such an un-synced or semi-synced client.
Clients MUST respond with at least the data column sidecars of the first
blob-carrying block that exists in the range, if they have it, and no more than
compute_max_request_data_column_sidecars() sidecars.
Clients MUST include all data column sidecars of each block from which they include data column sidecars.
The following data column sidecars, where they exist, MUST be sent in
(slot, column_index) order.
Slots that do not contain known data columns MUST be skipped, mimicking the
behaviour of the BlocksByRange request. Only response chunks with known data
columns should therefore be sent.
Clients MAY limit the number of data column sidecars in the response.
The response MUST contain no more than count * NUMBER_OF_COLUMNS data column
sidecars.
Clients MUST respond with data columns sidecars from their view of the current
fork choice -- that is, data column sidecars as included by blocks from the
single chain defined by the current head. Of note, blocks from slots before the
finalization MUST lead to the finalized block reported in the Status
handshake.
Clients MUST respond with data column sidecars that are consistent from a single chain within the context of the request.
After the initial data column sidecar, clients MAY stop in the process of responding if their fork choice changes the view of the chain in the context of the request.
For each successful response_chunk, the ForkDigest context epoch is
determined by
compute_epoch_at_slot(data_column_sidecar.signed_block_header.message.slot).
Per fork_version = compute_fork_version(epoch):
epoch |
Chunk SSZ type |
|---|---|
FULU_FORK_EPOCH and later |
fulu.DataColumnSidecar |
DataColumnSidecarsByRoot v1¶
Protocol ID: /eth2/beacon_chain/req/data_column_sidecars_by_root/1/
[New in Fulu:EIP7594]
Request Content:
Response Content:
Requests data column sidecars by block root and column indices. The response is
a list of DataColumnSidecar whose length is less than or equal to
requested_columns_count, where
requested_columns_count = sum(len(r.columns) for r in request). It may be less
in the case that the responding peer is missing blocks or sidecars.
Before consuming the next response chunk, the response reader SHOULD verify the
data column sidecar is well-formatted through verify_data_column_sidecar, has
valid inclusion proof through verify_data_column_sidecar_inclusion_proof, and
is correct w.r.t. the expected KZG commitments through
verify_data_column_sidecar_kzg_proofs.
No more than compute_max_request_data_column_sidecars() may be requested at a
time.
The response MUST consist of zero or more response_chunk. Each successful
response_chunk MUST contain a single DataColumnSidecar payload.
Clients MUST support requesting sidecars since minimum_request_epoch, where
minimum_request_epoch = max(current_epoch - MIN_EPOCHS_FOR_DATA_COLUMN_SIDECARS_REQUESTS, FULU_FORK_EPOCH).
If any root in the request content references a block earlier than
minimum_request_epoch, peers MAY respond with error code
3: ResourceUnavailable or not include the data column sidecar in the response.
Clients MUST respond with at least one sidecar, if they have it. Clients MAY limit the number of blocks and sidecars in the response.
Clients SHOULD include a sidecar in the response as soon as it passes the gossip validation rules. Clients SHOULD NOT respond with sidecars related to blocks that fail gossip validation rules. Clients SHOULD NOT respond with sidecars related to blocks that fail the beacon-chain state transition
For each successful response_chunk, the ForkDigest context epoch is
determined by
compute_epoch_at_slot(data_column_sidecar.signed_block_header.message.slot).
Per fork_version = compute_fork_version(epoch):
epoch |
Chunk SSZ type |
|---|---|
FULU_FORK_EPOCH and later |
fulu.DataColumnSidecar |
GetMetaData v3¶
Protocol ID: /eth2/beacon_chain/req/metadata/3/
No Request Content.
Response Content:
Requests the MetaData of a peer, using the new MetaData definition given above
that is extended from Altair. Other conditions for the GetMetaData protocol
are unchanged from the Altair p2p networking document.
The discovery domain: discv5¶
ENR structure¶
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):
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.
Custody group count¶
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.
Peer scoring¶
Due to the deterministic custody functions, a node knows exactly what a peer should be able to respond to. In the event that a peer does not respond to samples of their custodied rows/columns, a node may downscore or disconnect from a peer.
Supernodes¶
A supernode is a node which subscribes to all data column sidecar subnets, custodies all data column sidecars, and performs reconstruction and cross-seeding. Being a supernode requires considerably higher bandwidth, storage, and computation resources. In order to reconstruct missing data, there must be at least one supernode on the network. Due to validator custody requirements, a node which is connected to validator(s) with a combined balance greater than or equal to 4096 ETH must be a supernode. Moreover, any node with the necessary resources may altruistically be a supernode. Therefore, there are expected to be many (hundreds) of supernodes on mainnet and it is likely (though not necessary) for a node to be connected to several of these by chance.