feat: update networking config to match CL spec (#8510)

**Motivation**

Conform to CL spec

**Description**

- move networking constants to config
- remove constants no longer part of spec
- enable "p2p-interface.md" in config test

Closes https://github.com/ChainSafe/lodestar/issues/6351
Closes https://github.com/ChainSafe/lodestar/issues/7529
This commit is contained in:
Nico Flaig
2025-10-13 14:44:41 +01:00
committed by GitHub
parent 6604fa5719
commit 598c1ec54e
30 changed files with 165 additions and 135 deletions

View File

@@ -1,5 +1,7 @@
import {
ATTESTATION_SUBNET_COUNT,
ATTESTATION_SUBNET_EXTRA_BITS,
ATTESTATION_SUBNET_PREFIX_BITS,
BASE_REWARDS_PER_EPOCH,
BLOB_TX_TYPE,
BLS_WITHDRAWAL_PREFIX,
@@ -25,15 +27,15 @@ import {
DOMAIN_SYNC_COMMITTEE,
DOMAIN_SYNC_COMMITTEE_SELECTION_PROOF,
DOMAIN_VOLUNTARY_EXIT,
EPOCHS_PER_RANDOM_SUBNET_SUBSCRIPTION,
ETH1_ADDRESS_WITHDRAWAL_PREFIX,
FAR_FUTURE_EPOCH,
FULL_EXIT_REQUEST_AMOUNT,
GENESIS_EPOCH,
GENESIS_SLOT,
JUSTIFICATION_BITS_LENGTH,
MAX_CONCURRENT_REQUESTS,
NODE_ID_BITS,
PROPOSER_WEIGHT,
RANDOM_SUBNETS_PER_VALIDATOR,
SYNC_COMMITTEE_SUBNET_COUNT,
SYNC_REWARD_WEIGHT,
TARGET_AGGREGATORS_PER_COMMITTEE,
@@ -84,9 +86,13 @@ export const specConstants = {
// phase0/validator.md
TARGET_AGGREGATORS_PER_COMMITTEE,
RANDOM_SUBNETS_PER_VALIDATOR,
EPOCHS_PER_RANDOM_SUBNET_SUBSCRIPTION,
// phase0/p2p-interface.md
NODE_ID_BITS,
MAX_CONCURRENT_REQUESTS,
ATTESTATION_SUBNET_COUNT,
ATTESTATION_SUBNET_EXTRA_BITS,
ATTESTATION_SUBNET_PREFIX_BITS,
// altair/beacon-chain.md
// ## Participation flag indices

View File

@@ -179,10 +179,13 @@ export function getValidatorApi(
/**
* Validator clock may be advanced from beacon's clock. If the validator requests a resource in a
* future slot, wait some time instead of rejecting the request because it's in the future.
* This value is the same to MAXIMUM_GOSSIP_CLOCK_DISPARITY_SEC.
* This value is the same to MAXIMUM_GOSSIP_CLOCK_DISPARITY.
* For very fast networks, reduce clock disparity to half a slot.
*/
const MAX_API_CLOCK_DISPARITY_SEC = Math.min(0.5, config.SLOT_DURATION_MS / 2000);
const MAX_API_CLOCK_DISPARITY_SEC = Math.min(
config.MAXIMUM_GOSSIP_CLOCK_DISPARITY / 1000,
config.SLOT_DURATION_MS / 2000
);
const MAX_API_CLOCK_DISPARITY_MS = MAX_API_CLOCK_DISPARITY_SEC * 1000;
/** Compute and cache the genesis block root */

View File

@@ -257,7 +257,7 @@ export class BeaconChain implements IBeaconChain {
this.attestationPool = new AttestationPool(config, clock, this.opts?.preaggregateSlotDistance, metrics);
this.aggregatedAttestationPool = new AggregatedAttestationPool(this.config, metrics);
this.syncCommitteeMessagePool = new SyncCommitteeMessagePool(config, clock, this.opts?.preaggregateSlotDistance);
this.syncContributionAndProofPool = new SyncContributionAndProofPool(clock, metrics, logger);
this.syncContributionAndProofPool = new SyncContributionAndProofPool(config, clock, metrics, logger);
this.seenAggregatedAttestations = new SeenAggregatedAttestations(metrics);
this.seenContributionAndProof = new SeenContributionAndProof(metrics);

View File

@@ -1,10 +1,10 @@
import {Signature, aggregateSignatures} from "@chainsafe/blst";
import {BitArray} from "@chainsafe/ssz";
import {ChainForkConfig} from "@lodestar/config";
import {SYNC_COMMITTEE_SIZE, SYNC_COMMITTEE_SUBNET_SIZE} from "@lodestar/params";
import {G2_POINT_AT_INFINITY} from "@lodestar/state-transition";
import {Root, Slot, SubnetID, altair, ssz} from "@lodestar/types";
import {Logger, MapDef, toRootHex} from "@lodestar/utils";
import {MAXIMUM_GOSSIP_CLOCK_DISPARITY} from "../../constants/constants.js";
import {Metrics} from "../../metrics/metrics.js";
import {IClock} from "../../util/clock.js";
import {InsertOutcome, OpPoolError, OpPoolErrorCode} from "./types.js";
@@ -51,6 +51,7 @@ export class SyncContributionAndProofPool {
private lowestPermissibleSlot = 0;
constructor(
private readonly config: ChainForkConfig,
private readonly clock: IClock,
private readonly metrics: Metrics | null = null,
private logger: Logger | null = null
@@ -92,7 +93,7 @@ export class SyncContributionAndProofPool {
// Reject ContributionAndProofs of previous slots
// for api ContributionAndProofs, we allow them to be added to the pool
if (!priority && slot < this.clock.slotWithPastTolerance(MAXIMUM_GOSSIP_CLOCK_DISPARITY)) {
if (!priority && slot < this.clock.slotWithPastTolerance(this.config.MAXIMUM_GOSSIP_CLOCK_DISPARITY / 1000)) {
return InsertOutcome.Late;
}

View File

@@ -36,7 +36,6 @@ import {
ssz,
} from "@lodestar/types";
import {assert, toRootHex} from "@lodestar/utils";
import {MAXIMUM_GOSSIP_CLOCK_DISPARITY_SEC} from "../../constants/index.js";
import {sszDeserializeSingleAttestation} from "../../network/gossip/topic.js";
import {getShufflingDependentRoot} from "../../util/dependentRoot.js";
import {
@@ -570,8 +569,8 @@ async function validateAttestationNoSignatureCheck(
* Note: We do not queue future attestations for later processing
*/
export function verifyPropagationSlotRange(fork: ForkName, chain: IBeaconChain, attestationSlot: Slot): void {
// slot with future tolerance of MAXIMUM_GOSSIP_CLOCK_DISPARITY_SEC
const latestPermissibleSlot = chain.clock.slotWithFutureTolerance(MAXIMUM_GOSSIP_CLOCK_DISPARITY_SEC);
// slot with future tolerance of MAXIMUM_GOSSIP_CLOCK_DISPARITY
const latestPermissibleSlot = chain.clock.slotWithFutureTolerance(chain.config.MAXIMUM_GOSSIP_CLOCK_DISPARITY / 1000);
if (attestationSlot > latestPermissibleSlot) {
throw new AttestationError(GossipAction.IGNORE, {
code: AttestationErrorCode.FUTURE_SLOT,
@@ -586,9 +585,9 @@ export function verifyPropagationSlotRange(fork: ForkName, chain: IBeaconChain,
// see: https://github.com/ethereum/consensus-specs/pull/3360
if (ForkSeq[fork] < ForkSeq.deneb) {
const earliestPermissibleSlot = Math.max(
// slot with past tolerance of MAXIMUM_GOSSIP_CLOCK_DISPARITY_SEC
// ATTESTATION_PROPAGATION_SLOT_RANGE = SLOTS_PER_EPOCH
chain.clock.slotWithPastTolerance(MAXIMUM_GOSSIP_CLOCK_DISPARITY_SEC) - SLOTS_PER_EPOCH,
// slot with past tolerance of MAXIMUM_GOSSIP_CLOCK_DISPARITY
chain.clock.slotWithPastTolerance(chain.config.MAXIMUM_GOSSIP_CLOCK_DISPARITY / 1000) -
chain.config.ATTESTATION_PROPAGATION_SLOT_RANGE,
0
);
@@ -614,7 +613,7 @@ export function verifyPropagationSlotRange(fork: ForkName, chain: IBeaconChain,
// lower bound for previous epoch is same as epoch of earliestPermissibleSlot
const currentEpochWithPastTolerance = computeEpochAtSlot(
chain.clock.slotWithPastTolerance(MAXIMUM_GOSSIP_CLOCK_DISPARITY_SEC)
chain.clock.slotWithPastTolerance(chain.config.MAXIMUM_GOSSIP_CLOCK_DISPARITY / 1000)
);
const earliestPermissiblePreviousEpoch = Math.max(currentEpochWithPastTolerance - 1, 0);

View File

@@ -11,7 +11,6 @@ import {
} from "@lodestar/state-transition";
import {SignedBeaconBlock, deneb} from "@lodestar/types";
import {sleep, toRootHex} from "@lodestar/utils";
import {MAXIMUM_GOSSIP_CLOCK_DISPARITY} from "../../constants/index.js";
import {BlockErrorCode, BlockGossipError, GossipAction} from "../errors/index.js";
import {IBeaconChain} from "../interface.js";
import {RegenCaller} from "../regen/index.js";
@@ -179,7 +178,7 @@ export async function validateGossipBlock(
// gossip validation promise without any extra infrastructure.
// Do the sleep at the end, since regen and signature validation can already take longer than `msToBlockSlot`.
const msToBlockSlot = computeTimeAtSlot(config, blockSlot, chain.genesisTime) * 1000 - Date.now();
if (msToBlockSlot <= MAXIMUM_GOSSIP_CLOCK_DISPARITY && msToBlockSlot > 0) {
if (msToBlockSlot <= config.MAXIMUM_GOSSIP_CLOCK_DISPARITY && msToBlockSlot > 0) {
// If block is between 0 and 500 ms early, hold it in a promise. Equivalent to a pending queue.
await sleep(msToBlockSlot);
}

View File

@@ -1,6 +1,5 @@
import {ChainForkConfig} from "@lodestar/config";
import {LightClientOptimisticUpdate} from "@lodestar/types";
import {MAXIMUM_GOSSIP_CLOCK_DISPARITY} from "../../constants/index.js";
import {assertLightClientServer} from "../../node/utils/lightclient.js";
import {IClock} from "../../util/clock.js";
import {GossipAction} from "../errors/index.js";
@@ -64,5 +63,7 @@ export function updateReceivedTooEarly(
update: Pick<LightClientOptimisticUpdate, "signatureSlot">
): boolean {
const fork = config.getForkName(update.signatureSlot);
return clock.msFromSlot(update.signatureSlot) < config.getSyncMessageDueMs(fork) - MAXIMUM_GOSSIP_CLOCK_DISPARITY;
return (
clock.msFromSlot(update.signatureSlot) < config.getSyncMessageDueMs(fork) - config.MAXIMUM_GOSSIP_CLOCK_DISPARITY
);
}

View File

@@ -7,9 +7,3 @@ export const ZERO_HASH = Buffer.alloc(32, 0);
export const ZERO_HASH_HEX = "0x" + "00".repeat(32);
export const EMPTY_SIGNATURE = Buffer.alloc(96, 0);
export const GRAFFITI_SIZE = 32;
/**
* The maximum milliseconds of clock disparity assumed between honest nodes.
*/
export const MAXIMUM_GOSSIP_CLOCK_DISPARITY = 500;
export const MAXIMUM_GOSSIP_CLOCK_DISPARITY_SEC = MAXIMUM_GOSSIP_CLOCK_DISPARITY / 1000;

View File

@@ -1,22 +1,3 @@
/**
* For more info on some of these constants:
* https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/phase0/p2p-interface.md#configuration
*/
// Gossip constants
/**
* The maximum number of slots during which an attestation can be propagated.
*/
export const ATTESTATION_PROPAGATION_SLOT_RANGE = 32;
/** The maximum allowed size of uncompressed gossip messages. */
export const GOSSIP_MAX_SIZE = 2 ** 20;
export const GOSSIP_MAX_SIZE_BELLATRIX = 10 * GOSSIP_MAX_SIZE;
/** The maximum allowed size of uncompressed req/resp chunked responses. */
export const MAX_CHUNK_SIZE = 2 ** 20;
export const MAX_CHUNK_SIZE_BELLATRIX = 10 * MAX_CHUNK_SIZE;
export enum GoodByeReasonCode {
INBOUND_DISCONNECT = -1,
CLIENT_SHUTDOWN = 1,

View File

@@ -1,6 +1,5 @@
import {ENR} from "@chainsafe/enr";
import {BeaconConfig} from "@lodestar/config";
import {MAXIMUM_GOSSIP_CLOCK_DISPARITY} from "../../constants/constants.js";
import {IClock} from "../../util/clock.js";
import {ENRKey} from "../metadata.js";
@@ -35,7 +34,7 @@ export function enrRelevance(enr: ENR, config: BeaconConfig, clock: IClock): ENR
}
// Check if fork digest's fork matches ours
const currentSlot = clock.slotWithFutureTolerance(MAXIMUM_GOSSIP_CLOCK_DISPARITY);
const currentSlot = clock.slotWithFutureTolerance(config.MAXIMUM_GOSSIP_CLOCK_DISPARITY / 1000);
const localForkInfo = config.getForkInfo(currentSlot);
// We only connect if the ENR's fork matches our current fork.
// We also allow it to be the previous fork due to delay and infrequent update of DHT.

View File

@@ -6,7 +6,6 @@ import {BeaconConfig, ForkBoundary} from "@lodestar/config";
import {ATTESTATION_SUBNET_COUNT, SLOTS_PER_EPOCH, SYNC_COMMITTEE_SUBNET_COUNT} from "@lodestar/params";
import {SubnetID} from "@lodestar/types";
import {Logger, Map2d, Map2dArr} from "@lodestar/utils";
import {GOSSIP_MAX_SIZE, GOSSIP_MAX_SIZE_BELLATRIX} from "../../constants/network.js";
import {RegistryMetricCreator} from "../../metrics/index.js";
import {callInNextEventLoop} from "../../util/eventLoop.js";
import {NetworkEvent, NetworkEventBus, NetworkEventData} from "../events.js";
@@ -117,15 +116,7 @@ export class Eth2Gossipsub extends GossipSub {
fastMsgIdFn: fastMsgIdFn,
msgIdFn: msgIdFn.bind(msgIdFn, gossipTopicCache),
msgIdToStrFn: msgIdToStrFn,
// Use the bellatrix max size if the merge is configured. pre-merge using this size
// could only be an issue on outgoing payloads, its highly unlikely we will send out
// a chunk bigger than GOSSIP_MAX_SIZE pre merge even on mainnet network.
//
// TODO: figure out a way to dynamically transition to the size
dataTransform: new DataTransformSnappy(
gossipTopicCache,
Number.isFinite(config.BELLATRIX_FORK_EPOCH) ? GOSSIP_MAX_SIZE_BELLATRIX : GOSSIP_MAX_SIZE
),
dataTransform: new DataTransformSnappy(gossipTopicCache, config.MAX_PAYLOAD_SIZE),
metricsRegister: metricsRegister as MetricsRegister | null,
metricsTopicStrToLabel: metricsRegister
? getMetricsTopicStrToLabel(networkConfig, {disableLightClientServer: opts.disableLightClientServer ?? false})

View File

@@ -1,5 +1,5 @@
import {ChainConfig} from "@lodestar/config";
import {BLOBSIDECAR_FIXED_SIZE, GENESIS_SLOT} from "@lodestar/params";
import {BLOB_SIDECAR_FIXED_SIZE, GENESIS_SLOT} from "@lodestar/params";
import {RespStatus, ResponseError, ResponseOutgoing} from "@lodestar/reqresp";
import {computeEpochAtSlot} from "@lodestar/state-transition";
import {Slot, deneb} from "@lodestar/types";
@@ -71,17 +71,17 @@ export function* iterateBlobBytesFromWrapper(
blockSlot: Slot
): Iterable<ResponseOutgoing> {
const allBlobSideCarsBytes = blobSideCarsBytesWrapped.slice(BLOB_SIDECARS_IN_WRAPPER_INDEX);
const blobsLen = allBlobSideCarsBytes.length / BLOBSIDECAR_FIXED_SIZE;
const blobsLen = allBlobSideCarsBytes.length / BLOB_SIDECAR_FIXED_SIZE;
for (let index = 0; index < blobsLen; index++) {
const blobSideCarBytes = allBlobSideCarsBytes.slice(
index * BLOBSIDECAR_FIXED_SIZE,
(index + 1) * BLOBSIDECAR_FIXED_SIZE
index * BLOB_SIDECAR_FIXED_SIZE,
(index + 1) * BLOB_SIDECAR_FIXED_SIZE
);
if (blobSideCarBytes.length !== BLOBSIDECAR_FIXED_SIZE) {
if (blobSideCarBytes.length !== BLOB_SIDECAR_FIXED_SIZE) {
throw new ResponseError(
RespStatus.SERVER_ERROR,
`Invalid blobSidecar index=${index} bytes length=${blobSideCarBytes.length} expected=${BLOBSIDECAR_FIXED_SIZE} for slot ${blockSlot} blobsLen=${blobsLen}`
`Invalid blobSidecar index=${index} bytes length=${blobSideCarBytes.length} expected=${BLOB_SIDECAR_FIXED_SIZE} for slot ${blockSlot} blobsLen=${blobsLen}`
);
}
yield {

View File

@@ -1,4 +1,4 @@
import {BLOBSIDECAR_FIXED_SIZE} from "@lodestar/params";
import {BLOB_SIDECAR_FIXED_SIZE} from "@lodestar/params";
import {RespStatus, ResponseError, ResponseOutgoing} from "@lodestar/reqresp";
import {computeEpochAtSlot} from "@lodestar/state-transition";
import {RootHex} from "@lodestar/types";
@@ -45,12 +45,12 @@ export async function* onBlobSidecarsByRoot(
}
const blobSidecarBytes = lastFetchedSideCars.bytes.slice(
index * BLOBSIDECAR_FIXED_SIZE,
(index + 1) * BLOBSIDECAR_FIXED_SIZE
index * BLOB_SIDECAR_FIXED_SIZE,
(index + 1) * BLOB_SIDECAR_FIXED_SIZE
);
if (blobSidecarBytes.length !== BLOBSIDECAR_FIXED_SIZE) {
if (blobSidecarBytes.length !== BLOB_SIDECAR_FIXED_SIZE) {
throw Error(
`Inconsistent state, blobSidecar blockRoot=${blockRootHex} index=${index} blobSidecarBytes=${blobSidecarBytes.length} expected=${BLOBSIDECAR_FIXED_SIZE}`
`Inconsistent state, blobSidecar blockRoot=${blockRootHex} index=${index} blobSidecarBytes=${blobSidecarBytes.length} expected=${BLOB_SIDECAR_FIXED_SIZE}`
);
}

View File

@@ -1,5 +1,5 @@
import {BeaconConfig, ForkBoundary} from "@lodestar/config";
import {ATTESTATION_SUBNET_COUNT, EPOCHS_PER_RANDOM_SUBNET_SUBSCRIPTION, SLOTS_PER_EPOCH} from "@lodestar/params";
import {ATTESTATION_SUBNET_COUNT, SLOTS_PER_EPOCH} from "@lodestar/params";
import {computeEpochAtSlot} from "@lodestar/state-transition";
import {Epoch, Slot, SubnetID, ssz} from "@lodestar/types";
import {Logger, MapDef} from "@lodestar/utils";
@@ -92,8 +92,8 @@ export class AttnetsService implements IAttnetsService {
const shortLivedSubnets = this.committeeSubnets.getActiveTtl(this.clock.currentSlot);
const longLivedSubscriptionsToSlot =
(Math.floor(this.clock.currentEpoch / EPOCHS_PER_RANDOM_SUBNET_SUBSCRIPTION) + 1) *
EPOCHS_PER_RANDOM_SUBNET_SUBSCRIPTION *
(Math.floor(this.clock.currentEpoch / this.config.EPOCHS_PER_SUBNET_SUBSCRIPTION) + 1) *
this.config.EPOCHS_PER_SUBNET_SUBSCRIPTION *
SLOTS_PER_EPOCH;
const longLivedSubnets = Array.from(this.longLivedSubscriptions).map((subnet) => ({
subnet,
@@ -234,11 +234,11 @@ export class AttnetsService implements IAttnetsService {
/**
* Run per epoch, clean-up operations that are not urgent
* Subscribe to new random subnets every EPOCHS_PER_RANDOM_SUBNET_SUBSCRIPTION epochs
* Subscribe to new random subnets every EPOCHS_PER_SUBNET_SUBSCRIPTION epochs
*/
private onEpoch = (epoch: Epoch): void => {
try {
if (epoch % EPOCHS_PER_RANDOM_SUBNET_SUBSCRIPTION === 0) {
if (epoch % this.config.EPOCHS_PER_SUBNET_SUBSCRIPTION === 0) {
this.recomputeLongLivedSubnets();
}
} catch (e) {
@@ -253,7 +253,7 @@ export class AttnetsService implements IAttnetsService {
}
const oldSubnets = this.longLivedSubscriptions;
const newSubnets = computeSubscribedSubnet(this.nodeId, this.clock.currentEpoch);
const newSubnets = computeSubscribedSubnet(this.config, this.nodeId, this.clock.currentEpoch);
this.logger.verbose("Recomputing long-lived subscriptions", {
epoch: this.clock.currentEpoch,
oldSubnets: Array.from(oldSubnets).join(","),

View File

@@ -1,11 +1,6 @@
import {digest} from "@chainsafe/as-sha256";
import {
ATTESTATION_SUBNET_COUNT,
ATTESTATION_SUBNET_PREFIX_BITS,
EPOCHS_PER_SUBNET_SUBSCRIPTION,
NODE_ID_BITS,
SUBNETS_PER_NODE,
} from "@lodestar/params";
import {ChainConfig} from "@lodestar/config";
import {ATTESTATION_SUBNET_COUNT, ATTESTATION_SUBNET_PREFIX_BITS, NODE_ID_BITS} from "@lodestar/params";
import {computeShuffledIndex} from "@lodestar/state-transition";
import {Epoch, ssz} from "@lodestar/types";
import {NodeId} from "./interface.js";
@@ -13,10 +8,10 @@ import {NodeId} from "./interface.js";
/**
* Spec https://github.com/ethereum/consensus-specs/blob/v1.4.0-alpha.3/specs/phase0/p2p-interface.md
*/
export function computeSubscribedSubnet(nodeId: NodeId, epoch: Epoch): number[] {
export function computeSubscribedSubnet(config: ChainConfig, nodeId: NodeId, epoch: Epoch): number[] {
const subnets: number[] = [];
for (let index = 0; index < SUBNETS_PER_NODE; index++) {
subnets.push(computeSubscribedSubnetByIndex(nodeId, epoch, index));
for (let index = 0; index < config.SUBNETS_PER_NODE; index++) {
subnets.push(computeSubscribedSubnetByIndex(config, nodeId, epoch, index));
}
return subnets;
}
@@ -24,11 +19,16 @@ export function computeSubscribedSubnet(nodeId: NodeId, epoch: Epoch): number[]
/**
* Spec https://github.com/ethereum/consensus-specs/blob/v1.4.0-alpha.3/specs/phase0/p2p-interface.md
*/
export function computeSubscribedSubnetByIndex(nodeId: NodeId, epoch: Epoch, index: number): number {
export function computeSubscribedSubnetByIndex(
config: ChainConfig,
nodeId: NodeId,
epoch: Epoch,
index: number
): number {
const nodeIdPrefix = getNodeIdPrefix(nodeId);
const nodeOffset = getNodeOffset(nodeId);
const permutationSeed = digest(
ssz.UintNum64.serialize(Math.floor((epoch + nodeOffset) / EPOCHS_PER_SUBNET_SUBSCRIPTION))
ssz.UintNum64.serialize(Math.floor((epoch + nodeOffset) / config.EPOCHS_PER_SUBNET_SUBSCRIPTION))
);
const permutatedPrefix = computeShuffledIndex(nodeIdPrefix, 1 << ATTESTATION_SUBNET_PREFIX_BITS, permutationSeed);
return (permutatedPrefix + index) % ATTESTATION_SUBNET_COUNT;

View File

@@ -4,7 +4,6 @@ import {ChainForkConfig} from "@lodestar/config";
import {computeEpochAtSlot, computeTimeAtSlot, getCurrentSlot} from "@lodestar/state-transition";
import type {Epoch, Slot} from "@lodestar/types";
import {ErrorAborted} from "@lodestar/utils";
import {MAXIMUM_GOSSIP_CLOCK_DISPARITY} from "../constants/constants.js";
export enum ClockEvent {
/**
@@ -101,7 +100,7 @@ export class Clock extends EventEmitter implements IClock {
get currentSlotWithGossipDisparity(): Slot {
const currentSlot = this.currentSlot;
const nextSlotTime = computeTimeAtSlot(this.config, currentSlot + 1, this.genesisTime) * 1000;
return nextSlotTime - Date.now() < MAXIMUM_GOSSIP_CLOCK_DISPARITY ? currentSlot + 1 : currentSlot;
return nextSlotTime - Date.now() < this.config.MAXIMUM_GOSSIP_CLOCK_DISPARITY ? currentSlot + 1 : currentSlot;
}
get currentEpoch(): Epoch {
@@ -130,12 +129,12 @@ export class Clock extends EventEmitter implements IClock {
}
const nextSlotTime = computeTimeAtSlot(this.config, currentSlot + 1, this.genesisTime) * 1000;
// we're too close to next slot, accept next slot
if (nextSlotTime - Date.now() < MAXIMUM_GOSSIP_CLOCK_DISPARITY) {
if (nextSlotTime - Date.now() < this.config.MAXIMUM_GOSSIP_CLOCK_DISPARITY) {
return slot === currentSlot + 1;
}
const currentSlotTime = computeTimeAtSlot(this.config, currentSlot, this.genesisTime) * 1000;
// we've just passed the current slot, accept previous slot
if (Date.now() - currentSlotTime < MAXIMUM_GOSSIP_CLOCK_DISPARITY) {
if (Date.now() - currentSlotTime < this.config.MAXIMUM_GOSSIP_CLOCK_DISPARITY) {
return slot === currentSlot - 1;
}
return false;

View File

@@ -46,7 +46,7 @@ async function downloadRemoteConstants(commit: string): Promise<string[]> {
for (const forkName of Object.values(ForkName)) {
// If some future fork does not specify one of this docs, refactor to fetch some docs only on some forks
for (const docName of ["beacon-chain.md", "validator.md"]) {
for (const docName of ["beacon-chain.md", "validator.md", "p2p-interface.md"]) {
downloadedSpecs.push(
fetch(`https://raw.githubusercontent.com/ethereum/consensus-specs/${commit}/specs/${forkName}/${docName}`).then(
(res) => res.text()

View File

@@ -138,7 +138,7 @@ vi.mock("../../src/chain/chain.js", async (importActual) => {
eth1: new Eth1ForBlockProduction(),
opPool: new OpPool(),
aggregatedAttestationPool: new AggregatedAttestationPool(config),
syncContributionAndProofPool: new SyncContributionAndProofPool(clock),
syncContributionAndProofPool: new SyncContributionAndProofPool(config, clock),
// @ts-expect-error
beaconProposerCache: new BeaconProposerCache(),
shufflingCache: new ShufflingCache(),

View File

@@ -35,6 +35,7 @@ describe("config api implementation", () => {
expect(specJson.SECONDS_PER_ETH1_BLOCK).toBe("14");
expect(specJson.DEPOSIT_CONTRACT_ADDRESS).toBe("0x00000000219ab540356cbb839cbe05303d7705fa");
expect(specJson.DEPOSIT_REQUEST_TYPE).toBe("0x00");
});
});
});

View File

@@ -1,6 +1,7 @@
import {beforeAll, beforeEach, describe, expect, it, vi} from "vitest";
import {SecretKey, Signature, fastAggregateVerify} from "@chainsafe/blst";
import {BitArray} from "@chainsafe/ssz";
import {config} from "@lodestar/config/default";
import {SYNC_COMMITTEE_SIZE, SYNC_COMMITTEE_SUBNET_COUNT} from "@lodestar/params";
import {newFilledArray} from "@lodestar/state-transition";
import {ssz} from "@lodestar/types";
@@ -29,7 +30,7 @@ describe("chain / opPools / SyncContributionAndProofPool", () => {
beforeEach(() => {
vi.spyOn(clockStub, "slotWithPastTolerance").mockReturnValue(slot);
cache = new SyncContributionAndProofPool(clockStub);
cache = new SyncContributionAndProofPool(config, clockStub);
cache.add(contributionAndProof, syncCommitteeParticipants);
});

View File

@@ -1,13 +1,6 @@
import {MockedObject, afterEach, beforeEach, describe, expect, it, vi} from "vitest";
import {createBeaconConfig} from "@lodestar/config";
import {
ATTESTATION_SUBNET_COUNT,
EPOCHS_PER_SUBNET_SUBSCRIPTION,
ForkName,
GENESIS_EPOCH,
SLOTS_PER_EPOCH,
SUBNETS_PER_NODE,
} from "@lodestar/params";
import {ATTESTATION_SUBNET_COUNT, ForkName, GENESIS_EPOCH, SLOTS_PER_EPOCH} from "@lodestar/params";
import {ZERO_HASH, getCurrentSlot} from "@lodestar/state-transition";
import {SubnetID} from "@lodestar/types";
import {bigIntToBytes} from "@lodestar/utils";
@@ -78,10 +71,10 @@ describe("AttnetsService", () => {
it("should change long lived subnets after EPOCHS_PER_SUBNET_SUBSCRIPTION", () => {
expect(gossipStub.subscribeTopic).toBeCalledTimes(2);
expect(gossipStub.subscribeTopic).toBeCalledTimes(SUBNETS_PER_NODE);
vi.advanceTimersByTime(config.SLOT_DURATION_MS * SLOTS_PER_EPOCH * EPOCHS_PER_SUBNET_SUBSCRIPTION);
expect(gossipStub.subscribeTopic).toBeCalledTimes(config.SUBNETS_PER_NODE);
vi.advanceTimersByTime(config.SLOT_DURATION_MS * SLOTS_PER_EPOCH * config.EPOCHS_PER_SUBNET_SUBSCRIPTION);
// SUBNETS_PER_NODE = 2 => 2 more calls
expect(gossipStub.subscribeTopic).toBeCalledTimes(2 * SUBNETS_PER_NODE);
expect(gossipStub.subscribeTopic).toBeCalledTimes(2 * config.SUBNETS_PER_NODE);
});
it("should subscribe to new fork 2 epochs before ALTAIR_FORK_EPOCH", () => {
@@ -94,7 +87,7 @@ describe("AttnetsService", () => {
expect(gossipStub.subscribeTopic).toBeCalledTimes(2);
const firstSubnet = (gossipStub.subscribeTopic.mock.calls[0][0] as unknown as {subnet: SubnetID}).subnet;
const secondSubnet = (gossipStub.subscribeTopic.mock.calls[1][0] as unknown as {subnet: SubnetID}).subnet;
expect(gossipStub.subscribeTopic).toBeCalledTimes(SUBNETS_PER_NODE);
expect(gossipStub.subscribeTopic).toBeCalledTimes(config.SUBNETS_PER_NODE);
vi.advanceTimersByTime(config.SLOT_DURATION_MS * SLOTS_PER_EPOCH * (ALTAIR_FORK_EPOCH - 2));
service.subscribeSubnetsNextBoundary({fork: ForkName.altair, epoch: config.ALTAIR_FORK_EPOCH});
// SUBNETS_PER_NODE = 2 => 2 more calls
@@ -108,7 +101,7 @@ describe("AttnetsService", () => {
subnet: secondSubnet,
})
);
expect(gossipStub.subscribeTopic).toBeCalledTimes(2 * SUBNETS_PER_NODE);
expect(gossipStub.subscribeTopic).toBeCalledTimes(2 * config.SUBNETS_PER_NODE);
// 2 epochs after the fork
vi.advanceTimersByTime(config.SLOT_DURATION_MS * 4);
service.unsubscribeSubnetsPrevBoundary({fork: ForkName.phase0, epoch: GENESIS_EPOCH});
@@ -122,7 +115,7 @@ describe("AttnetsService", () => {
});
it("should not subscribe to new short lived subnet if not aggregator", () => {
expect(gossipStub.subscribeTopic).toBeCalledTimes(SUBNETS_PER_NODE);
expect(gossipStub.subscribeTopic).toBeCalledTimes(config.SUBNETS_PER_NODE);
const firstSubnet = (gossipStub.subscribeTopic.mock.calls[0][0] as unknown as {subnet: SubnetID}).subnet;
const secondSubnet = (gossipStub.subscribeTopic.mock.calls[1][0] as unknown as {subnet: SubnetID}).subnet;
// should subscribe to new short lived subnet
@@ -137,11 +130,11 @@ describe("AttnetsService", () => {
};
service.addCommitteeSubscriptions([subscription]);
// no new subscription
expect(gossipStub.subscribeTopic).toBeCalledTimes(SUBNETS_PER_NODE);
expect(gossipStub.subscribeTopic).toBeCalledTimes(config.SUBNETS_PER_NODE);
});
it("should subscribe to new short lived subnet if aggregator", () => {
expect(gossipStub.subscribeTopic).toBeCalledTimes(SUBNETS_PER_NODE);
expect(gossipStub.subscribeTopic).toBeCalledTimes(config.SUBNETS_PER_NODE);
const firstSubnet = (gossipStub.subscribeTopic.mock.calls[0][0] as unknown as {subnet: SubnetID}).subnet;
const secondSubnet = (gossipStub.subscribeTopic.mock.calls[1][0] as unknown as {subnet: SubnetID}).subnet;
// should subscribe to new short lived subnet
@@ -156,17 +149,17 @@ describe("AttnetsService", () => {
};
service.addCommitteeSubscriptions([subscription]);
// it does not subscribe immediately
expect(gossipStub.subscribeTopic).toBeCalledTimes(SUBNETS_PER_NODE);
expect(gossipStub.subscribeTopic).toBeCalledTimes(config.SUBNETS_PER_NODE);
vi.advanceTimersByTime(config.SLOT_DURATION_MS * (subscription.slot - 2));
// then subscribe 2 slots before dutied slot
expect(gossipStub.subscribeTopic).toBeCalledTimes(SUBNETS_PER_NODE + 1);
expect(gossipStub.subscribeTopic).toBeCalledTimes(config.SUBNETS_PER_NODE + 1);
// then unsubscribe after the expiration
vi.advanceTimersByTime(config.SLOT_DURATION_MS * (subscription.slot + 1));
expect(gossipStub.unsubscribeTopic).toHaveBeenCalledWith(expect.objectContaining({subnet: newSubnet}));
});
it("should not subscribe to existing short lived subnet if aggregator", () => {
expect(gossipStub.subscribeTopic).toBeCalledTimes(SUBNETS_PER_NODE);
expect(gossipStub.subscribeTopic).toBeCalledTimes(config.SUBNETS_PER_NODE);
const firstSubnet = (gossipStub.subscribeTopic.mock.calls[0][0] as unknown as {subnet: SubnetID}).subnet;
// should not subscribe to existing short lived subnet
const subscription: CommitteeSubscription = {
@@ -176,7 +169,7 @@ describe("AttnetsService", () => {
isAggregator: true,
};
service.addCommitteeSubscriptions([subscription]);
expect(gossipStub.subscribeTopic).toBeCalledTimes(SUBNETS_PER_NODE);
expect(gossipStub.subscribeTopic).toBeCalledTimes(config.SUBNETS_PER_NODE);
// then should not subscribe after the expiration
vi.advanceTimersByTime(config.SLOT_DURATION_MS * (subscription.slot + 1));
expect(gossipStub.unsubscribeTopic).not.toHaveBeenCalled();

View File

@@ -1,4 +1,5 @@
import {describe, expect, it} from "vitest";
import {config} from "@lodestar/config/default";
import {ATTESTATION_SUBNET_PREFIX_BITS, NODE_ID_BITS} from "@lodestar/params";
import {bigIntToBytes} from "@lodestar/utils";
import {computeSubscribedSubnet, getNodeIdPrefix, getNodeOffset} from "../../../../src/network/subnets/util.js";
@@ -99,7 +100,7 @@ describe("computeSubscribedSubnet", () => {
for (const [index, {nodeId, epoch, expected}] of testCases.entries()) {
it(`test case ${index}`, () => {
// node is is of type uint256 = 32 bytes
expect(computeSubscribedSubnet(bigIntToBytes(BigInt(nodeId), 32, "be"), epoch)).toEqual(expected);
expect(computeSubscribedSubnet(config, bigIntToBytes(BigInt(nodeId), 32, "be"), epoch)).toEqual(expected);
});
}
});

View File

@@ -1,7 +1,6 @@
import {afterEach, beforeEach, describe, expect, it, vi} from "vitest";
import {config} from "@lodestar/config/default";
import {SLOTS_PER_EPOCH} from "@lodestar/params";
import {MAXIMUM_GOSSIP_CLOCK_DISPARITY} from "../../../src/constants/index.js";
import {Clock, ClockEvent} from "../../../src/util/clock.js";
describe("Clock", () => {
@@ -44,7 +43,7 @@ describe("Clock", () => {
describe("currentSlotWithGossipDisparity", () => {
it("should be next slot", () => {
vi.advanceTimersByTime(config.SLOT_DURATION_MS - (MAXIMUM_GOSSIP_CLOCK_DISPARITY - 50));
vi.advanceTimersByTime(config.SLOT_DURATION_MS - (config.MAXIMUM_GOSSIP_CLOCK_DISPARITY - 50));
expect(clock.currentSlotWithGossipDisparity).toBe(clock.currentSlot + 1);
});
@@ -64,14 +63,14 @@ describe("Clock", () => {
const nextSlot = clock.currentSlot + 1;
// "current slot could NOT be next slot if it's far away from next slot"
expect(clock.isCurrentSlotGivenGossipDisparity(nextSlot)).toBe(false);
vi.advanceTimersByTime(config.SLOT_DURATION_MS - (MAXIMUM_GOSSIP_CLOCK_DISPARITY - 50));
vi.advanceTimersByTime(config.SLOT_DURATION_MS - (config.MAXIMUM_GOSSIP_CLOCK_DISPARITY - 50));
// "current slot could be next slot if it's too close to next slot"
expect(clock.isCurrentSlotGivenGossipDisparity(nextSlot)).toBe(true);
});
it("should accept previous slot if it's just passed current slot", () => {
const previousSlot = clock.currentSlot - 1;
vi.advanceTimersByTime(MAXIMUM_GOSSIP_CLOCK_DISPARITY - 50);
vi.advanceTimersByTime(config.MAXIMUM_GOSSIP_CLOCK_DISPARITY - 50);
// "current slot could be previous slot if it's just passed to a slot"
expect(clock.isCurrentSlotGivenGossipDisparity(previousSlot)).toBe(true);
vi.advanceTimersByTime(100);

View File

@@ -130,10 +130,22 @@ export const chainConfig: ChainConfig = {
// Networking
// ---------------------------------------------------------------
// 2**10 (= 1024)
// 10 * 2**20 (= 10,485,760) bytes, 10 MiB
MAX_PAYLOAD_SIZE: 10485760,
// 2**10 (= 1,024) blocks
MAX_REQUEST_BLOCKS: 1024,
// `MIN_VALIDATOR_WITHDRAWABILITY_DELAY + CHURN_LIMIT_QUOTIENT // 2` (= 33024, ~5 months)
// 2**8 (= 256) epochs
EPOCHS_PER_SUBNET_SUBSCRIPTION: 256,
// MIN_VALIDATOR_WITHDRAWABILITY_DELAY + CHURN_LIMIT_QUOTIENT // 2 (= 33,024) epochs
MIN_EPOCHS_FOR_BLOCK_REQUESTS: 33024,
// 2**5 (= 32) slots
ATTESTATION_PROPAGATION_SLOT_RANGE: 32,
// 500ms
MAXIMUM_GOSSIP_CLOCK_DISPARITY: 500,
MESSAGE_DOMAIN_INVALID_SNAPPY: b("0x00000000"),
MESSAGE_DOMAIN_VALID_SNAPPY: b("0x01000000"),
// 2 subnets per node
SUBNETS_PER_NODE: 2,
// Deneb
// 2**7 (= 128)
@@ -166,6 +178,10 @@ export const chainConfig: ChainConfig = {
// `2**12` (= 4096 epochs, ~18 days)
MIN_EPOCHS_FOR_DATA_COLUMN_SIDECARS_REQUESTS: 4096,
// Gloas
// 2**7 (= 128) payloads
MAX_REQUEST_PAYLOADS: 128,
// Blob Scheduling
// ---------------------------------------------------------------
BLOB_SCHEDULE: [],

View File

@@ -125,10 +125,22 @@ export const chainConfig: ChainConfig = {
// Networking
// ---------------------------------------------------------------
// 2**10 (= 1024)
// 10 * 2**20 (= 10,485,760) bytes, 10 MiB
MAX_PAYLOAD_SIZE: 10485760,
// 2**10 (= 1,024) blocks
MAX_REQUEST_BLOCKS: 1024,
// [customized] `MIN_VALIDATOR_WITHDRAWABILITY_DELAY + CHURN_LIMIT_QUOTIENT // 2` (= 272)
// 2**8 (= 256) epochs
EPOCHS_PER_SUBNET_SUBSCRIPTION: 256,
// [customized] MIN_VALIDATOR_WITHDRAWABILITY_DELAY + CHURN_LIMIT_QUOTIENT // 2 (= 272) epochs
MIN_EPOCHS_FOR_BLOCK_REQUESTS: 272,
// 2**5 (= 32) slots
ATTESTATION_PROPAGATION_SLOT_RANGE: 32,
// 500ms
MAXIMUM_GOSSIP_CLOCK_DISPARITY: 500,
MESSAGE_DOMAIN_INVALID_SNAPPY: b("0x00000000"),
MESSAGE_DOMAIN_VALID_SNAPPY: b("0x01000000"),
// 2 subnets per node
SUBNETS_PER_NODE: 2,
// Deneb
// 2**7 (= 128)
@@ -161,6 +173,10 @@ export const chainConfig: ChainConfig = {
// `2**12` (= 4096 epochs, ~18 days)
MIN_EPOCHS_FOR_DATA_COLUMN_SIDECARS_REQUESTS: 4096,
// Gloas
// 2**7 (= 128) payloads
MAX_REQUEST_PAYLOADS: 128,
// Blob Scheduling
// ---------------------------------------------------------------
BLOB_SCHEDULE: [],

View File

@@ -91,9 +91,16 @@ export type ChainConfig = {
DEPOSIT_CONTRACT_ADDRESS: Uint8Array;
// Networking
MAX_PAYLOAD_SIZE: number;
MAX_REQUEST_BLOCKS: number;
MAX_REQUEST_BLOCKS_DENEB: number;
EPOCHS_PER_SUBNET_SUBSCRIPTION: number;
MIN_EPOCHS_FOR_BLOCK_REQUESTS: number;
ATTESTATION_PROPAGATION_SLOT_RANGE: number;
MAXIMUM_GOSSIP_CLOCK_DISPARITY: number;
MESSAGE_DOMAIN_INVALID_SNAPPY: Uint8Array;
MESSAGE_DOMAIN_VALID_SNAPPY: Uint8Array;
SUBNETS_PER_NODE: number;
MAX_REQUEST_BLOCKS_DENEB: number;
MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS: number;
MIN_EPOCHS_FOR_DATA_COLUMN_SIDECARS_REQUESTS: number;
BLOB_SIDECAR_SUBNET_COUNT: number;
@@ -112,6 +119,9 @@ export type ChainConfig = {
VALIDATOR_CUSTODY_REQUIREMENT: number;
BALANCE_PER_ADDITIONAL_CUSTODY_GROUP: number;
// Gloas
MAX_REQUEST_PAYLOADS: number;
// Blob Scheduling
BLOB_SCHEDULE: BlobSchedule;
};
@@ -196,9 +206,16 @@ export const chainConfigTypes: SpecTypes<ChainConfig> = {
DEPOSIT_CONTRACT_ADDRESS: "bytes",
// Networking
MAX_PAYLOAD_SIZE: "number",
MAX_REQUEST_BLOCKS: "number",
MAX_REQUEST_BLOCKS_DENEB: "number",
EPOCHS_PER_SUBNET_SUBSCRIPTION: "number",
MIN_EPOCHS_FOR_BLOCK_REQUESTS: "number",
ATTESTATION_PROPAGATION_SLOT_RANGE: "number",
MAXIMUM_GOSSIP_CLOCK_DISPARITY: "number",
MESSAGE_DOMAIN_INVALID_SNAPPY: "bytes",
MESSAGE_DOMAIN_VALID_SNAPPY: "bytes",
SUBNETS_PER_NODE: "number",
MAX_REQUEST_BLOCKS_DENEB: "number",
MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS: "number",
MIN_EPOCHS_FOR_DATA_COLUMN_SIDECARS_REQUESTS: "number",
BLOB_SIDECAR_SUBNET_COUNT: "number",
@@ -217,6 +234,9 @@ export const chainConfigTypes: SpecTypes<ChainConfig> = {
VALIDATOR_CUSTODY_REQUIREMENT: "number",
BALANCE_PER_ADDITIONAL_CUSTODY_GROUP: "number",
// Gloas
MAX_REQUEST_PAYLOADS: "number",
// Blob Scheduling
BLOB_SCHEDULE: "blob_schedule",
};

View File

@@ -193,14 +193,17 @@ export const PARTICIPATION_FLAG_WEIGHTS = [TIMELY_SOURCE_WEIGHT, TIMELY_TARGET_W
// phase0 validator
export const TARGET_AGGREGATORS_PER_COMMITTEE = 16;
export const RANDOM_SUBNETS_PER_VALIDATOR = 1;
export const EPOCHS_PER_RANDOM_SUBNET_SUBSCRIPTION = 256;
// phase0 networking
export const NODE_ID_BITS = 256;
export const MAX_CONCURRENT_REQUESTS = 2;
/** Rationale: https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/phase0/p2p-interface.md#why-are-there-attestation_subnet_count-attestation-subnets */
export const ATTESTATION_SUBNET_COUNT = 64;
export const SUBNETS_PER_NODE = 2;
export const NODE_ID_BITS = 256;
export const ATTESTATION_SUBNET_PREFIX_BITS = Math.log2(ATTESTATION_SUBNET_COUNT);
export const EPOCHS_PER_SUBNET_SUBSCRIPTION = 256;
export const ATTESTATION_SUBNET_EXTRA_BITS = 0;
export const ATTESTATION_SUBNET_PREFIX_BITS =
Math.ceil(Math.log2(ATTESTATION_SUBNET_COUNT)) + ATTESTATION_SUBNET_EXTRA_BITS;
// altair validator
@@ -284,7 +287,7 @@ export const KZG_COMMITMENT_GINDEX0 = 221184;
export const KZG_COMMITMENT_SUBTREE_INDEX0 = KZG_COMMITMENT_GINDEX0 - 2 ** KZG_COMMITMENT_INCLUSION_PROOF_DEPTH;
// ssz.deneb.BlobSidecars.elementType.fixedSize
export const BLOBSIDECAR_FIXED_SIZE = 131928;
export const BLOB_SIDECAR_FIXED_SIZE = 131928;
// Electra Misc
export const UNSET_DEPOSIT_REQUESTS_START_INDEX = 2n ** 64n - 1n;

View File

@@ -1,9 +1,8 @@
import {MAX_CONCURRENT_REQUESTS} from "@lodestar/params";
import {Logger, MapDef} from "@lodestar/utils";
type PeerIdStr = string;
type ProtocolID = string;
/** https://github.com/ethereum/consensus-specs/blob/master/specs/phase0/p2p-interface.md#constants */
const MAX_CONCURRENT_REQUESTS = 2;
/** Sometimes a peer request comes AFTER libp2p disconnect event, check for such peers every 2 minutes */
export const CHECK_DISCONNECTED_PEERS_INTERVAL_MS = 2 * 60 * 1000;

View File

@@ -7,12 +7,12 @@ import {ssz} from "../../../src/index.js";
// guarantee that these constants are correct.
describe(`${constants.ACTIVE_PRESET}/ blobs pre-computed constants`, () => {
const BLOBSIDECAR_FIXED_SIZE = ssz.deneb.BlobSidecars.elementType.fixedSize;
const BLOB_SIDECAR_FIXED_SIZE = ssz.deneb.BlobSidecars.elementType.fixedSize;
const KZG_COMMITMENT_GINDEX0 = Number(ssz.deneb.BeaconBlockBody.getPathInfo(["blobKzgCommitments", 0]).gindex);
const KZG_COMMITMENT_SUBTREE_INDEX0 = KZG_COMMITMENT_GINDEX0 - 2 ** constants.KZG_COMMITMENT_INCLUSION_PROOF_DEPTH;
const correctConstants = {
BLOBSIDECAR_FIXED_SIZE,
BLOB_SIDECAR_FIXED_SIZE,
KZG_COMMITMENT_GINDEX0,
KZG_COMMITMENT_SUBTREE_INDEX0,
};

View File

@@ -179,6 +179,13 @@ function getSpecCriticalParams(localConfig: ChainConfig): Record<keyof ConfigWit
DEPOSIT_CONTRACT_ADDRESS: true,
// Networking (non-critical as those do not affect consensus)
MAX_PAYLOAD_SIZE: false,
EPOCHS_PER_SUBNET_SUBSCRIPTION: false,
ATTESTATION_PROPAGATION_SLOT_RANGE: false,
MAXIMUM_GOSSIP_CLOCK_DISPARITY: false,
MESSAGE_DOMAIN_INVALID_SNAPPY: false,
MESSAGE_DOMAIN_VALID_SNAPPY: false,
SUBNETS_PER_NODE: false,
MAX_REQUEST_BLOCKS: false,
MAX_REQUEST_BLOCKS_DENEB: false,
MIN_EPOCHS_FOR_BLOCK_REQUESTS: false,
@@ -190,6 +197,7 @@ function getSpecCriticalParams(localConfig: ChainConfig): Record<keyof ConfigWit
MAX_REQUEST_BLOB_SIDECARS: false,
MAX_REQUEST_BLOB_SIDECARS_ELECTRA: false,
MAX_REQUEST_DATA_COLUMN_SIDECARS: false,
MAX_REQUEST_PAYLOADS: false,
// # Phase0Preset
/////////////////