diff --git a/packages/api/src/beacon/routes/events.ts b/packages/api/src/beacon/routes/events.ts index 7e94ab686f22..105da89fced5 100644 --- a/packages/api/src/beacon/routes/events.ts +++ b/packages/api/src/beacon/routes/events.ts @@ -1,4 +1,4 @@ -import {ContainerType} from "@chainsafe/ssz"; +import {ContainerType, ValueOf} from "@chainsafe/ssz"; import {Epoch, phase0, capella, Slot, ssz, StringType, RootHex, altair, UintNum64, allForks} from "@lodestar/types"; import {ChainForkConfig} from "@lodestar/config"; import {isForkExecution, ForkName} from "@lodestar/params"; @@ -7,6 +7,19 @@ import {RouteDef, TypeJson, WithVersion} from "../../utils/index.js"; import {HttpStatusCode} from "../../utils/client/httpStatusCode.js"; import {ApiClientResponse} from "../../interfaces.js"; +const stringType = new StringType(); +export const blobSidecarSSE = new ContainerType( + { + blockRoot: stringType, + index: ssz.BlobIndex, + slot: ssz.Slot, + kzgCommitment: stringType, + versionedHash: stringType, + }, + {typeName: "BlobSidecarSSE", jsonCase: "eth2"} +); +type BlobSidecarSSE = ValueOf; + // See /packages/api/src/routes/index.ts for reasoning and instructions to add new routes export enum EventType { @@ -39,6 +52,8 @@ export enum EventType { lightClientUpdate = "light_client_update", /** Payload attributes for block proposal */ payloadAttributes = "payload_attributes", + /** The node has received a valid blobSidecar (from P2P or API) */ + blobSidecar = "blob_sidecar", } export const eventTypes: {[K in EventType]: K} = { @@ -54,6 +69,7 @@ export const eventTypes: {[K in EventType]: K} = { [EventType.lightClientFinalityUpdate]: EventType.lightClientFinalityUpdate, [EventType.lightClientUpdate]: EventType.lightClientUpdate, [EventType.payloadAttributes]: EventType.payloadAttributes, + [EventType.blobSidecar]: EventType.blobSidecar, }; export type EventData = { @@ -95,6 +111,7 @@ export type EventData = { [EventType.lightClientFinalityUpdate]: allForks.LightClientFinalityUpdate; [EventType.lightClientUpdate]: allForks.LightClientUpdate; [EventType.payloadAttributes]: {version: ForkName; data: allForks.SSEPayloadAttributes}; + [EventType.blobSidecar]: BlobSidecarSSE; }; export type BeaconEvent = {[K in EventType]: {type: K; message: EventData[K]}}[EventType]; @@ -130,7 +147,6 @@ export type ReqTypes = { // The request is very simple: (topics) => {query: {topics}}, and the test will ensure compatibility server - client export function getTypeByEvent(config: ChainForkConfig): {[K in EventType]: TypeJson} { - const stringType = new StringType(); const getLightClientTypeFromHeader = (data: allForks.LightClientHeader): allForks.AllForksLightClientSSZTypes => { return config.getLightClientForkTypes(data.beacon.slot); }; @@ -190,6 +206,7 @@ export function getTypeByEvent(config: ChainForkConfig): {[K in EventType]: Type [EventType.payloadAttributes]: WithVersion((fork) => isForkExecution(fork) ? ssz.allForksExecution[fork].SSEPayloadAttributes : ssz.bellatrix.SSEPayloadAttributes ), + [EventType.blobSidecar]: blobSidecarSSE, [EventType.lightClientOptimisticUpdate]: { toJson: (data) => diff --git a/packages/api/test/unit/beacon/testData/events.ts b/packages/api/test/unit/beacon/testData/events.ts index cfd976cd8578..92e413037bcf 100644 --- a/packages/api/test/unit/beacon/testData/events.ts +++ b/packages/api/test/unit/beacon/testData/events.ts @@ -1,6 +1,6 @@ import {ssz} from "@lodestar/types"; import {ForkName} from "@lodestar/params"; -import {Api, EventData, EventType} from "../../../../src/beacon/routes/events.js"; +import {Api, EventData, EventType, blobSidecarSSE} from "../../../../src/beacon/routes/events.js"; import {GenericServerTestCases} from "../../../utils/genericServerTest.js"; const abortController = new AbortController(); @@ -109,4 +109,5 @@ export const eventTestData: EventData = { version: ForkName.bellatrix, data: ssz.bellatrix.SSEPayloadAttributes.defaultValue(), }, + [EventType.blobSidecar]: blobSidecarSSE.defaultValue(), }; diff --git a/packages/beacon-node/src/chain/blocks/importBlock.ts b/packages/beacon-node/src/chain/blocks/importBlock.ts index 55d798df8e3b..cd258e42c146 100644 --- a/packages/beacon-node/src/chain/blocks/importBlock.ts +++ b/packages/beacon-node/src/chain/blocks/importBlock.ts @@ -7,6 +7,7 @@ import { computeStartSlotAtEpoch, isStateValidatorsNodesPopulated, RootCache, + kzgCommitmentToVersionedHash, } from "@lodestar/state-transition"; import {routes} from "@lodestar/api"; import {ForkChoiceError, ForkChoiceErrorCode, EpochDifference, AncestorStatus} from "@lodestar/fork-choice"; @@ -18,7 +19,7 @@ import {isQueueErrorAborted} from "../../util/queue/index.js"; import {ChainEvent, ReorgEventData} from "../emitter.js"; import {REPROCESS_MIN_TIME_TO_NEXT_SLOT_SEC} from "../reprocess.js"; import type {BeaconChain} from "../chain.js"; -import {FullyVerifiedBlock, ImportBlockOpts, AttestationImportOpt} from "./types.js"; +import {FullyVerifiedBlock, ImportBlockOpts, AttestationImportOpt, BlockInputType} from "./types.js"; import {getCheckpointFromState} from "./utils/checkpoint.js"; import {writeBlockInputToDb} from "./writeBlockInputToDb.js"; @@ -89,13 +90,28 @@ export async function importBlock( this.metrics?.importBlock.bySource.inc({source}); this.logger.verbose("Added block to forkchoice and state cache", {slot: block.message.slot, root: blockRootHex}); + // We want to import block asap so call all event handler in the next event loop setTimeout(() => { + const slot = block.message.slot; this.emitter.emit(routes.events.EventType.block, { - block: toHexString(this.config.getForkTypes(block.message.slot).BeaconBlock.hashTreeRoot(block.message)), - slot: block.message.slot, + block: blockRootHex, + slot, executionOptimistic: blockSummary != null && isOptimisticBlock(blockSummary), }); + + if (blockInput.type === BlockInputType.postDeneb) { + for (const blobSidecar of blockInput.blobs) { + const {index, kzgCommitment} = blobSidecar; + this.emitter.emit(routes.events.EventType.blobSidecar, { + blockRoot: blockRootHex, + slot, + index, + kzgCommitment: toHexString(kzgCommitment), + versionedHash: toHexString(kzgCommitmentToVersionedHash(kzgCommitment)), + }); + } + } }, 0); // 3. Import attestations to fork choice