diff --git a/packages/api/src/builder/routes.ts b/packages/api/src/builder/routes.ts index b3bc3bb38efc..7d2da7225f98 100644 --- a/packages/api/src/builder/routes.ts +++ b/packages/api/src/builder/routes.ts @@ -1,6 +1,6 @@ import {fromHexString, toHexString} from "@chainsafe/ssz"; import {ssz, allForks, bellatrix, Slot, Root, BLSPubkey} from "@lodestar/types"; -import {ForkName, isForkExecution} from "@lodestar/params"; +import {ForkName, isForkExecution, isForkBlobs} from "@lodestar/params"; import {ChainForkConfig} from "@lodestar/config"; import { @@ -35,11 +35,14 @@ export type Api = { HttpStatusCode.NOT_FOUND | HttpStatusCode.BAD_REQUEST > >; - submitBlindedBlock( - signedBlock: allForks.SignedBlindedBeaconBlock | SignedBlindedBlockContents - ): Promise< + submitBlindedBlock(signedBlock: allForks.SignedBlindedBeaconBlock | SignedBlindedBlockContents): Promise< ApiClientResponse< - {[HttpStatusCode.OK]: {data: allForks.ExecutionPayload; version: ForkName}}, + { + [HttpStatusCode.OK]: { + data: allForks.ExecutionPayload | allForks.ExecutionPayloadAndBlobsBundle; + version: ForkName; + }; + }, HttpStatusCode.SERVICE_UNAVAILABLE > >; @@ -85,8 +88,13 @@ export function getReturnTypes(): ReturnTypes { getHeader: WithVersion((fork: ForkName) => isForkExecution(fork) ? ssz.allForksExecution[fork].SignedBuilderBid : ssz.bellatrix.SignedBuilderBid ), - submitBlindedBlock: WithVersion((fork: ForkName) => - isForkExecution(fork) ? ssz.allForksExecution[fork].ExecutionPayload : ssz.bellatrix.ExecutionPayload + submitBlindedBlock: WithVersion( + (fork: ForkName) => + isForkBlobs(fork) + ? ssz.allForksBlobs[fork].ExecutionPayloadAndBlobsBundle + : isForkExecution(fork) + ? ssz.allForksExecution[fork].ExecutionPayload + : ssz.bellatrix.ExecutionPayload ), }; } diff --git a/packages/api/src/utils/routes.ts b/packages/api/src/utils/routes.ts index 1763dd23c92a..c52ec02b4564 100644 --- a/packages/api/src/utils/routes.ts +++ b/packages/api/src/utils/routes.ts @@ -40,6 +40,12 @@ export function isSignedBlindedBlockContents( return (data as SignedBlindedBlockContents).signedBlindedBlobSidecars !== undefined; } +export function isExecutionPayloadAndBlobsBundle( + data: allForks.ExecutionPayload | allForks.ExecutionPayloadAndBlobsBundle +): data is allForks.ExecutionPayloadAndBlobsBundle { + return (data as allForks.ExecutionPayloadAndBlobsBundle).blobsBundle !== undefined; +} + /* eslint-disable @typescript-eslint/naming-convention */ export function AllForksSignedBlockContentsReqSerializer( 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 11b96d29fa3b..857776ab46ff 100644 --- a/packages/beacon-node/src/api/impl/beacon/blocks/index.ts +++ b/packages/beacon-node/src/api/impl/beacon/blocks/index.ts @@ -1,5 +1,5 @@ import {fromHexString, toHexString} from "@chainsafe/ssz"; -import {routes, ServerApi, isSignedBlockContents, isSignedBlindedBlockContents, ResponseFormat} from "@lodestar/api"; +import {routes, ServerApi, isSignedBlockContents, ResponseFormat} from "@lodestar/api"; import {computeTimeAtSlot} from "@lodestar/state-transition"; import {SLOTS_PER_HISTORICAL_ROOT} from "@lodestar/params"; import {sleep, toHex} from "@lodestar/utils"; @@ -140,15 +140,11 @@ export function getBeaconBlockApi({ ) => { const executionBuilder = chain.executionBuilder; if (!executionBuilder) throw Error("exeutionBuilder required to publish SignedBlindedBeaconBlock"); - // Mechanism for blobs & blocks on builder is not yet finalized - if (isSignedBlindedBlockContents(signedBlindedBlockOrContents)) { - throw Error("exeutionBuilder not yet implemented for deneb+ forks"); - } else { - const signedBlockOrContents = await executionBuilder.submitBlindedBlock(signedBlindedBlockOrContents); - // 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 - 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 + const signedBlockOrContents = await executionBuilder.submitBlindedBlock(signedBlindedBlockOrContents); + return publishBlock(signedBlockOrContents, {...opts, ignoreIfKnown: true}); }; return { diff --git a/packages/beacon-node/src/api/impl/validator/index.ts b/packages/beacon-node/src/api/impl/validator/index.ts index 84ece5c4363e..d5aeabe0efdb 100644 --- a/packages/beacon-node/src/api/impl/validator/index.ts +++ b/packages/beacon-node/src/api/impl/validator/index.ts @@ -1,5 +1,5 @@ import {fromHexString, toHexString} from "@chainsafe/ssz"; -import {routes, ServerApi, BlockContents, isBlindedBlockContents} from "@lodestar/api"; +import {routes, ServerApi, BlockContents, BlindedBlockContents} from "@lodestar/api"; import { CachedBeaconStateAllForks, computeStartSlotAtEpoch, @@ -305,14 +305,17 @@ export function getValidatorApi({ const version = config.getForkName(block.slot); if (isForkBlobs(version)) { - if (!isBlindedBlockContents(block)) { - throw Error(`Expected BlockContents response at fork=${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: block, version, executionPayloadValue}; + return { + data: {blindedBlock: block, blindedBlobSidecars} as BlindedBlockContents, + version, + executionPayloadValue, + }; } else { - if (isBlindedBlockContents(block)) { - throw Error(`Invalid BlockContents response at fork=${version}`); - } return {data: block, version, executionPayloadValue}; } } finally { diff --git a/packages/beacon-node/src/chain/chain.ts b/packages/beacon-node/src/chain/chain.ts index d7a394741e63..11b442c511b6 100644 --- a/packages/beacon-node/src/chain/chain.ts +++ b/packages/beacon-node/src/chain/chain.ts @@ -13,19 +13,7 @@ import { PubkeyIndexMap, } from "@lodestar/state-transition"; import {BeaconConfig} from "@lodestar/config"; -import { - allForks, - UintNum64, - Root, - phase0, - Slot, - RootHex, - Epoch, - ValidatorIndex, - deneb, - Wei, - bellatrix, -} from "@lodestar/types"; +import {allForks, UintNum64, Root, phase0, Slot, RootHex, Epoch, ValidatorIndex, deneb, Wei} from "@lodestar/types"; import {CheckpointWithHex, ExecutionStatus, IForkChoice, ProtoBlock} from "@lodestar/fork-choice"; import {ProcessShutdownCallback} from "@lodestar/validator"; import {Logger, isErrorAborted, pruneSetToMax, sleep, toHex} from "@lodestar/utils"; @@ -135,7 +123,7 @@ export class BeaconChain implements IBeaconChain { readonly producedBlobSidecarsCache = new Map(); readonly producedBlindedBlobSidecarsCache = new Map< BlockHash, - {blobSidecars: deneb.BlindedBlobSidecars; slot: Slot} + {blindedBlobSidecars: deneb.BlindedBlobSidecars; slot: Slot} >(); readonly producedBlockRoot = new Set(); @@ -521,7 +509,7 @@ 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 = toHex((block as bellatrix.BeaconBlock).body.executionPayload.blockHash); + const blockHash = blobs.blockHash; const blobSidecars = blobs.blobSidecars.map((blobSidecar) => ({ ...blobSidecar, blockRoot, @@ -535,6 +523,22 @@ export class BeaconChain implements IBeaconChain { this.producedBlobSidecarsCache, this.opts.maxCachedBlobSidecars ?? DEFAULT_MAX_CACHED_BLOB_SIDECARS ); + } 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, slot}); + pruneSetToMax( + this.producedBlindedBlobSidecarsCache, + this.opts.maxCachedBlobSidecars ?? DEFAULT_MAX_CACHED_BLOB_SIDECARS + ); } return {block, executionPayloadValue}; diff --git a/packages/beacon-node/src/chain/interface.ts b/packages/beacon-node/src/chain/interface.ts index 513176972616..688328a4e853 100644 --- a/packages/beacon-node/src/chain/interface.ts +++ b/packages/beacon-node/src/chain/interface.ts @@ -94,7 +94,10 @@ export interface IBeaconChain { readonly beaconProposerCache: BeaconProposerCache; readonly checkpointBalancesCache: CheckpointBalancesCache; readonly producedBlobSidecarsCache: Map; - readonly producedBlindedBlobSidecarsCache: Map; + readonly producedBlindedBlobSidecarsCache: Map< + BlockHash, + {blindedBlobSidecars: deneb.BlindedBlobSidecars; slot: Slot} + >; readonly producedBlockRoot: Set; readonly producedBlindedBlockRoot: Set; readonly opts: IChainOptions; diff --git a/packages/beacon-node/src/chain/produceBlock/produceBlockBody.ts b/packages/beacon-node/src/chain/produceBlock/produceBlockBody.ts index 5224aae65035..acefbbf765a1 100644 --- a/packages/beacon-node/src/chain/produceBlock/produceBlockBody.ts +++ b/packages/beacon-node/src/chain/produceBlock/produceBlockBody.ts @@ -35,7 +35,10 @@ 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} from "./validateBlobsAndKzgCommitments.js"; +import { + validateBlobsAndKzgCommitments, + validateBlindedBlobsAndKzgCommitments, +} from "./validateBlobsAndKzgCommitments.js"; // Time to provide the EL to generate a payload from new payload id const PAYLOAD_GENERATION_TIME_MS = 500; @@ -70,8 +73,9 @@ export enum BlobsResultType { } export type BlobsResult = - | {type: BlobsResultType.preDeneb | BlobsResultType.blinded} - | {type: BlobsResultType.produced; blobSidecars: deneb.BlobSidecars; blockHash: RootHex}; + | {type: BlobsResultType.preDeneb} + | {type: BlobsResultType.produced; blobSidecars: deneb.BlobSidecars; blockHash: RootHex} + | {type: BlobsResultType.blinded; blobSidecars: deneb.BlindedBlobSidecars; blockHash: RootHex}; export async function produceBlockBody( this: BeaconChain, @@ -195,16 +199,47 @@ export async function produceBlockBody( ); (blockBody as allForks.BlindedBeaconBlockBody).executionPayloadHeader = builderRes.header; executionPayloadValue = builderRes.executionPayloadValue; - this.logger.verbose("Fetched execution payload header from builder", {slot: blockSlot, executionPayloadValue}); + + const fetchedTime = Date.now() / 1000 - computeTimeAtSlot(this.config, blockSlot, this.genesisTime); + const prepType = "blinded"; + this.metrics?.blockPayload.payloadFetchedTime.observe({prepType}, fetchedTime); + this.logger.verbose("Fetched execution payload header from builder", { + slot: blockSlot, + executionPayloadValue, + prepType, + fetchedTime, + }); + if (ForkSeq[fork] >= ForkSeq.deneb) { - const {blobKzgCommitments} = builderRes; - if (blobKzgCommitments === undefined) { - throw Error(`Invalid builder getHeader response for fork=${fork}, missing blobKzgCommitments`); + const {blindedBlobsBundle} = builderRes; + if (blindedBlobsBundle === undefined) { + throw Error(`Invalid builder getHeader response for fork=${fork}, missing blindedBlobsBundle`); } - (blockBody as deneb.BlindedBeaconBlockBody).blobKzgCommitments = blobKzgCommitments; - blobsResult = {type: BlobsResultType.blinded}; - Object.assign(logMeta, {blobs: blobKzgCommitments.length}); + // validate blindedBlobsBundle + if (this.opts.sanityCheckExecutionEngineBlobs) { + validateBlindedBlobsAndKzgCommitments(builderRes.header, blindedBlobsBundle); + } + + (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}); } else { blobsResult = {type: BlobsResultType.preDeneb}; } @@ -270,7 +305,7 @@ export async function produceBlockBody( throw Error(`Missing blobsBundle response from getPayload at fork=${fork}`); } - // Optionally sanity-check that the KZG commitments match the versioned hashes in the transactions + // validate blindedBlobsBundle if (this.opts.sanityCheckExecutionEngineBlobs) { validateBlobsAndKzgCommitments(executionPayload, blobsBundle); } @@ -288,6 +323,7 @@ export async function produceBlockBody( kzgProof: proof, kzgCommitment: commitment, }; + // Other fields will be injected after postState is calculated return blobSidecar; }) as deneb.BlobSidecars; blobsResult = {type: BlobsResultType.produced, blobSidecars, blockHash}; @@ -443,21 +479,19 @@ async function prepareExecutionPayloadHeader( ): Promise<{ header: allForks.ExecutionPayloadHeader; executionPayloadValue: Wei; - blobKzgCommitments?: deneb.BlobKzgCommitments; + blindedBlobsBundle?: deneb.BlindedBlobsBundle; }> { if (!chain.executionBuilder) { throw Error("executionBuilder required"); } const parentHashRes = await getExecutionPayloadParentHash(chain, state); - if (parentHashRes.isPremerge) { - // TODO: Is this okay? throw Error("Execution builder disabled pre-merge"); } const {parentHash} = parentHashRes; - return chain.executionBuilder.getHeader(state.slot, parentHash, proposerPubKey); + return chain.executionBuilder.getHeader(fork, state.slot, parentHash, proposerPubKey); } export async function getExecutionPayloadParentHash( diff --git a/packages/beacon-node/src/chain/produceBlock/validateBlobsAndKzgCommitments.ts b/packages/beacon-node/src/chain/produceBlock/validateBlobsAndKzgCommitments.ts index 54e90672d189..0d00d0c8bd72 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} from "@lodestar/types"; +import {allForks, deneb} from "@lodestar/types"; import {BlobsBundle} from "../../execution/index.js"; /** @@ -13,3 +13,15 @@ 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/execution/builder/http.ts b/packages/beacon-node/src/execution/builder/http.ts index 48d8277f2c61..8301bc8ac5b0 100644 --- a/packages/beacon-node/src/execution/builder/http.ts +++ b/packages/beacon-node/src/execution/builder/http.ts @@ -2,7 +2,13 @@ import {byteArrayEquals, toHexString} from "@chainsafe/ssz"; import {allForks, bellatrix, Slot, Root, BLSPubkey, ssz, deneb, Wei} from "@lodestar/types"; import {ChainForkConfig} from "@lodestar/config"; import {getClient, Api as BuilderApi} from "@lodestar/api/builder"; -import {SLOTS_PER_EPOCH} from "@lodestar/params"; +import {SLOTS_PER_EPOCH, ForkExecution} from "@lodestar/params"; +import { + SignedBlockContents, + SignedBlindedBlockContents, + isExecutionPayloadAndBlobsBundle, + isSignedBlindedBlockContents, +} from "@lodestar/api"; import {ApiError} from "@lodestar/api"; import {Metrics} from "../../metrics/metrics.js"; @@ -88,27 +94,53 @@ export class ExecutionBuilderHttp implements IExecutionBuilder { } async getHeader( + fork: ForkExecution, slot: Slot, parentHash: Root, proposerPubKey: BLSPubkey ): Promise<{ header: allForks.ExecutionPayloadHeader; executionPayloadValue: Wei; - blobKzgCommitments?: deneb.BlobKzgCommitments; + blindedBlobsBundle?: deneb.BlindedBlobsBundle; }> { const res = await this.api.getHeader(slot, parentHash, proposerPubKey); ApiError.assert(res, "execution.builder.getheader"); const {header, value: executionPayloadValue} = res.response.data.message; - const {blobKzgCommitments} = res.response.data.message as {blobKzgCommitments?: deneb.BlobKzgCommitments}; - return {header, executionPayloadValue, blobKzgCommitments}; + const {blindedBlobsBundle} = res.response.data.message as deneb.BuilderBid; + return {header, executionPayloadValue, blindedBlobsBundle}; } - async submitBlindedBlock(signedBlock: allForks.SignedBlindedBeaconBlock): Promise { - const res = await this.api.submitBlindedBlock(signedBlock); + async submitBlindedBlock( + signedBlindedBlockOrContents: allForks.SignedBlindedBeaconBlock | SignedBlindedBlockContents + ): Promise { + const res = await this.api.submitBlindedBlock(signedBlindedBlockOrContents); ApiError.assert(res, "execution.builder.submitBlindedBlock"); - const executionPayload = res.response.data; - const expectedTransactionsRoot = signedBlock.message.body.executionPayloadHeader.transactionsRoot; - const actualTransactionsRoot = ssz.bellatrix.Transactions.hashTreeRoot(res.response.data.transactions); + const {data} = res.response; + + let executionPayload: allForks.ExecutionPayload; + let blobsBundle: deneb.BlobsBundle | null; + + if (isExecutionPayloadAndBlobsBundle(data)) { + executionPayload = data.executionPayload; + blobsBundle = data.blobsBundle; + } else { + executionPayload = data; + blobsBundle = null; + } + + let signedBlindedBlock: allForks.SignedBlindedBeaconBlock; + let signedBlindedBlobSidecars: deneb.SignedBlindedBlobSidecars | null; + if (isSignedBlindedBlockContents(signedBlindedBlockOrContents)) { + signedBlindedBlock = signedBlindedBlockOrContents.signedBlindedBlock; + signedBlindedBlobSidecars = signedBlindedBlockOrContents.signedBlindedBlobSidecars; + } else { + signedBlindedBlock = signedBlindedBlockOrContents; + signedBlindedBlobSidecars = null; + } + + // some validations for execution payload + const expectedTransactionsRoot = signedBlindedBlock.message.body.executionPayloadHeader.transactionsRoot; + const actualTransactionsRoot = ssz.bellatrix.Transactions.hashTreeRoot(executionPayload.transactions); if (!byteArrayEquals(expectedTransactionsRoot, actualTransactionsRoot)) { throw Error( `Invalid transactionsRoot of the builder payload, expected=${toHexString( @@ -116,10 +148,38 @@ export class ExecutionBuilderHttp implements IExecutionBuilder { )}, actual=${toHexString(actualTransactionsRoot)}` ); } - const fullySignedBlock: bellatrix.SignedBeaconBlock = { - ...signedBlock, - message: {...signedBlock.message, body: {...signedBlock.message.body, executionPayload}}, + + const signedBlock: bellatrix.SignedBeaconBlock = { + ...signedBlindedBlock, + message: {...signedBlindedBlock.message, body: {...signedBlindedBlock.message.body, executionPayload}}, }; - return fullySignedBlock; + + if (signedBlindedBlobSidecars !== null) { + if (blobsBundle === null) { + throw Error("Invalid Builder response with missing blobsBundle for deneb+ forks"); + } + if (signedBlindedBlobSidecars.length !== blobsBundle.blobs.length) { + throw Error( + `Invalid number of blobs returned by builder, expected=$${signedBlindedBlobSidecars.length} received=${blobsBundle.blobs.length}` + ); + } + const signedBlobSidecars = signedBlindedBlobSidecars.map((_v, i) => { + // signedBlindedBlobSidecars and blobsBundle can't be null as we checked above but + // typescript can't seem to figure that out + if (signedBlindedBlobSidecars === null || blobsBundle === null) { + throw Error("Internal Error - signedBlindedBlobSidecars or blobsBundle is null"); + } + + const signedBlindedBlobSidecar = signedBlindedBlobSidecars[i]; + const blob = blobsBundle.blobs[i]; + return {signature: signedBlindedBlobSidecar.signature, message: {...signedBlindedBlobSidecar.message, blob}}; + }); + return {signedBlock, signedBlobSidecars}; + } else { + if (blobsBundle !== null) { + throw Error("Invalid Builder response with blobsBundle for deneb- forks"); + } + return signedBlock; + } } } diff --git a/packages/beacon-node/src/execution/builder/interface.ts b/packages/beacon-node/src/execution/builder/interface.ts index 2bc7a19765a0..cb1f213c6b35 100644 --- a/packages/beacon-node/src/execution/builder/interface.ts +++ b/packages/beacon-node/src/execution/builder/interface.ts @@ -1,4 +1,6 @@ import {allForks, bellatrix, Root, Slot, BLSPubkey, deneb, Wei} from "@lodestar/types"; +import {ForkExecution} from "@lodestar/params"; +import {SignedBlockContents, SignedBlindedBlockContents} from "@lodestar/api"; export interface IExecutionBuilder { /** @@ -17,13 +19,16 @@ export interface IExecutionBuilder { checkStatus(): Promise; registerValidator(registrations: bellatrix.SignedValidatorRegistrationV1[]): Promise; getHeader( + fork: ForkExecution, slot: Slot, parentHash: Root, proposerPubKey: BLSPubkey ): Promise<{ header: allForks.ExecutionPayloadHeader; executionPayloadValue: Wei; - blobKzgCommitments?: deneb.BlobKzgCommitments; + blindedBlobsBundle?: deneb.BlindedBlobsBundle; }>; - submitBlindedBlock(signedBlock: allForks.SignedBlindedBeaconBlock): Promise; + submitBlindedBlock( + signedBlock: allForks.SignedBlindedBeaconBlock | SignedBlindedBlockContents + ): Promise; } diff --git a/packages/params/src/forkName.ts b/packages/params/src/forkName.ts index b7428707050e..142684c313f4 100644 --- a/packages/params/src/forkName.ts +++ b/packages/params/src/forkName.ts @@ -40,6 +40,6 @@ export function isForkWithdrawals(fork: ForkName): fork is ForkWithdrawals { export type ForkPreBlobs = ForkPreWithdrawals | ForkName.capella; export type ForkBlobs = Exclude; -export function isForkBlobs(fork: ForkName): fork is ForkName.deneb { +export function isForkBlobs(fork: ForkName): fork is ForkBlobs { return isForkWithdrawals(fork) && fork !== ForkName.capella; } diff --git a/packages/types/src/allForks/sszTypes.ts b/packages/types/src/allForks/sszTypes.ts index 023d7bc86369..463e5c57bd0d 100644 --- a/packages/types/src/allForks/sszTypes.ts +++ b/packages/types/src/allForks/sszTypes.ts @@ -156,5 +156,6 @@ 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 779485cb73da..6233a2f499f5 100644 --- a/packages/types/src/allForks/types.ts +++ b/packages/types/src/allForks/types.ts @@ -75,6 +75,7 @@ export type FullOrBlindedBlobSidecars = deneb.BlobSidecars | deneb.BlindedBlobSi export type BuilderBid = bellatrix.BuilderBid | capella.BuilderBid | deneb.BuilderBid; export type SignedBuilderBid = bellatrix.SignedBuilderBid | capella.SignedBuilderBid | deneb.SignedBuilderBid; +export type ExecutionPayloadAndBlobsBundle = deneb.ExecutionPayloadAndBlobsBundle; export type LightClientHeader = altair.LightClientHeader | capella.LightClientHeader | deneb.LightClientHeader; export type LightClientBootstrap = @@ -287,4 +288,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 a527cf3b4f48..96509d1d898b 100644 --- a/packages/types/src/deneb/sszTypes.ts +++ b/packages/types/src/deneb/sszTypes.ts @@ -149,6 +149,15 @@ export const SignedBlobSidecar = new ContainerType( ); export const SignedBlobSidecars = new ListCompositeType(SignedBlobSidecar, MAX_BLOB_COMMITMENTS_PER_BLOCK); +export const BlobsBundle = new ContainerType( + { + commitments: BlobKzgCommitments, + proofs: KZGProofs, + blobs: Blobs, + }, + {typeName: "BlobsBundle", jsonCase: "eth2"} +); + export const BlindedBlobSidecar = new ContainerType( { blockRoot: Root, @@ -204,12 +213,21 @@ 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, value: UintBn256, pubkey: BLSPubkey, - blobKzgCommitments: BlobKzgCommitments, }, {typeName: "BuilderBid", jsonCase: "eth2"} ); @@ -222,6 +240,14 @@ export const SignedBuilderBid = new ContainerType( {typeName: "SignedBuilderBid", jsonCase: "eth2"} ); +export const ExecutionPayloadAndBlobsBundle = new ContainerType( + { + executionPayload: ExecutionPayload, + blobsBundle: BlobsBundle, + }, + {typeName: "ExecutionPayloadAndBlobsBundle", jsonCase: "eth2"} +); + // We don't spread capella.BeaconState fields since we need to replace // latestExecutionPayloadHeader and we cannot keep order doing that export const BeaconState = new ContainerType( diff --git a/packages/types/src/deneb/types.ts b/packages/types/src/deneb/types.ts index 93ea514aea75..1d6eb5fca5aa 100644 --- a/packages/types/src/deneb/types.ts +++ b/packages/types/src/deneb/types.ts @@ -16,6 +16,8 @@ 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 BlobKzgCommitments = ValueOf; export type KZGProofs = ValueOf; @@ -40,6 +42,7 @@ export type SignedBlindedBeaconBlock = ValueOf; export type BuilderBid = ValueOf; export type SignedBuilderBid = ValueOf; export type SSEPayloadAttributes = ValueOf;