mirror of
https://github.com/ChainSafe/lodestar.git
synced 2026-01-08 23:28:10 -05:00
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:
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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})
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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}`
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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(","),
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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: [],
|
||||
|
||||
@@ -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: [],
|
||||
|
||||
@@ -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",
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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
|
||||
/////////////////
|
||||
|
||||
Reference in New Issue
Block a user