diff --git a/packages/beacon-node/src/chain/errors/blobSidecarError.ts b/packages/beacon-node/src/chain/errors/blobSidecarError.ts index 410574594911..71118d8b8f9d 100644 --- a/packages/beacon-node/src/chain/errors/blobSidecarError.ts +++ b/packages/beacon-node/src/chain/errors/blobSidecarError.ts @@ -26,7 +26,7 @@ export enum BlobSidecarErrorCode { } export type BlobSidecarErrorType = - | {code: BlobSidecarErrorCode.INVALID_INDEX; blobIdx: number; gossipIndex: number} + | {code: BlobSidecarErrorCode.INVALID_INDEX; blobIdx: number; subnet: number} | {code: BlobSidecarErrorCode.INVALID_KZG; blobIdx: number} | {code: BlobSidecarErrorCode.INVALID_KZG_TXS} | {code: BlobSidecarErrorCode.INCORRECT_SLOT; blockSlot: Slot; blobSlot: Slot; blobIdx: number} diff --git a/packages/beacon-node/src/chain/validation/blobSidecar.ts b/packages/beacon-node/src/chain/validation/blobSidecar.ts index 3b90972f62b1..e2f4ed267d01 100644 --- a/packages/beacon-node/src/chain/validation/blobSidecar.ts +++ b/packages/beacon-node/src/chain/validation/blobSidecar.ts @@ -1,6 +1,7 @@ +import {ChainConfig} from "@lodestar/config"; import {KZG_COMMITMENT_INCLUSION_PROOF_DEPTH, KZG_COMMITMENT_SUBTREE_INDEX0} from "@lodestar/params"; import {computeStartSlotAtEpoch, getBlockHeaderProposerSignatureSet} from "@lodestar/state-transition"; -import {Root, Slot, deneb, ssz} from "@lodestar/types"; +import {BlobIndex, Root, Slot, deneb, ssz} from "@lodestar/types"; import {toRootHex, verifyMerkleBranch} from "@lodestar/utils"; import {byteArrayEquals} from "../../util/bytes.js"; @@ -13,16 +14,16 @@ import {RegenCaller} from "../regen/index.js"; export async function validateGossipBlobSidecar( chain: IBeaconChain, blobSidecar: deneb.BlobSidecar, - gossipIndex: number + subnet: number ): Promise { const blobSlot = blobSidecar.signedBlockHeader.message.slot; - // [REJECT] The sidecar is for the correct topic -- i.e. sidecar.index matches the topic {index}. - if (blobSidecar.index !== gossipIndex) { + // [REJECT] The sidecar is for the correct subnet -- i.e. `compute_subnet_for_blob_sidecar(sidecar.index) == subnet_id`. + if (computeSubnetForBlobSidecar(blobSidecar.index, chain.config) !== subnet) { throw new BlobSidecarGossipError(GossipAction.REJECT, { code: BlobSidecarErrorCode.INVALID_INDEX, blobIdx: blobSidecar.index, - gossipIndex, + subnet, }); } @@ -225,3 +226,7 @@ function validateInclusionProof(blobSidecar: deneb.BlobSidecar): boolean { blobSidecar.signedBlockHeader.message.bodyRoot ); } + +function computeSubnetForBlobSidecar(blobIndex: BlobIndex, config: ChainConfig): number { + return blobIndex % config.BLOB_SIDECAR_SUBNET_COUNT; +} diff --git a/packages/beacon-node/src/network/gossip/interface.ts b/packages/beacon-node/src/network/gossip/interface.ts index 9939ed5af657..be7293524ce9 100644 --- a/packages/beacon-node/src/network/gossip/interface.ts +++ b/packages/beacon-node/src/network/gossip/interface.ts @@ -54,7 +54,7 @@ export interface IGossipTopic { export type GossipTopicTypeMap = { [GossipType.beacon_block]: {type: GossipType.beacon_block}; - [GossipType.blob_sidecar]: {type: GossipType.blob_sidecar; index: number}; + [GossipType.blob_sidecar]: {type: GossipType.blob_sidecar; subnet: number}; [GossipType.beacon_aggregate_and_proof]: {type: GossipType.beacon_aggregate_and_proof}; [GossipType.beacon_attestation]: {type: GossipType.beacon_attestation; subnet: number}; [GossipType.voluntary_exit]: {type: GossipType.voluntary_exit}; diff --git a/packages/beacon-node/src/network/gossip/topic.ts b/packages/beacon-node/src/network/gossip/topic.ts index 1c34440df2b8..88ef4143f8ff 100644 --- a/packages/beacon-node/src/network/gossip/topic.ts +++ b/packages/beacon-node/src/network/gossip/topic.ts @@ -73,7 +73,7 @@ function stringifyGossipTopicType(topic: GossipTopic): string { case GossipType.sync_committee: return `${topic.type}_${topic.subnet}`; case GossipType.blob_sidecar: - return `${topic.type}_${topic.index}`; + return `${topic.type}_${topic.subnet}`; } } @@ -181,10 +181,10 @@ export function parseGossipTopic(forkDigestContext: ForkDigestContext, topicStr: } if (gossipTypeStr.startsWith(GossipType.blob_sidecar)) { - const indexStr = gossipTypeStr.slice(GossipType.blob_sidecar.length + 1); // +1 for '_' concatenating the topic name and the index - const index = parseInt(indexStr, 10); - if (Number.isNaN(index)) throw Error(`index ${indexStr} is not a number`); - return {type: GossipType.blob_sidecar, index, fork, encoding}; + const subnetStr = gossipTypeStr.slice(GossipType.blob_sidecar.length + 1); // +1 for '_' concatenating the topic name and the subnet + const subnet = parseInt(subnetStr, 10); + if (Number.isNaN(subnet)) throw Error(`subnet ${subnetStr} is not a number`); + return {type: GossipType.blob_sidecar, subnet, fork, encoding}; } throw Error(`Unknown gossip type ${gossipTypeStr}`); @@ -211,10 +211,10 @@ export function getCoreTopicsAtFork( {type: GossipType.attester_slashing}, ]; - // After Deneb also track blob_sidecar_{index} + // After Deneb also track blob_sidecar_{subnet_id} if (ForkSeq[fork] >= ForkSeq.deneb) { - for (let index = 0; index < config.MAX_BLOBS_PER_BLOCK; index++) { - topics.push({type: GossipType.blob_sidecar, index}); + for (let subnet = 0; subnet < config.BLOB_SIDECAR_SUBNET_COUNT; subnet++) { + topics.push({type: GossipType.blob_sidecar, subnet}); } } diff --git a/packages/beacon-node/src/network/network.ts b/packages/beacon-node/src/network/network.ts index 870fbf303ff4..d8370fc22b5f 100644 --- a/packages/beacon-node/src/network/network.ts +++ b/packages/beacon-node/src/network/network.ts @@ -312,9 +312,9 @@ export class Network implements INetwork { async publishBlobSidecar(blobSidecar: deneb.BlobSidecar): Promise { const slot = blobSidecar.signedBlockHeader.message.slot; const fork = this.config.getForkName(slot); - const index = blobSidecar.index; + const subnet = blobSidecar.index; - return this.publishGossip({type: GossipType.blob_sidecar, fork, index}, blobSidecar, { + return this.publishGossip({type: GossipType.blob_sidecar, fork, subnet}, blobSidecar, { ignoreDuplicatePublishError: true, }); } diff --git a/packages/beacon-node/src/network/processor/gossipHandlers.ts b/packages/beacon-node/src/network/processor/gossipHandlers.ts index 029e7ae4db42..3fb7b06e700a 100644 --- a/packages/beacon-node/src/network/processor/gossipHandlers.ts +++ b/packages/beacon-node/src/network/processor/gossipHandlers.ts @@ -179,7 +179,7 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand async function validateBeaconBlob( blobSidecar: deneb.BlobSidecar, blobBytes: Uint8Array, - gossipIndex: number, + subnet: number, peerIdStr: string, seenTimestampSec: number ): Promise { @@ -202,7 +202,7 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand ); try { - await validateGossipBlobSidecar(chain, blobSidecar, gossipIndex); + await validateGossipBlobSidecar(chain, blobSidecar, subnet); const recvToValidation = Date.now() / 1000 - seenTimestampSec; const validationTime = recvToValidation - recvToValLatency; @@ -212,10 +212,10 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand logger.debug("Received gossip blob", { slot: slot, root: blockHex, - curentSlot: chain.clock.currentSlot, + currentSlot: chain.clock.currentSlot, peerId: peerIdStr, delaySec, - gossipIndex, + subnet, ...blockInputMeta, recvToValLatency, recvToValidation, @@ -363,7 +363,7 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand const blockInput = await validateBeaconBlob( blobSidecar, serializedData, - topic.index, + topic.subnet, peerIdStr, seenTimestampSec ); diff --git a/packages/beacon-node/test/e2e/api/impl/config.test.ts b/packages/beacon-node/test/e2e/api/impl/config.test.ts index 474826050a3a..078e210cb0cc 100644 --- a/packages/beacon-node/test/e2e/api/impl/config.test.ts +++ b/packages/beacon-node/test/e2e/api/impl/config.test.ts @@ -10,8 +10,6 @@ const CONSTANT_NAMES_SKIP_LIST = new Set([ // This constant can also be derived from existing constants so it's not critical. // PARTICIPATION_FLAG_WEIGHTS = [TIMELY_SOURCE_WEIGHT, TIMELY_TARGET_WEIGHT, TIMELY_HEAD_WEIGHT] "PARTICIPATION_FLAG_WEIGHTS", - // TODO DENEB: Configure the blob subnets in a followup PR - "BLOB_SIDECAR_SUBNET_COUNT", ]); describe("api / impl / config", () => { diff --git a/packages/beacon-node/test/unit/network/gossip/topic.test.ts b/packages/beacon-node/test/unit/network/gossip/topic.test.ts index f68c2d737fd6..4b323865061e 100644 --- a/packages/beacon-node/test/unit/network/gossip/topic.test.ts +++ b/packages/beacon-node/test/unit/network/gossip/topic.test.ts @@ -17,7 +17,7 @@ describe("network / gossip / topic", () => { ], [GossipType.blob_sidecar]: [ { - topic: {type: GossipType.blob_sidecar, index: 1, fork: ForkName.deneb, encoding}, + topic: {type: GossipType.blob_sidecar, subnet: 1, fork: ForkName.deneb, encoding}, topicStr: "/eth2/46acb19a/blob_sidecar_1/ssz_snappy", }, ], diff --git a/packages/config/src/chainConfig/configs/mainnet.ts b/packages/config/src/chainConfig/configs/mainnet.ts index ae9a2ec74d1f..a4adc04a1756 100644 --- a/packages/config/src/chainConfig/configs/mainnet.ts +++ b/packages/config/src/chainConfig/configs/mainnet.ts @@ -101,6 +101,7 @@ export const chainConfig: ChainConfig = { // Deneb // `2**12` (= 4096 epochs, ~18 days) MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS: 4096, + BLOB_SIDECAR_SUBNET_COUNT: 6, MAX_BLOBS_PER_BLOCK: 6, // MAX_REQUEST_BLOCKS_DENEB * MAX_BLOBS_PER_BLOCK MAX_REQUEST_BLOB_SIDECARS: 768, diff --git a/packages/config/src/chainConfig/configs/minimal.ts b/packages/config/src/chainConfig/configs/minimal.ts index 6f536bc7732c..d16b03e82c28 100644 --- a/packages/config/src/chainConfig/configs/minimal.ts +++ b/packages/config/src/chainConfig/configs/minimal.ts @@ -98,6 +98,7 @@ export const chainConfig: ChainConfig = { // Deneb // `2**12` (= 4096 epochs, ~18 days) MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS: 4096, + BLOB_SIDECAR_SUBNET_COUNT: 6, MAX_BLOBS_PER_BLOCK: 6, // MAX_REQUEST_BLOCKS_DENEB * MAX_BLOBS_PER_BLOCK MAX_REQUEST_BLOB_SIDECARS: 768, diff --git a/packages/config/src/chainConfig/types.ts b/packages/config/src/chainConfig/types.ts index 513324bdbcb8..291dcc8601a7 100644 --- a/packages/config/src/chainConfig/types.ts +++ b/packages/config/src/chainConfig/types.ts @@ -72,6 +72,7 @@ export type ChainConfig = { // Networking MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS: number; + BLOB_SIDECAR_SUBNET_COUNT: number; MAX_BLOBS_PER_BLOCK: number; MAX_REQUEST_BLOB_SIDECARS: number; }; @@ -138,6 +139,7 @@ export const chainConfigTypes: SpecTypes = { // Networking MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS: "number", + BLOB_SIDECAR_SUBNET_COUNT: "number", MAX_BLOBS_PER_BLOCK: "number", MAX_REQUEST_BLOB_SIDECARS: "number", }; diff --git a/packages/validator/src/util/params.ts b/packages/validator/src/util/params.ts index e8fd1a1e6dc4..825f60e8c7fa 100644 --- a/packages/validator/src/util/params.ts +++ b/packages/validator/src/util/params.ts @@ -136,6 +136,7 @@ function getSpecCriticalParams(localConfig: ChainConfig): Record