mirror of
https://github.com/status-im/nimbus-eth2.git
synced 2025-01-11 14:54:12 +00:00
f1ddcfff0f
* support connecting to peers without bellatrix Make discovery fork ID aware of scheduled Bellatrix fork to enable connections to peers that don't have Bellatrix scheduled yet. Without this, has peering issues with peers on older SW version. * expand tests with compatibility checks * more exhaustive compatibility checks
209 lines
8.9 KiB
Nim
209 lines
8.9 KiB
Nim
# beacon_chain
|
|
# Copyright (c) 2018-2022 Status Research & Development GmbH
|
|
# Licensed and distributed under either of
|
|
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
|
|
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
|
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
|
|
|
when (NimMajor, NimMinor) < (1, 4):
|
|
{.push raises: [Defect].}
|
|
else:
|
|
{.push raises: [].}
|
|
|
|
import
|
|
"."/[helpers, forks],
|
|
"."/datatypes/base
|
|
|
|
export base
|
|
|
|
const
|
|
# https://github.com/ethereum/consensus-specs/blob/v1.2.0-rc.2/specs/phase0/p2p-interface.md#topics-and-messages
|
|
topicBeaconBlocksSuffix* = "beacon_block/ssz_snappy"
|
|
topicVoluntaryExitsSuffix* = "voluntary_exit/ssz_snappy"
|
|
topicProposerSlashingsSuffix* = "proposer_slashing/ssz_snappy"
|
|
topicAttesterSlashingsSuffix* = "attester_slashing/ssz_snappy"
|
|
topicAggregateAndProofsSuffix* = "beacon_aggregate_and_proof/ssz_snappy"
|
|
|
|
# https://github.com/ethereum/consensus-specs/blob/v1.2.0-rc.2/specs/phase0/p2p-interface.md#configuration
|
|
MAX_CHUNK_SIZE* = 1 * 1024 * 1024 # bytes
|
|
GOSSIP_MAX_SIZE* = 1 * 1024 * 1024 # bytes
|
|
TTFB_TIMEOUT* = 5.seconds
|
|
RESP_TIMEOUT* = 10.seconds
|
|
|
|
# https://github.com/ethereum/consensus-specs/blob/v1.2.0-rc.3/specs/bellatrix/p2p-interface.md#configuration
|
|
GOSSIP_MAX_SIZE_BELLATRIX* = 10 * 1024 * 1024 # bytes
|
|
MAX_CHUNK_SIZE_BELLATRIX* = 10 * 1024 * 1024 # bytes
|
|
|
|
defaultEth2TcpPort* = 9000
|
|
defaultEth2TcpPortDesc* = $defaultEth2TcpPort
|
|
|
|
# This is not part of the spec! But its port which uses Lighthouse
|
|
defaultEth2RestPort* = 5052
|
|
defaultEth2RestPortDesc* = $defaultEth2RestPort
|
|
|
|
enrAttestationSubnetsField* = "attnets"
|
|
enrSyncSubnetsField* = "syncnets"
|
|
enrForkIdField* = "eth2"
|
|
|
|
template eth2Prefix(forkDigest: ForkDigest): string =
|
|
"/eth2/" & $forkDigest & "/"
|
|
|
|
func getBeaconBlocksTopic*(forkDigest: ForkDigest): string =
|
|
eth2Prefix(forkDigest) & topicBeaconBlocksSuffix
|
|
|
|
func getVoluntaryExitsTopic*(forkDigest: ForkDigest): string =
|
|
eth2Prefix(forkDigest) & topicVoluntaryExitsSuffix
|
|
|
|
func getProposerSlashingsTopic*(forkDigest: ForkDigest): string =
|
|
eth2Prefix(forkDigest) & topicProposerSlashingsSuffix
|
|
|
|
func getAttesterSlashingsTopic*(forkDigest: ForkDigest): string =
|
|
eth2Prefix(forkDigest) & topicAttesterSlashingsSuffix
|
|
|
|
func getAggregateAndProofsTopic*(forkDigest: ForkDigest): string =
|
|
eth2Prefix(forkDigest) & topicAggregateAndProofsSuffix
|
|
|
|
# https://github.com/ethereum/consensus-specs/blob/v1.2.0-rc.1/specs/phase0/validator.md#broadcast-attestation
|
|
func compute_subnet_for_attestation*(
|
|
committees_per_slot: uint64, slot: Slot, committee_index: CommitteeIndex):
|
|
SubnetId =
|
|
# Compute the correct subnet for an attestation for Phase 0.
|
|
# Note, this mimics expected Phase 1 behavior where attestations will be
|
|
# mapped to their shard subnet.
|
|
let
|
|
slots_since_epoch_start = slot.since_epoch_start()
|
|
committees_since_epoch_start =
|
|
committees_per_slot * slots_since_epoch_start
|
|
|
|
SubnetId(
|
|
(committees_since_epoch_start + committee_index.asUInt64) mod
|
|
ATTESTATION_SUBNET_COUNT)
|
|
|
|
# https://github.com/ethereum/consensus-specs/blob/v1.2.0-rc.1/specs/phase0/validator.md#broadcast-attestation
|
|
func getAttestationTopic*(forkDigest: ForkDigest,
|
|
subnetId: SubnetId): string =
|
|
## For subscribing and unsubscribing to/from a subnet.
|
|
eth2Prefix(forkDigest) & "beacon_attestation_" & $(subnetId) & "/ssz_snappy"
|
|
|
|
# https://github.com/ethereum/consensus-specs/blob/v1.2.0-rc.3/specs/altair/p2p-interface.md#topics-and-messages
|
|
func getSyncCommitteeTopic*(forkDigest: ForkDigest,
|
|
subcommitteeIdx: SyncSubcommitteeIndex): string =
|
|
## For subscribing and unsubscribing to/from a subnet.
|
|
eth2Prefix(forkDigest) & "sync_committee_" & $subcommitteeIdx & "/ssz_snappy"
|
|
|
|
# https://github.com/ethereum/consensus-specs/blob/v1.2.0-rc.3/specs/altair/p2p-interface.md#topics-and-messages
|
|
func getSyncCommitteeContributionAndProofTopic*(forkDigest: ForkDigest): string =
|
|
## For subscribing and unsubscribing to/from a subnet.
|
|
eth2Prefix(forkDigest) & "sync_committee_contribution_and_proof/ssz_snappy"
|
|
|
|
# https://github.com/ethereum/consensus-specs/blob/v1.2.0-rc.3/specs/altair/light-client/p2p-interface.md#light_client_finality_update
|
|
func getLightClientFinalityUpdateTopic*(forkDigest: ForkDigest): string =
|
|
## For broadcasting or obtaining the latest `LightClientFinalityUpdate`.
|
|
eth2Prefix(forkDigest) & "light_client_finality_update/ssz_snappy"
|
|
|
|
# https://github.com/ethereum/consensus-specs/blob/v1.2.0-rc.3/specs/altair/light-client/p2p-interface.md#light_client_optimistic_update
|
|
func getLightClientOptimisticUpdateTopic*(forkDigest: ForkDigest): string =
|
|
## For broadcasting or obtaining the latest `LightClientOptimisticUpdate`.
|
|
eth2Prefix(forkDigest) & "light_client_optimistic_update/ssz_snappy"
|
|
|
|
func getENRForkID*(cfg: RuntimeConfig,
|
|
epoch: Epoch,
|
|
genesis_validators_root: Eth2Digest): ENRForkID =
|
|
let
|
|
current_fork_version = cfg.forkVersionAtEpoch(epoch)
|
|
next_fork_version = if cfg.nextForkEpochAtEpoch(epoch) == FAR_FUTURE_EPOCH:
|
|
current_fork_version
|
|
else:
|
|
cfg.forkVersionAtEpoch(cfg.nextForkEpochAtEpoch(epoch))
|
|
fork_digest = compute_fork_digest(current_fork_version,
|
|
genesis_validators_root)
|
|
ENRForkID(
|
|
fork_digest: fork_digest,
|
|
next_fork_version: next_fork_version,
|
|
next_fork_epoch: cfg.nextForkEpochAtEpoch(epoch))
|
|
|
|
func getDiscoveryForkID*(cfg: RuntimeConfig,
|
|
epoch: Epoch,
|
|
genesis_validators_root: Eth2Digest): ENRForkID =
|
|
# Until 1 epoch from fork, return pre-fork value.
|
|
if cfg.nextForkEpochAtEpoch(epoch) - epoch <= 1:
|
|
getENRForkID(cfg, epoch, genesis_validators_root)
|
|
else:
|
|
let
|
|
current_fork_version = cfg.forkVersionAtEpoch(epoch)
|
|
fork_digest = compute_fork_digest(current_fork_version,
|
|
genesis_validators_root)
|
|
ENRForkID(
|
|
fork_digest: fork_digest,
|
|
next_fork_version: current_fork_version,
|
|
next_fork_epoch: FAR_FUTURE_EPOCH)
|
|
|
|
# https://github.com/ethereum/consensus-specs/blob/v1.2.0-rc.3/specs/altair/p2p-interface.md#transitioning-the-gossip
|
|
type GossipState* = set[BeaconStateFork]
|
|
func getTargetGossipState*(
|
|
epoch, ALTAIR_FORK_EPOCH, BELLATRIX_FORK_EPOCH: Epoch, isBehind: bool):
|
|
GossipState =
|
|
if isBehind:
|
|
{}
|
|
|
|
# The order of these checks doesn't matter.
|
|
elif epoch >= BELLATRIX_FORK_EPOCH:
|
|
{BeaconStateFork.Bellatrix}
|
|
elif epoch + 1 < ALTAIR_FORK_EPOCH:
|
|
{BeaconStateFork.Phase0}
|
|
|
|
# Order remaining checks so ALTAIR_FORK_EPOCH == BELLATRIX_FORK_EPOCH works
|
|
# and when the transition zones align contiguously, or are separated by
|
|
# intermediate pure-Altair epochs.
|
|
#
|
|
# In the first case, should never enable Altair, and there's also never
|
|
# a Phase -> Altair, or Altair -> Bellatrix gossip transition epoch. In
|
|
# contiguous Phase0 -> Altair and Altair -> Bellatrix transitions, that
|
|
# pure Altair state gossip state never occurs, but it works without any
|
|
# special cases so long as one checks for transition-to-fork+1 before a
|
|
# pure fork gossip state.
|
|
#
|
|
# Therefore, check for transition-to-merge before pure-Altair.
|
|
elif epoch + 1 >= BELLATRIX_FORK_EPOCH:
|
|
# As there are only two fork epochs and there's no transition to phase0
|
|
{if ALTAIR_FORK_EPOCH == BELLATRIX_FORK_EPOCH:
|
|
BeaconStateFork.Phase0
|
|
else:
|
|
BeaconStateFork.Altair,
|
|
BeaconStateFork.Bellatrix}
|
|
elif epoch >= ALTAIR_FORK_EPOCH:
|
|
{BeaconStateFork.Altair}
|
|
|
|
# Must be after the case which catches phase0 => merge
|
|
elif epoch + 1 >= ALTAIR_FORK_EPOCH:
|
|
{BeaconStateFork.Phase0, BeaconStateFork.Altair}
|
|
else:
|
|
raiseAssert "Unknown target gossip state"
|
|
|
|
func nearSyncCommitteePeriod*(epoch: Epoch): Option[uint64] =
|
|
# https://github.com/ethereum/consensus-specs/blob/v1.2.0-rc.1/specs/altair/validator.md#sync-committee-subnet-stability
|
|
if epoch.is_sync_committee_period():
|
|
return some 0'u64
|
|
let epochsBefore =
|
|
EPOCHS_PER_SYNC_COMMITTEE_PERIOD - epoch.since_sync_committee_period_start()
|
|
if epoch.is_sync_committee_period() or epochsBefore <= SYNC_COMMITTEE_SUBNET_COUNT:
|
|
return some epochsBefore
|
|
|
|
none(uint64)
|
|
|
|
func getSyncSubnets*(
|
|
nodeHasPubkey: proc(pubkey: ValidatorPubKey):
|
|
bool {.noSideEffect, raises: [Defect].},
|
|
syncCommittee: SyncCommittee): SyncnetBits =
|
|
var res: SyncnetBits
|
|
for i, pubkey in syncCommittee.pubkeys:
|
|
if not nodeHasPubkey(pubkey):
|
|
continue
|
|
|
|
# https://github.com/ethereum/consensus-specs/blob/v1.2.0-rc.1/specs/altair/validator.md#broadcast-sync-committee-message
|
|
# The first quarter of the pubkeys map to subnet 0, the second quarter to
|
|
# subnet 1, the third quarter to subnet 2 and the final quarter to subnet
|
|
# 3.
|
|
res.setBit(i div (SYNC_COMMITTEE_SIZE div SYNC_COMMITTEE_SUBNET_COUNT))
|
|
res
|