From 1aecf90a8ba77e05e400635abed6894391146ec0 Mon Sep 17 00:00:00 2001 From: Nico Flaig Date: Thu, 15 Aug 2024 11:36:13 +0100 Subject: [PATCH] feat: support post-electra attestation and attester slashing events (#7026) --- packages/api/src/beacon/client/events.ts | 2 +- packages/api/src/beacon/routes/events.ts | 35 +++++++++++++++---- packages/api/src/beacon/server/events.ts | 2 +- .../api/test/unit/beacon/oapiSpec.test.ts | 2 +- 4 files changed, 31 insertions(+), 10 deletions(-) diff --git a/packages/api/src/beacon/client/events.ts b/packages/api/src/beacon/client/events.ts index 2d63925a738a..34f14f2e8397 100644 --- a/packages/api/src/beacon/client/events.ts +++ b/packages/api/src/beacon/client/events.ts @@ -13,7 +13,7 @@ export type ApiClient = ApiClientMethods; */ export function getClient(config: ChainForkConfig, baseUrl: string): ApiClient { const definitions = getDefinitions(config); - const eventSerdes = getEventSerdes(); + const eventSerdes = getEventSerdes(config); return { eventstream: async ({topics, signal, onEvent, onError, onClose}) => { diff --git a/packages/api/src/beacon/routes/events.ts b/packages/api/src/beacon/routes/events.ts index 23be5e7c2288..1f041aa30194 100644 --- a/packages/api/src/beacon/routes/events.ts +++ b/packages/api/src/beacon/routes/events.ts @@ -13,6 +13,9 @@ import { LightClientOptimisticUpdate, LightClientFinalityUpdate, SSEPayloadAttributes, + Attestation, + AttesterSlashing, + sszTypesFor, } from "@lodestar/types"; import {ForkName} from "@lodestar/params"; @@ -104,10 +107,10 @@ export type EventData = { block: RootHex; executionOptimistic: boolean; }; - [EventType.attestation]: phase0.Attestation; + [EventType.attestation]: Attestation; [EventType.voluntaryExit]: phase0.SignedVoluntaryExit; [EventType.proposerSlashing]: phase0.ProposerSlashing; - [EventType.attesterSlashing]: phase0.AttesterSlashing; + [EventType.attesterSlashing]: AttesterSlashing; [EventType.blsToExecutionChange]: capella.SignedBLSToExecutionChange; [EventType.finalizedCheckpoint]: { block: RootHex; @@ -184,7 +187,7 @@ export type TypeJson = { fromJson: (data: unknown) => T; // client }; -export function getTypeByEvent(): {[K in EventType]: TypeJson} { +export function getTypeByEvent(config: ChainForkConfig): {[K in EventType]: TypeJson} { // eslint-disable-next-line @typescript-eslint/naming-convention const WithVersion = (getType: (fork: ForkName) => TypeJson): TypeJson<{data: T; version: ForkName}> => { return { @@ -225,10 +228,28 @@ export function getTypeByEvent(): {[K in EventType]: TypeJson} { {jsonCase: "eth2"} ), - [EventType.attestation]: ssz.phase0.Attestation, + [EventType.attestation]: { + toJson: (attestation) => { + const fork = config.getForkName(attestation.data.slot); + return sszTypesFor(fork).Attestation.toJson(attestation); + }, + fromJson: (attestation) => { + const fork = config.getForkName((attestation as Attestation).data.slot); + return sszTypesFor(fork).Attestation.fromJson(attestation); + }, + }, [EventType.voluntaryExit]: ssz.phase0.SignedVoluntaryExit, [EventType.proposerSlashing]: ssz.phase0.ProposerSlashing, - [EventType.attesterSlashing]: ssz.phase0.AttesterSlashing, + [EventType.attesterSlashing]: { + toJson: (attesterSlashing) => { + const fork = config.getForkName(Number(attesterSlashing.attestation1.data.slot)); + return sszTypesFor(fork).AttesterSlashing.toJson(attesterSlashing); + }, + fromJson: (attesterSlashing) => { + const fork = config.getForkName(Number((attesterSlashing as AttesterSlashing).attestation1.data.slot)); + return sszTypesFor(fork).AttesterSlashing.fromJson(attesterSlashing); + }, + }, [EventType.blsToExecutionChange]: ssz.capella.SignedBLSToExecutionChange, [EventType.finalizedCheckpoint]: new ContainerType( @@ -269,8 +290,8 @@ export function getTypeByEvent(): {[K in EventType]: TypeJson} { } // eslint-disable-next-line @typescript-eslint/explicit-function-return-type -export function getEventSerdes() { - const typeByEvent = getTypeByEvent(); +export function getEventSerdes(config: ChainForkConfig) { + const typeByEvent = getTypeByEvent(config); return { toJson: (event: BeaconEvent): unknown => { diff --git a/packages/api/src/beacon/server/events.ts b/packages/api/src/beacon/server/events.ts index cbeae24f6908..96212f006d8f 100644 --- a/packages/api/src/beacon/server/events.ts +++ b/packages/api/src/beacon/server/events.ts @@ -3,7 +3,7 @@ import {ApiError, ApplicationMethods, FastifyRoutes, createFastifyRoutes} from " import {Endpoints, getDefinitions, eventTypes, getEventSerdes} from "../routes/events.js"; export function getRoutes(config: ChainForkConfig, methods: ApplicationMethods): FastifyRoutes { - const eventSerdes = getEventSerdes(); + const eventSerdes = getEventSerdes(config); const serverRoutes = createFastifyRoutes(getDefinitions(config), methods); return { diff --git a/packages/api/test/unit/beacon/oapiSpec.test.ts b/packages/api/test/unit/beacon/oapiSpec.test.ts index efce60a5338f..a84b2cc36767 100644 --- a/packages/api/test/unit/beacon/oapiSpec.test.ts +++ b/packages/api/test/unit/beacon/oapiSpec.test.ts @@ -108,7 +108,7 @@ describe("eventstream event data", () => { } }); - const eventSerdes = routes.events.getEventSerdes(); + const eventSerdes = routes.events.getEventSerdes(config); const knownTopics = new Set(Object.values(routes.events.eventTypes)); for (const [topic, {value}] of Object.entries(eventstreamExamples ?? {}).filter(