From 267991ae56b5ff62fb1760385a4e4cd25c0ee5ce Mon Sep 17 00:00:00 2001 From: NC Date: Fri, 22 Dec 2023 22:22:07 +0800 Subject: [PATCH 1/6] fix: fix block value calculation in `produceBlockV3` (#6207) Fix block value calculation --- packages/beacon-node/src/api/impl/validator/index.ts | 6 +++--- packages/utils/src/ethConversion.ts | 12 ++++++++++++ packages/utils/src/index.ts | 1 + packages/validator/src/services/block.ts | 7 +++---- 4 files changed, 19 insertions(+), 7 deletions(-) create mode 100644 packages/utils/src/ethConversion.ts diff --git a/packages/beacon-node/src/api/impl/validator/index.ts b/packages/beacon-node/src/api/impl/validator/index.ts index 8f92fa483908..954a1dae7cad 100644 --- a/packages/beacon-node/src/api/impl/validator/index.ts +++ b/packages/beacon-node/src/api/impl/validator/index.ts @@ -35,7 +35,7 @@ import { phase0, } from "@lodestar/types"; import {ExecutionStatus} from "@lodestar/fork-choice"; -import {toHex, racePromisesWithCutoff, RaceEvent} from "@lodestar/utils"; +import {toHex, racePromisesWithCutoff, RaceEvent, gweiToWei} from "@lodestar/utils"; import {AttestationError, AttestationErrorCode, GossipAction, SyncCommitteeError} from "../../../chain/errors/index.js"; import {validateApiAggregateAndProof} from "../../../chain/validation/index.js"; import {ZERO_HASH} from "../../../constants/index.js"; @@ -541,8 +541,8 @@ export function getValidatorApi({ const consensusBlockValueBuilder = blindedBlock?.consensusBlockValue ?? BigInt(0); const consensusBlockValueEngine = fullBlock?.consensusBlockValue ?? BigInt(0); - const blockValueBuilder = builderPayloadValue + consensusBlockValueBuilder; - const blockValueEngine = enginePayloadValue + consensusBlockValueEngine; + const blockValueBuilder = builderPayloadValue + gweiToWei(consensusBlockValueBuilder); // Total block value is in wei + const blockValueEngine = enginePayloadValue + gweiToWei(consensusBlockValueEngine); // Total block value is in wei let selectedSource: ProducedBlockSource | null = null; diff --git a/packages/utils/src/ethConversion.ts b/packages/utils/src/ethConversion.ts new file mode 100644 index 000000000000..7aa8fa0cc63c --- /dev/null +++ b/packages/utils/src/ethConversion.ts @@ -0,0 +1,12 @@ +export const ETH_TO_GWEI = BigInt(10 ** 9); +export const GWEI_TO_WEI = BigInt(10 ** 9); +export const ETH_TO_WEI = ETH_TO_GWEI * GWEI_TO_WEI; + +type EthNumeric = bigint; + +/** + * Convert gwei to wei. + */ +export function gweiToWei(gwei: EthNumeric): EthNumeric { + return gwei * GWEI_TO_WEI; +} diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 9ecb78e62533..637b965af682 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -19,3 +19,4 @@ export * from "./url.js"; export * from "./verifyMerkleBranch.js"; export * from "./promise.js"; export * from "./waitFor.js"; +export * from "./ethConversion.js"; diff --git a/packages/validator/src/services/block.ts b/packages/validator/src/services/block.ts index d65ce2616fb5..cdcb184e54b8 100644 --- a/packages/validator/src/services/block.ts +++ b/packages/validator/src/services/block.ts @@ -12,7 +12,7 @@ import { } from "@lodestar/types"; import {ChainForkConfig} from "@lodestar/config"; import {ForkPreBlobs, ForkBlobs, ForkSeq} from "@lodestar/params"; -import {extendError, prettyBytes} from "@lodestar/utils"; +import {ETH_TO_GWEI, ETH_TO_WEI, extendError, gweiToWei, prettyBytes} from "@lodestar/utils"; import {Api, ApiError, routes} from "@lodestar/api"; import {IClock, LoggerVc} from "../util/index.js"; import {PubkeyHex} from "../types.js"; @@ -21,7 +21,6 @@ import {formatBigDecimal} from "../util/format.js"; import {ValidatorStore} from "./validatorStore.js"; import {BlockDutiesService, GENESIS_SLOT} from "./blockDuties.js"; -const ETH_TO_WEI = BigInt("1000000000000000000"); // display upto 5 decimal places const MAX_DECIMAL_FACTOR = BigInt("100000"); @@ -220,9 +219,9 @@ export class BlockProposingService { source: response.executionPayloadBlinded ? ProducedBlockSource.builder : ProducedBlockSource.engine, // winston logger doesn't like bigint executionPayloadValue: `${formatBigDecimal(response.executionPayloadValue, ETH_TO_WEI, MAX_DECIMAL_FACTOR)} ETH`, - consensusBlockValue: `${formatBigDecimal(response.consensusBlockValue, ETH_TO_WEI, MAX_DECIMAL_FACTOR)} ETH`, + consensusBlockValue: `${formatBigDecimal(response.consensusBlockValue, ETH_TO_GWEI, MAX_DECIMAL_FACTOR)} ETH`, totalBlockValue: `${formatBigDecimal( - response.executionPayloadValue + response.consensusBlockValue, + response.executionPayloadValue + gweiToWei(response.consensusBlockValue), ETH_TO_WEI, MAX_DECIMAL_FACTOR )} ETH`, From 61cf1a87600a1b756fd46d314821f6987d3d8b42 Mon Sep 17 00:00:00 2001 From: NC Date: Sat, 23 Dec 2023 20:10:21 +0800 Subject: [PATCH 2/6] feat: add response headers to produceBlockV3 (#6228) * Add `Eth-Consensus-Version` to produceBlockV3 * Lint * Lint * Add header --- packages/api/src/beacon/server/validator.ts | 26 +++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/packages/api/src/beacon/server/validator.ts b/packages/api/src/beacon/server/validator.ts index 6bf446e05a16..5d6c22557060 100644 --- a/packages/api/src/beacon/server/validator.ts +++ b/packages/api/src/beacon/server/validator.ts @@ -4,6 +4,28 @@ import {ServerRoutes, getGenericJsonServer} from "../../utils/server/index.js"; import {ServerApi} from "../../interfaces.js"; export function getRoutes(config: ChainForkConfig, api: ServerApi): ServerRoutes { - // All routes return JSON, use a server auto-generator - return getGenericJsonServer, ReqTypes>({routesData, getReturnTypes, getReqSerializers}, config, api); + const reqSerializers = getReqSerializers(); + const returnTypes = getReturnTypes(); + + // Most of routes return JSON, use a server auto-generator + const serverRoutes = getGenericJsonServer, ReqTypes>( + {routesData, getReturnTypes, getReqSerializers}, + config, + api + ); + return { + ...serverRoutes, + produceBlockV3: { + ...serverRoutes.produceBlockV3, + handler: async (req, res) => { + const response = await api.produceBlockV3(...reqSerializers.produceBlockV3.parseReq(req)); + void res.header("Eth-Consensus-Version", response.version); + void res.header("Eth-Execution-Payload-Blinded", response.executionPayloadBlinded); + void res.header("Eth-Execution-Payload-Value", response.executionPayloadValue); + void res.header("Eth-Consensus-Block-Value", response.consensusBlockValue); + + return returnTypes.produceBlockV3.toJson(response); + }, + }, + }; } From 6f4a9d6f5e9b4e09ed6693d181eff59fbf028c68 Mon Sep 17 00:00:00 2001 From: g11tech Date: Mon, 25 Dec 2023 14:54:00 +0530 Subject: [PATCH 3/6] feat: implement blob sidecars with inclusion proof instead of signtaures (#6089) modify the api fix validator handling fix val build refactor the beacon node impl for the new blobs architecture get the build working with no blobs fix the blob sidecars transmisssion relocate compute blob sidecars relocate verify merkle branch verify inclusion proof fix tests and types fix unit test update the spec test versions skip newly required merkle proof runner change the minimal/mainnet preset based constant strategy add other constants to tests apply feedback apply feedback relocate utils --- .../api/src/beacon/routes/beacon/block.ts | 40 ++---- packages/api/src/beacon/routes/validator.ts | 32 +++-- packages/api/src/builder/routes.ts | 2 +- packages/api/src/utils/routes.ts | 49 ++----- .../src/api/impl/beacon/blocks/index.ts | 71 +++++----- .../src/api/impl/validator/index.ts | 109 +++++++--------- .../src/chain/blocks/importBlock.ts | 2 +- .../blocks/verifyBlocksExecutionPayloads.ts | 2 +- packages/beacon-node/src/chain/chain.ts | 58 ++------- .../src/chain/errors/blobSidecarError.ts | 2 + packages/beacon-node/src/chain/interface.ts | 5 +- .../chain/produceBlock/produceBlockBody.ts | 63 ++------- .../validateBlobsAndKzgCommitments.ts | 14 +- .../chain/seenCache/seenGossipBlockInput.ts | 18 +-- .../src/chain/validation/blobSidecar.ts | 62 +++++---- .../src/db/repositories/blobSidecars.ts | 4 +- .../beacon-node/src/execution/builder/http.ts | 23 ++-- .../src/execution/builder/interface.ts | 6 +- .../beacon-node/src/execution/engine/mock.ts | 2 +- .../beacon-node/src/metrics/metrics/beacon.ts | 10 +- .../src/network/gossip/interface.ts | 4 +- .../beacon-node/src/network/gossip/topic.ts | 2 +- packages/beacon-node/src/network/interface.ts | 2 +- packages/beacon-node/src/network/network.ts | 16 +-- .../network/processor/extractSlotRootFns.ts | 4 +- .../src/network/processor/gossipHandlers.ts | 31 +++-- .../reqresp/beaconBlocksMaybeBlobsByRange.ts | 8 +- .../reqresp/handlers/blobSidecarsByRange.ts | 4 +- .../reqresp/handlers/blobSidecarsByRoot.ts | 3 +- packages/beacon-node/src/util/blobs.ts | 48 +++++++ packages/beacon-node/src/util/sszBytes.ts | 22 ++-- .../el-interop/ethereumjsdocker/post-merge.sh | 2 +- .../test/spec/presets/fork_choice.test.ts | 15 +-- .../test/spec/specTestVersioning.ts | 2 +- .../test/spec/utils/specTestIterator.ts | 1 + .../seenCache/seenGossipBlockInput.test.ts | 21 ++- .../beaconBlocksMaybeBlobsByRange.test.ts | 18 ++- .../beacon-node/test/unit/util/kzg.test.ts | 42 +++--- .../test/unit/util/sszBytes.test.ts | 26 ++-- packages/params/src/index.ts | 8 ++ packages/params/src/presets/mainnet.ts | 1 + packages/params/src/presets/minimal.ts | 3 +- packages/params/src/types.ts | 2 + .../test/e2e/ensure-config-is-synced.test.ts | 2 +- .../src/signatureSets/proposer.ts | 20 ++- .../state-transition/src/util/blindedBlock.ts | 86 ++----------- packages/state-transition/src/util/blobs.ts | 12 -- .../state-transition/src/util/blockRoot.ts | 12 ++ packages/state-transition/src/util/index.ts | 1 - packages/types/package.json | 4 +- packages/types/src/allForks/sszTypes.ts | 1 - packages/types/src/allForks/types.ts | 25 +--- packages/types/src/deneb/sszTypes.ts | 74 ++--------- packages/types/src/deneb/types.ts | 14 +- packages/types/src/utils/typeguards.ts | 30 +---- packages/types/test/constants/blobs.test.ts | 25 ++++ .../lightclient.test.ts} | 2 +- packages/validator/src/services/block.ts | 121 +++++++----------- .../validator/src/services/validatorStore.ts | 33 ----- .../src/util/externalSignerClient.ts | 7 - packages/validator/src/util/params.ts | 1 + 61 files changed, 521 insertions(+), 808 deletions(-) create mode 100644 packages/beacon-node/src/util/blobs.ts delete mode 100644 packages/state-transition/src/util/blobs.ts create mode 100644 packages/types/test/constants/blobs.test.ts rename packages/types/test/{unit/constants.test.ts => constants/lightclient.test.ts} (94%) diff --git a/packages/api/src/beacon/routes/beacon/block.ts b/packages/api/src/beacon/routes/beacon/block.ts index 53ebb93692dc..b56006fe4191 100644 --- a/packages/api/src/beacon/routes/beacon/block.ts +++ b/packages/api/src/beacon/routes/beacon/block.ts @@ -1,17 +1,7 @@ import {ContainerType} from "@chainsafe/ssz"; import {ForkName} from "@lodestar/params"; import {ChainForkConfig} from "@lodestar/config"; -import { - phase0, - allForks, - Slot, - Root, - ssz, - RootHex, - deneb, - isSignedBlockContents, - isSignedBlindedBlockContents, -} from "@lodestar/types"; +import {phase0, allForks, Slot, Root, ssz, RootHex, deneb, isSignedBlockContents} from "@lodestar/types"; import { RoutesData, @@ -30,10 +20,7 @@ import { import {HttpStatusCode} from "../../../utils/client/httpStatusCode.js"; import {parseAcceptHeader, writeAcceptHeader} from "../../../utils/acceptHeader.js"; import {ApiClientResponse, ResponseFormat} from "../../../interfaces.js"; -import { - allForksSignedBlockContentsReqSerializer, - allForksSignedBlindedBlockContentsReqSerializer, -} from "../../../utils/routes.js"; +import {allForksSignedBlockContentsReqSerializer} from "../../../utils/routes.js"; // See /packages/api/src/routes/index.ts for reasoning and instructions to add new routes @@ -207,7 +194,7 @@ export type Api = { * Publish a signed blinded block by submitting it to the mev relay and patching in the block * transactions beacon node gets in response. */ - publishBlindedBlock(blindedBlockOrContents: allForks.SignedBlindedBeaconBlockOrContents): Promise< + publishBlindedBlock(blindedBlock: allForks.SignedBlindedBeaconBlock): Promise< ApiClientResponse< { [HttpStatusCode.OK]: void; @@ -218,7 +205,7 @@ export type Api = { >; publishBlindedBlockV2( - blindedBlockOrContents: allForks.SignedBlindedBeaconBlockOrContents, + blindedBlockOrContents: allForks.SignedBlindedBeaconBlock, opts: {broadcastValidation?: BroadcastValidation} ): Promise< ApiClientResponse< @@ -315,16 +302,9 @@ export function getReqSerializers(config: ChainForkConfig): ReqSerializers config.getBlindedForkTypes(data.message.slot).SignedBeaconBlock; - const AllForksSignedBlindedBlockOrContents: TypeJson = { - toJson: (data) => - isSignedBlindedBlockContents(data) - ? allForksSignedBlindedBlockContentsReqSerializer(getSignedBlindedBeaconBlockType).toJson(data) - : getSignedBlindedBeaconBlockType(data).toJson(data), - - fromJson: (data) => - (data as {signed_blinded_block: unknown}).signed_blinded_block !== undefined - ? allForksSignedBlindedBlockContentsReqSerializer(getSignedBlindedBeaconBlockType).fromJson(data) - : getSignedBlindedBeaconBlockType(data as allForks.SignedBlindedBeaconBlock).fromJson(data), + const AllForksSignedBlindedBlock: TypeJson = { + toJson: (data) => getSignedBlindedBeaconBlockType(data).toJson(data), + fromJson: (data) => getSignedBlindedBeaconBlockType(data as allForks.SignedBlindedBeaconBlock).fromJson(data), }; return { @@ -353,14 +333,14 @@ export function getReqSerializers(config: ChainForkConfig): ReqSerializers ({ - body: AllForksSignedBlindedBlockOrContents.toJson(item), + body: AllForksSignedBlindedBlock.toJson(item), query: {broadcast_validation: broadcastValidation}, }), parseReq: ({body, query}) => [ - AllForksSignedBlindedBlockOrContents.fromJson(body), + AllForksSignedBlindedBlock.fromJson(body), {broadcastValidation: query.broadcast_validation as BroadcastValidation}, ], schema: { diff --git a/packages/api/src/beacon/routes/validator.ts b/packages/api/src/beacon/routes/validator.ts index f5ae20937a0e..f7a731783681 100644 --- a/packages/api/src/beacon/routes/validator.ts +++ b/packages/api/src/beacon/routes/validator.ts @@ -1,5 +1,5 @@ import {ContainerType, fromHexString, toHexString, Type} from "@chainsafe/ssz"; -import {ForkName, ForkBlobs, isForkBlobs, isForkExecution, ForkPreBlobs} from "@lodestar/params"; +import {ForkName, ForkBlobs, isForkBlobs, isForkExecution, ForkPreBlobs, ForkExecution} from "@lodestar/params"; import { allForks, altair, @@ -37,7 +37,7 @@ import { TypeJson, } from "../../utils/index.js"; import {fromU64Str, fromGraffitiHex, toU64Str, U64Str, toGraffitiHex} from "../../utils/serdes.js"; -import {allForksBlockContentsResSerializer, allForksBlindedBlockContentsResSerializer} from "../../utils/routes.js"; +import {allForksBlockContentsResSerializer} from "../../utils/routes.js"; import {ExecutionOptimistic} from "./beacon/block.js"; export enum BuilderSelection { @@ -59,14 +59,14 @@ export type ProduceBlockOrContentsRes = {executionPayloadValue: Wei; consensusBl | {data: allForks.BeaconBlock; version: ForkPreBlobs} | {data: allForks.BlockContents; version: ForkBlobs} ); -export type ProduceBlindedBlockOrContentsRes = {executionPayloadValue: Wei; consensusBlockValue: Gwei} & ( - | {data: allForks.BlindedBeaconBlock; version: ForkPreBlobs} - | {data: allForks.BlindedBlockContents; version: ForkBlobs} -); +export type ProduceBlindedBlockRes = {executionPayloadValue: Wei; consensusBlockValue: Gwei} & { + data: allForks.BlindedBeaconBlock; + version: ForkExecution; +}; export type ProduceFullOrBlindedBlockOrContentsRes = | (ProduceBlockOrContentsRes & {executionPayloadBlinded: false}) - | (ProduceBlindedBlockOrContentsRes & {executionPayloadBlinded: true}); + | (ProduceBlindedBlockRes & {executionPayloadBlinded: true}); // See /packages/api/src/routes/index.ts for reasoning and instructions to add new routes @@ -287,7 +287,7 @@ export type Api = { ): Promise< ApiClientResponse< { - [HttpStatusCode.OK]: ProduceBlindedBlockOrContentsRes; + [HttpStatusCode.OK]: ProduceBlindedBlockRes; }, HttpStatusCode.BAD_REQUEST | HttpStatusCode.SERVICE_UNAVAILABLE > @@ -721,13 +721,11 @@ export function getReturnTypes(): ReturnTypes { isForkBlobs(fork) ? allForksBlockContentsResSerializer(fork) : ssz[fork].BeaconBlock ) ) as TypeJson; - const produceBlindedBlockOrContents = WithBlockValues( - WithVersion((fork: ForkName) => - isForkBlobs(fork) - ? allForksBlindedBlockContentsResSerializer(fork) - : ssz.allForksBlinded[isForkExecution(fork) ? fork : ForkName.bellatrix].BeaconBlock + const produceBlindedBlock = WithBlockValues( + WithVersion( + (fork: ForkName) => ssz.allForksBlinded[isForkExecution(fork) ? fork : ForkName.bellatrix].BeaconBlock ) - ) as TypeJson; + ) as TypeJson; return { getAttesterDuties: WithDependentRootExecutionOptimistic(ArrayOf(AttesterDuty)), @@ -741,7 +739,7 @@ export function getReturnTypes(): ReturnTypes { if (data.executionPayloadBlinded) { return { execution_payload_blinded: true, - ...(produceBlindedBlockOrContents.toJson(data) as Record), + ...(produceBlindedBlock.toJson(data) as Record), }; } else { return { @@ -752,13 +750,13 @@ export function getReturnTypes(): ReturnTypes { }, fromJson: (data) => { if ((data as {execution_payload_blinded: true}).execution_payload_blinded) { - return {executionPayloadBlinded: true, ...produceBlindedBlockOrContents.fromJson(data)}; + return {executionPayloadBlinded: true, ...produceBlindedBlock.fromJson(data)}; } else { return {executionPayloadBlinded: false, ...produceBlockOrContents.fromJson(data)}; } }, }, - produceBlindedBlock: produceBlindedBlockOrContents, + produceBlindedBlock, produceAttestationData: ContainerData(ssz.phase0.AttestationData), produceSyncCommitteeContribution: ContainerData(ssz.altair.SyncCommitteeContribution), diff --git a/packages/api/src/builder/routes.ts b/packages/api/src/builder/routes.ts index 0136f1deeac4..6f5a55f0dcff 100644 --- a/packages/api/src/builder/routes.ts +++ b/packages/api/src/builder/routes.ts @@ -34,7 +34,7 @@ export type Api = { HttpStatusCode.NOT_FOUND | HttpStatusCode.BAD_REQUEST > >; - submitBlindedBlock(signedBlock: allForks.SignedBlindedBeaconBlockOrContents): Promise< + submitBlindedBlock(signedBlock: allForks.SignedBlindedBeaconBlock): Promise< ApiClientResponse< { [HttpStatusCode.OK]: { diff --git a/packages/api/src/utils/routes.ts b/packages/api/src/utils/routes.ts index 213a561efd58..77d177f7b24c 100644 --- a/packages/api/src/utils/routes.ts +++ b/packages/api/src/utils/routes.ts @@ -11,12 +11,14 @@ export function allForksSignedBlockContentsReqSerializer( return { toJson: (data) => ({ signed_block: blockSerializer(data.signedBlock).toJson(data.signedBlock), - signed_blob_sidecars: ssz.deneb.SignedBlobSidecars.toJson(data.signedBlobSidecars), + kzg_proofs: ssz.deneb.KZGProofs.toJson(data.kzgProofs), + blobs: ssz.deneb.Blobs.toJson(data.blobs), }), - fromJson: (data: {signed_block: unknown; signed_blob_sidecars: unknown}) => ({ + fromJson: (data: {signed_block: unknown; kzg_proofs: unknown; blobs: unknown}) => ({ signedBlock: blockSerializer(data.signed_block as allForks.SignedBeaconBlock).fromJson(data.signed_block), - signedBlobSidecars: ssz.deneb.SignedBlobSidecars.fromJson(data.signed_blob_sidecars), + kzgProofs: ssz.deneb.KZGProofs.fromJson(data.kzg_proofs), + blobs: ssz.deneb.Blobs.fromJson(data.blobs), }), }; } @@ -25,44 +27,13 @@ export function allForksBlockContentsResSerializer(fork: ForkBlobs): TypeJson ({ block: (ssz.allForks[fork].BeaconBlock as allForks.AllForksSSZTypes["BeaconBlock"]).toJson(data.block), - blob_sidecars: ssz.deneb.BlobSidecars.toJson(data.blobSidecars), + kzg_proofs: ssz.deneb.KZGProofs.toJson(data.kzgProofs), + blobs: ssz.deneb.Blobs.toJson(data.blobs), }), - fromJson: (data: {block: unknown; blob_sidecars: unknown}) => ({ + fromJson: (data: {block: unknown; blob_sidecars: unknown; kzg_proofs: unknown; blobs: unknown}) => ({ block: ssz.allForks[fork].BeaconBlock.fromJson(data.block), - blobSidecars: ssz.deneb.BlobSidecars.fromJson(data.blob_sidecars), - }), - }; -} - -export function allForksSignedBlindedBlockContentsReqSerializer( - blockSerializer: (data: allForks.SignedBlindedBeaconBlock) => TypeJson -): TypeJson { - return { - toJson: (data) => ({ - signed_blinded_block: blockSerializer(data.signedBlindedBlock).toJson(data.signedBlindedBlock), - signed_blinded_blob_sidecars: ssz.deneb.SignedBlindedBlobSidecars.toJson(data.signedBlindedBlobSidecars), - }), - - fromJson: (data: {signed_blinded_block: unknown; signed_blinded_blob_sidecars: unknown}) => ({ - signedBlindedBlock: blockSerializer(data.signed_blinded_block as allForks.SignedBlindedBeaconBlock).fromJson( - data.signed_blinded_block - ), - signedBlindedBlobSidecars: ssz.deneb.SignedBlindedBlobSidecars.fromJson(data.signed_blinded_blob_sidecars), - }), - }; -} - -export function allForksBlindedBlockContentsResSerializer(fork: ForkBlobs): TypeJson { - return { - toJson: (data) => ({ - blinded_block: (ssz.allForksBlinded[fork].BeaconBlock as allForks.AllForksBlindedSSZTypes["BeaconBlock"]).toJson( - data.blindedBlock - ), - blinded_blob_sidecars: ssz.deneb.BlindedBlobSidecars.toJson(data.blindedBlobSidecars), - }), - fromJson: (data: {blinded_block: unknown; blinded_blob_sidecars: unknown}) => ({ - blindedBlock: ssz.allForksBlinded[fork].BeaconBlock.fromJson(data.blinded_block), - blindedBlobSidecars: ssz.deneb.BlindedBlobSidecars.fromJson(data.blinded_blob_sidecars), + kzgProofs: ssz.deneb.KZGProofs.fromJson(data.kzg_proofs), + blobs: ssz.deneb.Blobs.fromJson(data.blobs), }), }; } diff --git a/packages/beacon-node/src/api/impl/beacon/blocks/index.ts b/packages/beacon-node/src/api/impl/beacon/blocks/index.ts index ef24724a34b1..89565426426e 100644 --- a/packages/beacon-node/src/api/impl/beacon/blocks/index.ts +++ b/packages/beacon-node/src/api/impl/beacon/blocks/index.ts @@ -1,16 +1,13 @@ import {fromHexString, toHexString} from "@chainsafe/ssz"; import {routes, ServerApi, ResponseFormat} from "@lodestar/api"; -import { - computeTimeAtSlot, - parseSignedBlindedBlockOrContents, - reconstructFullBlockOrContents, -} from "@lodestar/state-transition"; +import {computeTimeAtSlot, reconstructFullBlockOrContents} from "@lodestar/state-transition"; import {SLOTS_PER_HISTORICAL_ROOT} from "@lodestar/params"; import {sleep, toHex} from "@lodestar/utils"; import {allForks, deneb, isSignedBlockContents, ProducedBlockSource} from "@lodestar/types"; import {BlockSource, getBlockInput, ImportBlockOpts, BlockInput} from "../../../../chain/blocks/types.js"; import {promiseAllMaybeAsync} from "../../../../util/promises.js"; import {isOptimisticBlock} from "../../../../util/forkChoice.js"; +import {computeBlobSidecars} from "../../../../util/blobs.js"; import {BlockError, BlockErrorCode} from "../../../../chain/errors/index.js"; import {OpSource} from "../../../../metrics/validatorMonitor.js"; import {NetworkEvent} from "../../../../network/index.js"; @@ -45,22 +42,23 @@ export function getBeaconBlockApi({ opts: PublishBlockOpts = {} ) => { const seenTimestampSec = Date.now() / 1000; - let blockForImport: BlockInput, signedBlock: allForks.SignedBeaconBlock, signedBlobs: deneb.SignedBlobSidecars; + let blockForImport: BlockInput, signedBlock: allForks.SignedBeaconBlock, blobSidecars: deneb.BlobSidecars; if (isSignedBlockContents(signedBlockOrContents)) { - ({signedBlock, signedBlobSidecars: signedBlobs} = signedBlockOrContents); + ({signedBlock} = signedBlockOrContents); + blobSidecars = computeBlobSidecars(config, signedBlock, signedBlockOrContents); blockForImport = getBlockInput.postDeneb( config, signedBlock, BlockSource.api, - signedBlobs.map((sblob) => sblob.message), + blobSidecars, // don't bundle any bytes for block and blobs null, - signedBlobs.map(() => null) + blobSidecars.map(() => null) ); } else { signedBlock = signedBlockOrContents; - signedBlobs = []; + blobSidecars = []; // TODO: Once API supports submitting data as SSZ, replace null with blockBytes blockForImport = getBlockInput.preDeneb(config, signedBlock, BlockSource.api, null); } @@ -188,18 +186,15 @@ export function getBeaconBlockApi({ } throw e; }), - ...signedBlobs.map((signedBlob) => () => network.publishBlobSidecar(signedBlob)), + ...blobSidecars.map((blobSidecar) => () => network.publishBlobSidecar(blobSidecar)), ]; await promiseAllMaybeAsync(publishPromises); }; const publishBlindedBlock: ServerApi["publishBlindedBlock"] = async ( - signedBlindedBlockOrContents, + signedBlindedBlock, opts: PublishBlockOpts = {} ) => { - const {signedBlindedBlock, signedBlindedBlobSidecars} = - parseSignedBlindedBlockOrContents(signedBlindedBlockOrContents); - const slot = signedBlindedBlock.message.slot; const blockRoot = toHex( chain.config @@ -210,27 +205,31 @@ export function getBeaconBlockApi({ // Either the payload/blobs are cached from i) engine locally or ii) they are from the builder // // executionPayload can be null or a real payload in locally produced so check for presence of root - const source = chain.producedBlockRoot.has(blockRoot) ? ProducedBlockSource.engine : ProducedBlockSource.builder; - - const executionPayload = chain.producedBlockRoot.get(blockRoot) ?? null; - const blobSidecars = executionPayload - ? chain.producedBlobSidecarsCache.get(toHex(executionPayload.blockHash)) - : undefined; - const blobs = blobSidecars ? blobSidecars.map((blobSidecar) => blobSidecar.blob) : null; - - chain.logger.debug("Assembling blinded block for publishing", {source, blockRoot, slot}); + const executionPayload = chain.producedBlockRoot.get(blockRoot); + if (executionPayload !== undefined) { + const source = ProducedBlockSource.engine; + chain.logger.debug("Reconstructing signedBlockOrContents", {blockRoot, slot, source}); + + const contents = executionPayload + ? chain.producedContentsCache.get(toHex(executionPayload.blockHash)) ?? null + : null; + const signedBlockOrContents = reconstructFullBlockOrContents(signedBlindedBlock, {executionPayload, contents}); + + chain.logger.info("Publishing assembled block", {blockRoot, slot, source}); + return publishBlock(signedBlockOrContents, opts); + } else { + const source = ProducedBlockSource.builder; + chain.logger.debug("Reconstructing signedBlockOrContents", {blockRoot, slot, source}); - const signedBlockOrContents = - source === ProducedBlockSource.engine - ? reconstructFullBlockOrContents({signedBlindedBlock, signedBlindedBlobSidecars}, {executionPayload, blobs}) - : await reconstructBuilderBlockOrContents(chain, signedBlindedBlockOrContents); + const signedBlockOrContents = await reconstructBuilderBlockOrContents(chain, signedBlindedBlock); - // the full block is published by relay and it's possible that the block is already known to us - // by gossip - // - // see: https://github.com/ChainSafe/lodestar/issues/5404 - chain.logger.info("Publishing assembled block", {blockRoot, slot, source}); - return publishBlock(signedBlockOrContents, {...opts, ignoreIfKnown: true}); + // the full block is published by relay and it's possible that the block is already known to us + // by gossip + // + // see: https://github.com/ChainSafe/lodestar/issues/5404 + chain.logger.info("Publishing assembled block", {blockRoot, slot, source}); + return publishBlock(signedBlockOrContents, {...opts, ignoreIfKnown: true}); + } }; return { @@ -424,13 +423,13 @@ export function getBeaconBlockApi({ async function reconstructBuilderBlockOrContents( chain: ApiModules["chain"], - signedBlindedBlockOrContents: allForks.SignedBlindedBeaconBlockOrContents + signedBlindedBlock: allForks.SignedBlindedBeaconBlock ): Promise { const executionBuilder = chain.executionBuilder; if (!executionBuilder) { throw Error("exeutionBuilder required to publish SignedBlindedBeaconBlock"); } - const signedBlockOrContents = await executionBuilder.submitBlindedBlock(signedBlindedBlockOrContents); + const signedBlockOrContents = await executionBuilder.submitBlindedBlock(signedBlindedBlock); return signedBlockOrContents; } diff --git a/packages/beacon-node/src/api/impl/validator/index.ts b/packages/beacon-node/src/api/impl/validator/index.ts index 954a1dae7cad..3f813f32a3fd 100644 --- a/packages/beacon-node/src/api/impl/validator/index.ts +++ b/packages/beacon-node/src/api/impl/validator/index.ts @@ -9,7 +9,6 @@ import { computeEpochAtSlot, getCurrentSlot, beaconBlockToBlinded, - blobSidecarsToBlinded, } from "@lodestar/state-transition"; import { GENESIS_SLOT, @@ -31,7 +30,7 @@ import { allForks, BLSSignature, isBlindedBeaconBlock, - isBlindedBlockContents, + isBlockContents, phase0, } from "@lodestar/types"; import {ExecutionStatus} from "@lodestar/fork-choice"; @@ -280,7 +279,7 @@ export function getValidatorApi({ ); } - const produceBlindedBlockOrContents = async function produceBlindedBlockOrContents( + const produceBuilderBlindedBlock = async function produceBuilderBlindedBlock( slot: Slot, randaoReveal: BLSSignature, graffiti: string, @@ -288,7 +287,12 @@ export function getValidatorApi({ { skipHeadChecksAndUpdate, }: Omit & {skipHeadChecksAndUpdate?: boolean} = {} - ): Promise { + ): Promise { + const version = config.getForkName(slot); + if (!isForkExecution(version)) { + throw Error(`Invalid fork=${version} for produceBuilderBlindedBlock`); + } + const source = ProducedBlockSource.builder; metrics?.blockProductionRequests.inc({source}); @@ -329,31 +333,17 @@ export function getValidatorApi({ root: toHexString(config.getBlindedForkTypes(slot).BeaconBlock.hashTreeRoot(block)), }); - const version = config.getForkName(block.slot); if (chain.opts.persistProducedBlocks) { void chain.persistBlock(block, "produced_builder_block"); } - if (isForkBlobs(version)) { - const blockHash = toHex((block as bellatrix.BlindedBeaconBlock).body.executionPayloadHeader.blockHash); - const blindedBlobSidecars = chain.producedBlindedBlobSidecarsCache.get(blockHash); - if (blindedBlobSidecars === undefined) { - throw Error("blobSidecars missing in cache"); - } - return { - data: {blindedBlock: block, blindedBlobSidecars} as allForks.BlindedBlockContents, - version, - executionPayloadValue, - consensusBlockValue, - }; - } else { - return {data: block, version, executionPayloadValue, consensusBlockValue}; - } + + return {data: block, version, executionPayloadValue, consensusBlockValue}; } finally { if (timer) timer({source}); } }; - const produceFullBlockOrContents = async function produceFullBlockOrContents( + const produceEngineFullBlockOrContents = async function produceEngineFullBlockOrContents( slot: Slot, randaoReveal: BLSSignature, graffiti: string, @@ -407,12 +397,13 @@ export function getValidatorApi({ } if (isForkBlobs(version)) { const blockHash = toHex((block as bellatrix.BeaconBlock).body.executionPayload.blockHash); - const blobSidecars = chain.producedBlobSidecarsCache.get(blockHash); - if (blobSidecars === undefined) { - throw Error("blobSidecars missing in cache"); + const contents = chain.producedContentsCache.get(blockHash); + if (contents === undefined) { + throw Error("contents missing in cache"); } + return { - data: {block, blobSidecars} as allForks.BlockContents, + data: {block, ...contents} as allForks.BlockContents, version, executionPayloadValue, consensusBlockValue, @@ -460,12 +451,12 @@ export function getValidatorApi({ // Start calls for building execution and builder blocks const blindedBlockPromise = isBuilderEnabled ? // can't do fee recipient checks as builder bid doesn't return feeRecipient as of now - produceBlindedBlockOrContents(slot, randaoReveal, graffiti, { + produceBuilderBlindedBlock(slot, randaoReveal, graffiti, { feeRecipient, // skip checking and recomputing head in these individual produce calls skipHeadChecksAndUpdate: true, }).catch((e) => { - logger.error("produceBlindedBlockOrContents failed to produce block", {slot}, e); + logger.error("produceBuilderBlindedBlock failed to produce block", {slot}, e); return null; }) : null; @@ -481,13 +472,13 @@ export function getValidatorApi({ !isBuilderEnabled || builderSelection !== routes.validator.BuilderSelection.BuilderOnly ? // TODO deneb: builderSelection needs to be figured out if to be done beacon side // || builderSelection !== BuilderSelection.BuilderOnly - produceFullBlockOrContents(slot, randaoReveal, graffiti, { + produceEngineFullBlockOrContents(slot, randaoReveal, graffiti, { feeRecipient, strictFeeRecipientCheck, // skip checking and recomputing head in these individual produce calls skipHeadChecksAndUpdate: true, }).catch((e) => { - logger.error("produceFullBlockOrContents failed to produce block", {slot}, e); + logger.error("produceEngineFullBlockOrContents failed to produce block", {slot}, e); return null; }) : null; @@ -497,7 +488,7 @@ export function getValidatorApi({ // reference index of promises in the race const promisesOrder = [ProducedBlockSource.builder, ProducedBlockSource.engine]; [blindedBlock, fullBlock] = await racePromisesWithCutoff< - routes.validator.ProduceBlockOrContentsRes | routes.validator.ProduceBlindedBlockOrContentsRes | null + routes.validator.ProduceBlockOrContentsRes | routes.validator.ProduceBlindedBlockRes | null >( [blindedBlockPromise, fullBlockPromise], BLOCK_PRODUCTION_RACE_CUTOFF_MS, @@ -607,7 +598,7 @@ export function getValidatorApi({ executionPayloadBlinded: false; }; } else { - return {...blindedBlock, executionPayloadBlinded: true} as routes.validator.ProduceBlindedBlockOrContentsRes & { + return {...blindedBlock, executionPayloadBlinded: true} as routes.validator.ProduceBlindedBlockRes & { executionPayloadBlinded: true; }; } @@ -618,7 +609,7 @@ export function getValidatorApi({ randaoReveal, graffiti ) { - const producedData = await produceFullBlockOrContents(slot, randaoReveal, graffiti); + const producedData = await produceEngineFullBlockOrContents(slot, randaoReveal, graffiti); if (isForkBlobs(producedData.version)) { throw Error(`Invalid call to produceBlock for deneb+ fork=${producedData.version}`); } else { @@ -628,45 +619,35 @@ export function getValidatorApi({ } }; - const produceBlindedBlock: ServerApi["produceBlindedBlock"] = - async function produceBlindedBlock(slot, randaoReveal, graffiti) { - const producedData = await produceBlockV3(slot, randaoReveal, graffiti); - let blindedProducedData: routes.validator.ProduceBlindedBlockOrContentsRes; - - if (isForkBlobs(producedData.version)) { - if (isBlindedBlockContents(producedData.data as allForks.FullOrBlindedBlockContents)) { - blindedProducedData = producedData as routes.validator.ProduceBlindedBlockOrContentsRes; - } else { - // - const {block, blobSidecars} = producedData.data as allForks.BlockContents; - const blindedBlock = beaconBlockToBlinded(config, block as allForks.AllForksExecution["BeaconBlock"]); - const blindedBlobSidecars = blobSidecarsToBlinded(blobSidecars); - - blindedProducedData = { - ...producedData, - data: {blindedBlock, blindedBlobSidecars}, - } as routes.validator.ProduceBlindedBlockOrContentsRes; - } + const produceEngineOrBuilderBlindedBlock: ServerApi["produceBlindedBlock"] = + async function produceEngineOrBuilderBlindedBlock(slot, randaoReveal, graffiti) { + const {data, executionPayloadValue, consensusBlockValue, version} = await produceBlockV3( + slot, + randaoReveal, + graffiti + ); + if (!isForkExecution(version)) { + throw Error(`Invalid fork=${version} for produceEngineOrBuilderBlindedBlock`); + } + const executionPayloadBlinded = true; + + if (isBlockContents(data)) { + const {block} = data; + const blindedBlock = beaconBlockToBlinded(config, block as allForks.AllForksExecution["BeaconBlock"]); + return {executionPayloadValue, consensusBlockValue, data: blindedBlock, executionPayloadBlinded, version}; + } else if (isBlindedBeaconBlock(data)) { + return {executionPayloadValue, consensusBlockValue, data, executionPayloadBlinded, version}; } else { - if (isBlindedBeaconBlock(producedData.data)) { - blindedProducedData = producedData as routes.validator.ProduceBlindedBlockOrContentsRes; - } else { - const block = producedData.data; - const blindedBlock = beaconBlockToBlinded(config, block as allForks.AllForksExecution["BeaconBlock"]); - blindedProducedData = { - ...producedData, - data: blindedBlock, - } as routes.validator.ProduceBlindedBlockOrContentsRes; - } + const blindedBlock = beaconBlockToBlinded(config, data as allForks.AllForksExecution["BeaconBlock"]); + return {executionPayloadValue, consensusBlockValue, data: blindedBlock, executionPayloadBlinded, version}; } - return blindedProducedData; }; return { produceBlock, - produceBlockV2: produceFullBlockOrContents, + produceBlockV2: produceEngineFullBlockOrContents, produceBlockV3, - produceBlindedBlock, + produceBlindedBlock: produceEngineOrBuilderBlindedBlock, async produceAttestationData(committeeIndex, slot) { notWhileSyncing(); diff --git a/packages/beacon-node/src/chain/blocks/importBlock.ts b/packages/beacon-node/src/chain/blocks/importBlock.ts index feaddfbad39d..12b43359fa4e 100644 --- a/packages/beacon-node/src/chain/blocks/importBlock.ts +++ b/packages/beacon-node/src/chain/blocks/importBlock.ts @@ -7,7 +7,6 @@ import { computeStartSlotAtEpoch, isStateValidatorsNodesPopulated, RootCache, - kzgCommitmentToVersionedHash, } from "@lodestar/state-transition"; import {routes} from "@lodestar/api"; import {ForkChoiceError, ForkChoiceErrorCode, EpochDifference, AncestorStatus} from "@lodestar/fork-choice"; @@ -16,6 +15,7 @@ import {ZERO_HASH_HEX} from "../../constants/index.js"; import {toCheckpointHex} from "../stateCache/index.js"; import {isOptimisticBlock} from "../../util/forkChoice.js"; import {isQueueErrorAborted} from "../../util/queue/index.js"; +import {kzgCommitmentToVersionedHash} from "../../util/blobs.js"; import {ChainEvent, ReorgEventData} from "../emitter.js"; import {REPROCESS_MIN_TIME_TO_NEXT_SLOT_SEC} from "../reprocess.js"; import type {BeaconChain} from "../chain.js"; diff --git a/packages/beacon-node/src/chain/blocks/verifyBlocksExecutionPayloads.ts b/packages/beacon-node/src/chain/blocks/verifyBlocksExecutionPayloads.ts index 01d04e9a876a..5dbe104c9541 100644 --- a/packages/beacon-node/src/chain/blocks/verifyBlocksExecutionPayloads.ts +++ b/packages/beacon-node/src/chain/blocks/verifyBlocksExecutionPayloads.ts @@ -5,7 +5,6 @@ import { isExecutionBlockBodyType, isMergeTransitionBlock as isMergeTransitionBlockFn, isExecutionEnabled, - kzgCommitmentToVersionedHash, } from "@lodestar/state-transition"; import {bellatrix, allForks, Slot, deneb} from "@lodestar/types"; import { @@ -24,6 +23,7 @@ import {ForkSeq, SAFE_SLOTS_TO_IMPORT_OPTIMISTICALLY} from "@lodestar/params"; import {IExecutionEngine} from "../../execution/engine/interface.js"; import {BlockError, BlockErrorCode} from "../errors/index.js"; import {IClock} from "../../util/clock.js"; +import {kzgCommitmentToVersionedHash} from "../../util/blobs.js"; import {BlockProcessOpts} from "../options.js"; import {ExecutionPayloadStatus} from "../../execution/engine/interface.js"; import {IEth1ForBlockProduction} from "../../eth1/index.js"; diff --git a/packages/beacon-node/src/chain/chain.ts b/packages/beacon-node/src/chain/chain.ts index e093b4240828..5e38cf23f5de 100644 --- a/packages/beacon-node/src/chain/chain.ts +++ b/packages/beacon-node/src/chain/chain.ts @@ -88,7 +88,6 @@ import {CheckpointStateCache} from "./stateCache/stateContextCheckpointsCache.js * allow some margin if the node overloads. */ const DEFAULT_MAX_CACHED_PRODUCED_ROOTS = 4; -const DEFAULT_MAX_CACHED_BLOB_SIDECARS = 4; export class BeaconChain implements IBeaconChain { readonly genesisTime: UintNum64; @@ -138,8 +137,7 @@ export class BeaconChain implements IBeaconChain { readonly checkpointBalancesCache: CheckpointBalancesCache; readonly shufflingCache: ShufflingCache; /** Map keyed by executionPayload.blockHash of the block for those blobs */ - readonly producedBlobSidecarsCache = new Map(); - readonly producedBlindedBlobSidecarsCache = new Map(); + readonly producedContentsCache = new Map(); // Cache payload from the local execution so that produceBlindedBlock or produceBlockV3 and // send and get signed/published blinded versions which beacon can assemble into full before @@ -554,32 +552,9 @@ export class BeaconChain implements IBeaconChain { // publishing the blinded block's full version if (blobs.type === BlobsResultType.produced) { // body is of full type here - const blockHash = blobs.blockHash; - const blobSidecars = blobs.blobSidecars.map((blobSidecar) => ({ - ...blobSidecar, - blockRoot, - slot, - blockParentRoot: parentBlockRoot, - proposerIndex, - })); - - this.producedBlobSidecarsCache.set(blockHash, blobSidecars); - this.metrics?.blockProductionCaches.producedBlobSidecarsCache.set(this.producedBlobSidecarsCache.size); - } else if (blobs.type === BlobsResultType.blinded) { - // body is of blinded type here - const blockHash = blobs.blockHash; - const blindedBlobSidecars = blobs.blobSidecars.map((blindedBlobSidecar) => ({ - ...blindedBlobSidecar, - blockRoot, - slot, - blockParentRoot: parentBlockRoot, - proposerIndex, - })); - - this.producedBlindedBlobSidecarsCache.set(blockHash, blindedBlobSidecars); - this.metrics?.blockProductionCaches.producedBlindedBlobSidecarsCache.set( - this.producedBlindedBlobSidecarsCache.size - ); + const {blockHash, contents} = blobs; + this.producedContentsCache.set(blockHash, contents); + this.metrics?.blockProductionCaches.producedContentsCache.set(this.producedContentsCache.size); } return {block, executionPayloadValue, consensusBlockValue: proposerReward}; @@ -595,14 +570,14 @@ export class BeaconChain implements IBeaconChain { * kzg_aggregated_proof=compute_proof_from_blobs(blobs), * ) */ - getBlobSidecars(beaconBlock: deneb.BeaconBlock): deneb.BlobSidecars { + getContents(beaconBlock: deneb.BeaconBlock): deneb.Contents { const blockHash = toHex(beaconBlock.body.executionPayload.blockHash); - const blobSidecars = this.producedBlobSidecarsCache.get(blockHash); - if (!blobSidecars) { - throw Error(`No blobSidecars for executionPayload.blockHash ${blockHash}`); + const contents = this.producedContentsCache.get(blockHash); + if (!contents) { + throw Error(`No contents for executionPayload.blockHash ${blockHash}`); } - return blobSidecars; + return contents; } async processBlock(block: BlockInput, opts?: ImportBlockOpts): Promise { @@ -884,19 +859,8 @@ export class BeaconChain implements IBeaconChain { this.metrics?.blockProductionCaches.producedBlindedBlockRoot.set(this.producedBlindedBlockRoot.size); if (this.config.getForkSeq(slot) >= ForkSeq.deneb) { - pruneSetToMax( - this.producedBlobSidecarsCache, - this.opts.maxCachedBlobSidecars ?? DEFAULT_MAX_CACHED_BLOB_SIDECARS - ); - this.metrics?.blockProductionCaches.producedBlobSidecarsCache.set(this.producedBlobSidecarsCache.size); - - pruneSetToMax( - this.producedBlindedBlobSidecarsCache, - this.opts.maxCachedBlobSidecars ?? DEFAULT_MAX_CACHED_BLOB_SIDECARS - ); - this.metrics?.blockProductionCaches.producedBlindedBlobSidecarsCache.set( - this.producedBlindedBlobSidecarsCache.size - ); + pruneSetToMax(this.producedContentsCache, this.opts.maxCachedProducedRoots ?? DEFAULT_MAX_CACHED_PRODUCED_ROOTS); + this.metrics?.blockProductionCaches.producedContentsCache.set(this.producedContentsCache.size); } const metrics = this.metrics; diff --git a/packages/beacon-node/src/chain/errors/blobSidecarError.ts b/packages/beacon-node/src/chain/errors/blobSidecarError.ts index e242cbcb11ba..38a125bbb21e 100644 --- a/packages/beacon-node/src/chain/errors/blobSidecarError.ts +++ b/packages/beacon-node/src/chain/errors/blobSidecarError.ts @@ -21,6 +21,7 @@ export enum BlobSidecarErrorCode { PARENT_UNKNOWN = "BLOB_SIDECAR_ERROR_PARENT_UNKNOWN", NOT_LATER_THAN_PARENT = "BLOB_SIDECAR_ERROR_NOT_LATER_THAN_PARENT", PROPOSAL_SIGNATURE_INVALID = "BLOB_SIDECAR_ERROR_PROPOSAL_SIGNATURE_INVALID", + INCLUSION_PROOF_INVALID = "BLOB_SIDECAR_ERROR_INCLUSION_PROOF_INVALID", INCORRECT_PROPOSER = "BLOB_SIDECAR_ERROR_INCORRECT_PROPOSER", } @@ -37,6 +38,7 @@ export type BlobSidecarErrorType = | {code: BlobSidecarErrorCode.PARENT_UNKNOWN; parentRoot: RootHex} | {code: BlobSidecarErrorCode.NOT_LATER_THAN_PARENT; parentSlot: Slot; slot: Slot} | {code: BlobSidecarErrorCode.PROPOSAL_SIGNATURE_INVALID} + | {code: BlobSidecarErrorCode.INCLUSION_PROOF_INVALID} | {code: BlobSidecarErrorCode.INCORRECT_PROPOSER; proposerIndex: ValidatorIndex}; export class BlobSidecarGossipError extends GossipActionError {} diff --git a/packages/beacon-node/src/chain/interface.ts b/packages/beacon-node/src/chain/interface.ts index a209ec36276e..880a5e86071a 100644 --- a/packages/beacon-node/src/chain/interface.ts +++ b/packages/beacon-node/src/chain/interface.ts @@ -109,9 +109,8 @@ export interface IBeaconChain { readonly beaconProposerCache: BeaconProposerCache; readonly checkpointBalancesCache: CheckpointBalancesCache; - readonly producedBlobSidecarsCache: Map; + readonly producedContentsCache: Map; readonly producedBlockRoot: Map; - readonly producedBlindedBlobSidecarsCache: Map; readonly shufflingCache: ShufflingCache; readonly producedBlindedBlockRoot: Set; readonly opts: IChainOptions; @@ -153,7 +152,7 @@ export interface IBeaconChain { */ getBlockByRoot(root: RootHex): Promise<{block: allForks.SignedBeaconBlock; executionOptimistic: boolean} | null>; - getBlobSidecars(beaconBlock: deneb.BeaconBlock): deneb.BlobSidecars; + getContents(beaconBlock: deneb.BeaconBlock): deneb.Contents; produceBlock( blockAttributes: BlockAttributes diff --git a/packages/beacon-node/src/chain/produceBlock/produceBlockBody.ts b/packages/beacon-node/src/chain/produceBlock/produceBlockBody.ts index 1c522c54a93d..97f45c0ee289 100644 --- a/packages/beacon-node/src/chain/produceBlock/produceBlockBody.ts +++ b/packages/beacon-node/src/chain/produceBlock/produceBlockBody.ts @@ -35,10 +35,7 @@ import {PayloadId, IExecutionEngine, IExecutionBuilder, PayloadAttributes} from import {ZERO_HASH, ZERO_HASH_HEX} from "../../constants/index.js"; import {IEth1ForBlockProduction} from "../../eth1/index.js"; import {numToQuantity} from "../../eth1/provider/utils.js"; -import { - validateBlobsAndKzgCommitments, - validateBlindedBlobsAndKzgCommitments, -} from "./validateBlobsAndKzgCommitments.js"; +import {validateBlobsAndKzgCommitments} from "./validateBlobsAndKzgCommitments.js"; // Time to provide the EL to generate a payload from new payload id const PAYLOAD_GENERATION_TIME_MS = 500; @@ -74,8 +71,8 @@ export enum BlobsResultType { export type BlobsResult = | {type: BlobsResultType.preDeneb} - | {type: BlobsResultType.produced; blobSidecars: deneb.BlobSidecars; blockHash: RootHex} - | {type: BlobsResultType.blinded; blobSidecars: deneb.BlindedBlobSidecars; blockHash: RootHex}; + | {type: BlobsResultType.produced; contents: deneb.Contents; blockHash: RootHex} + | {type: BlobsResultType.blinded}; export async function produceBlockBody( this: BeaconChain, @@ -231,35 +228,14 @@ export async function produceBlockBody( }); if (ForkSeq[fork] >= ForkSeq.deneb) { - const {blindedBlobsBundle} = builderRes; - if (blindedBlobsBundle === undefined) { - throw Error(`Invalid builder getHeader response for fork=${fork}, missing blindedBlobsBundle`); - } - - // validate blindedBlobsBundle - if (this.opts.sanityCheckExecutionEngineBlobs) { - validateBlindedBlobsAndKzgCommitments(builderRes.header, blindedBlobsBundle); + const {blobKzgCommitments} = builderRes; + if (blobKzgCommitments === undefined) { + throw Error(`Invalid builder getHeader response for fork=${fork}, missing blobKzgCommitments`); } - (blockBody as deneb.BlindedBeaconBlockBody).blobKzgCommitments = blindedBlobsBundle.commitments; - const blockHash = toHex(builderRes.header.blockHash); - - const blobSidecars = Array.from({length: blindedBlobsBundle.blobRoots.length}, (_v, index) => { - const blobRoot = blindedBlobsBundle.blobRoots[index]; - const commitment = blindedBlobsBundle.commitments[index]; - const proof = blindedBlobsBundle.proofs[index]; - const blindedBlobSidecar = { - index, - blobRoot, - kzgProof: proof, - kzgCommitment: commitment, - }; - // Other fields will be injected after postState is calculated - return blindedBlobSidecar; - }) as deneb.BlindedBlobSidecars; - blobsResult = {type: BlobsResultType.blinded, blobSidecars, blockHash}; - - Object.assign(logMeta, {blobs: blindedBlobsBundle.commitments.length}); + (blockBody as deneb.BlindedBeaconBlockBody).blobKzgCommitments = blobKzgCommitments; + blobsResult = {type: BlobsResultType.blinded}; + Object.assign(logMeta, {blobs: blobKzgCommitments.length}); } else { blobsResult = {type: BlobsResultType.preDeneb}; } @@ -332,23 +308,10 @@ export async function produceBlockBody( (blockBody as deneb.BeaconBlockBody).blobKzgCommitments = blobsBundle.commitments; const blockHash = toHex(executionPayload.blockHash); + const contents = {kzgProofs: blobsBundle.proofs, blobs: blobsBundle.blobs}; + blobsResult = {type: BlobsResultType.produced, contents, blockHash}; - const blobSidecars = Array.from({length: blobsBundle.blobs.length}, (_v, index) => { - const blob = blobsBundle.blobs[index]; - const commitment = blobsBundle.commitments[index]; - const proof = blobsBundle.proofs[index]; - const blobSidecar = { - index, - blob, - kzgProof: proof, - kzgCommitment: commitment, - }; - // Other fields will be injected after postState is calculated - return blobSidecar; - }) as deneb.BlobSidecars; - blobsResult = {type: BlobsResultType.produced, blobSidecars, blockHash}; - - Object.assign(logMeta, {blobs: blobSidecars.length}); + Object.assign(logMeta, {blobs: blobsBundle.commitments.length}); } else { blobsResult = {type: BlobsResultType.preDeneb}; } @@ -502,7 +465,7 @@ async function prepareExecutionPayloadHeader( ): Promise<{ header: allForks.ExecutionPayloadHeader; executionPayloadValue: Wei; - blindedBlobsBundle?: deneb.BlindedBlobsBundle; + blobKzgCommitments?: deneb.BlobKzgCommitments; }> { if (!chain.executionBuilder) { throw Error("executionBuilder required"); diff --git a/packages/beacon-node/src/chain/produceBlock/validateBlobsAndKzgCommitments.ts b/packages/beacon-node/src/chain/produceBlock/validateBlobsAndKzgCommitments.ts index 0d00d0c8bd72..54e90672d189 100644 --- a/packages/beacon-node/src/chain/produceBlock/validateBlobsAndKzgCommitments.ts +++ b/packages/beacon-node/src/chain/produceBlock/validateBlobsAndKzgCommitments.ts @@ -1,4 +1,4 @@ -import {allForks, deneb} from "@lodestar/types"; +import {allForks} from "@lodestar/types"; import {BlobsBundle} from "../../execution/index.js"; /** @@ -13,15 +13,3 @@ export function validateBlobsAndKzgCommitments(payload: allForks.ExecutionPayloa ); } } - -export function validateBlindedBlobsAndKzgCommitments( - payload: allForks.ExecutionPayloadHeader, - blindedBlobsBundle: deneb.BlindedBlobsBundle -): void { - // sanity-check that the KZG commitments match the blobs (as produced by the execution engine) - if (blindedBlobsBundle.blobRoots.length !== blindedBlobsBundle.commitments.length) { - throw Error( - `BlindedBlobs bundle blobs len ${blindedBlobsBundle.blobRoots.length} != commitments len ${blindedBlobsBundle.commitments.length}` - ); - } -} diff --git a/packages/beacon-node/src/chain/seenCache/seenGossipBlockInput.ts b/packages/beacon-node/src/chain/seenCache/seenGossipBlockInput.ts index 1257feb844b1..8b767975c112 100644 --- a/packages/beacon-node/src/chain/seenCache/seenGossipBlockInput.ts +++ b/packages/beacon-node/src/chain/seenCache/seenGossipBlockInput.ts @@ -2,6 +2,7 @@ import {toHexString} from "@chainsafe/ssz"; import {deneb, RootHex, ssz, allForks} from "@lodestar/types"; import {ChainForkConfig} from "@lodestar/config"; import {pruneSetToMax} from "@lodestar/utils"; +import {BLOBSIDECAR_FIXED_SIZE} from "@lodestar/params"; import { BlockInput, @@ -12,11 +13,11 @@ import { GossipedInputType, } from "../blocks/types.js"; -export type GossipedBlockInput = +type GossipedBlockInput = | {type: GossipedInputType.block; signedBlock: allForks.SignedBeaconBlock; blockBytes: Uint8Array | null} - | {type: GossipedInputType.blob; signedBlob: deneb.SignedBlobSidecar; blobBytes: Uint8Array | null}; + | {type: GossipedInputType.blob; blobSidecar: deneb.BlobSidecar; blobBytes: Uint8Array | null}; -export type BlockInputCacheType = { +type BlockInputCacheType = { block?: allForks.SignedBeaconBlock; blockBytes?: Uint8Array | null; blobsCache: BlobsCache; @@ -26,8 +27,6 @@ export type BlockInputCacheType = { }; const MAX_GOSSIPINPUT_CACHE = 5; -// ssz.deneb.BlobSidecars.elementType.fixedSize, 131256 is size for mainnet preset; -const BLOBSIDECAR_FIXED_SIZE = ssz.deneb.BlobSidecars.elementType.fixedSize ?? 131256; /** * SeenGossipBlockInput tracks and caches the live blobs and blocks on the network to solve data availability @@ -68,13 +67,14 @@ export class SeenGossipBlockInput { blockCache.block = signedBlock; blockCache.blockBytes = blockBytes; } else { - const {signedBlob, blobBytes} = gossipedInput; - blockHex = toHexString(signedBlob.message.blockRoot); + const {blobSidecar, blobBytes} = gossipedInput; + const blockRoot = ssz.phase0.BeaconBlockHeader.hashTreeRoot(blobSidecar.signedBlockHeader.message); + blockHex = toHexString(blockRoot); blockCache = this.blockInputCache.get(blockHex) ?? getEmptyBlockInputCacheEntry(); // TODO: freetheblobs check if its the same blob or a duplicate and throw/take actions - blockCache.blobsCache.set(signedBlob.message.index, { - blobSidecar: signedBlob.message, + blockCache.blobsCache.set(blobSidecar.index, { + blobSidecar, // easily splice out the unsigned message as blob is a fixed length type blobBytes: blobBytes?.slice(0, BLOBSIDECAR_FIXED_SIZE) ?? null, }); diff --git a/packages/beacon-node/src/chain/validation/blobSidecar.ts b/packages/beacon-node/src/chain/validation/blobSidecar.ts index b5aab323c269..29aa4e763380 100644 --- a/packages/beacon-node/src/chain/validation/blobSidecar.ts +++ b/packages/beacon-node/src/chain/validation/blobSidecar.ts @@ -1,7 +1,8 @@ import {ChainForkConfig} from "@lodestar/config"; -import {deneb, Root, Slot} from "@lodestar/types"; -import {toHex} from "@lodestar/utils"; -import {getBlobProposerSignatureSet, computeStartSlotAtEpoch} from "@lodestar/state-transition"; +import {deneb, Root, Slot, ssz} from "@lodestar/types"; +import {toHex, verifyMerkleBranch} from "@lodestar/utils"; +import {computeStartSlotAtEpoch, getBlockHeaderProposerSignatureSet} from "@lodestar/state-transition"; +import {KZG_COMMITMENT_INCLUSION_PROOF_DEPTH, KZG_COMMITMENT_SUBTREE_INDEX0} from "@lodestar/params"; import {BlobSidecarGossipError, BlobSidecarErrorCode} from "../errors/blobSidecarError.js"; import {GossipAction} from "../errors/gossipValidation.js"; @@ -13,11 +14,10 @@ import {RegenCaller} from "../regen/index.js"; export async function validateGossipBlobSidecar( config: ChainForkConfig, chain: IBeaconChain, - signedBlob: deneb.SignedBlobSidecar, + blobSidecar: deneb.BlobSidecar, gossipIndex: number ): Promise { - const blobSidecar = signedBlob.message; - const blobSlot = blobSidecar.slot; + 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) { @@ -58,9 +58,10 @@ export async function validateGossipBlobSidecar( // reboot if the `observed_block_producers` cache is empty. In that case, without this // check, we will load the parent and state from disk only to find out later that we // already know this block. - const blockRoot = toHex(blobSidecar.blockRoot); - if (chain.forkChoice.getBlockHex(blockRoot) !== null) { - throw new BlobSidecarGossipError(GossipAction.IGNORE, {code: BlobSidecarErrorCode.ALREADY_KNOWN, root: blockRoot}); + const blockRoot = ssz.phase0.BeaconBlockHeader.hashTreeRoot(blobSidecar.signedBlockHeader.message); + const blockHex = toHex(blockRoot); + if (chain.forkChoice.getBlockHex(blockHex) !== null) { + throw new BlobSidecarGossipError(GossipAction.IGNORE, {code: BlobSidecarErrorCode.ALREADY_KNOWN, root: blockHex}); } // TODO: freetheblobs - check for badblock @@ -69,7 +70,7 @@ export async function validateGossipBlobSidecar( // _[IGNORE]_ The blob's block's parent (defined by `sidecar.block_parent_root`) has been seen (via both // gossip and non-gossip sources) (a client MAY queue blocks for processing once the parent block is // retrieved). - const parentRoot = toHex(blobSidecar.blockParentRoot); + const parentRoot = toHex(blobSidecar.signedBlockHeader.message.parentRoot); const parentBlock = chain.forkChoice.getBlockHex(parentRoot); if (parentBlock === null) { // If fork choice does *not* consider the parent to be a descendant of the finalized block, @@ -97,18 +98,16 @@ export async function validateGossipBlobSidecar( // getBlockSlotState also checks for whether the current finalized checkpoint is an ancestor of the block. // As a result, we throw an IGNORE (whereas the spec says we should REJECT for this scenario). // this is something we should change this in the future to make the code airtight to the spec. - // _[IGNORE]_ The blob's block's parent (defined by `sidecar.block_parent_root`) has been seen (via both - // gossip and non-gossip sources) // _[REJECT]_ The blob's block's parent (defined by `sidecar.block_parent_root`) passes validation - // The above validation will happen while importing + // [IGNORE] The block's parent (defined by block.parent_root) has been seen (via both gossip and non-gossip sources) (a client MAY queue blocks for processing once the parent block is retrieved). + // [REJECT] The block's parent (defined by block.parent_root) passes validation. const blockState = await chain.regen - .getBlockSlotState(parentRoot, blobSlot, {dontTransferCache: true}, RegenCaller.validateGossipBlob) + .getBlockSlotState(parentRoot, blobSlot, {dontTransferCache: true}, RegenCaller.validateGossipBlock) .catch(() => { throw new BlobSidecarGossipError(GossipAction.IGNORE, {code: BlobSidecarErrorCode.PARENT_UNKNOWN, parentRoot}); }); - // _[REJECT]_ The proposer signature, `signed_blob_sidecar.signature`, is valid with respect to the - // `sidecar.proposer_index` pubkey. - const signatureSet = getBlobProposerSignatureSet(blockState, signedBlob); + // [REJECT] The proposer signature, signed_beacon_block.signature, is valid with respect to the proposer_index pubkey. + const signatureSet = getBlockHeaderProposerSignatureSet(blockState, blobSidecar.signedBlockHeader); // Don't batch so verification is not delayed if (!(await chain.bls.verifySignatureSets([signatureSet], {verifyOnMainThread: true}))) { throw new BlobSidecarGossipError(GossipAction.REJECT, { @@ -116,6 +115,13 @@ export async function validateGossipBlobSidecar( }); } + // verify if the blob inclusion proof is correct + if (!validateInclusionProof(config, blobSidecar)) { + throw new BlobSidecarGossipError(GossipAction.REJECT, { + code: BlobSidecarErrorCode.INCLUSION_PROOF_INVALID, + }); + } + // _[IGNORE]_ The sidecar is the only sidecar with valid signature received for the tuple // `(sidecar.block_root, sidecar.index)` // @@ -127,7 +133,7 @@ export async function validateGossipBlobSidecar( // If the `proposer_index` cannot immediately be verified against the expected shuffling, the sidecar // MAY be queued for later processing while proposers for the block's branch are calculated -- in such // a case _do not_ `REJECT`, instead `IGNORE` this message. - const proposerIndex = blobSidecar.proposerIndex; + const proposerIndex = blobSidecar.signedBlockHeader.message.proposerIndex; if (blockState.epochCtx.getBeaconProposer(blobSlot) !== proposerIndex) { throw new BlobSidecarGossipError(GossipAction.REJECT, { code: BlobSidecarErrorCode.INCORRECT_PROPOSER, @@ -168,16 +174,18 @@ export function validateBlobSidecars( const proofs = []; for (let index = 0; index < blobSidecars.length; index++) { const blobSidecar = blobSidecars[index]; + const blobBlockHeader = blobSidecar.signedBlockHeader.message; + const blobBlockRoot = ssz.phase0.BeaconBlockHeader.hashTreeRoot(blobBlockHeader); if ( - blobSidecar.slot !== blockSlot || - !byteArrayEquals(blobSidecar.blockRoot, blockRoot) || + blobBlockHeader.slot !== blockSlot || + !byteArrayEquals(blobBlockRoot, blockRoot) || blobSidecar.index !== index || !byteArrayEquals(expectedKzgCommitments[index], blobSidecar.kzgCommitment) ) { throw new Error( - `Invalid blob with slot=${blobSidecar.slot} blockRoot=${toHex(blockRoot)} index=${ + `Invalid blob with slot=${blobBlockHeader.slot} blobBlockRoot=${toHex(blobBlockRoot)} index=${ blobSidecar.index - } for the block root=${toHex(blockRoot)} slot=${blockSlot} index=${index}` + } for the block blockRoot=${toHex(blockRoot)} slot=${blockSlot} index=${index}` ); } blobs.push(blobSidecar.blob); @@ -207,3 +215,13 @@ function validateBlobsAndProofs( throw Error("Invalid verifyBlobKzgProofBatch"); } } + +function validateInclusionProof(config: ChainForkConfig, blobSidecar: deneb.BlobSidecar): boolean { + return verifyMerkleBranch( + ssz.deneb.KZGCommitment.hashTreeRoot(blobSidecar.kzgCommitment), + blobSidecar.kzgCommitmentInclusionProof, + KZG_COMMITMENT_INCLUSION_PROOF_DEPTH, + KZG_COMMITMENT_SUBTREE_INDEX0 + blobSidecar.index, + blobSidecar.signedBlockHeader.message.bodyRoot + ); +} diff --git a/packages/beacon-node/src/db/repositories/blobSidecars.ts b/packages/beacon-node/src/db/repositories/blobSidecars.ts index 576a03df9e61..e5750ed31b58 100644 --- a/packages/beacon-node/src/db/repositories/blobSidecars.ts +++ b/packages/beacon-node/src/db/repositories/blobSidecars.ts @@ -2,6 +2,7 @@ import {ValueOf, ContainerType} from "@chainsafe/ssz"; import {ChainForkConfig} from "@lodestar/config"; import {Db, Repository} from "@lodestar/db"; import {ssz} from "@lodestar/types"; + import {Bucket, getBucketNameByValue} from "../buckets.js"; export const blobSidecarsWrapperSsz = new ContainerType( @@ -14,10 +15,7 @@ export const blobSidecarsWrapperSsz = new ContainerType( ); export type BlobSidecarsWrapper = ValueOf; - export const BLOB_SIDECARS_IN_WRAPPER_INDEX = 44; -// ssz.deneb.BlobSidecars.elementType.fixedSize; -export const BLOBSIDECAR_FIXED_SIZE = 131256; /** * blobSidecarsWrapper by block root (= hash_tree_root(SignedBeaconBlock.message)) diff --git a/packages/beacon-node/src/execution/builder/http.ts b/packages/beacon-node/src/execution/builder/http.ts index 20b7d4751c81..c47e8471f199 100644 --- a/packages/beacon-node/src/execution/builder/http.ts +++ b/packages/beacon-node/src/execution/builder/http.ts @@ -1,10 +1,6 @@ import {byteArrayEquals, toHexString} from "@chainsafe/ssz"; import {allForks, bellatrix, Slot, Root, BLSPubkey, ssz, deneb, Wei} from "@lodestar/types"; -import { - parseSignedBlindedBlockOrContents, - parseExecutionPayloadAndBlobsBundle, - reconstructFullBlockOrContents, -} from "@lodestar/state-transition"; +import {parseExecutionPayloadAndBlobsBundle, reconstructFullBlockOrContents} from "@lodestar/state-transition"; import {ChainForkConfig} from "@lodestar/config"; import {Logger} from "@lodestar/logger"; import {getClient, Api as BuilderApi} from "@lodestar/api/builder"; @@ -110,26 +106,23 @@ export class ExecutionBuilderHttp implements IExecutionBuilder { ): Promise<{ header: allForks.ExecutionPayloadHeader; executionPayloadValue: Wei; - blindedBlobsBundle?: deneb.BlindedBlobsBundle; + blobKzgCommitments?: deneb.BlobKzgCommitments; }> { const res = await this.api.getHeader(slot, parentHash, proposerPubKey); ApiError.assert(res, "execution.builder.getheader"); const {header, value: executionPayloadValue} = res.response.data.message; - const {blindedBlobsBundle} = res.response.data.message as deneb.BuilderBid; - return {header, executionPayloadValue, blindedBlobsBundle}; + const {blobKzgCommitments} = res.response.data.message as deneb.BuilderBid; + return {header, executionPayloadValue, blobKzgCommitments}; } async submitBlindedBlock( - signedBlindedBlockOrContents: allForks.SignedBlindedBeaconBlockOrContents + signedBlindedBlock: allForks.SignedBlindedBeaconBlock ): Promise { - const res = await this.api.submitBlindedBlock(signedBlindedBlockOrContents); + const res = await this.api.submitBlindedBlock(signedBlindedBlock); ApiError.assert(res, "execution.builder.submitBlindedBlock"); const {data} = res.response; const {executionPayload, blobsBundle} = parseExecutionPayloadAndBlobsBundle(data); - const {signedBlindedBlock, signedBlindedBlobSidecars} = - parseSignedBlindedBlockOrContents(signedBlindedBlockOrContents); - // some validations for execution payload const expectedTransactionsRoot = signedBlindedBlock.message.body.executionPayloadHeader.transactionsRoot; const actualTransactionsRoot = ssz.bellatrix.Transactions.hashTreeRoot(executionPayload.transactions); @@ -141,7 +134,7 @@ export class ExecutionBuilderHttp implements IExecutionBuilder { ); } - const blobs = blobsBundle ? blobsBundle.blobs : null; - return reconstructFullBlockOrContents({signedBlindedBlock, signedBlindedBlobSidecars}, {executionPayload, blobs}); + const contents = blobsBundle ? {blobs: blobsBundle.blobs, kzgProofs: blobsBundle.proofs} : null; + return reconstructFullBlockOrContents(signedBlindedBlock, {executionPayload, contents}); } } diff --git a/packages/beacon-node/src/execution/builder/interface.ts b/packages/beacon-node/src/execution/builder/interface.ts index e9a2cabb69ef..8754a3616610 100644 --- a/packages/beacon-node/src/execution/builder/interface.ts +++ b/packages/beacon-node/src/execution/builder/interface.ts @@ -25,9 +25,7 @@ export interface IExecutionBuilder { ): Promise<{ header: allForks.ExecutionPayloadHeader; executionPayloadValue: Wei; - blindedBlobsBundle?: deneb.BlindedBlobsBundle; + blobKzgCommitments?: deneb.BlobKzgCommitments; }>; - submitBlindedBlock( - signedBlock: allForks.SignedBlindedBeaconBlockOrContents - ): Promise; + submitBlindedBlock(signedBlock: allForks.SignedBlindedBeaconBlock): Promise; } diff --git a/packages/beacon-node/src/execution/engine/mock.ts b/packages/beacon-node/src/execution/engine/mock.ts index 83a5ea3a7ed6..5779713435a5 100644 --- a/packages/beacon-node/src/execution/engine/mock.ts +++ b/packages/beacon-node/src/execution/engine/mock.ts @@ -1,5 +1,4 @@ import crypto from "node:crypto"; -import {kzgCommitmentToVersionedHash} from "@lodestar/state-transition"; import {bellatrix, deneb, RootHex, ssz} from "@lodestar/types"; import {fromHex, toHex} from "@lodestar/utils"; import { @@ -12,6 +11,7 @@ import { } from "@lodestar/params"; import {ZERO_HASH_HEX} from "../../constants/index.js"; import {ckzg} from "../../util/kzg.js"; +import {kzgCommitmentToVersionedHash} from "../../util/blobs.js"; import {quantityToNum} from "../../eth1/provider/utils.js"; import { EngineApiRpcParamTypes, diff --git a/packages/beacon-node/src/metrics/metrics/beacon.ts b/packages/beacon-node/src/metrics/metrics/beacon.ts index 8d9094f19a25..e2eed75adb70 100644 --- a/packages/beacon-node/src/metrics/metrics/beacon.ts +++ b/packages/beacon-node/src/metrics/metrics/beacon.ts @@ -179,13 +179,9 @@ export function createBeaconMetrics(register: RegistryMetricCreator) { name: "beacon_blinded_blockroot_produced_cache_total", help: "Count of cached produded blinded block roots", }), - producedBlobSidecarsCache: register.gauge({ - name: "beacon_blobsidecars_produced_cache_total", - help: "Count of cached produced blob sidecars", - }), - producedBlindedBlobSidecarsCache: register.gauge({ - name: "beacon_blinded_blobsidecars_produced_cache_total", - help: "Count of cached produced blinded blob sidecars", + producedContentsCache: register.gauge({ + name: "beacon_contents_produced_cache_total", + help: "Count of cached produced blob contents", }), }, diff --git a/packages/beacon-node/src/network/gossip/interface.ts b/packages/beacon-node/src/network/gossip/interface.ts index 8e9013487a06..600f96193296 100644 --- a/packages/beacon-node/src/network/gossip/interface.ts +++ b/packages/beacon-node/src/network/gossip/interface.ts @@ -70,7 +70,7 @@ export type SSZTypeOfGossipTopic = T extends {type: infer export type GossipTypeMap = { [GossipType.beacon_block]: allForks.SignedBeaconBlock; - [GossipType.blob_sidecar]: deneb.SignedBlobSidecar; + [GossipType.blob_sidecar]: deneb.BlobSidecar; [GossipType.beacon_aggregate_and_proof]: phase0.SignedAggregateAndProof; [GossipType.beacon_attestation]: phase0.Attestation; [GossipType.voluntary_exit]: phase0.SignedVoluntaryExit; @@ -85,7 +85,7 @@ export type GossipTypeMap = { export type GossipFnByType = { [GossipType.beacon_block]: (signedBlock: allForks.SignedBeaconBlock) => Promise | void; - [GossipType.blob_sidecar]: (signedBlobSidecar: deneb.SignedBlobSidecar) => Promise | void; + [GossipType.blob_sidecar]: (blobSidecar: deneb.BlobSidecar) => Promise | void; [GossipType.beacon_aggregate_and_proof]: (aggregateAndProof: phase0.SignedAggregateAndProof) => Promise | void; [GossipType.beacon_attestation]: (attestation: phase0.Attestation) => Promise | void; [GossipType.voluntary_exit]: (voluntaryExit: phase0.SignedVoluntaryExit) => Promise | void; diff --git a/packages/beacon-node/src/network/gossip/topic.ts b/packages/beacon-node/src/network/gossip/topic.ts index de1a571c3330..c5cd68ffa1de 100644 --- a/packages/beacon-node/src/network/gossip/topic.ts +++ b/packages/beacon-node/src/network/gossip/topic.ts @@ -85,7 +85,7 @@ export function getGossipSSZType(topic: GossipTopic) { // beacon_block is updated in altair to support the updated SignedBeaconBlock type return ssz[topic.fork].SignedBeaconBlock; case GossipType.blob_sidecar: - return ssz.deneb.SignedBlobSidecar; + return ssz.deneb.BlobSidecar; case GossipType.beacon_aggregate_and_proof: return ssz.phase0.SignedAggregateAndProof; case GossipType.beacon_attestation: diff --git a/packages/beacon-node/src/network/interface.ts b/packages/beacon-node/src/network/interface.ts index 047263d15022..9531c8529acf 100644 --- a/packages/beacon-node/src/network/interface.ts +++ b/packages/beacon-node/src/network/interface.ts @@ -44,7 +44,7 @@ export interface INetwork extends INetworkCorePublic { // Gossip publishBeaconBlock(signedBlock: allForks.SignedBeaconBlock): Promise; - publishBlobSidecar(signedBlobSidecar: deneb.SignedBlobSidecar): Promise; + publishBlobSidecar(blobSidecar: deneb.BlobSidecar): Promise; publishBeaconAggregateAndProof(aggregateAndProof: phase0.SignedAggregateAndProof): Promise; publishBeaconAttestation(attestation: phase0.Attestation, subnet: number): Promise; publishVoluntaryExit(voluntaryExit: phase0.SignedVoluntaryExit): Promise; diff --git a/packages/beacon-node/src/network/network.ts b/packages/beacon-node/src/network/network.ts index d2571a2a92e0..200bd4fd3a8d 100644 --- a/packages/beacon-node/src/network/network.ts +++ b/packages/beacon-node/src/network/network.ts @@ -288,14 +288,14 @@ export class Network implements INetwork { }); } - async publishBlobSidecar(signedBlobSidecar: deneb.SignedBlobSidecar): Promise { - const fork = this.config.getForkName(signedBlobSidecar.message.slot); - const index = signedBlobSidecar.message.index; - return this.publishGossip( - {type: GossipType.blob_sidecar, fork, index}, - signedBlobSidecar, - {ignoreDuplicatePublishError: true} - ); + async publishBlobSidecar(blobSidecar: deneb.BlobSidecar): Promise { + const slot = blobSidecar.signedBlockHeader.message.slot; + const fork = this.config.getForkName(slot); + const index = blobSidecar.index; + + return this.publishGossip({type: GossipType.blob_sidecar, fork, index}, blobSidecar, { + ignoreDuplicatePublishError: true, + }); } async publishBeaconAggregateAndProof(aggregateAndProof: phase0.SignedAggregateAndProof): Promise { diff --git a/packages/beacon-node/src/network/processor/extractSlotRootFns.ts b/packages/beacon-node/src/network/processor/extractSlotRootFns.ts index 24fcfaae6cbc..d31cb3e2d7f9 100644 --- a/packages/beacon-node/src/network/processor/extractSlotRootFns.ts +++ b/packages/beacon-node/src/network/processor/extractSlotRootFns.ts @@ -4,7 +4,7 @@ import { getBlockRootFromSignedAggregateAndProofSerialized, getSlotFromAttestationSerialized, getSlotFromSignedAggregateAndProofSerialized, - getSlotFromSignedBlobSidecarSerialized, + getSlotFromBlobSidecarSerialized, getSlotFromSignedBeaconBlockSerialized, } from "../../util/sszBytes.js"; import {GossipType} from "../gossip/index.js"; @@ -43,7 +43,7 @@ export function createExtractBlockSlotRootFns(): ExtractSlotRootFns { return {slot}; }, [GossipType.blob_sidecar]: (data: Uint8Array): SlotOptionalRoot | null => { - const slot = getSlotFromSignedBlobSidecarSerialized(data); + const slot = getSlotFromBlobSidecarSerialized(data); if (slot === null) { return null; diff --git a/packages/beacon-node/src/network/processor/gossipHandlers.ts b/packages/beacon-node/src/network/processor/gossipHandlers.ts index 627533b54e1d..831b18670add 100644 --- a/packages/beacon-node/src/network/processor/gossipHandlers.ts +++ b/packages/beacon-node/src/network/processor/gossipHandlers.ts @@ -178,20 +178,23 @@ function getDefaultHandlers(modules: ValidatorFnsModules, options: GossipHandler } async function validateBeaconBlob( - signedBlob: deneb.SignedBlobSidecar, + blobSidecar: deneb.BlobSidecar, blobBytes: Uint8Array, gossipIndex: number, peerIdStr: string, seenTimestampSec: number ): Promise { - const slot = signedBlob.message.slot; - const blockHex = prettyBytes(signedBlob.message.blockRoot); + const blobBlockHeader = blobSidecar.signedBlockHeader.message; + const slot = blobBlockHeader.slot; + const blockRoot = ssz.phase0.BeaconBlockHeader.hashTreeRoot(blobBlockHeader); + const blockHex = prettyBytes(blockRoot); + const delaySec = chain.clock.secFromSlot(slot, seenTimestampSec); const recvToVal = Date.now() / 1000 - seenTimestampSec; const {blockInput, blockInputMeta} = chain.seenGossipBlockInput.getGossipBlockInput(config, { type: GossipedInputType.blob, - signedBlob, + blobSidecar, blobBytes, }); @@ -208,7 +211,7 @@ function getDefaultHandlers(modules: ValidatorFnsModules, options: GossipHandler }); try { - await validateGossipBlobSidecar(config, chain, signedBlob, gossipIndex); + await validateGossipBlobSidecar(config, chain, blobSidecar, gossipIndex); return blockInput; } catch (e) { if (e instanceof BlobSidecarGossipError) { @@ -219,7 +222,11 @@ function getDefaultHandlers(modules: ValidatorFnsModules, options: GossipHandler } if (e.action === GossipAction.REJECT) { - chain.persistInvalidSszValue(ssz.deneb.SignedBlobSidecar, signedBlob, `gossip_reject_slot_${slot}`); + chain.persistInvalidSszValue( + ssz.deneb.BlobSidecar, + blobSidecar, + `gossip_reject_slot_${slot}_index_${blobSidecar.index}` + ); } } @@ -317,11 +324,17 @@ function getDefaultHandlers(modules: ValidatorFnsModules, options: GossipHandler seenTimestampSec, }: GossipHandlerParamGeneric) => { const {serializedData} = gossipData; - const signedBlob = sszDeserialize(topic, serializedData); - if (config.getForkSeq(signedBlob.message.slot) < ForkSeq.deneb) { + const blobSidecar = sszDeserialize(topic, serializedData); + if (config.getForkSeq(blobSidecar.signedBlockHeader.message.slot) < ForkSeq.deneb) { throw new GossipActionError(GossipAction.REJECT, {code: "PRE_DENEB_BLOCK"}); } - const blockInput = await validateBeaconBlob(signedBlob, serializedData, topic.index, peerIdStr, seenTimestampSec); + const blockInput = await validateBeaconBlob( + blobSidecar, + serializedData, + topic.index, + peerIdStr, + seenTimestampSec + ); if (blockInput !== null) { // TODO DENEB: // diff --git a/packages/beacon-node/src/network/reqresp/beaconBlocksMaybeBlobsByRange.ts b/packages/beacon-node/src/network/reqresp/beaconBlocksMaybeBlobsByRange.ts index 10e7071f4fdb..41d3e901c41d 100644 --- a/packages/beacon-node/src/network/reqresp/beaconBlocksMaybeBlobsByRange.ts +++ b/packages/beacon-node/src/network/reqresp/beaconBlocksMaybeBlobsByRange.ts @@ -78,7 +78,9 @@ export function matchBlockWithBlobs( const blobSidecars: deneb.BlobSidecar[] = []; let blobSidecar: deneb.BlobSidecar; - while ((blobSidecar = allBlobSidecars[blobSideCarIndex])?.slot === block.data.message.slot) { + while ( + (blobSidecar = allBlobSidecars[blobSideCarIndex])?.signedBlockHeader.message.slot === block.data.message.slot + ) { blobSidecars.push(blobSidecar); lastMatchedSlot = block.data.message.slot; blobSideCarIndex++; @@ -111,14 +113,14 @@ export function matchBlockWithBlobs( if ( allBlobSidecars[blobSideCarIndex] !== undefined && // If there are no blobs, the blobs request can give 1 block outside the requested range - allBlobSidecars[blobSideCarIndex].slot <= endSlot + allBlobSidecars[blobSideCarIndex].signedBlockHeader.message.slot <= endSlot ) { throw Error( `Unmatched blobSidecars, blocks=${allBlocks.length}, blobs=${ allBlobSidecars.length } lastMatchedSlot=${lastMatchedSlot}, pending blobSidecars slots=${allBlobSidecars .slice(blobSideCarIndex) - .map((blb) => blb.slot) + .map((blb) => blb.signedBlockHeader.message.slot) .join(",")}` ); } diff --git a/packages/beacon-node/src/network/reqresp/handlers/blobSidecarsByRange.ts b/packages/beacon-node/src/network/reqresp/handlers/blobSidecarsByRange.ts index 2cd852492220..e3655cd90c6f 100644 --- a/packages/beacon-node/src/network/reqresp/handlers/blobSidecarsByRange.ts +++ b/packages/beacon-node/src/network/reqresp/handlers/blobSidecarsByRange.ts @@ -1,10 +1,10 @@ -import {GENESIS_SLOT, MAX_REQUEST_BLOCKS_DENEB} from "@lodestar/params"; +import {GENESIS_SLOT, MAX_REQUEST_BLOCKS_DENEB, BLOBSIDECAR_FIXED_SIZE} from "@lodestar/params"; import {ResponseError, ResponseOutgoing, RespStatus} from "@lodestar/reqresp"; import {deneb, Slot} from "@lodestar/types"; import {fromHex} from "@lodestar/utils"; import {IBeaconChain} from "../../../chain/index.js"; import {IBeaconDb} from "../../../db/index.js"; -import {BLOB_SIDECARS_IN_WRAPPER_INDEX, BLOBSIDECAR_FIXED_SIZE} from "../../../db/repositories/blobSidecars.js"; +import {BLOB_SIDECARS_IN_WRAPPER_INDEX} from "../../../db/repositories/blobSidecars.js"; export async function* onBlobSidecarsByRange( request: deneb.BlobSidecarsByRangeRequest, diff --git a/packages/beacon-node/src/network/reqresp/handlers/blobSidecarsByRoot.ts b/packages/beacon-node/src/network/reqresp/handlers/blobSidecarsByRoot.ts index 3bb162d019e3..6aa16a0c2629 100644 --- a/packages/beacon-node/src/network/reqresp/handlers/blobSidecarsByRoot.ts +++ b/packages/beacon-node/src/network/reqresp/handlers/blobSidecarsByRoot.ts @@ -1,9 +1,10 @@ import {ResponseError, ResponseOutgoing, RespStatus} from "@lodestar/reqresp"; +import {BLOBSIDECAR_FIXED_SIZE} from "@lodestar/params"; import {deneb, RootHex} from "@lodestar/types"; import {toHex, fromHex} from "@lodestar/utils"; import {IBeaconChain} from "../../../chain/index.js"; import {IBeaconDb} from "../../../db/index.js"; -import {BLOB_SIDECARS_IN_WRAPPER_INDEX, BLOBSIDECAR_FIXED_SIZE} from "../../../db/repositories/blobSidecars.js"; +import {BLOB_SIDECARS_IN_WRAPPER_INDEX} from "../../../db/repositories/blobSidecars.js"; export async function* onBlobSidecarsByRoot( requestBody: deneb.BlobSidecarsByRootRequest, diff --git a/packages/beacon-node/src/util/blobs.ts b/packages/beacon-node/src/util/blobs.ts new file mode 100644 index 000000000000..bbad27f684ed --- /dev/null +++ b/packages/beacon-node/src/util/blobs.ts @@ -0,0 +1,48 @@ +import SHA256 from "@chainsafe/as-sha256"; +import {Tree} from "@chainsafe/persistent-merkle-tree"; +import {VERSIONED_HASH_VERSION_KZG, KZG_COMMITMENT_GINDEX0, ForkName} from "@lodestar/params"; +import {deneb, ssz, allForks} from "@lodestar/types"; +import {ChainForkConfig} from "@lodestar/config"; +import {signedBlockToSignedHeader} from "@lodestar/state-transition"; + +type VersionHash = Uint8Array; + +export function kzgCommitmentToVersionedHash(kzgCommitment: deneb.KZGCommitment): VersionHash { + const hash = SHA256.digest(kzgCommitment); + // Equivalent to `VERSIONED_HASH_VERSION_KZG + hash(kzg_commitment)[1:]` + hash[0] = VERSIONED_HASH_VERSION_KZG; + return hash; +} + +export function computeInclusionProof( + fork: ForkName, + body: allForks.BeaconBlockBody, + index: number +): deneb.KzgCommitmentInclusionProof { + const bodyView = (ssz[fork].BeaconBlockBody as allForks.AllForksSSZTypes["BeaconBlockBody"]).toView(body); + const commitmentGindex = KZG_COMMITMENT_GINDEX0 + index; + return new Tree(bodyView.node).getSingleProof(BigInt(commitmentGindex)); +} + +export function computeBlobSidecars( + config: ChainForkConfig, + signedBlock: allForks.SignedBeaconBlock, + contents: deneb.Contents & {kzgCommitmentInclusionProofs?: deneb.KzgCommitmentInclusionProof[]} +): deneb.BlobSidecars { + const blobKzgCommitments = (signedBlock as deneb.SignedBeaconBlock).message.body.blobKzgCommitments; + if (blobKzgCommitments === undefined) { + throw Error("Invalid block with missing blobKzgCommitments for computeBlobSidecars"); + } + + const signedBlockHeader = signedBlockToSignedHeader(config, signedBlock); + const fork = config.getForkName(signedBlockHeader.message.slot); + + return blobKzgCommitments.map((kzgCommitment, index) => { + const blob = contents.blobs[index]; + const kzgProof = contents.kzgProofs[index]; + const kzgCommitmentInclusionProof = + contents.kzgCommitmentInclusionProofs?.[index] ?? computeInclusionProof(fork, signedBlock.message.body, index); + + return {index, blob, kzgCommitment, kzgProof, signedBlockHeader, kzgCommitmentInclusionProof}; + }); +} diff --git a/packages/beacon-node/src/util/sszBytes.ts b/packages/beacon-node/src/util/sszBytes.ts index 0c258df35041..cd12c4bd9c18 100644 --- a/packages/beacon-node/src/util/sszBytes.ts +++ b/packages/beacon-node/src/util/sszBytes.ts @@ -1,6 +1,7 @@ import {BitArray, deserializeUint8ArrayBitListFromBytes} from "@chainsafe/ssz"; import {BLSSignature, RootHex, Slot} from "@lodestar/types"; import {toHex} from "@lodestar/utils"; +import {BYTES_PER_FIELD_ELEMENT, FIELD_ELEMENTS_PER_BLOB} from "@lodestar/params"; export type BlockRootHex = RootHex; export type AttDataBase64 = string; @@ -180,23 +181,18 @@ export function getSlotFromSignedBeaconBlockSerialized(data: Uint8Array): Slot | } /** - * 4 + 96 = 100 - * ``` - * class SignedBlobSidecar(Container): - * message: BlobSidecar [fixed] - * signature: BLSSignature [fixed] - * * class BlobSidecar(Container): - * blockRoot: Root [fixed - 32 bytes ], - * index: BlobIndex [fixed - 8 bytes ], - * slot: Slot [fixed - 8 bytes] - * ... - * ``` + * index: BlobIndex [fixed - 8 bytes ], + * blob: Blob, BYTES_PER_FIELD_ELEMENT * FIELD_ELEMENTS_PER_BLOB + * kzgCommitment: Bytes48, + * kzgProof: Bytes48, + * signedBlockHeader: + * slot: 8 bytes */ -const SLOT_BYTES_POSITION_IN_SIGNED_BLOB_SIDECAR = 32 + 8; +const SLOT_BYTES_POSITION_IN_SIGNED_BLOB_SIDECAR = 8 + BYTES_PER_FIELD_ELEMENT * FIELD_ELEMENTS_PER_BLOB + 48 + 48; -export function getSlotFromSignedBlobSidecarSerialized(data: Uint8Array): Slot | null { +export function getSlotFromBlobSidecarSerialized(data: Uint8Array): Slot | null { if (data.length < SLOT_BYTES_POSITION_IN_SIGNED_BLOB_SIDECAR + SLOT_SIZE) { return null; } diff --git a/packages/beacon-node/test/scripts/el-interop/ethereumjsdocker/post-merge.sh b/packages/beacon-node/test/scripts/el-interop/ethereumjsdocker/post-merge.sh index dee850740370..fbf9dcaaf929 100755 --- a/packages/beacon-node/test/scripts/el-interop/ethereumjsdocker/post-merge.sh +++ b/packages/beacon-node/test/scripts/el-interop/ethereumjsdocker/post-merge.sh @@ -5,4 +5,4 @@ currentDir=$(pwd) . $scriptDir/common-setup.sh -docker run --rm -u $(id -u ${USER}):$(id -g ${USER}) --name custom-execution --network host -v $currentDir/$DATA_DIR:/data $EL_BINARY_DIR --dataDir /data/ethereumjs --gethGenesis /data/genesis.json --rpc --rpcEngine --jwt-secret /data/jwtsecret --logLevel debug --isSingleNode +docker run --rm -u $(id -u ${USER}):$(id -g ${USER}) --name custom-execution --network host -v $currentDir/$DATA_DIR:/data $EL_BINARY_DIR --dataDir /data/ethereumjs --gethGenesis /data/genesis.json --rpc --rpcEngineAddr 0.0.0.0 --rpcAddr 0.0.0.0 --rpcEngine --jwt-secret /data/jwtsecret --logLevel debug --isSingleNode diff --git a/packages/beacon-node/test/spec/presets/fork_choice.test.ts b/packages/beacon-node/test/spec/presets/fork_choice.test.ts index 0ab7b3b363b5..47d72c1226e1 100644 --- a/packages/beacon-node/test/spec/presets/fork_choice.test.ts +++ b/packages/beacon-node/test/spec/presets/fork_choice.test.ts @@ -1,7 +1,7 @@ import path from "node:path"; import {expect} from "chai"; import {toHexString} from "@chainsafe/ssz"; -import {BeaconStateAllForks, isExecutionStateType} from "@lodestar/state-transition"; +import {BeaconStateAllForks, isExecutionStateType, signedBlockToSignedHeader} from "@lodestar/state-transition"; import {InputType} from "@lodestar/spec-test-util"; import {CheckpointWithHex, ForkChoice} from "@lodestar/fork-choice"; import {phase0, allForks, bellatrix, ssz, RootHex, deneb} from "@lodestar/types"; @@ -10,6 +10,7 @@ import {createBeaconConfig} from "@lodestar/config"; import {ACTIVE_PRESET, ForkSeq, isForkBlobs} from "@lodestar/params"; import {BeaconChain} from "../../../src/chain/index.js"; import {ClockEvent} from "../../../src/util/clock.js"; +import {computeInclusionProof} from "../../../src/util/blobs.js"; import {createCachedBeaconStateTest} from "../../utils/cachedBeaconState.js"; import {testLogger} from "../../utils/logger.js"; import {getConfig} from "../../utils/config.js"; @@ -195,20 +196,14 @@ const forkChoiceTest = throw Error("Invalid blobs or proofs lengths"); } - const blockRoot = config - .getForkTypes(signedBlock.message.slot) - .BeaconBlock.hashTreeRoot(signedBlock.message); const blobSidecars: deneb.BlobSidecars = blobs.map((blob, index) => { return { - blockRoot, index, - slot, blob, - // proofs isn't undefined here but typescript(check types) can't figure it out - kzgProof: (proofs ?? [])[index], kzgCommitment: commitments[index], - blockParentRoot: signedBlock.message.parentRoot, - proposerIndex: signedBlock.message.proposerIndex, + kzgProof: (proofs ?? [])[index], + signedBlockHeader: signedBlockToSignedHeader(config, signedBlock), + kzgCommitmentInclusionProof: computeInclusionProof(fork, signedBlock.message.body, index), }; }); diff --git a/packages/beacon-node/test/spec/specTestVersioning.ts b/packages/beacon-node/test/spec/specTestVersioning.ts index 3f1aad878e65..20125520321d 100644 --- a/packages/beacon-node/test/spec/specTestVersioning.ts +++ b/packages/beacon-node/test/spec/specTestVersioning.ts @@ -15,7 +15,7 @@ import {DownloadTestsOptions} from "@lodestar/spec-test-util"; const __dirname = path.dirname(fileURLToPath(import.meta.url)); export const ethereumConsensusSpecsTests: DownloadTestsOptions = { - specVersion: "v1.4.0-beta.2-hotfix", + specVersion: "v1.4.0-beta.5", // Target directory is the host package root: 'packages/*/spec-tests' outputDir: path.join(__dirname, "../../spec-tests"), specTestsRepoUrl: "https://github.com/ethereum/consensus-spec-tests", diff --git a/packages/beacon-node/test/spec/utils/specTestIterator.ts b/packages/beacon-node/test/spec/utils/specTestIterator.ts index a9310d53ac81..084d3d00fd48 100644 --- a/packages/beacon-node/test/spec/utils/specTestIterator.ts +++ b/packages/beacon-node/test/spec/utils/specTestIterator.ts @@ -64,6 +64,7 @@ export const defaultSkipOpts: SkipOpts = { "capella/light_client/single_merkle_proof/BeaconBlockBody", "deneb/light_client/single_merkle_proof/BeaconBlockBody", ], + skippedRunners: ["merkle_proof"], }; /** diff --git a/packages/beacon-node/test/unit/chain/seenCache/seenGossipBlockInput.test.ts b/packages/beacon-node/test/unit/chain/seenCache/seenGossipBlockInput.test.ts index 05919efdbcbf..c389e1b81e70 100644 --- a/packages/beacon-node/test/unit/chain/seenCache/seenGossipBlockInput.test.ts +++ b/packages/beacon-node/test/unit/chain/seenCache/seenGossipBlockInput.test.ts @@ -72,6 +72,7 @@ describe("SeenGossipBlockInput", () => { ], ], ]; + // lets start from a random slot to build cases let slot = 7456; for (const testCase of testCases) { @@ -86,10 +87,15 @@ describe("SeenGossipBlockInput", () => { ssz.deneb.KZGCommitment.defaultValue() ); - const blockRoot = ssz.deneb.BeaconBlock.hashTreeRoot(signedBlock.message); - const signedBlobSidecars = Array.from({length: numBlobs}, (_val, index) => { - const message = {...ssz.deneb.BlobSidecar.defaultValue(), index, blockRoot, slot}; - return {message, signature: ssz.BLSSignature.defaultValue()}; + // create a dummy signed block header with matching body root + const bodyRoot = ssz.deneb.BeaconBlockBody.hashTreeRoot(signedBlock.message.body); + const signedBlockHeader = ssz.phase0.SignedBeaconBlockHeader.defaultValue(); + signedBlockHeader.message.slot = signedBlock.message.slot; + signedBlockHeader.message.bodyRoot = bodyRoot; + + const blobSidecars = Array.from({length: numBlobs}, (_val, index) => { + const message = {...ssz.deneb.BlobSidecar.defaultValue(), signedBlockHeader, index}; + return message; }); for (const testEvent of events) { @@ -114,11 +120,12 @@ describe("SeenGossipBlockInput", () => { } } else { const index = parseInt(inputEvent.split("blob")[1] ?? "0"); - const signedBlob = signedBlobSidecars[index]; - expect(signedBlob).not.equal(undefined); + const blobSidecar = blobSidecars[index]; + expect(blobSidecar).not.equal(undefined); + const blockInputRes = seenGossipBlockInput.getGossipBlockInput(config, { type: GossipedInputType.blob, - signedBlob, + blobSidecar, blobBytes: null, }); diff --git a/packages/beacon-node/test/unit/network/beaconBlocksMaybeBlobsByRange.test.ts b/packages/beacon-node/test/unit/network/beaconBlocksMaybeBlobsByRange.test.ts index 189327a6a5ab..56fb64104744 100644 --- a/packages/beacon-node/test/unit/network/beaconBlocksMaybeBlobsByRange.test.ts +++ b/packages/beacon-node/test/unit/network/beaconBlocksMaybeBlobsByRange.test.ts @@ -30,16 +30,21 @@ describe("beaconBlocksMaybeBlobsByRange", () => { rangeRequest.count = 1; const block1 = ssz.deneb.SignedBeaconBlock.defaultValue(); + const blockheader1 = ssz.phase0.SignedBeaconBlockHeader.defaultValue(); + blockheader1.message.slot = 1; block1.message.slot = 1; block1.message.body.blobKzgCommitments.push(ssz.deneb.KZGCommitment.defaultValue()); const blobSidecar1 = ssz.deneb.BlobSidecar.defaultValue(); - blobSidecar1.slot = 1; + blobSidecar1.signedBlockHeader = blockheader1; const block2 = ssz.deneb.SignedBeaconBlock.defaultValue(); block2.message.slot = 2; + const blockheader2 = ssz.phase0.SignedBeaconBlockHeader.defaultValue(); + blockheader2.message.slot = 2; + block2.message.body.blobKzgCommitments.push(ssz.deneb.KZGCommitment.defaultValue()); const blobSidecar2 = ssz.deneb.BlobSidecar.defaultValue(); - blobSidecar2.slot = 2; + blobSidecar2.signedBlockHeader = blockheader2; const block3 = ssz.deneb.SignedBeaconBlock.defaultValue(); block3.message.slot = 3; @@ -47,13 +52,18 @@ describe("beaconBlocksMaybeBlobsByRange", () => { const block4 = ssz.deneb.SignedBeaconBlock.defaultValue(); block4.message.slot = 4; + const blockheader4 = ssz.phase0.SignedBeaconBlockHeader.defaultValue(); + blockheader4.message.slot = 4; + // two blobsidecars block4.message.body.blobKzgCommitments.push(ssz.deneb.KZGCommitment.defaultValue()); block4.message.body.blobKzgCommitments.push(ssz.deneb.KZGCommitment.defaultValue()); const blobSidecar41 = ssz.deneb.BlobSidecar.defaultValue(); - blobSidecar41.slot = 4; + + blobSidecar41.signedBlockHeader = blockheader4; + const blobSidecar42 = ssz.deneb.BlobSidecar.defaultValue(); - blobSidecar42.slot = 4; + blobSidecar42.signedBlockHeader = blockheader4; blobSidecar42.index = 1; // Array of testcases which are array of matched blocks with/without (if empty) sidecars diff --git a/packages/beacon-node/test/unit/util/kzg.test.ts b/packages/beacon-node/test/unit/util/kzg.test.ts index 5bcaf1071cf6..b50bd2c0f1a7 100644 --- a/packages/beacon-node/test/unit/util/kzg.test.ts +++ b/packages/beacon-node/test/unit/util/kzg.test.ts @@ -1,7 +1,8 @@ import {describe, it, expect, afterEach, beforeAll} from "vitest"; import {bellatrix, deneb, ssz} from "@lodestar/types"; import {BYTES_PER_FIELD_ELEMENT, BLOB_TX_TYPE} from "@lodestar/params"; -import {kzgCommitmentToVersionedHash} from "@lodestar/state-transition"; +import {createBeaconConfig, createChainForkConfig, defaultChainConfig} from "@lodestar/config"; +import {computeBlobSidecars, kzgCommitmentToVersionedHash} from "../../../src/util/blobs.js"; import {loadEthereumTrustedSetup, initCKZG, ckzg, FIELD_ELEMENTS_PER_BLOB_MAINNET} from "../../../src/util/kzg.js"; import {validateBlobSidecars, validateGossipBlobSidecar} from "../../../src/chain/validation/blobSidecar.js"; import {getMockedBeaconChain} from "../../__mocks__/mockedBeaconChain.js"; @@ -30,8 +31,18 @@ describe("C-KZG", async () => { expect(ckzg.verifyBlobKzgProofBatch(blobs, commitments, proofs)).toBe(true); }); + /* eslint-disable @typescript-eslint/naming-convention */ it("BlobSidecars", async () => { - const chain = getMockedBeaconChain(); + const chainConfig = createChainForkConfig({ + ...defaultChainConfig, + ALTAIR_FORK_EPOCH: 0, + BELLATRIX_FORK_EPOCH: 0, + DENEB_FORK_EPOCH: 0, + }); + const genesisValidatorsRoot = Buffer.alloc(32, 0xaa); + const config = createBeaconConfig(chainConfig, genesisValidatorsRoot); + + const chain = getMockedBeaconChain({config}); afterEachCallbacks.push(() => chain.close()); const slot = 0; @@ -45,34 +56,17 @@ describe("C-KZG", async () => { signedBeaconBlock.message.body.blobKzgCommitments.push(kzgCommitment); } const blockRoot = ssz.deneb.BeaconBlock.hashTreeRoot(signedBeaconBlock.message); + const kzgProofs = blobs.map((blob, index) => ckzg.computeBlobKzgProof(blob, kzgCommitments[index])); + const blobSidecars: deneb.BlobSidecars = computeBlobSidecars(chain.config, signedBeaconBlock, {blobs, kzgProofs}); - const blobSidecars: deneb.BlobSidecars = blobs.map((blob, index) => { - return { - blockRoot, - index, - slot, - blob, - kzgProof: ckzg.computeBlobKzgProof(blob, kzgCommitments[index]), - kzgCommitment: kzgCommitments[index], - blockParentRoot: Buffer.alloc(32), - proposerIndex: 0, - }; - }); - - const signedBlobSidecars: deneb.SignedBlobSidecar[] = blobSidecars.map((blobSidecar) => { - const signedBlobSidecar = ssz.deneb.SignedBlobSidecar.defaultValue(); - signedBlobSidecar.message = blobSidecar; - return signedBlobSidecar; - }); - - expect(signedBlobSidecars.length).toBe(2); + expect(blobSidecars.length).toBe(2); // Full validation validateBlobSidecars(slot, blockRoot, kzgCommitments, blobSidecars); - signedBlobSidecars.forEach(async (signedBlobSidecar) => { + blobSidecars.forEach(async (blobSidecar) => { try { - await validateGossipBlobSidecar(chain.config, chain, signedBlobSidecar, signedBlobSidecar.message.index); + await validateGossipBlobSidecar(chain.config, chain, blobSidecar, blobSidecar.index); } catch (error) { // We expect some error from here // console.log(error); diff --git a/packages/beacon-node/test/unit/util/sszBytes.test.ts b/packages/beacon-node/test/unit/util/sszBytes.test.ts index 2ffaa98e6cfe..bb5fc67a7ce6 100644 --- a/packages/beacon-node/test/unit/util/sszBytes.test.ts +++ b/packages/beacon-node/test/unit/util/sszBytes.test.ts @@ -11,7 +11,7 @@ import { getSlotFromSignedAggregateAndProofSerialized, getSignatureFromAttestationSerialized, getSlotFromSignedBeaconBlockSerialized, - getSlotFromSignedBlobSidecarSerialized, + getSlotFromBlobSidecarSerialized, } from "../../../src/util/sszBytes.js"; describe("attestation SSZ serialized picking", () => { @@ -146,20 +146,20 @@ describe("signedBeaconBlock SSZ serialized picking", () => { }); }); -describe("signedBlobSidecar SSZ serialized picking", () => { - const testCases = [ssz.deneb.SignedBlobSidecar.defaultValue(), signedBlobSidecarFromValues(1_000_000)]; +describe("BlobSidecar SSZ serialized picking", () => { + const testCases = [ssz.deneb.BlobSidecar.defaultValue(), blobSidecarFromValues(1_000_000)]; - for (const [i, signedBlobSidecar] of testCases.entries()) { - const bytes = ssz.deneb.SignedBlobSidecar.serialize(signedBlobSidecar); - it(`signedBlobSidecar ${i}`, () => { - expect(getSlotFromSignedBlobSidecarSerialized(bytes)).toBe(signedBlobSidecar.message.slot); + for (const [i, blobSidecar] of testCases.entries()) { + const bytes = ssz.deneb.BlobSidecar.serialize(blobSidecar); + it(`blobSidecar ${i}`, () => { + expect(getSlotFromBlobSidecarSerialized(bytes)).toBe(blobSidecar.signedBlockHeader.message.slot); }); } - it("signedBlobSidecar - invalid data", () => { + it("blobSidecar - invalid data", () => { const invalidSlotDataSizes = [0, 20, 38]; for (const size of invalidSlotDataSizes) { - expect(getSlotFromSignedBlobSidecarSerialized(Buffer.alloc(size))).toBeNull(); + expect(getSlotFromBlobSidecarSerialized(Buffer.alloc(size))).toBeNull(); } }); }); @@ -198,8 +198,8 @@ function signedBeaconBlockFromValues(slot: Slot): phase0.SignedBeaconBlock { return signedBeaconBlock; } -function signedBlobSidecarFromValues(slot: Slot): deneb.SignedBlobSidecar { - const signedBlobSidecar = ssz.deneb.SignedBlobSidecar.defaultValue(); - signedBlobSidecar.message.slot = slot; - return signedBlobSidecar; +function blobSidecarFromValues(slot: Slot): deneb.BlobSidecar { + const blobSidecar = ssz.deneb.BlobSidecar.defaultValue(); + blobSidecar.signedBlockHeader.message.slot = slot; + return blobSidecar; } diff --git a/packages/params/src/index.ts b/packages/params/src/index.ts index 3d784356ae0f..e0623537d7f0 100644 --- a/packages/params/src/index.ts +++ b/packages/params/src/index.ts @@ -90,6 +90,7 @@ export const { FIELD_ELEMENTS_PER_BLOB, MAX_BLOB_COMMITMENTS_PER_BLOCK, MAX_BLOBS_PER_BLOCK, + KZG_COMMITMENT_INCLUSION_PROOF_DEPTH, } = activePreset; //////////// @@ -235,3 +236,10 @@ export const INTERVALS_PER_SLOT = 3; export const BYTES_PER_FIELD_ELEMENT = 32; export const BLOB_TX_TYPE = 0x03; export const VERSIONED_HASH_VERSION_KZG = 0x01; + +// ssz.deneb.BeaconBlockBody.getPathInfo(['blobKzgCommitments',0]).gindex +export const KZG_COMMITMENT_GINDEX0 = ACTIVE_PRESET === PresetName.minimal ? 864 : 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 = ACTIVE_PRESET === PresetName.minimal ? 131672 : 131928; diff --git a/packages/params/src/presets/mainnet.ts b/packages/params/src/presets/mainnet.ts index f29b1668ac44..9b591103edf5 100644 --- a/packages/params/src/presets/mainnet.ts +++ b/packages/params/src/presets/mainnet.ts @@ -115,4 +115,5 @@ export const mainnetPreset: BeaconPreset = { FIELD_ELEMENTS_PER_BLOB: 4096, MAX_BLOB_COMMITMENTS_PER_BLOCK: 4096, MAX_BLOBS_PER_BLOCK: 6, + KZG_COMMITMENT_INCLUSION_PROOF_DEPTH: 17, }; diff --git a/packages/params/src/presets/minimal.ts b/packages/params/src/presets/minimal.ts index 34d690045117..ad86cbf89e61 100644 --- a/packages/params/src/presets/minimal.ts +++ b/packages/params/src/presets/minimal.ts @@ -119,7 +119,8 @@ export const minimalPreset: BeaconPreset = { // DENEB /////////// // https://github.com/ethereum/consensus-specs/blob/dev/presets/minimal/eip4844.yaml - FIELD_ELEMENTS_PER_BLOB: 4, + FIELD_ELEMENTS_PER_BLOB: 4096, MAX_BLOB_COMMITMENTS_PER_BLOCK: 16, MAX_BLOBS_PER_BLOCK: 6, + KZG_COMMITMENT_INCLUSION_PROOF_DEPTH: 9, }; diff --git a/packages/params/src/types.ts b/packages/params/src/types.ts index 67d258bdd0c9..3c5ba6381131 100644 --- a/packages/params/src/types.ts +++ b/packages/params/src/types.ts @@ -81,6 +81,7 @@ export type BeaconPreset = { FIELD_ELEMENTS_PER_BLOB: number; MAX_BLOB_COMMITMENTS_PER_BLOCK: number; MAX_BLOBS_PER_BLOCK: number; + KZG_COMMITMENT_INCLUSION_PROOF_DEPTH: number; }; /** @@ -165,6 +166,7 @@ export const beaconPresetTypes: BeaconPresetTypes = { FIELD_ELEMENTS_PER_BLOB: "number", MAX_BLOB_COMMITMENTS_PER_BLOCK: "number", MAX_BLOBS_PER_BLOCK: "number", + KZG_COMMITMENT_INCLUSION_PROOF_DEPTH: "number", }; type BeaconPresetTypes = { diff --git a/packages/params/test/e2e/ensure-config-is-synced.test.ts b/packages/params/test/e2e/ensure-config-is-synced.test.ts index dfd57990e694..06fb4bae000c 100644 --- a/packages/params/test/e2e/ensure-config-is-synced.test.ts +++ b/packages/params/test/e2e/ensure-config-is-synced.test.ts @@ -8,7 +8,7 @@ import {loadConfigYaml} from "../yaml.js"; // Not e2e, but slow. Run with e2e tests /** https://github.com/ethereum/consensus-specs/releases */ -const specConfigCommit = "v1.4.0-beta.2"; +const specConfigCommit = "v1.4.0-beta.5"; describe("Ensure config is synced", function () { vi.setConfig({testTimeout: 60 * 1000}); diff --git a/packages/state-transition/src/signatureSets/proposer.ts b/packages/state-transition/src/signatureSets/proposer.ts index a00bcacc7c99..135ac7ed5c7a 100644 --- a/packages/state-transition/src/signatureSets/proposer.ts +++ b/packages/state-transition/src/signatureSets/proposer.ts @@ -1,5 +1,5 @@ -import {DOMAIN_BEACON_PROPOSER, DOMAIN_BLOB_SIDECAR} from "@lodestar/params"; -import {allForks, isBlindedBeaconBlock, isBlindedBlobSidecar, ssz} from "@lodestar/types"; +import {DOMAIN_BEACON_PROPOSER} from "@lodestar/params"; +import {allForks, isBlindedBeaconBlock, phase0, ssz} from "@lodestar/types"; import {computeSigningRoot} from "../util/index.js"; import {ISignatureSet, SignatureSetType, verifySignatureSet} from "../util/signatureSets.js"; import {CachedBeaconStateAllForks} from "../types.js"; @@ -17,7 +17,7 @@ export function getBlockProposerSignatureSet( signedBlock: allForks.FullOrBlindedSignedBeaconBlock ): ISignatureSet { const {config, epochCtx} = state; - const domain = state.config.getDomain(state.slot, DOMAIN_BEACON_PROPOSER, signedBlock.message.slot); + const domain = config.getDomain(state.slot, DOMAIN_BEACON_PROPOSER, signedBlock.message.slot); const blockType = isBlindedBeaconBlock(signedBlock.message) ? config.getBlindedForkTypes(signedBlock.message.slot).BeaconBlock @@ -31,19 +31,17 @@ export function getBlockProposerSignatureSet( }; } -export function getBlobProposerSignatureSet( +export function getBlockHeaderProposerSignatureSet( state: CachedBeaconStateAllForks, - signedBlob: allForks.FullOrBlindedSignedBlobSidecar + signedBlockHeader: phase0.SignedBeaconBlockHeader ): ISignatureSet { const {config, epochCtx} = state; - const domain = config.getDomain(state.slot, DOMAIN_BLOB_SIDECAR, signedBlob.message.slot); - - const blockType = isBlindedBlobSidecar(signedBlob.message) ? ssz.deneb.BlindedBlobSidecar : ssz.deneb.BlobSidecar; + const domain = config.getDomain(state.slot, DOMAIN_BEACON_PROPOSER, signedBlockHeader.message.slot); return { type: SignatureSetType.single, - pubkey: epochCtx.index2pubkey[signedBlob.message.proposerIndex], - signingRoot: computeSigningRoot(blockType, signedBlob.message, domain), - signature: signedBlob.signature, + pubkey: epochCtx.index2pubkey[signedBlockHeader.message.proposerIndex], + signingRoot: computeSigningRoot(ssz.phase0.BeaconBlockHeader, signedBlockHeader.message, domain), + signature: signedBlockHeader.signature, }; } diff --git a/packages/state-transition/src/util/blindedBlock.ts b/packages/state-transition/src/util/blindedBlock.ts index 8c271e7fec81..5b6cf42d3cef 100644 --- a/packages/state-transition/src/util/blindedBlock.ts +++ b/packages/state-transition/src/util/blindedBlock.ts @@ -1,24 +1,9 @@ import {ChainForkConfig} from "@lodestar/config"; import {ForkSeq} from "@lodestar/params"; -import { - allForks, - phase0, - Root, - deneb, - ssz, - isBlindedBeaconBlock, - isBlindedBlobSidecar, - isSignedBlindedBlockContents, - isExecutionPayloadAndBlobsBundle, -} from "@lodestar/types"; +import {allForks, phase0, Root, deneb, isBlindedBeaconBlock, isExecutionPayloadAndBlobsBundle} from "@lodestar/types"; import {executionPayloadToPayloadHeader} from "./execution.js"; -type ParsedSignedBlindedBlockOrContents = { - signedBlindedBlock: allForks.SignedBlindedBeaconBlock; - signedBlindedBlobSidecars: deneb.SignedBlindedBlobSidecars | null; -}; - export function blindedOrFullBlockHashTreeRoot( config: ChainForkConfig, blindedOrFull: allForks.FullOrBlindedBeaconBlock @@ -30,17 +15,6 @@ export function blindedOrFullBlockHashTreeRoot( config.getForkTypes(blindedOrFull.slot).BeaconBlock.hashTreeRoot(blindedOrFull); } -export function blindedOrFullBlobSidecarHashTreeRoot( - config: ChainForkConfig, - blindedOrFull: allForks.FullOrBlindedBlobSidecar -): Root { - return isBlindedBlobSidecar(blindedOrFull) - ? // Blinded - config.getBlobsForkTypes(blindedOrFull.slot).BlindedBlobSidecar.hashTreeRoot(blindedOrFull) - : // Full - config.getBlobsForkTypes(blindedOrFull.slot).BlobSidecar.hashTreeRoot(blindedOrFull); -} - export function blindedOrFullBlockToHeader( config: ChainForkConfig, blindedOrFull: allForks.FullOrBlindedBeaconBlock @@ -70,13 +44,6 @@ export function beaconBlockToBlinded( return blindedBlock; } -export function blobSidecarsToBlinded(blobSidecars: deneb.BlobSidecars): deneb.BlindedBlobSidecars { - return blobSidecars.map((blobSidecar) => { - const blobRoot = ssz.deneb.Blob.hashTreeRoot(blobSidecar.blob); - return {...blobSidecar, blobRoot} as deneb.BlindedBlobSidecar; - }); -} - export function signedBlindedBlockToFull( signedBlindedBlock: allForks.SignedBlindedBeaconBlock, executionPayload: allForks.ExecutionPayload | null @@ -100,33 +67,6 @@ export function signedBlindedBlockToFull( return signedBlock; } -export function signedBlindedBlobSidecarsToFull( - signedBlindedBlobSidecars: deneb.SignedBlindedBlobSidecars, - blobs: deneb.Blobs -): deneb.SignedBlobSidecars { - const signedBlobSidecars = signedBlindedBlobSidecars.map((signedBlindedBlobSidecar, index) => { - const signedBlobSidecar = { - ...signedBlindedBlobSidecar, - message: {...signedBlindedBlobSidecar.message, blob: blobs[index]}, - }; - delete (signedBlobSidecar.message as {blobRoot?: deneb.BlindedBlob}).blobRoot; - return signedBlobSidecar; - }); - return signedBlobSidecars; -} - -export function parseSignedBlindedBlockOrContents( - signedBlindedBlockOrContents: allForks.SignedBlindedBeaconBlockOrContents -): ParsedSignedBlindedBlockOrContents { - if (isSignedBlindedBlockContents(signedBlindedBlockOrContents)) { - const signedBlindedBlock = signedBlindedBlockOrContents.signedBlindedBlock; - const signedBlindedBlobSidecars = signedBlindedBlockOrContents.signedBlindedBlobSidecars; - return {signedBlindedBlock, signedBlindedBlobSidecars}; - } else { - return {signedBlindedBlock: signedBlindedBlockOrContents, signedBlindedBlobSidecars: null}; - } -} - export function parseExecutionPayloadAndBlobsBundle( data: allForks.ExecutionPayload | allForks.ExecutionPayloadAndBlobsBundle ): {executionPayload: allForks.ExecutionPayload; blobsBundle: deneb.BlobsBundle | null} { @@ -141,27 +81,23 @@ export function parseExecutionPayloadAndBlobsBundle( } export function reconstructFullBlockOrContents( - {signedBlindedBlock, signedBlindedBlobSidecars}: ParsedSignedBlindedBlockOrContents, - {executionPayload, blobs}: {executionPayload: allForks.ExecutionPayload | null; blobs: deneb.Blobs | null} + signedBlindedBlock: allForks.SignedBlindedBeaconBlock, + { + executionPayload, + contents, + }: { + executionPayload: allForks.ExecutionPayload | null; + contents: deneb.Contents | null; + } ): allForks.SignedBeaconBlockOrContents { const signedBlock = signedBlindedBlockToFull(signedBlindedBlock, executionPayload); - if (signedBlindedBlobSidecars !== null) { + if (contents !== null) { if (executionPayload === null) { throw Error("Missing locally produced executionPayload for deneb+ publishBlindedBlock"); } - if (blobs === null) { - throw Error("Missing blobs from the local execution cache"); - } - if (blobs.length !== signedBlindedBlobSidecars.length) { - throw Error( - `Length mismatch signedBlindedBlobSidecars=${signedBlindedBlobSidecars.length} blobs=${blobs.length}` - ); - } - const signedBlobSidecars = signedBlindedBlobSidecarsToFull(signedBlindedBlobSidecars, blobs); - - return {signedBlock, signedBlobSidecars} as allForks.SignedBeaconBlockOrContents; + return {signedBlock, ...contents} as allForks.SignedBeaconBlockOrContents; } else { return signedBlock as allForks.SignedBeaconBlockOrContents; } diff --git a/packages/state-transition/src/util/blobs.ts b/packages/state-transition/src/util/blobs.ts deleted file mode 100644 index 8b6ea84362c4..000000000000 --- a/packages/state-transition/src/util/blobs.ts +++ /dev/null @@ -1,12 +0,0 @@ -import SHA256 from "@chainsafe/as-sha256"; -import {VERSIONED_HASH_VERSION_KZG} from "@lodestar/params"; -import {deneb} from "@lodestar/types"; - -type VersionHash = Uint8Array; - -export function kzgCommitmentToVersionedHash(kzgCommitment: deneb.KZGCommitment): VersionHash { - const hash = SHA256.digest(kzgCommitment); - // Equivalent to `VERSIONED_HASH_VERSION_KZG + hash(kzg_commitment)[1:]` - hash[0] = VERSIONED_HASH_VERSION_KZG; - return hash; -} diff --git a/packages/state-transition/src/util/blockRoot.ts b/packages/state-transition/src/util/blockRoot.ts index 7aa5de52cdfe..1e1df38ef4fe 100644 --- a/packages/state-transition/src/util/blockRoot.ts +++ b/packages/state-transition/src/util/blockRoot.ts @@ -54,3 +54,15 @@ export function blockToHeader(config: ChainForkConfig, block: allForks.BeaconBlo bodyRoot: config.getForkTypes(block.slot).BeaconBlockBody.hashTreeRoot(block.body), }; } + +export function signedBlockToSignedHeader( + config: ChainForkConfig, + signedBlock: allForks.SignedBeaconBlock +): phase0.SignedBeaconBlockHeader { + const message = blockToHeader(config, signedBlock.message); + const signature = signedBlock.signature; + return { + message, + signature, + }; +} diff --git a/packages/state-transition/src/util/index.ts b/packages/state-transition/src/util/index.ts index bbc9bf8a8654..3f2e91da9a77 100644 --- a/packages/state-transition/src/util/index.ts +++ b/packages/state-transition/src/util/index.ts @@ -4,7 +4,6 @@ export * from "./attestation.js"; export * from "./attesterStatus.js"; export * from "./balance.js"; export * from "./blindedBlock.js"; -export * from "./blobs.js"; export * from "./capella.js"; export * from "./execution.js"; export * from "./blockRoot.js"; diff --git a/packages/types/package.json b/packages/types/package.json index 5c7a6599d890..42dffc436875 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -61,7 +61,9 @@ "check-types": "tsc", "lint": "eslint --color --ext .ts src/ test/", "lint:fix": "yarn run lint --fix", - "test:unit": "mocha 'test/**/*.test.ts'", + "test:unit": "yarn test:constants:minimal && yarn test:constants:mainnet && mocha 'test/unit/**/*.test.ts'", + "test:constants:minimal": "LODESTAR_PRESET=minimal mocha 'test/constants/**/*.test.ts'", + "test:constants:mainnet": "LODESTAR_PRESET=mainnet mocha 'test/constants/**/*.test.ts'", "test:browsers": "yarn karma start karma.config.cjs", "check-readme": "typescript-docs-verifier" }, diff --git a/packages/types/src/allForks/sszTypes.ts b/packages/types/src/allForks/sszTypes.ts index 463e5c57bd0d..7174bc52e89c 100644 --- a/packages/types/src/allForks/sszTypes.ts +++ b/packages/types/src/allForks/sszTypes.ts @@ -155,7 +155,6 @@ export const allForksLightClient = { export const allForksBlobs = { deneb: { BlobSidecar: deneb.BlobSidecar, - BlindedBlobSidecar: deneb.BlindedBlobSidecar, ExecutionPayloadAndBlobsBundle: deneb.ExecutionPayloadAndBlobsBundle, }, }; diff --git a/packages/types/src/allForks/types.ts b/packages/types/src/allForks/types.ts index 01c597b8a245..59768a5a3308 100644 --- a/packages/types/src/allForks/types.ts +++ b/packages/types/src/allForks/types.ts @@ -68,31 +68,17 @@ export type FullOrBlindedBeaconBlockBody = BeaconBlockBody | BlindedBeaconBlockB export type FullOrBlindedBeaconBlock = BeaconBlock | BlindedBeaconBlock; export type FullOrBlindedSignedBeaconBlock = SignedBeaconBlock | SignedBlindedBeaconBlock; -export type FullOrBlindedBlobSidecar = deneb.BlobSidecar | deneb.BlindedBlobSidecar; -export type FullOrBlindedSignedBlobSidecar = deneb.SignedBlobSidecar | deneb.SignedBlindedBlobSidecar; - -export type FullOrBlindedBlobSidecars = deneb.BlobSidecars | deneb.BlindedBlobSidecars; -export type BlockContents = {block: BeaconBlock; blobSidecars: deneb.BlobSidecars}; +export type BlockContents = {block: BeaconBlock; kzgProofs: deneb.KZGProofs; blobs: deneb.Blobs}; export type SignedBlockContents = { signedBlock: SignedBeaconBlock; - signedBlobSidecars: deneb.SignedBlobSidecars; + kzgProofs: deneb.KZGProofs; + blobs: deneb.Blobs; }; -export type BlindedBlockContents = { - blindedBlock: BlindedBeaconBlock; - blindedBlobSidecars: deneb.BlindedBlobSidecars; -}; -export type SignedBlindedBlockContents = { - signedBlindedBlock: SignedBlindedBeaconBlock; - signedBlindedBlobSidecars: deneb.SignedBlindedBlobSidecars; -}; - -export type FullOrBlindedBlockContents = BlockContents | BlindedBlockContents; -export type FullOrBlindedBeaconBlockOrContents = FullOrBlindedBeaconBlock | FullOrBlindedBlockContents; export type BeaconBlockOrContents = BeaconBlock | BlockContents; -export type BlindedBeaconBlockOrContents = BlindedBeaconBlock | BlindedBlockContents; export type SignedBeaconBlockOrContents = SignedBeaconBlock | SignedBlockContents; -export type SignedBlindedBeaconBlockOrContents = SignedBlindedBeaconBlock | SignedBlindedBlockContents; + +export type FullOrBlindedBeaconBlockOrContents = BeaconBlockOrContents | BlindedBeaconBlock; export type BuilderBid = bellatrix.BuilderBid | capella.BuilderBid | deneb.BuilderBid; export type SignedBuilderBid = bellatrix.SignedBuilderBid | capella.SignedBuilderBid | deneb.SignedBuilderBid; @@ -308,6 +294,5 @@ export type AllForksLightClientSSZTypes = { export type AllForksBlobsSSZTypes = { BlobSidecar: AllForksTypeOf; - BlindedBlobSidecar: AllForksTypeOf; ExecutionPayloadAndBlobsBundle: AllForksTypeOf; }; diff --git a/packages/types/src/deneb/sszTypes.ts b/packages/types/src/deneb/sszTypes.ts index 96509d1d898b..b39e5f6281e1 100644 --- a/packages/types/src/deneb/sszTypes.ts +++ b/packages/types/src/deneb/sszTypes.ts @@ -8,6 +8,7 @@ import { BLOCK_BODY_EXECUTION_PAYLOAD_DEPTH as EXECUTION_PAYLOAD_DEPTH, EPOCHS_PER_SYNC_COMMITTEE_PERIOD, SLOTS_PER_EPOCH, + KZG_COMMITMENT_INCLUSION_PROOF_DEPTH, } from "@lodestar/params"; import {ssz as primitiveSsz} from "../primitive/index.js"; import {ssz as phase0Ssz} from "../phase0/index.js"; @@ -15,20 +16,8 @@ import {ssz as altairSsz} from "../altair/index.js"; import {ssz as bellatrixSsz} from "../bellatrix/index.js"; import {ssz as capellaSsz} from "../capella/index.js"; -const { - UintNum64, - Slot, - Root, - BLSSignature, - UintBn64, - UintBn256, - Bytes32, - Bytes48, - Bytes96, - BLSPubkey, - BlobIndex, - ValidatorIndex, -} = primitiveSsz; +const {UintNum64, Slot, Root, BLSSignature, UintBn64, UintBn256, Bytes32, Bytes48, Bytes96, BLSPubkey, BlobIndex} = + primitiveSsz; // Polynomial commitments // https://github.com/ethereum/consensus-specs/blob/dev/specs/eip4844/polynomial-commitments.md @@ -124,31 +113,22 @@ export const SignedBeaconBlock = new ContainerType( {typeName: "SignedBeaconBlock", jsonCase: "eth2"} ); +export const KzgCommitmentInclusionProof = new VectorCompositeType(Bytes32, KZG_COMMITMENT_INCLUSION_PROOF_DEPTH); + export const BlobSidecar = new ContainerType( { - blockRoot: Root, index: BlobIndex, - slot: Slot, - blockParentRoot: Root, - proposerIndex: ValidatorIndex, blob: Blob, kzgCommitment: KZGCommitment, kzgProof: KZGProof, + signedBlockHeader: phase0Ssz.SignedBeaconBlockHeader, + kzgCommitmentInclusionProof: KzgCommitmentInclusionProof, }, {typeName: "BlobSidecar", jsonCase: "eth2"} ); export const BlobSidecars = new ListCompositeType(BlobSidecar, MAX_BLOB_COMMITMENTS_PER_BLOCK); -export const SignedBlobSidecar = new ContainerType( - { - message: BlobSidecar, - signature: BLSSignature, - }, - {typeName: "SignedBlobSidecar", jsonCase: "eth2"} -); -export const SignedBlobSidecars = new ListCompositeType(SignedBlobSidecar, MAX_BLOB_COMMITMENTS_PER_BLOCK); - export const BlobsBundle = new ContainerType( { commitments: BlobKzgCommitments, @@ -158,35 +138,6 @@ export const BlobsBundle = new ContainerType( {typeName: "BlobsBundle", jsonCase: "eth2"} ); -export const BlindedBlobSidecar = new ContainerType( - { - blockRoot: Root, - index: BlobIndex, - slot: Slot, - blockParentRoot: Root, - proposerIndex: ValidatorIndex, - blobRoot: BlindedBlob, - kzgCommitment: KZGCommitment, - kzgProof: KZGProof, - }, - {typeName: "BlindedBlobSidecar", jsonCase: "eth2"} -); - -export const BlindedBlobSidecars = new ListCompositeType(BlindedBlobSidecar, MAX_BLOB_COMMITMENTS_PER_BLOCK); - -export const SignedBlindedBlobSidecar = new ContainerType( - { - message: BlindedBlobSidecar, - signature: BLSSignature, - }, - {typeName: "SignedBlindedBlobSidecar", jsonCase: "eth2"} -); - -export const SignedBlindedBlobSidecars = new ListCompositeType( - SignedBlindedBlobSidecar, - MAX_BLOB_COMMITMENTS_PER_BLOCK -); - export const BlindedBeaconBlockBody = new ContainerType( { ...altairSsz.BeaconBlockBody.fields, @@ -213,19 +164,10 @@ export const SignedBlindedBeaconBlock = new ContainerType( {typeName: "SignedBlindedBeaconBlock", jsonCase: "eth2"} ); -export const BlindedBlobsBundle = new ContainerType( - { - commitments: BlobKzgCommitments, - proofs: KZGProofs, - blobRoots: BlindedBlobs, - }, - {typeName: "BlindedBlobsBundle", jsonCase: "eth2"} -); - export const BuilderBid = new ContainerType( { header: ExecutionPayloadHeader, - blindedBlobsBundle: BlindedBlobsBundle, + blobKzgCommitments: BlobKzgCommitments, value: UintBn256, pubkey: BLSPubkey, }, diff --git a/packages/types/src/deneb/types.ts b/packages/types/src/deneb/types.ts index 1d6eb5fca5aa..0921ae2428e7 100644 --- a/packages/types/src/deneb/types.ts +++ b/packages/types/src/deneb/types.ts @@ -1,4 +1,5 @@ import {ValueOf} from "@chainsafe/ssz"; +import {BlockContents} from "../allForks/types.js"; import * as ssz from "./sszTypes.js"; export type KZGProof = ValueOf; @@ -6,19 +7,12 @@ export type KZGCommitment = ValueOf; export type Blob = ValueOf; export type Blobs = ValueOf; -export type BlindedBlob = ValueOf; -export type BlindedBlobs = ValueOf; export type BlobSidecar = ValueOf; export type BlobSidecars = ValueOf; -export type BlindedBlobSidecar = ValueOf; -export type BlindedBlobSidecars = ValueOf; -export type SignedBlobSidecar = ValueOf; -export type SignedBlobSidecars = ValueOf; -export type SignedBlindedBlobSidecar = ValueOf; -export type SignedBlindedBlobSidecars = ValueOf; export type ExecutionPayloadAndBlobsBundle = ValueOf; export type BlobsBundle = ValueOf; +export type KzgCommitmentInclusionProof = ValueOf; export type BlobKzgCommitments = ValueOf; export type KZGProofs = ValueOf; export type BLSFieldElement = ValueOf; @@ -42,7 +36,6 @@ export type SignedBlindedBeaconBlock = ValueOf; export type BuilderBid = ValueOf; export type SignedBuilderBid = ValueOf; export type SSEPayloadAttributes = ValueOf; @@ -53,3 +46,6 @@ export type LightClientUpdate = ValueOf; export type LightClientFinalityUpdate = ValueOf; export type LightClientOptimisticUpdate = ValueOf; export type LightClientStore = ValueOf; + +export type ProducedBlobSidecars = Omit; +export type Contents = Omit; diff --git a/packages/types/src/utils/typeguards.ts b/packages/types/src/utils/typeguards.ts index 0b9bee97d17a..781738c3dbad 100644 --- a/packages/types/src/utils/typeguards.ts +++ b/packages/types/src/utils/typeguards.ts @@ -5,21 +5,15 @@ import { FullOrBlindedBeaconBlockBody, FullOrBlindedExecutionPayload, ExecutionPayloadHeader, - FullOrBlindedBlobSidecar, - FullOrBlindedSignedBlobSidecar, BlindedBeaconBlockBody, BlindedBeaconBlock, BlockContents, - SignedBlindedBlockContents, SignedBlindedBeaconBlock, - BlindedBlockContents, SignedBlockContents, SignedBeaconBlock, - SignedBlindedBeaconBlockOrContents, ExecutionPayload, ExecutionPayloadAndBlobsBundle, } from "../allForks/types.js"; -import {ts as deneb} from "../deneb/index.js"; export function isBlindedExecution(payload: FullOrBlindedExecutionPayload): payload is ExecutionPayloadHeader { // we just check transactionsRoot for determinging as it the base field @@ -42,32 +36,12 @@ export function isBlindedSignedBeaconBlock( return (signedBlock as SignedBlindedBeaconBlock).message.body.executionPayloadHeader !== undefined; } -export function isBlindedBlobSidecar(blob: FullOrBlindedBlobSidecar): blob is deneb.BlindedBlobSidecar { - return (blob as deneb.BlindedBlobSidecar).blobRoot !== undefined; -} - -export function isBlindedSignedBlobSidecar( - blob: FullOrBlindedSignedBlobSidecar -): blob is deneb.SignedBlindedBlobSidecar { - return (blob as deneb.SignedBlindedBlobSidecar).message.blobRoot !== undefined; -} - export function isBlockContents(data: FullOrBlindedBeaconBlockOrContents): data is BlockContents { - return (data as BlockContents).blobSidecars !== undefined; + return (data as BlockContents).kzgProofs !== undefined; } export function isSignedBlockContents(data: SignedBeaconBlock | SignedBlockContents): data is SignedBlockContents { - return (data as SignedBlockContents).signedBlobSidecars !== undefined; -} - -export function isBlindedBlockContents(data: FullOrBlindedBeaconBlockOrContents): data is BlindedBlockContents { - return (data as BlindedBlockContents).blindedBlobSidecars !== undefined; -} - -export function isSignedBlindedBlockContents( - data: SignedBlindedBeaconBlockOrContents -): data is SignedBlindedBlockContents { - return (data as SignedBlindedBlockContents).signedBlindedBlobSidecars !== undefined; + return (data as SignedBlockContents).kzgProofs !== undefined; } export function isExecutionPayloadAndBlobsBundle( diff --git a/packages/types/test/constants/blobs.test.ts b/packages/types/test/constants/blobs.test.ts new file mode 100644 index 000000000000..42dce38e8636 --- /dev/null +++ b/packages/types/test/constants/blobs.test.ts @@ -0,0 +1,25 @@ +import {expect} from "chai"; +import * as constants from "@lodestar/params"; +import {ssz} from "../../src/index.js"; + +// NOTE: This test is here and not in lodestar-params, to prevent lodestar-params depending on SSZ +// Since lodestar-params and lodestar-types are in the same mono-repo, running this test here is enough +// guarantee that these constants are correct. + +describe(`${constants.ACTIVE_PRESET}/ blobs pre-computed constants`, () => { + const BLOBSIDECAR_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, + KZG_COMMITMENT_GINDEX0, + KZG_COMMITMENT_SUBTREE_INDEX0, + }; + + for (const [key, expectedValue] of Object.entries(correctConstants)) { + it(key, () => { + expect((constants as unknown as Record)[key]).to.equal(expectedValue); + }); + } +}); diff --git a/packages/types/test/unit/constants.test.ts b/packages/types/test/constants/lightclient.test.ts similarity index 94% rename from packages/types/test/unit/constants.test.ts rename to packages/types/test/constants/lightclient.test.ts index 09cbec8bf1b5..3d8652ccc44c 100644 --- a/packages/types/test/unit/constants.test.ts +++ b/packages/types/test/constants/lightclient.test.ts @@ -6,7 +6,7 @@ import {ssz} from "../../src/index.js"; // Since lodestar-params and lodestar-types are in the same mono-repo, running this test here is enough // guarantee that these constants are correct. -describe("Lightclient pre-computed constants", () => { +describe(`${constants.ACTIVE_PRESET}/ Lightclient pre-computed constants`, () => { const FINALIZED_ROOT_GINDEX = bnToNum(ssz.altair.BeaconState.getPathInfo(["finalizedCheckpoint", "root"]).gindex); const FINALIZED_ROOT_DEPTH = floorlog2(FINALIZED_ROOT_GINDEX); const FINALIZED_ROOT_INDEX = FINALIZED_ROOT_GINDEX % 2 ** FINALIZED_ROOT_DEPTH; diff --git a/packages/validator/src/services/block.ts b/packages/validator/src/services/block.ts index cdcb184e54b8..14c4f83a6282 100644 --- a/packages/validator/src/services/block.ts +++ b/packages/validator/src/services/block.ts @@ -4,14 +4,13 @@ import { Slot, BLSSignature, allForks, - isBlindedBeaconBlock, + isBlindedSignedBeaconBlock, ProducedBlockSource, deneb, isBlockContents, - isBlindedBlockContents, } from "@lodestar/types"; import {ChainForkConfig} from "@lodestar/config"; -import {ForkPreBlobs, ForkBlobs, ForkSeq} from "@lodestar/params"; +import {ForkPreBlobs, ForkBlobs, ForkSeq, ForkExecution} from "@lodestar/params"; import {ETH_TO_GWEI, ETH_TO_WEI, extendError, gweiToWei, prettyBytes} from "@lodestar/utils"; import {Api, ApiError, routes} from "@lodestar/api"; import {IClock, LoggerVc} from "../util/index.js"; @@ -27,31 +26,27 @@ const MAX_DECIMAL_FACTOR = BigInt("100000"); // The following combination of blocks and blobs can be produced // i) a full block pre deneb // ii) a full block and full blobs post deneb -// iii) a blinded block pre deneb as a result of beacon/execution race -// iv) a blinded block + blinded blobs as a result of beacon/execution race +// iii) a blinded block post bellatrix type FullOrBlindedBlockWithContents = | { version: ForkPreBlobs; block: allForks.BeaconBlock; - blobs: null; + contents: null; executionPayloadBlinded: false; } | { version: ForkBlobs; block: allForks.BeaconBlock; - blobs: deneb.BlobSidecars; + contents: { + kzgProofs: deneb.KZGProofs; + blobs: deneb.Blobs; + }; executionPayloadBlinded: false; } | { - version: ForkPreBlobs; + version: ForkExecution; block: allForks.BlindedBeaconBlock; - blobs: null; - executionPayloadBlinded: true; - } - | { - version: ForkBlobs; - block: allForks.BlindedBeaconBlock; - blobs: deneb.BlindedBlobSidecars; + contents: null; executionPayloadBlinded: true; }; @@ -131,38 +126,30 @@ export class BlockProposingService { this.metrics?.proposerStepCallProduceBlock.observe(this.clock.secFromSlot(slot)); const produceBlockFn = this.opts.useProduceBlockV3 ? this.produceBlockWrapper : this.produceBlockV2Wrapper; - const blockContents = await produceBlockFn(this.config, slot, randaoReveal, graffiti, { + const produceOpts = { feeRecipient, strictFeeRecipientCheck, builderSelection, - }).catch((e: Error) => { - this.metrics?.blockProposingErrors.inc({error: "produce"}); - throw extendError(e, "Failed to produce block"); - }); + }; + const blockContents = await produceBlockFn(this.config, slot, randaoReveal, graffiti, produceOpts).catch( + (e: Error) => { + this.metrics?.blockProposingErrors.inc({error: "produce"}); + throw extendError(e, "Failed to produce block"); + } + ); this.logger.debug("Produced block", {...debugLogCtx, ...blockContents.debugLogCtx}); this.metrics?.blocksProduced.inc(); - const signedBlockPromise = this.validatorStore.signBlock(pubkey, blockContents.block, slot, this.logger); - const signedBlobPromises = - blockContents.blobs !== null - ? blockContents.blobs.map((blob) => this.validatorStore.signBlob(pubkey, blob, slot)) - : undefined; - let signedBlock: allForks.FullOrBlindedSignedBeaconBlock, - signedBlobs: allForks.FullOrBlindedSignedBlobSidecar[] | undefined; - if (signedBlobPromises !== undefined) { - [signedBlock, ...signedBlobs] = await Promise.all([signedBlockPromise, ...signedBlobPromises]); - } else { - signedBlock = await signedBlockPromise; - signedBlobs = undefined; - } + const signedBlock = await this.validatorStore.signBlock(pubkey, blockContents.block, slot); - await this.publishBlockWrapper(signedBlock, signedBlobs, { - broadcastValidation: this.opts.broadcastValidation, - }).catch((e: Error) => { + const {broadcastValidation} = this.opts; + const publishOpts = {broadcastValidation}; + await this.publishBlockWrapper(signedBlock, blockContents.contents, publishOpts).catch((e: Error) => { this.metrics?.blockProposingErrors.inc({error: "publish"}); throw extendError(e, "Failed to publish block"); }); + this.metrics?.proposerStepCallPublishBlock.observe(this.clock.secFromSlot(slot)); this.metrics?.blocksPublished.inc(); this.logger.info("Published block", {...logCtx, graffiti, ...blockContents.debugLogCtx}); @@ -173,30 +160,22 @@ export class BlockProposingService { private publishBlockWrapper = async ( signedBlock: allForks.FullOrBlindedSignedBeaconBlock, - signedBlobSidecars?: allForks.FullOrBlindedSignedBlobSidecar[], + contents: {kzgProofs: deneb.KZGProofs; blobs: deneb.Blobs} | null, opts: {broadcastValidation?: routes.beacon.BroadcastValidation} = {} ): Promise => { - if (signedBlobSidecars === undefined) { - ApiError.assert( - isBlindedBeaconBlock(signedBlock.message) - ? await this.api.beacon.publishBlindedBlockV2(signedBlock as allForks.SignedBlindedBeaconBlock, opts) - : await this.api.beacon.publishBlockV2(signedBlock as allForks.SignedBeaconBlock, opts) - ); + if (isBlindedSignedBeaconBlock(signedBlock)) { + if (contents !== null) { + this.logger.warn( + "Ignoring contents while publishing blinded block - publishing beacon should assemble it from its local cache or builder" + ); + } + ApiError.assert(await this.api.beacon.publishBlindedBlockV2(signedBlock, opts)); } else { - ApiError.assert( - isBlindedBeaconBlock(signedBlock.message) - ? await this.api.beacon.publishBlindedBlockV2( - { - signedBlindedBlock: signedBlock, - signedBlindedBlobSidecars: signedBlobSidecars, - } as allForks.SignedBlindedBlockContents, - opts - ) - : await this.api.beacon.publishBlockV2( - {signedBlock, signedBlobSidecars} as allForks.SignedBlockContents, - opts - ) - ); + if (contents === null) { + ApiError.assert(await this.api.beacon.publishBlockV2(signedBlock, opts)); + } else { + ApiError.assert(await this.api.beacon.publishBlockV2({...contents, signedBlock}, opts)); + } } }; @@ -269,28 +248,18 @@ function parseProduceBlockResponse( debugLogCtx: Record ): FullOrBlindedBlockWithContents & DebugLogCtx { if (response.executionPayloadBlinded) { - if (isBlindedBlockContents(response.data)) { - return { - block: response.data.blindedBlock, - blobs: response.data.blindedBlobSidecars, - version: response.version, - executionPayloadBlinded: true, - debugLogCtx, - } as FullOrBlindedBlockWithContents & DebugLogCtx; - } else { - return { - block: response.data, - blobs: null, - version: response.version, - executionPayloadBlinded: true, - debugLogCtx, - } as FullOrBlindedBlockWithContents & DebugLogCtx; - } + return { + block: response.data, + contents: null, + version: response.version, + executionPayloadBlinded: true, + debugLogCtx, + } as FullOrBlindedBlockWithContents & DebugLogCtx; } else { if (isBlockContents(response.data)) { return { block: response.data.block, - blobs: response.data.blobSidecars, + contents: {blobs: response.data.blobs, kzgProofs: response.data.kzgProofs}, version: response.version, executionPayloadBlinded: false, debugLogCtx, @@ -298,7 +267,7 @@ function parseProduceBlockResponse( } else { return { block: response.data, - blobs: null, + contents: null, version: response.version, executionPayloadBlinded: false, debugLogCtx, diff --git a/packages/validator/src/services/validatorStore.ts b/packages/validator/src/services/validatorStore.ts index e2736a09754a..698de1dc7053 100644 --- a/packages/validator/src/services/validatorStore.ts +++ b/packages/validator/src/services/validatorStore.ts @@ -7,7 +7,6 @@ import { computeDomain, ZERO_HASH, blindedOrFullBlockHashTreeRoot, - blindedOrFullBlobSidecarHashTreeRoot, } from "@lodestar/state-transition"; import {BeaconConfig} from "@lodestar/config"; import { @@ -20,7 +19,6 @@ import { DOMAIN_SYNC_COMMITTEE, DOMAIN_SYNC_COMMITTEE_SELECTION_PROOF, DOMAIN_APPLICATION_BUILDER, - DOMAIN_BLOB_SIDECAR, } from "@lodestar/params"; import { allForks, @@ -395,37 +393,6 @@ export class ValidatorStore { } as allForks.FullOrBlindedSignedBeaconBlock; } - async signBlob( - pubkey: BLSPubkey, - blindedOrFull: allForks.FullOrBlindedBlobSidecar, - currentSlot: Slot - ): Promise { - // Make sure the block slot is not higher than the current slot to avoid potential attacks. - if (blindedOrFull.slot > currentSlot) { - throw Error(`Not signing block with slot ${blindedOrFull.slot} greater than current slot ${currentSlot}`); - } - - // Duties are filtered before-hard by doppelganger-safe, this assert should never throw - this.assertDoppelgangerSafe(pubkey); - - const signingSlot = blindedOrFull.slot; - const domain = this.config.getDomain(signingSlot, DOMAIN_BLOB_SIDECAR); - const blobRoot = blindedOrFullBlobSidecarHashTreeRoot(this.config, blindedOrFull); - // Don't use `computeSigningRoot()` here to compute the objectRoot in typesafe function blindedOrFullBlockHashTreeRoot() - const signingRoot = ssz.phase0.SigningData.hashTreeRoot({objectRoot: blobRoot, domain}); - - // Slashing protection is not required as blobs are binded to blocks which are already protected - const signableMessage: SignableMessage = { - type: SignableMessageType.BLOB, - data: blindedOrFull, - }; - - return { - message: blindedOrFull, - signature: await this.getSignature(pubkey, signingRoot, signingSlot, signableMessage), - } as allForks.FullOrBlindedSignedBlobSidecar; - } - async signRandao(pubkey: BLSPubkey, slot: Slot): Promise { const signingSlot = slot; const domain = this.config.getDomain(slot, DOMAIN_RANDAO); diff --git a/packages/validator/src/util/externalSignerClient.ts b/packages/validator/src/util/externalSignerClient.ts index 2716533e536f..90c6e1f464c8 100644 --- a/packages/validator/src/util/externalSignerClient.ts +++ b/packages/validator/src/util/externalSignerClient.ts @@ -15,7 +15,6 @@ export enum SignableMessageType { AGGREGATE_AND_PROOF = "AGGREGATE_AND_PROOF", ATTESTATION = "ATTESTATION", BLOCK_V2 = "BLOCK_V2", - BLOB = "BLOB", DEPOSIT = "DEPOSIT", RANDAO_REVEAL = "RANDAO_REVEAL", VOLUNTARY_EXIT = "VOLUNTARY_EXIT", @@ -65,7 +64,6 @@ export type SignableMessage = | {type: SignableMessageType.AGGREGATE_AND_PROOF; data: phase0.AggregateAndProof} | {type: SignableMessageType.ATTESTATION; data: phase0.AttestationData} | {type: SignableMessageType.BLOCK_V2; data: allForks.FullOrBlindedBeaconBlock} - | {type: SignableMessageType.BLOB; data: allForks.FullOrBlindedBlobSidecar} | {type: SignableMessageType.DEPOSIT; data: ValueOf} | {type: SignableMessageType.RANDAO_REVEAL; data: {epoch: Epoch}} | {type: SignableMessageType.VOLUNTARY_EXIT; data: phase0.VoluntaryExit} @@ -88,7 +86,6 @@ const requiresForkInfo: Record = { [SignableMessageType.SYNC_COMMITTEE_CONTRIBUTION_AND_PROOF]: true, [SignableMessageType.VALIDATOR_REGISTRATION]: false, [SignableMessageType.BLS_TO_EXECUTION_CHANGE]: true, - [SignableMessageType.BLOB]: true, }; type Web3SignerSerializedRequest = { @@ -232,9 +229,5 @@ function serializerSignableMessagePayload(config: BeaconConfig, payload: Signabl case SignableMessageType.BLS_TO_EXECUTION_CHANGE: return {BLS_TO_EXECUTION_CHANGE: ssz.capella.BLSToExecutionChange.toJson(payload.data)}; - - case SignableMessageType.BLOB: - // TODO DENEB: freetheblobs - throw Error("web3signer for blob signing not yet implemented"); } } diff --git a/packages/validator/src/util/params.ts b/packages/validator/src/util/params.ts index 37908afaf86c..1431f4f3c56e 100644 --- a/packages/validator/src/util/params.ts +++ b/packages/validator/src/util/params.ts @@ -210,5 +210,6 @@ function getSpecCriticalParams(localConfig: ChainConfig): Record Date: Mon, 25 Dec 2023 13:52:13 +0100 Subject: [PATCH 4/6] chore: fix yarn install warnings related to vitest (#6232) * Restore yarn warnings lint script to previous version * Add vite package resolution * Update vite-plugin-node-polyfills package to 0.18.0 --- package.json | 5 +++-- scripts/assert_no_yarn_warnings.sh | 15 +-------------- yarn.lock | 28 +++++++++++++--------------- 3 files changed, 17 insertions(+), 31 deletions(-) diff --git a/package.json b/package.json index 158ac2affe68..cf9eb3e1b716 100644 --- a/package.json +++ b/package.json @@ -97,7 +97,7 @@ "ts-node": "^10.9.1", "typescript": "^5.2.2", "typescript-docs-verifier": "^2.5.0", - "vite-plugin-node-polyfills": "^0.17.0", + "vite-plugin-node-polyfills": "^0.18.0", "vite-plugin-top-level-await": "^1.3.1", "vitest": "^1.0.2", "vitest-when": "^0.3.0", @@ -108,6 +108,7 @@ "resolutions": { "dns-over-http-resolver": "^2.1.1", "chai": "^4.3.10", - "loupe": "^2.3.6" + "loupe": "^2.3.6", + "vite": "^5.0.0" } } diff --git a/scripts/assert_no_yarn_warnings.sh b/scripts/assert_no_yarn_warnings.sh index 9b7ff1d76779..60dace730f92 100755 --- a/scripts/assert_no_yarn_warnings.sh +++ b/scripts/assert_no_yarn_warnings.sh @@ -5,21 +5,8 @@ OUTPUT=$(yarn install --check-files 2>&1) echo $OUTPUT -MATCH=("warning") - -# There are few yarn warnings we can't find a fix for. Excluding those. -# TODO: Keep checking occasionally if the warnings are fixed upstream. -EXCLUDE=("Pattern \[\".*\"\] is trying to unpack in the same destination") -ARGS=() - -for m in "${MATCH[@]}"; do ARGS+=(-e "$m"); done -for e in "${EXCLUDE[@]}"; do ARGS+=(--exclude "$e"); done -COMMAND="grep -qi ${ARGS[@]}" - -echo "Running $COMMAND" - # grep the output for 'warning' -if echo "$OUTPUT" | ${COMMAND}; then +if echo "$OUTPUT" | grep -qi 'warning'; then echo "There were warnings in yarn install --check-files" exit 1 else diff --git a/yarn.lock b/yarn.lock index 4663c7aaf0ee..f1957f200456 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4943,15 +4943,6 @@ buffer-indexof-polyfill@~1.0.0: resolved "https://registry.yarnpkg.com/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz#d2732135c5999c64b277fcf9b1abe3498254729c" integrity sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A== -"buffer-polyfill@npm:buffer@^6.0.3", buffer@^6.0.3: - name buffer-polyfill - version "6.0.3" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" - integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== - dependencies: - base64-js "^1.3.1" - ieee754 "^1.2.1" - buffer-xor@^1.0.3: version "1.0.3" resolved "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz" @@ -4981,6 +4972,15 @@ buffer@^5.2.1, buffer@^5.4.3, buffer@^5.5.0, buffer@^5.7.1: base64-js "^1.3.1" ieee754 "^1.1.13" +buffer@^6.0.3: + name buffer-polyfill + version "6.0.3" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" + integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.2.1" + buffers@~0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/buffers/-/buffers-0.1.1.tgz#b24579c3bed4d6d396aeee6d9a8ae7f5482ab7bb" @@ -15191,15 +15191,13 @@ vite-node@1.0.2: picocolors "^1.0.0" vite "^5.0.0" -vite-plugin-node-polyfills@^0.17.0: - version "0.17.0" - resolved "https://registry.yarnpkg.com/vite-plugin-node-polyfills/-/vite-plugin-node-polyfills-0.17.0.tgz#1044a4955174ddaeedbc3679b179e1efac9da006" - integrity sha512-iPmPn7376e5u6QvoTSJa16hf5Q0DFwHFXJk2uYpsNlmI3JdPms7hWyh55o+OysJ5jo9J5XPhLC9sMOYifwFd1w== +vite-plugin-node-polyfills@^0.18.0: + version "0.18.0" + resolved "https://registry.yarnpkg.com/vite-plugin-node-polyfills/-/vite-plugin-node-polyfills-0.18.0.tgz#2ad147960f7a35dbbb1c9f9c1ae928bd0f438c1e" + integrity sha512-zkdLD3gpOhLFyxYRMJ5apk0RcODhomuS3XQgExowiX8naoc251JfcP3toqnfDlMdF0xuPYahre/H38xAcq8ApA== dependencies: "@rollup/plugin-inject" "^5.0.5" - buffer-polyfill "npm:buffer@^6.0.3" node-stdlib-browser "^1.2.0" - process "^0.11.10" vite-plugin-top-level-await@^1.3.1: version "1.3.1" From a573b92bb6184114c0424e916cf619890529ba4c Mon Sep 17 00:00:00 2001 From: Nico Flaig Date: Mon, 25 Dec 2023 16:14:31 +0100 Subject: [PATCH 5/6] test: fix flaky e2e tests (#6231) --- packages/cli/test/utils/runUtils.ts | 14 ++++---------- scripts/vitest/customMatchers.ts | 12 ++++++++---- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/packages/cli/test/utils/runUtils.ts b/packages/cli/test/utils/runUtils.ts index 9fd53a03c67a..8d2846d6e664 100644 --- a/packages/cli/test/utils/runUtils.ts +++ b/packages/cli/test/utils/runUtils.ts @@ -17,20 +17,14 @@ export function findApiToken(dirpath: string): string { } export function expectDeepEquals(a: T, b: T, message: string): void { - try { - expect(a).toEqual(b); - } catch (e) { - expect.fail(message); - } + expect(a).toEqualWithMessage(b, message); } /** * Similar to `expectDeepEquals` but only checks presence of all elements in array, irrespective of their order. */ export function expectDeepEqualsUnordered(a: T[], b: T[], message: string): void { - try { - expect(a.sort()).toEqual(b.sort()); - } catch (e) { - expect.fail(message); - } + expect(a).toEqualWithMessage(expect.arrayContaining(b), message); + expect(b).toEqualWithMessage(expect.arrayContaining(a), message); + expect(a).toHaveLength(b.length); } diff --git a/scripts/vitest/customMatchers.ts b/scripts/vitest/customMatchers.ts index ef569dd23686..227c0a2c0c76 100644 --- a/scripts/vitest/customMatchers.ts +++ b/scripts/vitest/customMatchers.ts @@ -42,7 +42,7 @@ expect.extend({ toBeWithMessage(received: unknown, expected: unknown, message: string) { if (Object.is(received, expected)) { return { - message: () => "Expected value is truthy", + message: () => "Received value is the same as expected value", pass: true, }; } @@ -50,25 +50,27 @@ expect.extend({ return { pass: false, message: () => message, + actual: received, + expected, }; }, toSatisfy(received: unknown, func: (received: unknown) => boolean) { if (func(received)) { return { - message: () => "Expected value satisfied the condition", + message: () => "Received value satisfied the condition", pass: true, }; } return { pass: false, - message: () => "Expected value did not satisfy the condition", + message: () => "Received value did not satisfy the condition", }; }, toEqualWithMessage(received: unknown, expected: unknown, message: string) { if (this.equals(received, expected)) { return { - message: () => "Expected value is truthy", + message: () => "Received value equals expected value", pass: true, }; } @@ -76,6 +78,8 @@ expect.extend({ return { pass: false, message: () => message, + actual: received, + expected, }; }, }); From 21851b24cec7b856824c4381b6b40aeab62fd583 Mon Sep 17 00:00:00 2001 From: Nazar Hussain Date: Mon, 25 Dec 2023 21:04:40 +0100 Subject: [PATCH 6/6] deps: upgrade vitest (#6237) * Upgrade vitest * Change the browser version to fix the download error * Add a browser version fix * Fix the browser download path * Fix the browser download version --- .github/workflows/test.yml | 4 +- package.json | 10 +- yarn.lock | 308 ++++++++++++++++++------------------- 3 files changed, 161 insertions(+), 161 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 76d62ae576be..43ceee898d85 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -271,9 +271,9 @@ jobs: key: ${{ runner.os }}-node-${{ matrix.node }}-${{ github.sha }} fail-on-cache-miss: true - name: Install Chrome browser - run: npx @puppeteer/browsers install chrome + run: npx @puppeteer/browsers install chromedriver@latest --path /tmp - name: Install Firefox browser - run: npx @puppeteer/browsers install firefox + run: npx @puppeteer/browsers install firefox@latest --path /tmp - name: Browser tests run: | export DISPLAY=':99.0' diff --git a/package.json b/package.json index cf9eb3e1b716..8e6dad1fdea2 100644 --- a/package.json +++ b/package.json @@ -53,8 +53,8 @@ "@types/sinon-chai": "^3.2.9", "@typescript-eslint/eslint-plugin": "6.7.2", "@typescript-eslint/parser": "6.7.2", - "@vitest/coverage-v8": "^1.0.1", - "@vitest/browser": "^1.0.1", + "@vitest/coverage-v8": "^1.1.0", + "@vitest/browser": "^1.1.0", "c8": "^8.0.1", "chai": "^4.3.8", "chai-as-promised": "^7.1.1", @@ -98,11 +98,11 @@ "typescript": "^5.2.2", "typescript-docs-verifier": "^2.5.0", "vite-plugin-node-polyfills": "^0.18.0", - "vite-plugin-top-level-await": "^1.3.1", - "vitest": "^1.0.2", + "vite-plugin-top-level-await": "^1.4.1", + "vitest": "^1.1.0", "vitest-when": "^0.3.0", "wait-port": "^1.1.0", - "webdriverio": "^8.24.12", + "webdriverio": "^8.27.0", "webpack": "^5.88.2" }, "resolutions": { diff --git a/yarn.lock b/yarn.lock index f1957f200456..db39fcfe612c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2716,7 +2716,7 @@ estree-walker "^2.0.2" magic-string "^0.30.3" -"@rollup/plugin-virtual@^3.0.1": +"@rollup/plugin-virtual@^3.0.2": version "3.0.2" resolved "https://registry.yarnpkg.com/@rollup/plugin-virtual/-/plugin-virtual-3.0.2.tgz#17e17eeecb4c9fa1c0a6e72c9e5f66382fddbb82" integrity sha512-10monEYsBp3scM4/ND4LNH5Rxvh3e/cVeL3jWTgZ2SrQ+BmUoQcopVQvnaMcOnykb1VkxUFuDAN+0FnpTFRy2A== @@ -2928,74 +2928,74 @@ resolved "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz" integrity sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ== -"@swc/core-darwin-arm64@1.3.93": - version "1.3.93" - resolved "https://registry.yarnpkg.com/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.93.tgz#aefd94625451988286bebccb1c072bae0a36bcdb" - integrity sha512-gEKgk7FVIgltnIfDO6GntyuQBBlAYg5imHpRgLxB1zSI27ijVVkksc6QwISzFZAhKYaBWIsFSVeL9AYSziAF7A== - -"@swc/core-darwin-x64@1.3.93": - version "1.3.93" - resolved "https://registry.yarnpkg.com/@swc/core-darwin-x64/-/core-darwin-x64-1.3.93.tgz#18409c6effdf508ddf1ebccfa77d35aaa6cd72f0" - integrity sha512-ZQPxm/fXdDQtn3yrYSL/gFfA8OfZ5jTi33yFQq6vcg/Y8talpZ+MgdSlYM0FkLrZdMTYYTNFiuBQuuvkA+av+Q== - -"@swc/core-linux-arm-gnueabihf@1.3.93": - version "1.3.93" - resolved "https://registry.yarnpkg.com/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.93.tgz#23a97bc94a8b2f23fb6cc4bc9d8936899e5eeff5" - integrity sha512-OYFMMI2yV+aNe3wMgYhODxHdqUB/jrK0SEMHHS44GZpk8MuBXEF+Mcz4qjkY5Q1EH7KVQqXb/gVWwdgTHpjM2A== - -"@swc/core-linux-arm64-gnu@1.3.93": - version "1.3.93" - resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.93.tgz#7a17406a7cf76a959a617626d5ee2634ae9afa26" - integrity sha512-BT4dT78odKnJMNiq5HdjBsv29CiIdcCcImAPxeFqAeFw1LL6gh9nzI8E96oWc+0lVT5lfhoesCk4Qm7J6bty8w== - -"@swc/core-linux-arm64-musl@1.3.93": - version "1.3.93" - resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.93.tgz#a30be7780090afefd3b8706398418cbe1d23db49" - integrity sha512-yH5fWEl1bktouC0mhh0Chuxp7HEO4uCtS/ly1Vmf18gs6wZ8DOOkgAEVv2dNKIryy+Na++ljx4Ym7C8tSJTrLw== - -"@swc/core-linux-x64-gnu@1.3.93": - version "1.3.93" - resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.93.tgz#41e903fd82e059952d16051b442cbe65ee5b8cb3" - integrity sha512-OFUdx64qvrGJhXKEyxosHxgoUVgba2ztYh7BnMiU5hP8lbI8G13W40J0SN3CmFQwPP30+3oEbW7LWzhKEaYjlg== - -"@swc/core-linux-x64-musl@1.3.93": - version "1.3.93" - resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.93.tgz#0866807545c44eac9b3254b374310ad5e1c573f9" - integrity sha512-4B8lSRwEq1XYm6xhxHhvHmKAS7pUp1Q7E33NQ2TlmFhfKvCOh86qvThcjAOo57x8DRwmpvEVrqvpXtYagMN6Ig== - -"@swc/core-win32-arm64-msvc@1.3.93": - version "1.3.93" - resolved "https://registry.yarnpkg.com/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.93.tgz#c72411dea2fd4f62a832f71a6e15424d849e7610" - integrity sha512-BHShlxtkven8ZjjvZ5QR6sC5fZCJ9bMujEkiha6W4cBUTY7ce7qGFyHmQd+iPC85d9kD/0cCiX/Xez8u0BhO7w== - -"@swc/core-win32-ia32-msvc@1.3.93": - version "1.3.93" - resolved "https://registry.yarnpkg.com/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.93.tgz#05c2b031b976af4ef81f5073ee114254678a5d5d" - integrity sha512-nEwNWnz4JzYAK6asVvb92yeylfxMYih7eMQOnT7ZVlZN5ba9WF29xJ6kcQKs9HRH6MvWhz9+wRgv3FcjlU6HYA== - -"@swc/core-win32-x64-msvc@1.3.93": - version "1.3.93" - resolved "https://registry.yarnpkg.com/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.93.tgz#f8748b3fd1879f13084b1b0814edf328c662935c" - integrity sha512-jibQ0zUr4kwJaQVwgmH+svS04bYTPnPw/ZkNInzxS+wFAtzINBYcU8s2PMWbDb2NGYiRSEeoSGyAvS9H+24JFA== - -"@swc/core@^1.3.10": - version "1.3.93" - resolved "https://registry.yarnpkg.com/@swc/core/-/core-1.3.93.tgz#be4282aa44deffb0e5081a2613bac00335600630" - integrity sha512-690GRr1wUGmGYZHk7fUduX/JUwViMF2o74mnZYIWEcJaCcd9MQfkhsxPBtjeg6tF+h266/Cf3RPYhsFBzzxXcA== +"@swc/core-darwin-arm64@1.3.101": + version "1.3.101" + resolved "https://registry.yarnpkg.com/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.101.tgz#9ffdc0e77c31b20877fa7405c82905e0c76738d0" + integrity sha512-mNFK+uHNPRXSnfTOG34zJOeMl2waM4hF4a2NY7dkMXrPqw9CoJn4MwTXJcyMiSz1/BnNjjTCHF3Yhj0jPxmkzQ== + +"@swc/core-darwin-x64@1.3.101": + version "1.3.101" + resolved "https://registry.yarnpkg.com/@swc/core-darwin-x64/-/core-darwin-x64-1.3.101.tgz#e50130e21e3cfd3029fd6cea43e8309b58ad9fa6" + integrity sha512-B085j8XOx73Fg15KsHvzYWG262bRweGr3JooO1aW5ec5pYbz5Ew9VS5JKYS03w2UBSxf2maWdbPz2UFAxg0whw== + +"@swc/core-linux-arm-gnueabihf@1.3.101": + version "1.3.101" + resolved "https://registry.yarnpkg.com/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.101.tgz#8cd36328e794b3c42b6c8e578bb1f42e59ba0231" + integrity sha512-9xLKRb6zSzRGPqdz52Hy5GuB1lSjmLqa0lST6MTFads3apmx4Vgs8Y5NuGhx/h2I8QM4jXdLbpqQlifpzTlSSw== + +"@swc/core-linux-arm64-gnu@1.3.101": + version "1.3.101" + resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.101.tgz#d15e3885eb13a1512ba62f00ce4f5bb19f710a0c" + integrity sha512-oE+r1lo7g/vs96Weh2R5l971dt+ZLuhaUX+n3BfDdPxNHfObXgKMjO7E+QS5RbGjv/AwiPCxQmbdCp/xN5ICJA== + +"@swc/core-linux-arm64-musl@1.3.101": + version "1.3.101" + resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.101.tgz#851d4cc1079b091fee36f5f64335232210749d7a" + integrity sha512-OGjYG3H4BMOTnJWJyBIovCez6KiHF30zMIu4+lGJTCrxRI2fAjGLml3PEXj8tC3FMcud7U2WUn6TdG0/te2k6g== + +"@swc/core-linux-x64-gnu@1.3.101": + version "1.3.101" + resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.101.tgz#3a2a7c584db2e05a798e28361440424914563fa3" + integrity sha512-/kBMcoF12PRO/lwa8Z7w4YyiKDcXQEiLvM+S3G9EvkoKYGgkkz4Q6PSNhF5rwg/E3+Hq5/9D2R+6nrkF287ihg== + +"@swc/core-linux-x64-musl@1.3.101": + version "1.3.101" + resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.101.tgz#45d1d53945994f08e93703b8de24ccac88538d0c" + integrity sha512-kDN8lm4Eew0u1p+h1l3JzoeGgZPQ05qDE0czngnjmfpsH2sOZxVj1hdiCwS5lArpy7ktaLu5JdRnx70MkUzhXw== + +"@swc/core-win32-arm64-msvc@1.3.101": + version "1.3.101" + resolved "https://registry.yarnpkg.com/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.101.tgz#b2610b8354e5fbca7cc5be3f728e61b046227fa8" + integrity sha512-9Wn8TTLWwJKw63K/S+jjrZb9yoJfJwCE2RV5vPCCWmlMf3U1AXj5XuWOLUX+Rp2sGKau7wZKsvywhheWm+qndQ== + +"@swc/core-win32-ia32-msvc@1.3.101": + version "1.3.101" + resolved "https://registry.yarnpkg.com/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.101.tgz#c919175bb4cd5e9fcfa56fbd3708167c1d445c68" + integrity sha512-onO5KvICRVlu2xmr4//V2je9O2XgS1SGKpbX206KmmjcJhXN5EYLSxW9qgg+kgV5mip+sKTHTAu7IkzkAtElYA== + +"@swc/core-win32-x64-msvc@1.3.101": + version "1.3.101" + resolved "https://registry.yarnpkg.com/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.101.tgz#17743fe425caffc596fde5965c9c4cf9a48aa26a" + integrity sha512-T3GeJtNQV00YmiVw/88/nxJ/H43CJvFnpvBHCVn17xbahiVUOPOduh3rc9LgAkKiNt/aV8vU3OJR+6PhfMR7UQ== + +"@swc/core@^1.3.100": + version "1.3.101" + resolved "https://registry.yarnpkg.com/@swc/core/-/core-1.3.101.tgz#4e8f1583094a73c410e48a0bebdeccdc6c66d4a5" + integrity sha512-w5aQ9qYsd/IYmXADAnkXPGDMTqkQalIi+kfFf/MHRKTpaOL7DHjMXwPp/n8hJ0qNjRvchzmPtOqtPBiER50d8A== dependencies: "@swc/counter" "^0.1.1" "@swc/types" "^0.1.5" optionalDependencies: - "@swc/core-darwin-arm64" "1.3.93" - "@swc/core-darwin-x64" "1.3.93" - "@swc/core-linux-arm-gnueabihf" "1.3.93" - "@swc/core-linux-arm64-gnu" "1.3.93" - "@swc/core-linux-arm64-musl" "1.3.93" - "@swc/core-linux-x64-gnu" "1.3.93" - "@swc/core-linux-x64-musl" "1.3.93" - "@swc/core-win32-arm64-msvc" "1.3.93" - "@swc/core-win32-ia32-msvc" "1.3.93" - "@swc/core-win32-x64-msvc" "1.3.93" + "@swc/core-darwin-arm64" "1.3.101" + "@swc/core-darwin-x64" "1.3.101" + "@swc/core-linux-arm-gnueabihf" "1.3.101" + "@swc/core-linux-arm64-gnu" "1.3.101" + "@swc/core-linux-arm64-musl" "1.3.101" + "@swc/core-linux-x64-gnu" "1.3.101" + "@swc/core-linux-x64-musl" "1.3.101" + "@swc/core-win32-arm64-msvc" "1.3.101" + "@swc/core-win32-ia32-msvc" "1.3.101" + "@swc/core-win32-x64-msvc" "1.3.101" "@swc/counter@^0.1.1": version "0.1.2" @@ -3665,19 +3665,19 @@ "@typescript-eslint/types" "6.7.2" eslint-visitor-keys "^3.4.1" -"@vitest/browser@^1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@vitest/browser/-/browser-1.0.1.tgz#d908e8015c5a449e0db28636c6afb969a8be9fcf" - integrity sha512-zKJvgfZMzahaFrIS5fbYnP2We+KRPJQUfog4mjOCOOVpLbk5DWtDD15XPYKaIY2IydD0ir0aCPrlcKlWGrcNww== +"@vitest/browser@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@vitest/browser/-/browser-1.1.0.tgz#b3c3e06d04506309a1e163103e1f65ee1391c262" + integrity sha512-59Uwoiw/zAQPmqgIKrzev8HNfeNlD8Q/nDyP9Xqg1D3kaM0tcOT/wk5RnZFW5f0JdguK0c1+vSeOPUSrOja1hQ== dependencies: estree-walker "^3.0.3" magic-string "^0.30.5" sirv "^2.0.3" -"@vitest/coverage-v8@^1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@vitest/coverage-v8/-/coverage-v8-1.0.1.tgz#b1249ca6e8f2617e56c7a15caa546e8b1abae4c7" - integrity sha512-Z4a7ig4VjUCT/P+LRB3IZrBRXb9xWRUM8rSBH9cKgfrU1Oe01/K2WJKtGshOnQwXZoSfQtwCGpbnHmB/qJwjcw== +"@vitest/coverage-v8@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@vitest/coverage-v8/-/coverage-v8-1.1.0.tgz#bc0bbb99fcb608f72794701a86302ff3aabbc125" + integrity sha512-kHQRk70vTdXAyQY2C0vKOHPyQD/R6IUzcGdO4vCuyr4alE5Yg1+Sk2jSdjlIrTTXdcNEs+ReWVM09mmSFJpzyQ== dependencies: "@ampproject/remapping" "^2.2.1" "@bcoe/v8-coverage" "^0.2.3" @@ -3693,57 +3693,57 @@ test-exclude "^6.0.0" v8-to-istanbul "^9.2.0" -"@vitest/expect@1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-1.0.2.tgz#7fc5ee3fe0e649f5a5e3df1a9744efe0163d1237" - integrity sha512-mAIo/8uddSWkjQMLFcjqZP3WmkwvvN0OtlyZIu33jFnwme3vZds8m8EDMxtj+Uzni2DwtPfHNjJcTM8zTV1f4A== +"@vitest/expect@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-1.1.0.tgz#f58eef7de090ad65f30bb93ec54fa9f94c9d1d5d" + integrity sha512-9IE2WWkcJo2BR9eqtY5MIo3TPmS50Pnwpm66A6neb2hvk/QSLfPXBz2qdiwUOQkwyFuuXEUj5380CbwfzW4+/w== dependencies: - "@vitest/spy" "1.0.2" - "@vitest/utils" "1.0.2" + "@vitest/spy" "1.1.0" + "@vitest/utils" "1.1.0" chai "^4.3.10" -"@vitest/runner@1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-1.0.2.tgz#aad21c03fdcd1f380564fad37be7d5a2feb2f733" - integrity sha512-ZcHJXPT2kg/9Hc4fNkCbItlsgZSs3m4vQbxB8LCSdzpbG85bExCmSvu6K9lWpMNdoKfAr1Jn0BwS9SWUcGnbTQ== +"@vitest/runner@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-1.1.0.tgz#b3bf60f4a78f4324ca09811dd0f87b721a96b534" + integrity sha512-zdNLJ00pm5z/uhbWF6aeIJCGMSyTyWImy3Fcp9piRGvueERFlQFbUwCpzVce79OLm2UHk9iwaMSOaU9jVHgNVw== dependencies: - "@vitest/utils" "1.0.2" + "@vitest/utils" "1.1.0" p-limit "^5.0.0" pathe "^1.1.1" -"@vitest/snapshot@1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-1.0.2.tgz#df11b066c9593e3539640a41f38452a6b5889da1" - integrity sha512-9ClDz2/aV5TfWA4reV7XR9p+hE0e7bifhwxlURugj3Fw0YXeTFzHmKCNEHd6wOIFMfthbGGwhlq7TOJ2jDO4/g== +"@vitest/snapshot@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-1.1.0.tgz#b9924e4303382b43bb2c31061b173e69a6fb3437" + integrity sha512-5O/wyZg09V5qmNmAlUgCBqflvn2ylgsWJRRuPrnHEfDNT6tQpQ8O1isNGgo+VxofISHqz961SG3iVvt3SPK/QQ== dependencies: magic-string "^0.30.5" pathe "^1.1.1" pretty-format "^29.7.0" -"@vitest/spy@1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-1.0.2.tgz#c28205427e77e589e3f0e6017f55d1c5b9defee3" - integrity sha512-YlnHmDntp+zNV3QoTVFI5EVHV0AXpiThd7+xnDEbWnD6fw0TH/J4/+3GFPClLimR39h6nA5m0W4Bjm5Edg4A/A== +"@vitest/spy@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-1.1.0.tgz#7f40697e4fc217ac8c3cc89a865d1751b263f561" + integrity sha512-sNOVSU/GE+7+P76qYo+VXdXhXffzWZcYIPQfmkiRxaNCSPiLANvQx5Mx6ZURJ/ndtEkUJEpvKLXqAYTKEY+lTg== dependencies: tinyspy "^2.2.0" -"@vitest/utils@1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-1.0.2.tgz#fbc483a62d13a02fa4e2b470fbf565fdd616a242" - integrity sha512-GPQkGHAnFAP/+seSbB9pCsj339yRrMgILoI5H2sPevTLCYgBq0VRjF8QSllmnQyvf0EontF6KUIt2t5s2SmqoQ== +"@vitest/utils@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-1.1.0.tgz#d177a5f41bdb484bbb43c8d73a77ca782df068b5" + integrity sha512-z+s510fKmYz4Y41XhNs3vcuFTFhcij2YF7F8VQfMEYAAUfqQh0Zfg7+w9xdgFGhPf3tX3TicAe+8BDITk6ampQ== dependencies: diff-sequences "^29.6.3" loupe "^2.3.7" pretty-format "^29.7.0" -"@wdio/config@8.24.12": - version "8.24.12" - resolved "https://registry.yarnpkg.com/@wdio/config/-/config-8.24.12.tgz#07d30aafcf0ef476e9930623b9c8e0f986943d00" - integrity sha512-3HW7qG1rIHzOIybV6oHR1CqLghsN0G3Xzs90ZciGL8dYhtcLtYCHwuWmBw4mkaB5xViU4AmZDuj7ChiG8Cr6Qw== +"@wdio/config@8.27.0": + version "8.27.0" + resolved "https://registry.yarnpkg.com/@wdio/config/-/config-8.27.0.tgz#c738d8108b5161cf3f80bb34d0e1f4d700b1a9ce" + integrity sha512-zYM5daeiBVVAbQj0ASymAt0RUsocLVIwKiUHNa8gg/1GsZnztGjetXExSp1gXlxtMVM5xWUSKjh6ceFK79gWDQ== dependencies: "@wdio/logger" "8.24.12" - "@wdio/types" "8.24.12" - "@wdio/utils" "8.24.12" + "@wdio/types" "8.27.0" + "@wdio/utils" "8.27.0" decamelize "^6.0.0" deepmerge-ts "^5.0.0" glob "^10.2.2" @@ -3781,21 +3781,21 @@ dependencies: "@types/node" "^20.1.0" -"@wdio/types@8.24.12": - version "8.24.12" - resolved "https://registry.yarnpkg.com/@wdio/types/-/types-8.24.12.tgz#c7a182ecc7effdd8ed7ea1967567a84da2c89100" - integrity sha512-SaD3OacDiW06DvSgAQ7sDBbpiI9qZRg7eoVYeBg3uSGVtUq84vTETRhhV7D6xTC00IqZu+mmN2TY5/q+7Gqy7w== +"@wdio/types@8.27.0": + version "8.27.0" + resolved "https://registry.yarnpkg.com/@wdio/types/-/types-8.27.0.tgz#ef2e3a9ae083f08ee5fe5bf9e5dfc70cc55cebcb" + integrity sha512-LbP9FKh8r0uW9/dKhTIUCC1Su8PsP9TmzGKXkWt6/IMacgJiB/zW3u1CgyaLw9lG0UiQORHGoeJX9zB2HZAh4w== dependencies: "@types/node" "^20.1.0" -"@wdio/utils@8.24.12": - version "8.24.12" - resolved "https://registry.yarnpkg.com/@wdio/utils/-/utils-8.24.12.tgz#4d4e03d62728b181f44c05584f3988659c6c7a38" - integrity sha512-uzwZyBVgqz0Wz1KL3aOUaQsxT8TNkzxti4NNTSMrU256qAPqc/n75rB7V73QASapCMpy70mZZTsuPgQYYj4ytQ== +"@wdio/utils@8.27.0": + version "8.27.0" + resolved "https://registry.yarnpkg.com/@wdio/utils/-/utils-8.27.0.tgz#6cb9b29649b4e301a959a8e8aea831edec635d55" + integrity sha512-4BY+JBQssVn003P5lA289uDMie3LtGinHze5btkcW9timB6VaU+EeZS4eKTPC0pziizLhteVvXYxv3YTpeeRfA== dependencies: "@puppeteer/browsers" "^1.6.0" "@wdio/logger" "8.24.12" - "@wdio/types" "8.24.12" + "@wdio/types" "8.27.0" decamelize "^6.0.0" deepmerge-ts "^5.1.0" edgedriver "^5.3.5" @@ -6313,10 +6313,10 @@ devtools-protocol@0.0.1147663: resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.1147663.tgz#4ec5610b39a6250d1f87e6b9c7e16688ed0ac78e" integrity sha512-hyWmRrexdhbZ1tcJUGpO95ivbRhWXz++F4Ko+n21AY5PNln2ovoJw+8ZMNDTtip+CNFQfrtLVh/w4009dXO/eQ== -devtools-protocol@^0.0.1233178: - version "0.0.1233178" - resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.1233178.tgz#dfc83cdc487c0cae8f059047293be9d6267a19f9" - integrity sha512-jmMfyaqlzddwmDaSR1AQ+5ek+f7rupZdxKuPdkRcoxrZoF70Idg/4dTgXA08TLPmwAwB54gh49Wm2l/gRM0eUg== +devtools-protocol@^0.0.1237913: + version "0.0.1237913" + resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.1237913.tgz#ac0208ff0cbe9c53646753576b5c1d788e3caa38" + integrity sha512-Pxtmz2ZIqBkpU82HaIdsvCQBG94yTC4xajrEsWx9p38QKEfBCJktSazsHkrjf9j3dVVNPhg5LR21F6KWeXpjiQ== dezalgo@^1.0.4: version "1.0.4" @@ -15112,7 +15112,7 @@ uuid@^3.3.2, uuid@^3.3.3: resolved "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== -uuid@^9.0.0: +uuid@^9.0.0, uuid@^9.0.1: version "9.0.1" resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== @@ -15180,10 +15180,10 @@ vary@^1: resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz" integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= -vite-node@1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-1.0.2.tgz#5e6096e31b851f245ccbd353bf3939130dfd0224" - integrity sha512-h7BbMJf46fLvFW/9Ygo3snkIBEHFh6fHpB4lge98H5quYrDhPFeI3S0LREz328uqPWSnii2yeJXktQ+Pmqk5BQ== +vite-node@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-1.1.0.tgz#0ebcb7398692e378954786dfba28e905e28a76b4" + integrity sha512-jV48DDUxGLEBdHCQvxL1mEh7+naVy+nhUUUaPAZLd3FJgXuxQiewHcfeZebbJ6onDqNGkP4r3MhQ342PRlG81Q== dependencies: cac "^6.7.14" debug "^4.3.4" @@ -15199,14 +15199,14 @@ vite-plugin-node-polyfills@^0.18.0: "@rollup/plugin-inject" "^5.0.5" node-stdlib-browser "^1.2.0" -vite-plugin-top-level-await@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/vite-plugin-top-level-await/-/vite-plugin-top-level-await-1.3.1.tgz#7e7293be01489b508692627529c0a3b3218a23a3" - integrity sha512-55M1h4NAwkrpxPNOJIBzKZFihqLUzIgnElLSmPNPMR2Fn9+JHKaNg3sVX1Fq+VgvuBksQYxiD3OnwQAUu7kaPQ== +vite-plugin-top-level-await@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/vite-plugin-top-level-await/-/vite-plugin-top-level-await-1.4.1.tgz#607dfe084157550fa33df18062b99ceea774cd9c" + integrity sha512-hogbZ6yT7+AqBaV6lK9JRNvJDn4/IJvHLu6ET06arNfo0t2IsyCaon7el9Xa8OumH+ESuq//SDf8xscZFE0rWw== dependencies: - "@rollup/plugin-virtual" "^3.0.1" - "@swc/core" "^1.3.10" - uuid "^9.0.0" + "@rollup/plugin-virtual" "^3.0.2" + "@swc/core" "^1.3.100" + uuid "^9.0.1" vite@^5.0.0: version "5.0.6" @@ -15224,16 +15224,16 @@ vitest-when@^0.3.0: resolved "https://registry.yarnpkg.com/vitest-when/-/vitest-when-0.3.0.tgz#663d4274f1e7302bd24ec00dda8269d20b2eff04" integrity sha512-wYfmzd+GkvdNNhbeb/40PnKpetUP5I7qxvdbu1OAXRXaLrnLfSrJTa/dMIbqqrc8SA0vhonpw5p0RHDXwhDM1Q== -vitest@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/vitest/-/vitest-1.0.2.tgz#a7c3bf41bd5ef8c1c781c98c84a749d26b31f944" - integrity sha512-F3NVwwpXfRSDnJmyv+ALPwSRVt0zDkRRE18pwUHSUPXAlWQ47rY1dc99ziMW5bBHyqwK2ERjMisLNoef64qk9w== - dependencies: - "@vitest/expect" "1.0.2" - "@vitest/runner" "1.0.2" - "@vitest/snapshot" "1.0.2" - "@vitest/spy" "1.0.2" - "@vitest/utils" "1.0.2" +vitest@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/vitest/-/vitest-1.1.0.tgz#47ba67c564aa137b53b0197d2a992908e7f5b04d" + integrity sha512-oDFiCrw7dd3Jf06HoMtSRARivvyjHJaTxikFxuqJjO76U436PqlVw1uLn7a8OSPrhSfMGVaRakKpA2lePdw79A== + dependencies: + "@vitest/expect" "1.1.0" + "@vitest/runner" "1.1.0" + "@vitest/snapshot" "1.1.0" + "@vitest/spy" "1.1.0" + "@vitest/utils" "1.1.0" acorn-walk "^8.3.0" cac "^6.7.14" chai "^4.3.10" @@ -15248,7 +15248,7 @@ vitest@^1.0.2: tinybench "^2.5.1" tinypool "^0.8.1" vite "^5.0.0" - vite-node "1.0.2" + vite-node "1.1.0" why-is-node-running "^2.2.2" vm-browserify@^1.0.1: @@ -15508,40 +15508,40 @@ web3@^4.0.3: web3-utils "^4.0.3" web3-validator "^1.0.2" -webdriver@8.24.12: - version "8.24.12" - resolved "https://registry.yarnpkg.com/webdriver/-/webdriver-8.24.12.tgz#fd443550f2fa25498af8d6a7a1261dc3d6c4f462" - integrity sha512-03DQIClHoaAqTsmDkxGwo4HwHfkn9LzJ1wfNyUerzKg8DnyXeiT6ILqj6EXLfsvh5zddU2vhYGLFXSerPgkuOQ== +webdriver@8.27.0: + version "8.27.0" + resolved "https://registry.yarnpkg.com/webdriver/-/webdriver-8.27.0.tgz#27e936a03c08b2d72ed6bd01a6a46f8189ef0abf" + integrity sha512-n1IA+rR3u84XxU9swiKUM06BkEC0GDimfZkBML57cny+utQOUbdM/mBpqCUnkWX/RBz/p2EfHdKNyOs3/REaog== dependencies: "@types/node" "^20.1.0" "@types/ws" "^8.5.3" - "@wdio/config" "8.24.12" + "@wdio/config" "8.27.0" "@wdio/logger" "8.24.12" "@wdio/protocols" "8.24.12" - "@wdio/types" "8.24.12" - "@wdio/utils" "8.24.12" + "@wdio/types" "8.27.0" + "@wdio/utils" "8.27.0" deepmerge-ts "^5.1.0" got "^12.6.1" ky "^0.33.0" ws "^8.8.0" -webdriverio@^8.24.12: - version "8.24.12" - resolved "https://registry.yarnpkg.com/webdriverio/-/webdriverio-8.24.12.tgz#05a2107ae8a3927e1a01503a05fc2050fa4e06bd" - integrity sha512-Ddu0NNRMVkTzRzqvm3m0wt2eLUn+Plz2Cj+1QXDnVpddYJvk9J3elZC2hqNyscEtecQ+h2y3r36OcJqkl9jPag== +webdriverio@^8.27.0: + version "8.27.0" + resolved "https://registry.yarnpkg.com/webdriverio/-/webdriverio-8.27.0.tgz#4068b0164ab66bfb62d6eb6b8d97df2d140922d5" + integrity sha512-Qh5VCiBjEmxnmXcL1QEFoDzFqTtaWKrXriuU5G0yHKCModGAt2G7IHTkAok3CpmkVJfZpEvY630aP1MvgDtFhw== dependencies: "@types/node" "^20.1.0" - "@wdio/config" "8.24.12" + "@wdio/config" "8.27.0" "@wdio/logger" "8.24.12" "@wdio/protocols" "8.24.12" "@wdio/repl" "8.24.12" - "@wdio/types" "8.24.12" - "@wdio/utils" "8.24.12" + "@wdio/types" "8.27.0" + "@wdio/utils" "8.27.0" archiver "^6.0.0" aria-query "^5.0.0" css-shorthand-properties "^1.1.1" css-value "^0.0.1" - devtools-protocol "^0.0.1233178" + devtools-protocol "^0.0.1237913" grapheme-splitter "^1.0.2" import-meta-resolve "^4.0.0" is-plain-obj "^4.1.0" @@ -15553,7 +15553,7 @@ webdriverio@^8.24.12: resq "^1.9.1" rgb2hex "0.2.5" serialize-error "^11.0.1" - webdriver "8.24.12" + webdriver "8.27.0" webidl-conversions@^3.0.0: version "3.0.1"