From 551f95d891ee0aafac9ae487ee697434ea07eb48 Mon Sep 17 00:00:00 2001 From: Navie Chan Date: Mon, 15 Jul 2024 12:58:14 +0300 Subject: [PATCH 01/21] Initial commit --- .../api/src/beacon/routes/beacon/block.ts | 25 ++- packages/api/src/beacon/routes/beacon/pool.ts | 169 +++++++++++++++++- .../api/test/unit/beacon/testData/beacon.ts | 25 ++- .../src/api/impl/beacon/blocks/index.ts | 8 + .../src/api/impl/beacon/pool/index.ts | 25 +++ .../beacon-node/src/chain/opPools/opPool.ts | 1 + packages/flare/src/cmds/selfSlashAttester.ts | 2 +- .../validator/src/services/attestation.ts | 2 +- .../test/unit/services/attestation.test.ts | 6 +- packages/validator/test/utils/apiStub.ts | 2 +- 10 files changed, 249 insertions(+), 16 deletions(-) diff --git a/packages/api/src/beacon/routes/beacon/block.ts b/packages/api/src/beacon/routes/beacon/block.ts index 99fbf7d45645..7c6e9c67c56b 100644 --- a/packages/api/src/beacon/routes/beacon/block.ts +++ b/packages/api/src/beacon/routes/beacon/block.ts @@ -8,7 +8,6 @@ import { deneb, isSignedBlockContents, SignedBeaconBlock, - BeaconBlockBody, SignedBeaconBlockOrContents, SignedBlindedBeaconBlock, SignedBlockContents, @@ -27,6 +26,7 @@ import { import {getExecutionForkTypes, toForkName} from "../../../utils/fork.js"; import {fromHeaders} from "../../../utils/headers.js"; import {WireFormat} from "../../../utils/wireFormat.js"; +import {AttestationList, AttestationListPhase0} from "./pool.js"; // See /packages/api/src/routes/index.ts for reasoning and instructions to add new routes @@ -101,10 +101,22 @@ export type Endpoints = { "GET", BlockArgs, {params: {block_id: string}}, - BeaconBlockBody["attestations"], + AttestationListPhase0, ExecutionOptimisticAndFinalizedMeta >; + /** + * Get block attestations + * Retrieves attestation included in requested block. + */ + getBlockAttestationsV2: Endpoint< + "GET", + BlockArgs, + {params: {block_id: string}}, + AttestationList, + ExecutionOptimisticFinalizedAndVersionMeta + >; + /** * Get block header * Retrieves block header for given block id. @@ -251,6 +263,15 @@ export function getDefinitions(config: ChainForkConfig): RouteDefinitions ssz[fork].BeaconBlockBody.fields.attestations), + meta: ExecutionOptimisticFinalizedAndVersionCodec, + }, + }, getBlockHeader: { url: "/eth/v1/beacon/headers/{block_id}", method: "GET", diff --git a/packages/api/src/beacon/routes/beacon/pool.ts b/packages/api/src/beacon/routes/beacon/pool.ts index 499e8af432d7..e2ba915d07e7 100644 --- a/packages/api/src/beacon/routes/beacon/pool.ts +++ b/packages/api/src/beacon/routes/beacon/pool.ts @@ -2,7 +2,7 @@ import {ValueOf} from "@chainsafe/ssz"; import {ChainForkConfig} from "@lodestar/config"; import {ForkSeq} from "@lodestar/params"; -import {phase0, capella, CommitteeIndex, Slot, ssz} from "@lodestar/types"; +import {phase0, capella, CommitteeIndex, Slot, ssz, electra} from "@lodestar/types"; import {Schema, Endpoint, RouteDefinitions} from "../../../utils/index.js"; import { ArrayOf, @@ -23,16 +23,21 @@ import {fromHeaders} from "../../../utils/headers.js"; const AttestationListTypePhase0 = ArrayOf(ssz.phase0.Attestation); const AttestationListTypeElectra = ArrayOf(ssz.electra.Attestation); -const AttesterSlashingListType = ArrayOf(ssz.phase0.AttesterSlashing); +const AttesterSlashingListTypePhase0 = ArrayOf(ssz.phase0.AttesterSlashing); +const AttesterSlashingListTypeElectra = ArrayOf(ssz.electra.AttesterSlashing); const ProposerSlashingListType = ArrayOf(ssz.phase0.ProposerSlashing); const SignedVoluntaryExitListType = ArrayOf(ssz.phase0.SignedVoluntaryExit); const SignedBLSToExecutionChangeListType = ArrayOf(ssz.capella.SignedBLSToExecutionChange); const SyncCommitteeMessageListType = ArrayOf(ssz.altair.SyncCommitteeMessage); -type AttestationListPhase0 = ValueOf; +export type AttestationListPhase0 = ValueOf; type AttestationListElectra = ValueOf; -type AttestationList = AttestationListPhase0 | AttestationListElectra; -type AttesterSlashingList = ValueOf; +export type AttestationList = AttestationListPhase0 | AttestationListElectra; + +type AttesterSlashingPhase0 = ValueOf; +type AttesterSlashingElectra = ValueOf; +type AttesterSlashingList = AttesterSlashingPhase0 | AttesterSlashingElectra; + type ProposerSlashingList = ValueOf; type SignedVoluntaryExitList = ValueOf; type SignedBLSToExecutionChangeList = ValueOf; @@ -44,6 +49,18 @@ export type Endpoints = { * Retrieves attestations known by the node but not necessarily incorporated into any block */ getPoolAttestations: Endpoint< + "GET", + {slot?: Slot; committeeIndex?: CommitteeIndex}, + {query: {slot?: number; committee_index?: number}}, + AttestationList, + EmptyMeta + >; + + /** + * Get Attestations from operations pool + * Retrieves attestations known by the node but not necessarily incorporated into any block + */ + getPoolAttestationsV2: Endpoint< "GET", {slot?: Slot; committeeIndex?: CommitteeIndex}, {query: {slot?: number; committee_index?: number}}, @@ -60,10 +77,23 @@ export type Endpoints = { "GET", EmptyArgs, EmptyRequest, - AttesterSlashingList, + AttesterSlashingPhase0, EmptyMeta >; + /** + * Get AttesterSlashings from operations pool + * Retrieves attester slashings known by the node but not necessarily incorporated into any block + */ + getPoolAttesterSlashingsV2: Endpoint< + // ⏎ + "GET", + EmptyArgs, + EmptyRequest, + AttesterSlashingList, + VersionMeta + >; + /** * Get ProposerSlashings from operations pool * Retrieves proposer slashings known by the node but not necessarily incorporated into any block @@ -112,6 +142,22 @@ export type Endpoints = { * If one or more attestations fail validation the node MUST return a 400 error with details of which attestations have failed, and why. */ submitPoolAttestations: Endpoint< + "POST", + {signedAttestations: AttestationListPhase0}, + {body: unknown}, + EmptyResponseData, + EmptyMeta + >; + + /** + * Submit Attestation objects to node + * Submits Attestation objects to the node. Each attestation in the request body is processed individually. + * + * If an attestation is validated successfully the node MUST publish that attestation on the appropriate subnet. + * + * If one or more attestations fail validation the node MUST return a 400 error with details of which attestations have failed, and why. + */ + submitPoolAttestationsV2: Endpoint< "POST", {signedAttestations: AttestationList}, {body: unknown; headers: {[MetaHeader.Version]: string}}, @@ -131,6 +177,18 @@ export type Endpoints = { EmptyMeta >; + /** + * Submit AttesterSlashing object to node's pool + * Submits AttesterSlashing object to node's pool and if passes validation node MUST broadcast it to network. + */ + submitPoolAttesterSlashingsV2: Endpoint< + "POST", + {attesterSlashing: phase0.AttesterSlashing | electra.AttesterSlashing}, + {body: unknown; headers: {[MetaHeader.Version]: string}}, + EmptyResponseData, + EmptyMeta + >; + /** * Submit ProposerSlashing object to node's pool * Submits ProposerSlashing object to node's pool and if passes validation node MUST broadcast it to network. @@ -190,6 +248,19 @@ export function getDefinitions(config: ChainForkConfig): RouteDefinitions ({slot: query.slot, committeeIndex: query.committee_index}), schema: {query: {slot: Schema.Uint, committee_index: Schema.Uint}}, }, + resp: { + data: AttestationListTypePhase0, + meta: EmptyMetaCodec, + }, + }, + getPoolAttestationsV2: { + url: "/eth/v2/beacon/pool/attestations", + method: "GET", + req: { + writeReq: ({slot, committeeIndex}) => ({query: {slot, committee_index: committeeIndex}}), + parseReq: ({query}) => ({slot: query.slot, committeeIndex: query.committee_index}), + schema: {query: {slot: Schema.Uint, committee_index: Schema.Uint}}, + }, resp: { data: WithVersion((fork) => ForkSeq[fork] >= ForkSeq.electra ? AttestationListTypeElectra : AttestationListTypePhase0 @@ -202,10 +273,21 @@ export function getDefinitions(config: ChainForkConfig): RouteDefinitions + ForkSeq[fork] >= ForkSeq.electra ? AttesterSlashingListTypeElectra : AttesterSlashingListTypePhase0 + ), + meta: VersionCodec, + }, + }, getPoolProposerSlashings: { url: "/eth/v1/beacon/pool/proposer_slashings", method: "GET", @@ -236,6 +318,22 @@ export function getDefinitions(config: ChainForkConfig): RouteDefinitions ({ + body: AttestationListTypePhase0.toJson(signedAttestations), + }), + parseReqJson: ({body}) => ({signedAttestations: AttestationListTypePhase0.fromJson(body)}), + writeReqSsz: ({signedAttestations}) => ({body: AttestationListTypePhase0.serialize(signedAttestations)}), + parseReqSsz: ({body}) => ({signedAttestations: AttestationListTypePhase0.deserialize(body)}), + schema: { + body: Schema.ObjectArray, + }, + }, + resp: EmptyResponseCodec, + }, + submitPoolAttestationsV2: { + url: "/eth/v2/beacon/pool/attestations", + method: "POST", req: { writeReqJson: ({signedAttestations}) => { const fork = config.getForkName(signedAttestations[0].data.slot); @@ -302,6 +400,63 @@ export function getDefinitions(config: ChainForkConfig): RouteDefinitions { + const fork = config.getForkName(Number(attesterSlashing.attestation1.data.slot)); + return { + body: + ForkSeq[fork] >= ForkSeq.electra + ? ssz.electra.AttesterSlashing.toJson(attesterSlashing as electra.AttesterSlashing) + : ssz.phase0.AttesterSlashing.toJson(attesterSlashing as phase0.AttesterSlashing), + headers: {[MetaHeader.Version]: fork}, + }; + }, + parseReqJson: ({body, headers}) => { + const versionHeader = fromHeaders(headers, MetaHeader.Version, false); + const fork = + versionHeader !== undefined + ? toForkName(versionHeader) + : config.getForkName( + Number((body as {attestations1: {data: {slot: string}}})?.attestations1.data.slot ?? 0) + ); + + return { + attesterSlashing: + ForkSeq[fork] >= ForkSeq.electra + ? ssz.electra.AttesterSlashing.fromJson(body) + : ssz.phase0.AttesterSlashing.fromJson(body), + }; + }, + writeReqSsz: ({attesterSlashing}) => { + const fork = config.getForkName(Number(attesterSlashing.attestation1.data.slot)); + return { + body: + ForkSeq[fork] >= ForkSeq.electra + ? ssz.electra.AttesterSlashing.serialize(attesterSlashing as electra.AttesterSlashing) + : ssz.electra.AttesterSlashing.serialize(attesterSlashing as phase0.AttesterSlashing), + headers: {[MetaHeader.Version]: fork}, + }; + }, + parseReqSsz: ({body, headers}) => { + const versionHeader = fromHeaders(headers, MetaHeader.Version, true); + const fork = toForkName(versionHeader); + return { + attesterSlashing: + ForkSeq[fork] >= ForkSeq.electra + ? ssz.electra.AttesterSlashing.deserialize(body) + : ssz.phase0.AttesterSlashing.deserialize(body), + }; + }, + schema: { + body: Schema.ObjectArray, + headers: {[MetaHeader.Version]: Schema.String}, + }, + }, + resp: EmptyResponseCodec, + }, submitPoolProposerSlashings: { url: "/eth/v1/beacon/pool/proposer_slashings", method: "POST", diff --git a/packages/api/test/unit/beacon/testData/beacon.ts b/packages/api/test/unit/beacon/testData/beacon.ts index da13d4c7783c..a1f8674bf1d3 100644 --- a/packages/api/test/unit/beacon/testData/beacon.ts +++ b/packages/api/test/unit/beacon/testData/beacon.ts @@ -49,6 +49,13 @@ export const testData: GenericServerTestCases = { args: {blockId: "head"}, res: {data: [ssz.phase0.Attestation.defaultValue()], meta: {executionOptimistic: true, finalized: false}}, }, + getBlockAttestationsV2: { + args: {blockId: "head"}, + res: { + data: [ssz.electra.Attestation.defaultValue()], + meta: {executionOptimistic: true, finalized: false, version: ForkName.electra}, + }, + }, getBlockHeader: { args: {blockId: "head"}, res: {data: blockHeaderResponse, meta: {executionOptimistic: true, finalized: false}}, @@ -92,12 +99,20 @@ export const testData: GenericServerTestCases = { getPoolAttestations: { args: {slot: 1, committeeIndex: 2}, - res: {data: [ssz.phase0.Attestation.defaultValue()], meta: {version: ForkName.deneb}}, + res: {data: [ssz.phase0.Attestation.defaultValue()]}, + }, + getPoolAttestationsV2: { + args: {slot: 1, committeeIndex: 2}, + res: {data: [ssz.electra.Attestation.defaultValue()], meta: {version: ForkName.electra}}, }, getPoolAttesterSlashings: { args: undefined, res: {data: [ssz.phase0.AttesterSlashing.defaultValue()]}, }, + getPoolAttesterSlashingsV2: { + args: undefined, + res: {data: [ssz.electra.AttesterSlashing.defaultValue()], meta: {version: ForkName.electra}}, + }, getPoolProposerSlashings: { args: undefined, res: {data: [ssz.phase0.ProposerSlashing.defaultValue()]}, @@ -114,10 +129,18 @@ export const testData: GenericServerTestCases = { args: {signedAttestations: [ssz.phase0.Attestation.defaultValue()]}, res: undefined, }, + submitPoolAttestationsV2: { + args: {signedAttestations: [ssz.electra.Attestation.defaultValue()]}, + res: undefined, + }, submitPoolAttesterSlashings: { args: {attesterSlashing: ssz.phase0.AttesterSlashing.defaultValue()}, res: undefined, }, + submitPoolAttesterSlashingsV2: { + args: {attesterSlashing: ssz.electra.AttesterSlashing.defaultValue()}, + res: undefined, + }, submitPoolProposerSlashings: { args: {proposerSlashing: ssz.phase0.ProposerSlashing.defaultValue()}, res: undefined, 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 1b8a59cc8967..445266a51729 100644 --- a/packages/beacon-node/src/api/impl/beacon/blocks/index.ts +++ b/packages/beacon-node/src/api/impl/beacon/blocks/index.ts @@ -413,6 +413,14 @@ export function getBeaconBlockApi({ }; }, + async getBlockAttestationsV2({blockId}) { + const {block, executionOptimistic, finalized} = await resolveBlockId(chain, blockId); + return { + data: Array.from(block.message.body.attestations), + meta: {executionOptimistic, finalized, version: config.getForkName(block.message.slot)}, + }; + }, + async getBlockRoot({blockId}) { // Fast path: From head state already available in memory get historical blockRoot const slot = typeof blockId === "string" ? parseInt(blockId) : blockId; diff --git a/packages/beacon-node/src/api/impl/beacon/pool/index.ts b/packages/beacon-node/src/api/impl/beacon/pool/index.ts index 19c11362c910..0edc561dba63 100644 --- a/packages/beacon-node/src/api/impl/beacon/pool/index.ts +++ b/packages/beacon-node/src/api/impl/beacon/pool/index.ts @@ -27,6 +27,17 @@ export function getBeaconPoolApi({ async getPoolAttestations({slot, committeeIndex}) { // Already filtered by slot let attestations = chain.aggregatedAttestationPool.getAll(slot); + + if (committeeIndex !== undefined) { + attestations = attestations.filter((attestation) => committeeIndex === attestation.data.index); + } + + return {data: attestations}; + }, + + async getPoolAttestationsV2({slot, committeeIndex}) { + // Already filtered by slot + let attestations = chain.aggregatedAttestationPool.getAll(slot); const fork = chain.config.getForkName(slot ?? attestations[0].data.slot) ?? ForkName.phase0; if (committeeIndex !== undefined) { @@ -40,6 +51,10 @@ export function getBeaconPoolApi({ return {data: chain.opPool.getAllAttesterSlashings()}; }, + async getPoolAttesterSlashingsV2() { + return {data: chain.opPool.getAllAttesterSlashings(), meta: {version: ForkName.phase0}}; + }, + async getPoolProposerSlashings() { return {data: chain.opPool.getAllProposerSlashings()}; }, @@ -109,12 +124,22 @@ export function getBeaconPoolApi({ } }, + async submitPoolAttestationsV2({signedAttestations}) { + // TODO Electra: Refactor submitPoolAttestations and submitPoolAttestationsV2 + await this.submitPoolAttestations({signedAttestations}); + }, + async submitPoolAttesterSlashings({attesterSlashing}) { await validateApiAttesterSlashing(chain, attesterSlashing); chain.opPool.insertAttesterSlashing(attesterSlashing); await network.publishAttesterSlashing(attesterSlashing); }, + async submitPoolAttesterSlashingsV2({attesterSlashing}) { + // TODO Electra: Refactor submitPoolAttesterSlashings and submitPoolAttesterSlashingsV2 + await this.submitPoolAttesterSlashings({attesterSlashing}); + }, + async submitPoolProposerSlashings({proposerSlashing}) { await validateApiProposerSlashing(chain, proposerSlashing); chain.opPool.insertProposerSlashing(proposerSlashing); diff --git a/packages/beacon-node/src/chain/opPools/opPool.ts b/packages/beacon-node/src/chain/opPools/opPool.ts index bc7ae31d949b..7566bcc5475e 100644 --- a/packages/beacon-node/src/chain/opPools/opPool.ts +++ b/packages/beacon-node/src/chain/opPools/opPool.ts @@ -284,6 +284,7 @@ export class OpPool { } /** For beacon pool API */ + // TODO Electra: Update to adapt electra.AttesterSlashing getAllAttesterSlashings(): phase0.AttesterSlashing[] { return Array.from(this.attesterSlashings.values()).map((attesterSlashings) => attesterSlashings.attesterSlashing); } diff --git a/packages/flare/src/cmds/selfSlashAttester.ts b/packages/flare/src/cmds/selfSlashAttester.ts index 8b43e6a92cb0..cb29ed94bb74 100644 --- a/packages/flare/src/cmds/selfSlashAttester.ts +++ b/packages/flare/src/cmds/selfSlashAttester.ts @@ -130,7 +130,7 @@ export async function selfSlashAttesterHandler(args: SelfSlashArgs): Promise Date: Mon, 15 Jul 2024 13:04:56 +0300 Subject: [PATCH 02/21] getAggregatedAttestationV2 --- packages/api/src/beacon/routes/validator.ts | 40 +++++++++++++++++++ .../test/unit/beacon/testData/validator.ts | 6 ++- .../src/api/impl/validator/index.ts | 6 ++- .../src/chain/opPools/attestationPool.ts | 1 + .../validator/src/services/attestation.ts | 2 +- .../test/unit/services/attestation.test.ts | 4 +- packages/validator/test/utils/apiStub.ts | 2 +- 7 files changed, 55 insertions(+), 6 deletions(-) diff --git a/packages/api/src/beacon/routes/validator.ts b/packages/api/src/beacon/routes/validator.ts index c0078c97d5f8..d7f461eddd9e 100644 --- a/packages/api/src/beacon/routes/validator.ts +++ b/packages/api/src/beacon/routes/validator.ts @@ -398,6 +398,23 @@ export type Endpoints = { * Returns an aggregated `Attestation` object with same `AttestationData` root. */ getAggregatedAttestation: Endpoint< + "GET", + { + /** HashTreeRoot of AttestationData that validator want's aggregated */ + attestationDataRoot: Root; + slot: Slot; + }, + {query: {attestation_data_root: string; slot: number}}, + phase0.Attestation, + EmptyMeta + >; + + /** + * Get aggregated attestation + * Aggregates all attestations matching given attestation data root, slot and committee index + * Returns an aggregated `Attestation` object with same `AttestationData` root. + */ + getAggregatedAttestationV2: Endpoint< "GET", { /** HashTreeRoot of AttestationData that validator want's aggregated */ @@ -799,6 +816,29 @@ export function getDefinitions(config: ChainForkConfig): RouteDefinitions ({ + query: {attestation_data_root: toHexString(attestationDataRoot), slot}, + }), + parseReq: ({query}) => ({ + attestationDataRoot: fromHexString(query.attestation_data_root), + slot: query.slot, + }), + schema: { + query: { + attestation_data_root: Schema.StringRequired, + slot: Schema.UintRequired, + }, + }, + }, + resp: { + data: ssz.phase0.Attestation, + meta: EmptyMetaCodec + }, + }, + getAggregatedAttestationV2: { + url: "/eth/v2/validator/aggregate_attestation", + method: "GET", req: { writeReq: ({attestationDataRoot, slot, committeeIndex}) => ({ query: {attestation_data_root: toHexString(attestationDataRoot), slot, committeeIndex}, diff --git a/packages/api/test/unit/beacon/testData/validator.ts b/packages/api/test/unit/beacon/testData/validator.ts index b27fc9c38733..98379f934ba6 100644 --- a/packages/api/test/unit/beacon/testData/validator.ts +++ b/packages/api/test/unit/beacon/testData/validator.ts @@ -99,8 +99,12 @@ export const testData: GenericServerTestCases = { res: {data: ssz.altair.SyncCommitteeContribution.defaultValue()}, }, getAggregatedAttestation: { + args: {attestationDataRoot: ZERO_HASH, slot: 32000}, + res: {data: ssz.phase0.Attestation.defaultValue()}, + }, + getAggregatedAttestationV2: { args: {attestationDataRoot: ZERO_HASH, slot: 32000, committeeIndex: 2}, - res: {data: ssz.phase0.Attestation.defaultValue(), meta: {version: ForkName.phase0}}, + res: {data: ssz.electra.Attestation.defaultValue(), meta: {version: ForkName.electra}}, }, publishAggregateAndProofs: { args: {signedAggregateAndProofs: [ssz.phase0.SignedAggregateAndProof.defaultValue()]}, diff --git a/packages/beacon-node/src/api/impl/validator/index.ts b/packages/beacon-node/src/api/impl/validator/index.ts index d1559209d5a0..e2366a884633 100644 --- a/packages/beacon-node/src/api/impl/validator/index.ts +++ b/packages/beacon-node/src/api/impl/validator/index.ts @@ -1067,7 +1067,11 @@ export function getValidatorApi( }; }, - async getAggregatedAttestation({attestationDataRoot, slot, committeeIndex}) { + async getAggregatedAttestation({}) { + throw new Error("Not implemented"); + }, + + async getAggregatedAttestationV2({attestationDataRoot, slot, committeeIndex}) { notWhileSyncing(); await waitForSlot(slot); // Must never request for a future slot > currentSlot diff --git a/packages/beacon-node/src/chain/opPools/attestationPool.ts b/packages/beacon-node/src/chain/opPools/attestationPool.ts index 8daae6b663c7..a8c8940a0a0f 100644 --- a/packages/beacon-node/src/chain/opPools/attestationPool.ts +++ b/packages/beacon-node/src/chain/opPools/attestationPool.ts @@ -144,6 +144,7 @@ export class AttestationPool { /** * For validator API to get an aggregate */ + // TODO Electra: Change attestation pool to accomodate pre-electra request getAggregate(slot: Slot, committeeIndex: CommitteeIndex, dataRootHex: RootHex): Attestation | null { const aggregate = this.aggregateByIndexByRootBySlot.get(slot)?.get(dataRootHex)?.get(committeeIndex); if (!aggregate) { diff --git a/packages/validator/src/services/attestation.ts b/packages/validator/src/services/attestation.ts index 2e9f6c930dcb..4728f5525255 100644 --- a/packages/validator/src/services/attestation.ts +++ b/packages/validator/src/services/attestation.ts @@ -258,7 +258,7 @@ export class AttestationService { } this.logger.verbose("Aggregating attestations", logCtx); - const res = await this.api.validator.getAggregatedAttestation({ + const res = await this.api.validator.getAggregatedAttestationV2({ attestationDataRoot: ssz.phase0.AttestationData.hashTreeRoot(attestation), slot: attestation.slot, committeeIndex, diff --git a/packages/validator/test/unit/services/attestation.test.ts b/packages/validator/test/unit/services/attestation.test.ts index 5d7cbba4aa5f..34d921103fd7 100644 --- a/packages/validator/test/unit/services/attestation.test.ts +++ b/packages/validator/test/unit/services/attestation.test.ts @@ -101,7 +101,7 @@ describe("AttestationService", function () { // Mock beacon's attestation and aggregates endpoints api.validator.produceAttestationData.mockResolvedValue(mockApiResponse({data: attestation.data})); - api.validator.getAggregatedAttestation.mockResolvedValue( + api.validator.getAggregatedAttestationV2.mockResolvedValue( mockApiResponse({data: attestation, meta: {version: ForkName.phase0}}) ); @@ -151,7 +151,7 @@ describe("AttestationService", function () { expect(api.beacon.submitPoolAttestationsV2).toHaveBeenCalledOnce(); expect(api.beacon.submitPoolAttestationsV2).toHaveBeenCalledWith({signedAttestations: [attestation]}); - // Must submit the aggregate received through getAggregatedAttestation() then createAndSignAggregateAndProof() + // Must submit the aggregate received through getAggregatedAttestationV2() then createAndSignAggregateAndProof() expect(api.validator.publishAggregateAndProofs).toHaveBeenCalledOnce(); expect(api.validator.publishAggregateAndProofs).toHaveBeenCalledWith({signedAggregateAndProofs: [aggregate]}); }); diff --git a/packages/validator/test/utils/apiStub.ts b/packages/validator/test/utils/apiStub.ts index 397dc5fc8fe1..dd9e8ba6b66f 100644 --- a/packages/validator/test/utils/apiStub.ts +++ b/packages/validator/test/utils/apiStub.ts @@ -32,7 +32,7 @@ export function getApiClientStub(): ApiClientStub { publishContributionAndProofs: vi.fn(), submitSyncCommitteeSelections: vi.fn(), produceAttestationData: vi.fn(), - getAggregatedAttestation: vi.fn(), + getAggregatedAttestationV2: vi.fn(), publishAggregateAndProofs: vi.fn(), submitBeaconCommitteeSelections: vi.fn(), }, From ea6accecba1a7ba6297bb394983f60d25cabe46a Mon Sep 17 00:00:00 2001 From: Navie Chan Date: Mon, 15 Jul 2024 13:29:36 +0300 Subject: [PATCH 03/21] Lint --- packages/api/src/beacon/routes/validator.ts | 2 +- packages/beacon-node/src/api/impl/validator/index.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/api/src/beacon/routes/validator.ts b/packages/api/src/beacon/routes/validator.ts index d7f461eddd9e..5ae38af26bcd 100644 --- a/packages/api/src/beacon/routes/validator.ts +++ b/packages/api/src/beacon/routes/validator.ts @@ -833,7 +833,7 @@ export function getDefinitions(config: ChainForkConfig): RouteDefinitions Date: Mon, 15 Jul 2024 14:10:12 +0300 Subject: [PATCH 04/21] Fix minor flaw --- packages/api/src/beacon/routes/beacon/pool.ts | 2 +- .../beacon-node/src/api/impl/beacon/pool/index.ts | 11 +++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/packages/api/src/beacon/routes/beacon/pool.ts b/packages/api/src/beacon/routes/beacon/pool.ts index e2ba915d07e7..9b45160ab564 100644 --- a/packages/api/src/beacon/routes/beacon/pool.ts +++ b/packages/api/src/beacon/routes/beacon/pool.ts @@ -52,7 +52,7 @@ export type Endpoints = { "GET", {slot?: Slot; committeeIndex?: CommitteeIndex}, {query: {slot?: number; committee_index?: number}}, - AttestationList, + AttestationListPhase0, EmptyMeta >; diff --git a/packages/beacon-node/src/api/impl/beacon/pool/index.ts b/packages/beacon-node/src/api/impl/beacon/pool/index.ts index 0edc561dba63..3a6c53ee8f43 100644 --- a/packages/beacon-node/src/api/impl/beacon/pool/index.ts +++ b/packages/beacon-node/src/api/impl/beacon/pool/index.ts @@ -68,6 +68,10 @@ export function getBeaconPoolApi({ }, async submitPoolAttestations({signedAttestations}) { + await this.submitPoolAttestationsV2({signedAttestations}); + }, + + async submitPoolAttestationsV2({signedAttestations}) { const seenTimestampSec = Date.now() / 1000; const errors: Error[] = []; @@ -94,7 +98,7 @@ export function getBeaconPoolApi({ metrics?.opPool.attestationPoolInsertOutcome.inc({insertOutcome}); } - chain.emitter.emit(routes.events.EventType.attestation, {data: attestation, version: ForkName.phase0}); + chain.emitter.emit(routes.events.EventType.attestation, {data: attestation, version: fork}); const sentPeers = await network.publishBeaconAttestation(attestation, subnet); metrics?.onPoolSubmitUnaggregatedAttestation(seenTimestampSec, indexedAttestation, subnet, sentPeers); @@ -124,11 +128,6 @@ export function getBeaconPoolApi({ } }, - async submitPoolAttestationsV2({signedAttestations}) { - // TODO Electra: Refactor submitPoolAttestations and submitPoolAttestationsV2 - await this.submitPoolAttestations({signedAttestations}); - }, - async submitPoolAttesterSlashings({attesterSlashing}) { await validateApiAttesterSlashing(chain, attesterSlashing); chain.opPool.insertAttesterSlashing(attesterSlashing); From ecf21f2826b692117f3027eedd1e7f2744fb0c15 Mon Sep 17 00:00:00 2001 From: Navie Chan Date: Wed, 17 Jul 2024 14:22:05 +0900 Subject: [PATCH 05/21] Add publishAggregateAndProofsV2 --- packages/api/src/beacon/routes/validator.ts | 30 ++++++++++++++++++- .../test/unit/beacon/testData/validator.ts | 4 +++ .../src/api/impl/validator/index.ts | 4 +++ .../src/chain/validation/aggregateAndProof.ts | 2 +- .../validator/src/services/attestation.ts | 2 +- .../test/unit/services/attestation.test.ts | 6 ++-- packages/validator/test/utils/apiStub.ts | 2 +- 7 files changed, 43 insertions(+), 7 deletions(-) diff --git a/packages/api/src/beacon/routes/validator.ts b/packages/api/src/beacon/routes/validator.ts index 5ae38af26bcd..a01b7d66764d 100644 --- a/packages/api/src/beacon/routes/validator.ts +++ b/packages/api/src/beacon/routes/validator.ts @@ -432,6 +432,18 @@ export type Endpoints = { * Verifies given aggregate and proofs and publishes them on appropriate gossipsub topic. */ publishAggregateAndProofs: Endpoint< + "POST", + {signedAggregateAndProofs: SignedAggregateAndProofListPhase0}, + {body: unknown}, + EmptyResponseData, + EmptyMeta + >; + + /** + * Publish multiple aggregate and proofs + * Verifies given aggregate and proofs and publishes them on appropriate gossipsub topic. + */ + publishAggregateAndProofsV2: Endpoint< "POST", {signedAggregateAndProofs: SignedAggregateAndProofList}, {body: unknown; headers: {[MetaHeader.Version]: string}}, @@ -864,7 +876,23 @@ export function getDefinitions(config: ChainForkConfig): RouteDefinitions ({ + body: SignedAggregateAndProofListPhase0Type.toJson(signedAggregateAndProofs), + }), + parseReqJson: ({body}) => ({signedAggregateAndProofs: SignedAggregateAndProofListPhase0Type.fromJson(body)}), + writeReqSsz: ({signedAggregateAndProofs}) => ({body: SignedAggregateAndProofListPhase0Type.serialize(signedAggregateAndProofs)}), + parseReqSsz: ({body}) => ({signedAggregateAndProofs: SignedAggregateAndProofListPhase0Type.deserialize(body)}), + schema: { + body: Schema.ObjectArray, + }, + }, + resp: EmptyResponseCodec, + }, + publishAggregateAndProofsV2: { + url: "/eth/v2/validator/aggregate_and_proofs", method: "POST", req: { writeReqJson: ({signedAggregateAndProofs}) => { diff --git a/packages/api/test/unit/beacon/testData/validator.ts b/packages/api/test/unit/beacon/testData/validator.ts index 98379f934ba6..578513c18641 100644 --- a/packages/api/test/unit/beacon/testData/validator.ts +++ b/packages/api/test/unit/beacon/testData/validator.ts @@ -110,6 +110,10 @@ export const testData: GenericServerTestCases = { args: {signedAggregateAndProofs: [ssz.phase0.SignedAggregateAndProof.defaultValue()]}, res: undefined, }, + publishAggregateAndProofsV2: { + args: {signedAggregateAndProofs: [ssz.electra.SignedAggregateAndProof.defaultValue()]}, + res: undefined, + }, publishContributionAndProofs: { args: {contributionAndProofs: [ssz.altair.SignedContributionAndProof.defaultValue()]}, res: undefined, diff --git a/packages/beacon-node/src/api/impl/validator/index.ts b/packages/beacon-node/src/api/impl/validator/index.ts index 10746144edeb..574205e82926 100644 --- a/packages/beacon-node/src/api/impl/validator/index.ts +++ b/packages/beacon-node/src/api/impl/validator/index.ts @@ -1095,6 +1095,10 @@ export function getValidatorApi( }, async publishAggregateAndProofs({signedAggregateAndProofs}) { + await this.publishAggregateAndProofsV2({signedAggregateAndProofs}); + }, + + async publishAggregateAndProofsV2({signedAggregateAndProofs}) { notWhileSyncing(); const seenTimestampSec = Date.now() / 1000; diff --git a/packages/beacon-node/src/chain/validation/aggregateAndProof.ts b/packages/beacon-node/src/chain/validation/aggregateAndProof.ts index c2684131c4c4..c2c1680dc7f6 100644 --- a/packages/beacon-node/src/chain/validation/aggregateAndProof.ts +++ b/packages/beacon-node/src/chain/validation/aggregateAndProof.ts @@ -28,7 +28,7 @@ export type AggregateAndProofValidationResult = { export async function validateApiAggregateAndProof( fork: ForkName, chain: IBeaconChain, - signedAggregateAndProof: phase0.SignedAggregateAndProof + signedAggregateAndProof: SignedAggregateAndProof ): Promise { const skipValidationKnownAttesters = true; const prioritizeBls = true; diff --git a/packages/validator/src/services/attestation.ts b/packages/validator/src/services/attestation.ts index 4728f5525255..577454b3b878 100644 --- a/packages/validator/src/services/attestation.ts +++ b/packages/validator/src/services/attestation.ts @@ -289,7 +289,7 @@ export class AttestationService { if (signedAggregateAndProofs.length > 0) { try { - (await this.api.validator.publishAggregateAndProofs({signedAggregateAndProofs})).assertOk(); + (await this.api.validator.publishAggregateAndProofsV2({signedAggregateAndProofs})).assertOk(); this.logger.info("Published aggregateAndProofs", {...logCtx, count: signedAggregateAndProofs.length}); this.metrics?.publishedAggregates.inc(signedAggregateAndProofs.length); } catch (e) { diff --git a/packages/validator/test/unit/services/attestation.test.ts b/packages/validator/test/unit/services/attestation.test.ts index 34d921103fd7..7df7b49e7aa8 100644 --- a/packages/validator/test/unit/services/attestation.test.ts +++ b/packages/validator/test/unit/services/attestation.test.ts @@ -106,7 +106,7 @@ describe("AttestationService", function () { ); api.beacon.submitPoolAttestationsV2.mockResolvedValue(mockApiResponse({})); - api.validator.publishAggregateAndProofs.mockResolvedValue(mockApiResponse({})); + api.validator.publishAggregateAndProofsV2.mockResolvedValue(mockApiResponse({})); if (opts.distributedAggregationSelection) { // Mock distributed validator middleware client selections endpoint @@ -152,8 +152,8 @@ describe("AttestationService", function () { expect(api.beacon.submitPoolAttestationsV2).toHaveBeenCalledWith({signedAttestations: [attestation]}); // Must submit the aggregate received through getAggregatedAttestationV2() then createAndSignAggregateAndProof() - expect(api.validator.publishAggregateAndProofs).toHaveBeenCalledOnce(); - expect(api.validator.publishAggregateAndProofs).toHaveBeenCalledWith({signedAggregateAndProofs: [aggregate]}); + expect(api.validator.publishAggregateAndProofsV2).toHaveBeenCalledOnce(); + expect(api.validator.publishAggregateAndProofsV2).toHaveBeenCalledWith({signedAggregateAndProofs: [aggregate]}); }); }); } diff --git a/packages/validator/test/utils/apiStub.ts b/packages/validator/test/utils/apiStub.ts index dd9e8ba6b66f..b347ae4f59dd 100644 --- a/packages/validator/test/utils/apiStub.ts +++ b/packages/validator/test/utils/apiStub.ts @@ -33,7 +33,7 @@ export function getApiClientStub(): ApiClientStub { submitSyncCommitteeSelections: vi.fn(), produceAttestationData: vi.fn(), getAggregatedAttestationV2: vi.fn(), - publishAggregateAndProofs: vi.fn(), + V2: vi.fn(), submitBeaconCommitteeSelections: vi.fn(), }, httpClient: httpClientStub, From 1d2b1498f9bc02fca0f88ebe2dec6b0b831e519f Mon Sep 17 00:00:00 2001 From: Navie Chan Date: Wed, 17 Jul 2024 16:25:21 +0900 Subject: [PATCH 06/21] Fix spelling --- packages/api/src/beacon/routes/validator.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/api/src/beacon/routes/validator.ts b/packages/api/src/beacon/routes/validator.ts index a01b7d66764d..7e785f121820 100644 --- a/packages/api/src/beacon/routes/validator.ts +++ b/packages/api/src/beacon/routes/validator.ts @@ -422,7 +422,7 @@ export type Endpoints = { slot: Slot; committeeIndex: number; }, - {query: {attestation_data_root: string; slot: number; committeeIndex: number}}, + {query: {attestation_data_root: string; slot: number; committee_index: number}}, Attestation, VersionMeta >; @@ -853,18 +853,18 @@ export function getDefinitions(config: ChainForkConfig): RouteDefinitions ({ - query: {attestation_data_root: toHexString(attestationDataRoot), slot, committeeIndex}, + query: {attestation_data_root: toHexString(attestationDataRoot), slot, committee_index: committeeIndex}, }), parseReq: ({query}) => ({ attestationDataRoot: fromHexString(query.attestation_data_root), slot: query.slot, - committeeIndex: query.committeeIndex, + committeeIndex: query.committee_index, }), schema: { query: { attestation_data_root: Schema.StringRequired, slot: Schema.UintRequired, - committeeIndex: Schema.UintRequired, + committee_index: Schema.UintRequired, }, }, }, From a4fb02c100c839cec40bd52d2688c9cff2aa3c2a Mon Sep 17 00:00:00 2001 From: Navie Chan Date: Thu, 18 Jul 2024 04:12:28 +0900 Subject: [PATCH 07/21] Fix CI --- packages/api/src/beacon/routes/beacon/pool.ts | 2 +- packages/api/src/beacon/routes/validator.ts | 6 ++++-- packages/api/test/unit/beacon/testData/beacon.ts | 2 +- packages/api/test/unit/beacon/testData/validator.ts | 2 +- packages/validator/test/utils/apiStub.ts | 2 +- 5 files changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/api/src/beacon/routes/beacon/pool.ts b/packages/api/src/beacon/routes/beacon/pool.ts index 9b45160ab564..e2716cb86b0e 100644 --- a/packages/api/src/beacon/routes/beacon/pool.ts +++ b/packages/api/src/beacon/routes/beacon/pool.ts @@ -451,7 +451,7 @@ export function getDefinitions(config: ChainForkConfig): RouteDefinitions ({ body: SignedAggregateAndProofListPhase0Type.toJson(signedAggregateAndProofs), }), parseReqJson: ({body}) => ({signedAggregateAndProofs: SignedAggregateAndProofListPhase0Type.fromJson(body)}), - writeReqSsz: ({signedAggregateAndProofs}) => ({body: SignedAggregateAndProofListPhase0Type.serialize(signedAggregateAndProofs)}), + writeReqSsz: ({signedAggregateAndProofs}) => ({ + body: SignedAggregateAndProofListPhase0Type.serialize(signedAggregateAndProofs), + }), parseReqSsz: ({body}) => ({signedAggregateAndProofs: SignedAggregateAndProofListPhase0Type.deserialize(body)}), schema: { body: Schema.ObjectArray, diff --git a/packages/api/test/unit/beacon/testData/beacon.ts b/packages/api/test/unit/beacon/testData/beacon.ts index a1f8674bf1d3..ac47a58777eb 100644 --- a/packages/api/test/unit/beacon/testData/beacon.ts +++ b/packages/api/test/unit/beacon/testData/beacon.ts @@ -130,7 +130,7 @@ export const testData: GenericServerTestCases = { res: undefined, }, submitPoolAttestationsV2: { - args: {signedAttestations: [ssz.electra.Attestation.defaultValue()]}, + args: {signedAttestations: [ssz.phase0.Attestation.defaultValue()]}, res: undefined, }, submitPoolAttesterSlashings: { diff --git a/packages/api/test/unit/beacon/testData/validator.ts b/packages/api/test/unit/beacon/testData/validator.ts index 578513c18641..d4fae4bfe290 100644 --- a/packages/api/test/unit/beacon/testData/validator.ts +++ b/packages/api/test/unit/beacon/testData/validator.ts @@ -111,7 +111,7 @@ export const testData: GenericServerTestCases = { res: undefined, }, publishAggregateAndProofsV2: { - args: {signedAggregateAndProofs: [ssz.electra.SignedAggregateAndProof.defaultValue()]}, + args: {signedAggregateAndProofs: [ssz.phase0.SignedAggregateAndProof.defaultValue()]}, res: undefined, }, publishContributionAndProofs: { diff --git a/packages/validator/test/utils/apiStub.ts b/packages/validator/test/utils/apiStub.ts index b347ae4f59dd..508674811b2f 100644 --- a/packages/validator/test/utils/apiStub.ts +++ b/packages/validator/test/utils/apiStub.ts @@ -33,7 +33,7 @@ export function getApiClientStub(): ApiClientStub { submitSyncCommitteeSelections: vi.fn(), produceAttestationData: vi.fn(), getAggregatedAttestationV2: vi.fn(), - V2: vi.fn(), + publishAggregateAndProofsV2: vi.fn(), submitBeaconCommitteeSelections: vi.fn(), }, httpClient: httpClientStub, From d4686264a901033af18a2bb996821b27952e7232 Mon Sep 17 00:00:00 2001 From: Navie Chan Date: Thu, 18 Jul 2024 04:26:06 +0900 Subject: [PATCH 08/21] Fix spec test From da9fd8019c629dfab9eb1f2ed2e5c5f6af60cbbe Mon Sep 17 00:00:00 2001 From: Nico Flaig Date: Thu, 18 Jul 2024 13:30:27 +0100 Subject: [PATCH 09/21] Clean up events api --- packages/api/src/beacon/routes/events.ts | 8 +- .../api/test/unit/beacon/testData/events.ts | 98 +++++++++---------- 2 files changed, 50 insertions(+), 56 deletions(-) diff --git a/packages/api/src/beacon/routes/events.ts b/packages/api/src/beacon/routes/events.ts index e15790fd3bea..4c32289e3fe2 100644 --- a/packages/api/src/beacon/routes/events.ts +++ b/packages/api/src/beacon/routes/events.ts @@ -107,10 +107,10 @@ export type EventData = { block: RootHex; executionOptimistic: boolean; }; - [EventType.attestation]: {version: ForkName; data: Attestation}; + [EventType.attestation]: phase0.Attestation; [EventType.voluntaryExit]: phase0.SignedVoluntaryExit; [EventType.proposerSlashing]: phase0.ProposerSlashing; - [EventType.attesterSlashing]: {version: ForkName; data: AttesterSlashing}; + [EventType.attesterSlashing]: phase0.AttesterSlashing; [EventType.blsToExecutionChange]: capella.SignedBLSToExecutionChange; [EventType.finalizedCheckpoint]: { block: RootHex; @@ -228,10 +228,10 @@ export function getTypeByEvent(): {[K in EventType]: TypeJson} { {jsonCase: "eth2"} ), - [EventType.attestation]: WithVersion((fork) => sszTypesFor(fork).Attestation), + [EventType.attestation]: ssz.phase0.Attestation, [EventType.voluntaryExit]: ssz.phase0.SignedVoluntaryExit, [EventType.proposerSlashing]: ssz.phase0.ProposerSlashing, - [EventType.attesterSlashing]: WithVersion((fork) => sszTypesFor(fork).AttesterSlashing), + [EventType.attesterSlashing]: ssz.phase0.AttesterSlashing, [EventType.blsToExecutionChange]: ssz.capella.SignedBLSToExecutionChange, [EventType.finalizedCheckpoint]: new ContainerType( diff --git a/packages/api/test/unit/beacon/testData/events.ts b/packages/api/test/unit/beacon/testData/events.ts index 7966d0ea3b8a..8a7610a26836 100644 --- a/packages/api/test/unit/beacon/testData/events.ts +++ b/packages/api/test/unit/beacon/testData/events.ts @@ -31,21 +31,18 @@ export const eventTestData: EventData = { block: "0x9a2fefd2fdb57f74993c7780ea5b9030d2897b615b89f808011ca5aebed54eaf", executionOptimistic: false, }, - [EventType.attestation]: { - version: ForkName.altair, - data: ssz.phase0.Attestation.fromJson({ - aggregation_bits: "0x01", - signature: - "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505", - data: { - slot: "1", - index: "1", - beacon_block_root: "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", - source: {epoch: "1", root: "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"}, - target: {epoch: "1", root: "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"}, - }, - }), - }, + [EventType.attestation]: ssz.phase0.Attestation.fromJson({ + aggregation_bits: "0x01", + signature: + "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505", + data: { + slot: "1", + index: "1", + beacon_block_root: "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + source: {epoch: "1", root: "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"}, + target: {epoch: "1", root: "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"}, + }, + }), [EventType.voluntaryExit]: ssz.phase0.SignedVoluntaryExit.fromJson({ message: {epoch: "1", validator_index: "1"}, signature: @@ -75,47 +72,44 @@ export const eventTestData: EventData = { "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", }, }), - [EventType.attesterSlashing]: { - version: ForkName.altair, - data: ssz.phase0.AttesterSlashing.fromJson({ - attestation_1: { - attesting_indices: ["0", "1"], - data: { - slot: "0", - index: "0", - beacon_block_root: "0x0000000000000000000000000000000000000000000000000000000000000000", - source: { - epoch: "0", - root: "0x0000000000000000000000000000000000000000000000000000000000000000", - }, - target: { - epoch: "0", - root: "0x0000000000000000000000000000000000000000000000000000000000000000", - }, + [EventType.attesterSlashing]: ssz.phase0.AttesterSlashing.fromJson({ + attestation_1: { + attesting_indices: ["0", "1"], + data: { + slot: "0", + index: "0", + beacon_block_root: "0x0000000000000000000000000000000000000000000000000000000000000000", + source: { + epoch: "0", + root: "0x0000000000000000000000000000000000000000000000000000000000000000", + }, + target: { + epoch: "0", + root: "0x0000000000000000000000000000000000000000000000000000000000000000", }, - signature: - "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", }, - attestation_2: { - attesting_indices: ["0", "1"], - data: { - slot: "0", - index: "0", - beacon_block_root: "0x0000000000000000000000000000000000000000000000000000000000000000", - source: { - epoch: "0", - root: "0x0000000000000000000000000000000000000000000000000000000000000000", - }, - target: { - epoch: "0", - root: "0x0000000000000000000000000000000000000000000000000000000000000000", - }, + signature: + "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + }, + attestation_2: { + attesting_indices: ["0", "1"], + data: { + slot: "0", + index: "0", + beacon_block_root: "0x0000000000000000000000000000000000000000000000000000000000000000", + source: { + epoch: "0", + root: "0x0000000000000000000000000000000000000000000000000000000000000000", + }, + target: { + epoch: "0", + root: "0x0000000000000000000000000000000000000000000000000000000000000000", }, - signature: - "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", }, - }), - }, + signature: + "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + }, + }), [EventType.blsToExecutionChange]: ssz.capella.SignedBLSToExecutionChange.fromJson({ message: { validator_index: "1", From df119e873cc27cccd600893999102e031ce910ce Mon Sep 17 00:00:00 2001 From: Nico Flaig Date: Thu, 18 Jul 2024 13:30:52 +0100 Subject: [PATCH 10/21] Run against latest beacon api spec --- packages/api/test/unit/beacon/oapiSpec.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/api/test/unit/beacon/oapiSpec.test.ts b/packages/api/test/unit/beacon/oapiSpec.test.ts index 00f1a3d8731d..2ccf4720db27 100644 --- a/packages/api/test/unit/beacon/oapiSpec.test.ts +++ b/packages/api/test/unit/beacon/oapiSpec.test.ts @@ -21,9 +21,9 @@ import {testData as validatorTestData} from "./testData/validator.js"; // eslint-disable-next-line @typescript-eslint/naming-convention const __dirname = path.dirname(fileURLToPath(import.meta.url)); -const version = "v2.5.0"; +const version = "v2.6.0-alpha.1"; const openApiFile: OpenApiFile = { - url: `https://github.com/ethereum/beacon-APIs/releases/download/${version}/beacon-node-oapi.json`, + url: `https://raw.githubusercontent.com/nflaig/beacon-api-spec/main/${version}/beacon-node-oapi.json`, filepath: path.join(__dirname, "../../../oapi-schemas/beacon-node-oapi.json"), version: RegExp(version), }; From bd7ce70ec8364781af9ad2d87be2dddb89770fe0 Mon Sep 17 00:00:00 2001 From: Nico Flaig Date: Thu, 18 Jul 2024 13:45:24 +0100 Subject: [PATCH 11/21] Revert changes to emitted events --- packages/api/src/beacon/routes/events.ts | 3 --- .../beacon-node/src/api/impl/beacon/pool/index.ts | 2 +- packages/beacon-node/src/chain/blocks/importBlock.ts | 10 ++-------- .../src/network/processor/gossipHandlers.ts | 11 ++++------- .../test/unit/api/impl/events/events.test.ts | 6 +----- 5 files changed, 8 insertions(+), 24 deletions(-) diff --git a/packages/api/src/beacon/routes/events.ts b/packages/api/src/beacon/routes/events.ts index 4c32289e3fe2..23be5e7c2288 100644 --- a/packages/api/src/beacon/routes/events.ts +++ b/packages/api/src/beacon/routes/events.ts @@ -13,9 +13,6 @@ import { LightClientOptimisticUpdate, LightClientFinalityUpdate, SSEPayloadAttributes, - Attestation, - AttesterSlashing, - sszTypesFor, } from "@lodestar/types"; import {ForkName} from "@lodestar/params"; diff --git a/packages/beacon-node/src/api/impl/beacon/pool/index.ts b/packages/beacon-node/src/api/impl/beacon/pool/index.ts index 3a6c53ee8f43..6f4d84ab659c 100644 --- a/packages/beacon-node/src/api/impl/beacon/pool/index.ts +++ b/packages/beacon-node/src/api/impl/beacon/pool/index.ts @@ -98,7 +98,7 @@ export function getBeaconPoolApi({ metrics?.opPool.attestationPoolInsertOutcome.inc({insertOutcome}); } - chain.emitter.emit(routes.events.EventType.attestation, {data: attestation, version: fork}); + chain.emitter.emit(routes.events.EventType.attestation, attestation); const sentPeers = await network.publishBeaconAttestation(attestation, subnet); metrics?.onPoolSubmitUnaggregatedAttestation(seenTimestampSec, indexedAttestation, subnet, sentPeers); diff --git a/packages/beacon-node/src/chain/blocks/importBlock.ts b/packages/beacon-node/src/chain/blocks/importBlock.ts index c01543289b07..a7d5451daca6 100644 --- a/packages/beacon-node/src/chain/blocks/importBlock.ts +++ b/packages/beacon-node/src/chain/blocks/importBlock.ts @@ -428,18 +428,12 @@ export async function importBlock( } if (this.emitter.listenerCount(routes.events.EventType.attestation)) { for (const attestation of block.message.body.attestations) { - this.emitter.emit(routes.events.EventType.attestation, { - version: this.config.getForkName(blockSlot), - data: attestation, - }); + this.emitter.emit(routes.events.EventType.attestation, attestation); } } if (this.emitter.listenerCount(routes.events.EventType.attesterSlashing)) { for (const attesterSlashing of block.message.body.attesterSlashings) { - this.emitter.emit(routes.events.EventType.attesterSlashing, { - version: this.config.getForkName(blockSlot), - data: attesterSlashing, - }); + this.emitter.emit(routes.events.EventType.attesterSlashing, attesterSlashing); } } if (this.emitter.listenerCount(routes.events.EventType.proposerSlashing)) { diff --git a/packages/beacon-node/src/network/processor/gossipHandlers.ts b/packages/beacon-node/src/network/processor/gossipHandlers.ts index 01255719bbb7..c9a7d20a0b66 100644 --- a/packages/beacon-node/src/network/processor/gossipHandlers.ts +++ b/packages/beacon-node/src/network/processor/gossipHandlers.ts @@ -455,10 +455,7 @@ function getDefaultHandlers(modules: ValidatorFnsModules, options: GossipHandler } } - chain.emitter.emit(routes.events.EventType.attestation, { - version: fork, - data: signedAggregateAndProof.message.aggregate, - }); + chain.emitter.emit(routes.events.EventType.attestation, signedAggregateAndProof.message.aggregate); }, [GossipType.beacon_attestation]: async ({ gossipData, @@ -510,7 +507,7 @@ function getDefaultHandlers(modules: ValidatorFnsModules, options: GossipHandler } } - chain.emitter.emit(routes.events.EventType.attestation, {version: fork, data: attestation}); + chain.emitter.emit(routes.events.EventType.attestation, attestation); }, [GossipType.attester_slashing]: async ({ @@ -530,7 +527,7 @@ function getDefaultHandlers(modules: ValidatorFnsModules, options: GossipHandler logger.error("Error adding attesterSlashing to pool", {}, e as Error); } - chain.emitter.emit(routes.events.EventType.attesterSlashing, {version: topic.fork, data: attesterSlashing}); + chain.emitter.emit(routes.events.EventType.attesterSlashing, attesterSlashing); }, [GossipType.proposer_slashing]: async ({ @@ -718,7 +715,7 @@ function getBatchHandlers(modules: ValidatorFnsModules, options: GossipHandlerOp } } - chain.emitter.emit(routes.events.EventType.attestation, {version: fork, data: attestation}); + chain.emitter.emit(routes.events.EventType.attestation, attestation); } if (batchableBls) { diff --git a/packages/beacon-node/test/unit/api/impl/events/events.test.ts b/packages/beacon-node/test/unit/api/impl/events/events.test.ts index b1f85b5e6e44..e031c3ac9958 100644 --- a/packages/beacon-node/test/unit/api/impl/events/events.test.ts +++ b/packages/beacon-node/test/unit/api/impl/events/events.test.ts @@ -2,7 +2,6 @@ import {describe, it, expect, beforeEach, afterEach, vi, MockedObject} from "vit import {routes} from "@lodestar/api"; import {config} from "@lodestar/config/default"; import {ssz} from "@lodestar/types"; -import {ForkName} from "@lodestar/params"; import {BeaconChain, ChainEventEmitter, HeadEventData} from "../../../../../src/chain/index.js"; import {getEventsApi} from "../../../../../src/api/impl/events/index.js"; import {ZERO_HASH_HEX} from "../../../../../src/constants/constants.js"; @@ -67,10 +66,7 @@ describe("Events api impl", function () { it("should ignore not sent topics", async function () { const events = getEvents([routes.events.EventType.head]); - chainEventEmmitter.emit(routes.events.EventType.attestation, { - version: ForkName.phase0, - data: ssz.phase0.Attestation.defaultValue(), - }); + chainEventEmmitter.emit(routes.events.EventType.attestation, ssz.phase0.Attestation.defaultValue()); chainEventEmmitter.emit(routes.events.EventType.head, headEventData); expect(events).toHaveLength(1); From 2556d42df3bba6e44df5447d79f4040aa38d300e Mon Sep 17 00:00:00 2001 From: NC <17676176+ensi321@users.noreply.github.com> Date: Mon, 22 Jul 2024 22:33:01 +0900 Subject: [PATCH 12/21] Update packages/api/src/beacon/routes/beacon/pool.ts Co-authored-by: Nico Flaig --- packages/api/src/beacon/routes/beacon/pool.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/api/src/beacon/routes/beacon/pool.ts b/packages/api/src/beacon/routes/beacon/pool.ts index e2716cb86b0e..866e7f0d74d6 100644 --- a/packages/api/src/beacon/routes/beacon/pool.ts +++ b/packages/api/src/beacon/routes/beacon/pool.ts @@ -34,9 +34,9 @@ export type AttestationListPhase0 = ValueOf; type AttestationListElectra = ValueOf; export type AttestationList = AttestationListPhase0 | AttestationListElectra; -type AttesterSlashingPhase0 = ValueOf; -type AttesterSlashingElectra = ValueOf; -type AttesterSlashingList = AttesterSlashingPhase0 | AttesterSlashingElectra; +type AttesterSlashingListPhase0 = ValueOf; +type AttesterSlashingListElectra = ValueOf; +type AttesterSlashingList = AttesterSlashingListPhase0 | AttesterSlashingListElectra; type ProposerSlashingList = ValueOf; type SignedVoluntaryExitList = ValueOf; From 88e593e6de65666752817c963cfbefe9769c3080 Mon Sep 17 00:00:00 2001 From: NC <17676176+ensi321@users.noreply.github.com> Date: Mon, 22 Jul 2024 22:37:46 +0900 Subject: [PATCH 13/21] Update packages/api/src/beacon/routes/beacon/pool.ts Co-authored-by: Nico Flaig --- packages/api/src/beacon/routes/beacon/pool.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/api/src/beacon/routes/beacon/pool.ts b/packages/api/src/beacon/routes/beacon/pool.ts index 866e7f0d74d6..d9c9f2d9f7b3 100644 --- a/packages/api/src/beacon/routes/beacon/pool.ts +++ b/packages/api/src/beacon/routes/beacon/pool.ts @@ -77,7 +77,7 @@ export type Endpoints = { "GET", EmptyArgs, EmptyRequest, - AttesterSlashingPhase0, + AttesterSlashingListPhase0, EmptyMeta >; From dadc6b9e76877143066758fbb15425cf60231570 Mon Sep 17 00:00:00 2001 From: NC <17676176+ensi321@users.noreply.github.com> Date: Mon, 22 Jul 2024 22:39:13 +0900 Subject: [PATCH 14/21] Update packages/api/src/beacon/routes/beacon/pool.ts Co-authored-by: Nico Flaig --- packages/api/src/beacon/routes/beacon/pool.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/api/src/beacon/routes/beacon/pool.ts b/packages/api/src/beacon/routes/beacon/pool.ts index d9c9f2d9f7b3..388c98fb36d1 100644 --- a/packages/api/src/beacon/routes/beacon/pool.ts +++ b/packages/api/src/beacon/routes/beacon/pool.ts @@ -409,8 +409,8 @@ export function getDefinitions(config: ChainForkConfig): RouteDefinitions= ForkSeq.electra - ? ssz.electra.AttesterSlashing.toJson(attesterSlashing as electra.AttesterSlashing) - : ssz.phase0.AttesterSlashing.toJson(attesterSlashing as phase0.AttesterSlashing), + ? ssz.electra.AttesterSlashing.toJson(attesterSlashing) + : ssz.phase0.AttesterSlashing.toJson(attesterSlashing), headers: {[MetaHeader.Version]: fork}, }; }, From 0d3e2bd358d0e2e8d35f948f3c717d1bcda4559c Mon Sep 17 00:00:00 2001 From: NC <17676176+ensi321@users.noreply.github.com> Date: Mon, 22 Jul 2024 22:39:31 +0900 Subject: [PATCH 15/21] Update packages/api/src/beacon/routes/beacon/pool.ts Co-authored-by: Nico Flaig --- packages/api/src/beacon/routes/beacon/pool.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/api/src/beacon/routes/beacon/pool.ts b/packages/api/src/beacon/routes/beacon/pool.ts index 388c98fb36d1..2f4ffcbb1c67 100644 --- a/packages/api/src/beacon/routes/beacon/pool.ts +++ b/packages/api/src/beacon/routes/beacon/pool.ts @@ -183,7 +183,7 @@ export type Endpoints = { */ submitPoolAttesterSlashingsV2: Endpoint< "POST", - {attesterSlashing: phase0.AttesterSlashing | electra.AttesterSlashing}, + {attesterSlashing: AttesterSlashing}, {body: unknown; headers: {[MetaHeader.Version]: string}}, EmptyResponseData, EmptyMeta From c657fd9e7e69cc522b0f3319c688e7714bdef10c Mon Sep 17 00:00:00 2001 From: NC <17676176+ensi321@users.noreply.github.com> Date: Mon, 22 Jul 2024 23:56:15 +0900 Subject: [PATCH 16/21] Address comment --- .../api/src/beacon/routes/beacon/block.ts | 8 +++--- packages/api/src/beacon/routes/beacon/pool.ts | 28 ++++++------------- packages/api/src/beacon/routes/validator.ts | 11 ++------ .../src/api/impl/beacon/pool/index.ts | 2 +- packages/validator/test/utils/apiStub.ts | 1 + 5 files changed, 17 insertions(+), 33 deletions(-) diff --git a/packages/api/src/beacon/routes/beacon/block.ts b/packages/api/src/beacon/routes/beacon/block.ts index 7c6e9c67c56b..2c141e3bdefd 100644 --- a/packages/api/src/beacon/routes/beacon/block.ts +++ b/packages/api/src/beacon/routes/beacon/block.ts @@ -12,8 +12,9 @@ import { SignedBlindedBeaconBlock, SignedBlockContents, sszTypesFor, + BeaconBlockBody, } from "@lodestar/types"; -import {ForkName, ForkPreExecution, isForkBlobs, isForkExecution} from "@lodestar/params"; +import {ForkName, ForkPreElectra, ForkPreExecution, isForkBlobs, isForkExecution} from "@lodestar/params"; import {Endpoint, RequestCodec, RouteDefinitions, Schema} from "../../../utils/index.js"; import {EmptyMeta, EmptyResponseCodec, EmptyResponseData, WithVersion} from "../../../utils/codecs.js"; import { @@ -26,7 +27,6 @@ import { import {getExecutionForkTypes, toForkName} from "../../../utils/fork.js"; import {fromHeaders} from "../../../utils/headers.js"; import {WireFormat} from "../../../utils/wireFormat.js"; -import {AttestationList, AttestationListPhase0} from "./pool.js"; // See /packages/api/src/routes/index.ts for reasoning and instructions to add new routes @@ -101,7 +101,7 @@ export type Endpoints = { "GET", BlockArgs, {params: {block_id: string}}, - AttestationListPhase0, + BeaconBlockBody["attestations"], ExecutionOptimisticAndFinalizedMeta >; @@ -113,7 +113,7 @@ export type Endpoints = { "GET", BlockArgs, {params: {block_id: string}}, - AttestationList, + BeaconBlockBody["attestations"], ExecutionOptimisticFinalizedAndVersionMeta >; diff --git a/packages/api/src/beacon/routes/beacon/pool.ts b/packages/api/src/beacon/routes/beacon/pool.ts index 2f4ffcbb1c67..497bdfe98a17 100644 --- a/packages/api/src/beacon/routes/beacon/pool.ts +++ b/packages/api/src/beacon/routes/beacon/pool.ts @@ -1,8 +1,8 @@ /* eslint-disable @typescript-eslint/naming-convention */ import {ValueOf} from "@chainsafe/ssz"; import {ChainForkConfig} from "@lodestar/config"; -import {ForkSeq} from "@lodestar/params"; -import {phase0, capella, CommitteeIndex, Slot, ssz, electra} from "@lodestar/types"; +import {ForkPreElectra, ForkSeq, isForkElectra} from "@lodestar/params"; +import {phase0, capella, CommitteeIndex, Slot, ssz, electra, AttesterSlashing} from "@lodestar/types"; import {Schema, Endpoint, RouteDefinitions} from "../../../utils/index.js"; import { ArrayOf, @@ -171,7 +171,7 @@ export type Endpoints = { */ submitPoolAttesterSlashings: Endpoint< "POST", - {attesterSlashing: phase0.AttesterSlashing}, + {attesterSlashing: AttesterSlashing}, {body: unknown}, EmptyResponseData, EmptyMeta @@ -262,9 +262,7 @@ export function getDefinitions(config: ChainForkConfig): RouteDefinitions - ForkSeq[fork] >= ForkSeq.electra ? AttestationListTypeElectra : AttestationListTypePhase0 - ), + data: WithVersion((fork) => (isForkElectra(fork) ? AttestationListTypeElectra : AttestationListTypePhase0)), meta: VersionCodec, }, }, @@ -283,7 +281,7 @@ export function getDefinitions(config: ChainForkConfig): RouteDefinitions - ForkSeq[fork] >= ForkSeq.electra ? AttesterSlashingListTypeElectra : AttesterSlashingListTypePhase0 + isForkElectra(fork) ? AttesterSlashingListTypeElectra : AttesterSlashingListTypePhase0 ), meta: VersionCodec, }, @@ -346,11 +344,8 @@ export function getDefinitions(config: ChainForkConfig): RouteDefinitions { - const versionHeader = fromHeaders(headers, MetaHeader.Version, false); - const fork = - versionHeader !== undefined - ? toForkName(versionHeader) - : config.getForkName(Number((body as {data: {slot: string}}[])[0]?.data.slot ?? 0)); + const versionHeader = fromHeaders(headers, MetaHeader.Version, true); + const fork = toForkName(versionHeader); return { signedAttestations: @@ -415,13 +410,8 @@ export function getDefinitions(config: ChainForkConfig): RouteDefinitions { - const versionHeader = fromHeaders(headers, MetaHeader.Version, false); - const fork = - versionHeader !== undefined - ? toForkName(versionHeader) - : config.getForkName( - Number((body as {attestations1: {data: {slot: string}}})?.attestations1.data.slot ?? 0) - ); + const versionHeader = fromHeaders(headers, MetaHeader.Version, true); + const fork = toForkName(versionHeader); return { attesterSlashing: diff --git a/packages/api/src/beacon/routes/validator.ts b/packages/api/src/beacon/routes/validator.ts index 9ce7595332bd..510160b2e027 100644 --- a/packages/api/src/beacon/routes/validator.ts +++ b/packages/api/src/beacon/routes/validator.ts @@ -912,15 +912,8 @@ export function getDefinitions(config: ChainForkConfig): RouteDefinitions { - const versionHeader = fromHeaders(headers, MetaHeader.Version, false); - const fork = - versionHeader !== undefined - ? toForkName(versionHeader) - : config.getForkName( - Number( - (body as {message: {aggregate: {data: {slot: string}}}}[])[0]?.message.aggregate.data.slot ?? 0 - ) - ); + const versionHeader = fromHeaders(headers, MetaHeader.Version, true); + const fork = toForkName(versionHeader); return { signedAggregateAndProofs: diff --git a/packages/beacon-node/src/api/impl/beacon/pool/index.ts b/packages/beacon-node/src/api/impl/beacon/pool/index.ts index 6f4d84ab659c..5a66ebfe174e 100644 --- a/packages/beacon-node/src/api/impl/beacon/pool/index.ts +++ b/packages/beacon-node/src/api/impl/beacon/pool/index.ts @@ -38,7 +38,7 @@ export function getBeaconPoolApi({ async getPoolAttestationsV2({slot, committeeIndex}) { // Already filtered by slot let attestations = chain.aggregatedAttestationPool.getAll(slot); - const fork = chain.config.getForkName(slot ?? attestations[0].data.slot) ?? ForkName.phase0; + const fork = chain.config.getForkName(slot ?? attestations[0]?.data.slot ?? chain.clock.currentSlot); if (committeeIndex !== undefined) { attestations = attestations.filter((attestation) => committeeIndex === attestation.data.index); diff --git a/packages/validator/test/utils/apiStub.ts b/packages/validator/test/utils/apiStub.ts index 508674811b2f..839e273a3c10 100644 --- a/packages/validator/test/utils/apiStub.ts +++ b/packages/validator/test/utils/apiStub.ts @@ -19,6 +19,7 @@ export function getApiClientStub(): ApiClientStub { publishBlindedBlockV2: vi.fn(), publishBlockV2: vi.fn(), submitPoolSyncCommitteeSignatures: vi.fn(), + submitPoolAttestations: vi.fn(), submitPoolAttestationsV2: vi.fn(), }, validator: { From e4f52b4cb79bac3f16cf3e34ae17e985664c8aa9 Mon Sep 17 00:00:00 2001 From: NC <17676176+ensi321@users.noreply.github.com> Date: Tue, 23 Jul 2024 01:34:11 +0900 Subject: [PATCH 17/21] Add api stub back --- packages/validator/test/utils/apiStub.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/validator/test/utils/apiStub.ts b/packages/validator/test/utils/apiStub.ts index 839e273a3c10..382d7b18266a 100644 --- a/packages/validator/test/utils/apiStub.ts +++ b/packages/validator/test/utils/apiStub.ts @@ -33,7 +33,9 @@ export function getApiClientStub(): ApiClientStub { publishContributionAndProofs: vi.fn(), submitSyncCommitteeSelections: vi.fn(), produceAttestationData: vi.fn(), + getAggregatedAttestation: vi.fn(), getAggregatedAttestationV2: vi.fn(), + publishAggregateAndProofs: vi.fn(), publishAggregateAndProofsV2: vi.fn(), submitBeaconCommitteeSelections: vi.fn(), }, From 9b9836e426228461e04e9cae790401c5adeed809 Mon Sep 17 00:00:00 2001 From: NC <17676176+ensi321@users.noreply.github.com> Date: Tue, 23 Jul 2024 20:45:13 +0900 Subject: [PATCH 18/21] Add todos --- packages/api/src/beacon/routes/beacon/pool.ts | 4 ++-- packages/beacon-node/src/api/impl/validator/index.ts | 1 + packages/validator/src/services/attestation.ts | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/api/src/beacon/routes/beacon/pool.ts b/packages/api/src/beacon/routes/beacon/pool.ts index 497bdfe98a17..4b512c242465 100644 --- a/packages/api/src/beacon/routes/beacon/pool.ts +++ b/packages/api/src/beacon/routes/beacon/pool.ts @@ -30,9 +30,9 @@ const SignedVoluntaryExitListType = ArrayOf(ssz.phase0.SignedVoluntaryExit); const SignedBLSToExecutionChangeListType = ArrayOf(ssz.capella.SignedBLSToExecutionChange); const SyncCommitteeMessageListType = ArrayOf(ssz.altair.SyncCommitteeMessage); -export type AttestationListPhase0 = ValueOf; +type AttestationListPhase0 = ValueOf; type AttestationListElectra = ValueOf; -export type AttestationList = AttestationListPhase0 | AttestationListElectra; +type AttestationList = AttestationListPhase0 | AttestationListElectra; type AttesterSlashingListPhase0 = ValueOf; type AttesterSlashingListElectra = ValueOf; diff --git a/packages/beacon-node/src/api/impl/validator/index.ts b/packages/beacon-node/src/api/impl/validator/index.ts index 574205e82926..543831c778a6 100644 --- a/packages/beacon-node/src/api/impl/validator/index.ts +++ b/packages/beacon-node/src/api/impl/validator/index.ts @@ -1067,6 +1067,7 @@ export function getValidatorApi( }; }, + // TODO Electra: Implement getAggregatedAttestation to properly handle pre-electra async getAggregatedAttestation() { throw new Error("Not implemented. Use getAggregatedAttestationV2 for now."); }, diff --git a/packages/validator/src/services/attestation.ts b/packages/validator/src/services/attestation.ts index 577454b3b878..cadcef04758c 100644 --- a/packages/validator/src/services/attestation.ts +++ b/packages/validator/src/services/attestation.ts @@ -225,6 +225,7 @@ export class AttestationService { ...(this.opts?.disableAttestationGrouping && {index: attestationNoCommittee.index}), }; try { + // TODO Electra: Ensure calling V2 works in pre-electra (await this.api.beacon.submitPoolAttestationsV2({signedAttestations})).assertOk(); this.logger.info("Published attestations", {...logCtx, count: signedAttestations.length}); this.metrics?.publishedAttestations.inc(signedAttestations.length); From c9394c6cd088602f6f471b45c6452d2a9232c0bc Mon Sep 17 00:00:00 2001 From: Nico Flaig Date: Tue, 30 Jul 2024 13:04:07 +0100 Subject: [PATCH 19/21] Review PR --- packages/api/src/beacon/routes/beacon/pool.ts | 78 ++++++++----------- packages/api/src/beacon/routes/validator.ts | 63 +++++++-------- .../src/api/impl/beacon/pool/index.ts | 1 + .../src/api/impl/validator/index.ts | 2 +- .../src/chain/opPools/attestationPool.ts | 2 +- 5 files changed, 62 insertions(+), 84 deletions(-) diff --git a/packages/api/src/beacon/routes/beacon/pool.ts b/packages/api/src/beacon/routes/beacon/pool.ts index 4b512c242465..aa2c36d3bc92 100644 --- a/packages/api/src/beacon/routes/beacon/pool.ts +++ b/packages/api/src/beacon/routes/beacon/pool.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/naming-convention */ import {ValueOf} from "@chainsafe/ssz"; import {ChainForkConfig} from "@lodestar/config"; -import {ForkPreElectra, ForkSeq, isForkElectra} from "@lodestar/params"; +import {isForkElectra} from "@lodestar/params"; import {phase0, capella, CommitteeIndex, Slot, ssz, electra, AttesterSlashing} from "@lodestar/types"; import {Schema, Endpoint, RouteDefinitions} from "../../../utils/index.js"; import { @@ -171,7 +171,7 @@ export type Endpoints = { */ submitPoolAttesterSlashings: Endpoint< "POST", - {attesterSlashing: AttesterSlashing}, + {attesterSlashing: phase0.AttesterSlashing}, {body: unknown}, EmptyResponseData, EmptyMeta @@ -334,44 +334,37 @@ export function getDefinitions(config: ChainForkConfig): RouteDefinitions { - const fork = config.getForkName(signedAttestations[0].data.slot); + const fork = config.getForkName(signedAttestations[0]?.data.slot ?? 0); return { - body: - ForkSeq[fork] >= ForkSeq.electra - ? AttestationListTypeElectra.toJson(signedAttestations as AttestationListElectra) - : AttestationListTypePhase0.toJson(signedAttestations as AttestationListPhase0), + body: isForkElectra(fork) + ? AttestationListTypeElectra.toJson(signedAttestations as AttestationListElectra) + : AttestationListTypePhase0.toJson(signedAttestations as AttestationListPhase0), headers: {[MetaHeader.Version]: fork}, }; }, parseReqJson: ({body, headers}) => { - const versionHeader = fromHeaders(headers, MetaHeader.Version, true); - const fork = toForkName(versionHeader); - + const fork = toForkName(fromHeaders(headers, MetaHeader.Version)); return { - signedAttestations: - ForkSeq[fork] >= ForkSeq.electra - ? AttestationListTypeElectra.fromJson(body) - : AttestationListTypePhase0.fromJson(body), + signedAttestations: isForkElectra(fork) + ? AttestationListTypeElectra.fromJson(body) + : AttestationListTypePhase0.fromJson(body), }; }, writeReqSsz: ({signedAttestations}) => { - const fork = config.getForkName(signedAttestations[0].data.slot); + const fork = config.getForkName(signedAttestations[0]?.data.slot ?? 0); return { - body: - ForkSeq[fork] >= ForkSeq.electra - ? AttestationListTypeElectra.serialize(signedAttestations as AttestationListElectra) - : AttestationListTypePhase0.serialize(signedAttestations as AttestationListPhase0), + body: isForkElectra(fork) + ? AttestationListTypeElectra.serialize(signedAttestations as AttestationListElectra) + : AttestationListTypePhase0.serialize(signedAttestations as AttestationListPhase0), headers: {[MetaHeader.Version]: fork}, }; }, parseReqSsz: ({body, headers}) => { - const versionHeader = fromHeaders(headers, MetaHeader.Version, true); - const fork = toForkName(versionHeader); + const fork = toForkName(fromHeaders(headers, MetaHeader.Version)); return { - signedAttestations: - ForkSeq[fork] >= ForkSeq.electra - ? AttestationListTypeElectra.deserialize(body) - : AttestationListTypePhase0.deserialize(body), + signedAttestations: isForkElectra(fork) + ? AttestationListTypeElectra.deserialize(body) + : AttestationListTypePhase0.deserialize(body), }; }, schema: { @@ -402,42 +395,35 @@ export function getDefinitions(config: ChainForkConfig): RouteDefinitions { const fork = config.getForkName(Number(attesterSlashing.attestation1.data.slot)); return { - body: - ForkSeq[fork] >= ForkSeq.electra - ? ssz.electra.AttesterSlashing.toJson(attesterSlashing) - : ssz.phase0.AttesterSlashing.toJson(attesterSlashing), + body: isForkElectra(fork) + ? ssz.electra.AttesterSlashing.toJson(attesterSlashing) + : ssz.phase0.AttesterSlashing.toJson(attesterSlashing), headers: {[MetaHeader.Version]: fork}, }; }, parseReqJson: ({body, headers}) => { - const versionHeader = fromHeaders(headers, MetaHeader.Version, true); - const fork = toForkName(versionHeader); - + const fork = toForkName(fromHeaders(headers, MetaHeader.Version)); return { - attesterSlashing: - ForkSeq[fork] >= ForkSeq.electra - ? ssz.electra.AttesterSlashing.fromJson(body) - : ssz.phase0.AttesterSlashing.fromJson(body), + attesterSlashing: isForkElectra(fork) + ? ssz.electra.AttesterSlashing.fromJson(body) + : ssz.phase0.AttesterSlashing.fromJson(body), }; }, writeReqSsz: ({attesterSlashing}) => { const fork = config.getForkName(Number(attesterSlashing.attestation1.data.slot)); return { - body: - ForkSeq[fork] >= ForkSeq.electra - ? ssz.electra.AttesterSlashing.serialize(attesterSlashing as electra.AttesterSlashing) - : ssz.electra.AttesterSlashing.serialize(attesterSlashing as phase0.AttesterSlashing), + body: isForkElectra(fork) + ? ssz.electra.AttesterSlashing.serialize(attesterSlashing as electra.AttesterSlashing) + : ssz.electra.AttesterSlashing.serialize(attesterSlashing as phase0.AttesterSlashing), headers: {[MetaHeader.Version]: fork}, }; }, parseReqSsz: ({body, headers}) => { - const versionHeader = fromHeaders(headers, MetaHeader.Version, true); - const fork = toForkName(versionHeader); + const fork = toForkName(fromHeaders(headers, MetaHeader.Version)); return { - attesterSlashing: - ForkSeq[fork] >= ForkSeq.electra - ? ssz.electra.AttesterSlashing.deserialize(body) - : ssz.phase0.AttesterSlashing.deserialize(body), + attesterSlashing: isForkElectra(fork) + ? ssz.electra.AttesterSlashing.deserialize(body) + : ssz.phase0.AttesterSlashing.deserialize(body), }; }, schema: { diff --git a/packages/api/src/beacon/routes/validator.ts b/packages/api/src/beacon/routes/validator.ts index 510160b2e027..ed7c0fd9ba0a 100644 --- a/packages/api/src/beacon/routes/validator.ts +++ b/packages/api/src/beacon/routes/validator.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/naming-convention */ import {ContainerType, fromHexString, toHexString, Type, ValueOf} from "@chainsafe/ssz"; import {ChainForkConfig} from "@lodestar/config"; -import {isForkBlobs, ForkSeq} from "@lodestar/params"; +import {isForkBlobs, isForkElectra} from "@lodestar/params"; import { altair, BLSSignature, @@ -203,7 +203,7 @@ export const AttesterDutyListType = ArrayOf(AttesterDutyType); export const ProposerDutyListType = ArrayOf(ProposerDutyType); export const SyncDutyListType = ArrayOf(SyncDutyType); export const SignedAggregateAndProofListPhase0Type = ArrayOf(ssz.phase0.SignedAggregateAndProof); -export const SignedAggregateAndProofListElectaType = ArrayOf(ssz.electra.SignedAggregateAndProof); +export const SignedAggregateAndProofListElectraType = ArrayOf(ssz.electra.SignedAggregateAndProof); export const SignedContributionAndProofListType = ArrayOf(ssz.altair.SignedContributionAndProof); export const BeaconCommitteeSubscriptionListType = ArrayOf(BeaconCommitteeSubscriptionType); export const SyncCommitteeSubscriptionListType = ArrayOf(SyncCommitteeSubscriptionType); @@ -221,8 +221,8 @@ export type ProposerDutyList = ValueOf; export type SyncDuty = ValueOf; export type SyncDutyList = ValueOf; export type SignedAggregateAndProofListPhase0 = ValueOf; -export type SignedAggregateAndProofListElecta = ValueOf; -export type SignedAggregateAndProofList = SignedAggregateAndProofListPhase0 | SignedAggregateAndProofListElecta; +export type SignedAggregateAndProofListElectra = ValueOf; +export type SignedAggregateAndProofList = SignedAggregateAndProofListPhase0 | SignedAggregateAndProofListElectra; export type SignedContributionAndProofList = ValueOf; export type BeaconCommitteeSubscription = ValueOf; export type BeaconCommitteeSubscriptionList = ValueOf; @@ -869,9 +869,7 @@ export function getDefinitions(config: ChainForkConfig): RouteDefinitions - ForkSeq[fork] >= ForkSeq.electra ? ssz.electra.Attestation : ssz.phase0.Attestation - ), + data: WithVersion((fork) => (isForkElectra(fork) ? ssz.electra.Attestation : ssz.phase0.Attestation)), meta: VersionCodec, }, }, @@ -900,50 +898,43 @@ export function getDefinitions(config: ChainForkConfig): RouteDefinitions { const fork = config.getForkName(signedAggregateAndProofs[0]?.message.aggregate.data.slot ?? 0); return { - body: - ForkSeq[fork] >= ForkSeq.electra - ? SignedAggregateAndProofListElectaType.toJson( - signedAggregateAndProofs as SignedAggregateAndProofListElecta - ) - : SignedAggregateAndProofListPhase0Type.toJson( - signedAggregateAndProofs as SignedAggregateAndProofListPhase0 - ), + body: isForkElectra(fork) + ? SignedAggregateAndProofListElectraType.toJson( + signedAggregateAndProofs as SignedAggregateAndProofListElectra + ) + : SignedAggregateAndProofListPhase0Type.toJson( + signedAggregateAndProofs as SignedAggregateAndProofListPhase0 + ), headers: {[MetaHeader.Version]: fork}, }; }, parseReqJson: ({body, headers}) => { - const versionHeader = fromHeaders(headers, MetaHeader.Version, true); - const fork = toForkName(versionHeader); - + const fork = toForkName(fromHeaders(headers, MetaHeader.Version)); return { - signedAggregateAndProofs: - ForkSeq[fork] >= ForkSeq.electra - ? SignedAggregateAndProofListElectaType.fromJson(body) - : SignedAggregateAndProofListPhase0Type.fromJson(body), + signedAggregateAndProofs: isForkElectra(fork) + ? SignedAggregateAndProofListElectraType.fromJson(body) + : SignedAggregateAndProofListPhase0Type.fromJson(body), }; }, writeReqSsz: ({signedAggregateAndProofs}) => { const fork = config.getForkName(signedAggregateAndProofs[0]?.message.aggregate.data.slot ?? 0); return { - body: - ForkSeq[fork] >= ForkSeq.electra - ? SignedAggregateAndProofListElectaType.serialize( - signedAggregateAndProofs as SignedAggregateAndProofListElecta - ) - : SignedAggregateAndProofListPhase0Type.serialize( - signedAggregateAndProofs as SignedAggregateAndProofListPhase0 - ), + body: isForkElectra(fork) + ? SignedAggregateAndProofListElectraType.serialize( + signedAggregateAndProofs as SignedAggregateAndProofListElectra + ) + : SignedAggregateAndProofListPhase0Type.serialize( + signedAggregateAndProofs as SignedAggregateAndProofListPhase0 + ), headers: {[MetaHeader.Version]: fork}, }; }, parseReqSsz: ({body, headers}) => { - const versionHeader = fromHeaders(headers, MetaHeader.Version, true); - const fork = toForkName(versionHeader); + const fork = toForkName(fromHeaders(headers, MetaHeader.Version)); return { - signedAggregateAndProofs: - ForkSeq[fork] >= ForkSeq.electra - ? SignedAggregateAndProofListElectaType.deserialize(body) - : SignedAggregateAndProofListPhase0Type.deserialize(body), + signedAggregateAndProofs: isForkElectra(fork) + ? SignedAggregateAndProofListElectraType.deserialize(body) + : SignedAggregateAndProofListPhase0Type.deserialize(body), }; }, schema: { diff --git a/packages/beacon-node/src/api/impl/beacon/pool/index.ts b/packages/beacon-node/src/api/impl/beacon/pool/index.ts index 5a66ebfe174e..bc8c838a5401 100644 --- a/packages/beacon-node/src/api/impl/beacon/pool/index.ts +++ b/packages/beacon-node/src/api/impl/beacon/pool/index.ts @@ -52,6 +52,7 @@ export function getBeaconPoolApi({ }, async getPoolAttesterSlashingsV2() { + // TODO Electra: Determine fork based on data returned by api return {data: chain.opPool.getAllAttesterSlashings(), meta: {version: ForkName.phase0}}; }, diff --git a/packages/beacon-node/src/api/impl/validator/index.ts b/packages/beacon-node/src/api/impl/validator/index.ts index 543831c778a6..298e7c6f4483 100644 --- a/packages/beacon-node/src/api/impl/validator/index.ts +++ b/packages/beacon-node/src/api/impl/validator/index.ts @@ -1083,7 +1083,7 @@ export function getValidatorApi( if (!aggregate) { throw new ApiError( 404, - `No aggregated attestation for slot=${slot} committeeIndex=${committeeIndex}, dataRoot=${dataRootHex}` + `No aggregated attestation for slot=${slot}, committeeIndex=${committeeIndex}, dataRoot=${dataRootHex}` ); } diff --git a/packages/beacon-node/src/chain/opPools/attestationPool.ts b/packages/beacon-node/src/chain/opPools/attestationPool.ts index a8c8940a0a0f..f125b8c941db 100644 --- a/packages/beacon-node/src/chain/opPools/attestationPool.ts +++ b/packages/beacon-node/src/chain/opPools/attestationPool.ts @@ -234,7 +234,7 @@ function attestationToAggregate(attestation: Attestation): AggregateFast { } /** - * Unwrap AggregateFast to phase0.Attestation + * Unwrap AggregateFast to Attestation */ function fastToAttestation(aggFast: AggregateFast): Attestation { return {...aggFast, signature: aggFast.signature.toBytes()}; From b498e6cf8d931332efee90214270e5d799fd4ea6 Mon Sep 17 00:00:00 2001 From: NC <17676176+ensi321@users.noreply.github.com> Date: Mon, 5 Aug 2024 14:56:00 +0800 Subject: [PATCH 20/21] Fix rebase --- packages/beacon-node/src/api/impl/beacon/blocks/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 445266a51729..9ff1a06019e1 100644 --- a/packages/beacon-node/src/api/impl/beacon/blocks/index.ts +++ b/packages/beacon-node/src/api/impl/beacon/blocks/index.ts @@ -414,7 +414,7 @@ export function getBeaconBlockApi({ }, async getBlockAttestationsV2({blockId}) { - const {block, executionOptimistic, finalized} = await resolveBlockId(chain, blockId); + const {block, executionOptimistic, finalized} = await getBlockResponse(chain, blockId); return { data: Array.from(block.message.body.attestations), meta: {executionOptimistic, finalized, version: config.getForkName(block.message.slot)}, From 9c0d54e982a961be97c1f01774bda55ff8ef1a48 Mon Sep 17 00:00:00 2001 From: NC <17676176+ensi321@users.noreply.github.com> Date: Mon, 5 Aug 2024 15:32:18 +0800 Subject: [PATCH 21/21] Lint --- packages/beacon-node/src/chain/blocks/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/beacon-node/src/chain/blocks/types.ts b/packages/beacon-node/src/chain/blocks/types.ts index 80c94e2d6fde..8b793932e951 100644 --- a/packages/beacon-node/src/chain/blocks/types.ts +++ b/packages/beacon-node/src/chain/blocks/types.ts @@ -1,7 +1,7 @@ import {CachedBeaconStateAllForks, computeEpochAtSlot} from "@lodestar/state-transition"; import {MaybeValidExecutionStatus, DataAvailabilityStatus} from "@lodestar/fork-choice"; import {deneb, Slot, RootHex, SignedBeaconBlock} from "@lodestar/types"; -import {ForkSeq, ForkName, ForkBlobs} from "@lodestar/params"; +import {ForkSeq, ForkBlobs} from "@lodestar/params"; import {ChainForkConfig} from "@lodestar/config"; export enum BlockInputType {