Skip to content

Commit

Permalink
feat: implement EIP-7549 (#6689)
Browse files Browse the repository at this point in the history
* initial commit

* lint

* Add getAttestingIndices and update getIndexedAttestation

* Update gossip validation

* Update attestation gossip validation

* aggregateAndProof validation

* clean up

* Validator

* Misc

* Fix the build erros

* feat: get attestations for electra block (#6732)

* feat: getAttestationsForBlock() for electra

* chore: fix lint

* fix: MAX_ATTESTATIONS_PER_GROUP_ELECTRA and address PR comments

* chore: unit test aggregateConsolidation

* Fix rebase mistake

* Address my own comment :)

---------

Co-authored-by: Navie Chan <naviechan@gmail.com>

* Fix check-types

* Address comments

---------

Co-authored-by: Nazar Hussain <nazarhussain@gmail.com>
Co-authored-by: tuyennhv <tuyen@chainsafe.io>
  • Loading branch information
3 people authored May 8, 2024
1 parent 2ac48ef commit 838add9
Show file tree
Hide file tree
Showing 30 changed files with 599 additions and 164 deletions.
4 changes: 2 additions & 2 deletions packages/api/src/beacon/routes/beacon/pool.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {phase0, altair, capella, CommitteeIndex, Slot, ssz} from "@lodestar/types";
import {phase0, altair, capella, CommitteeIndex, Slot, ssz, allForks} from "@lodestar/types";
import {ApiClientResponse} from "../../../interfaces.js";
import {HttpStatusCode} from "../../../utils/client/httpStatusCode.js";
import {
Expand Down Expand Up @@ -80,7 +80,7 @@ export type Api = {
* @throws ApiError
*/
submitPoolAttestations(
attestations: phase0.Attestation[]
attestations: allForks.Attestation[]
): Promise<ApiClientResponse<{[HttpStatusCode.OK]: void}, HttpStatusCode.BAD_REQUEST>>;

/**
Expand Down
8 changes: 4 additions & 4 deletions packages/api/src/beacon/routes/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,10 @@ export type EventData = {
block: RootHex;
executionOptimistic: boolean;
};
[EventType.attestation]: phase0.Attestation;
[EventType.attestation]: {version: ForkName; data: allForks.Attestation};
[EventType.voluntaryExit]: phase0.SignedVoluntaryExit;
[EventType.proposerSlashing]: phase0.ProposerSlashing;
[EventType.attesterSlashing]: phase0.AttesterSlashing;
[EventType.attesterSlashing]: {version: ForkName; data: allForks.AttesterSlashing};
[EventType.blsToExecutionChange]: capella.SignedBLSToExecutionChange;
[EventType.finalizedCheckpoint]: {
block: RootHex;
Expand Down Expand Up @@ -180,10 +180,10 @@ export function getTypeByEvent(): {[K in EventType]: TypeJson<EventData[K]>} {
{jsonCase: "eth2"}
),

[EventType.attestation]: ssz.phase0.Attestation,
[EventType.attestation]: WithVersion((fork) => ssz.allForks[fork].Attestation),
[EventType.voluntaryExit]: ssz.phase0.SignedVoluntaryExit,
[EventType.proposerSlashing]: ssz.phase0.ProposerSlashing,
[EventType.attesterSlashing]: ssz.phase0.AttesterSlashing,
[EventType.attesterSlashing]: WithVersion((fork) => ssz.allForks[fork].AttesterSlashing),
[EventType.blsToExecutionChange]: ssz.capella.SignedBLSToExecutionChange,

[EventType.finalizedCheckpoint]: new ContainerType(
Expand Down
6 changes: 3 additions & 3 deletions packages/api/src/beacon/routes/validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,7 @@ export type Api = {
slot: Slot
): Promise<
ApiClientResponse<
{[HttpStatusCode.OK]: {data: phase0.Attestation}},
{[HttpStatusCode.OK]: {data: allForks.Attestation; version: ForkName}},
HttpStatusCode.BAD_REQUEST | HttpStatusCode.NOT_FOUND
>
>;
Expand All @@ -354,7 +354,7 @@ export type Api = {
* @throws ApiError
*/
publishAggregateAndProofs(
signedAggregateAndProofs: phase0.SignedAggregateAndProof[]
signedAggregateAndProofs: allForks.SignedAggregateAndProof[] // TODO Electra: Add version
): Promise<ApiClientResponse<{[HttpStatusCode.OK]: void}, HttpStatusCode.BAD_REQUEST>>;

publishContributionAndProofs(
Expand Down Expand Up @@ -786,7 +786,7 @@ export function getReturnTypes(): ReturnTypes<Api> {

produceAttestationData: ContainerData(ssz.phase0.AttestationData),
produceSyncCommitteeContribution: ContainerData(ssz.altair.SyncCommitteeContribution),
getAggregatedAttestation: ContainerData(ssz.phase0.Attestation),
getAggregatedAttestation: WithVersion((fork) => ssz.allForks[fork].Attestation),
submitBeaconCommitteeSelections: ContainerData(ArrayOf(BeaconCommitteeSelection)),
submitSyncCommitteeSelections: ContainerData(ArrayOf(SyncCommitteeSelection)),
getLiveness: jsonType("snake"),
Expand Down
4 changes: 2 additions & 2 deletions packages/beacon-node/src/api/impl/beacon/pool/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {routes, ServerApi} from "@lodestar/api";
import {Epoch, ssz} from "@lodestar/types";
import {SYNC_COMMITTEE_SUBNET_SIZE} from "@lodestar/params";
import {ForkName, SYNC_COMMITTEE_SUBNET_SIZE} from "@lodestar/params";
import {validateApiAttestation} from "../../../../chain/validation/index.js";
import {validateApiAttesterSlashing} from "../../../../chain/validation/attesterSlashing.js";
import {validateApiProposerSlashing} from "../../../../chain/validation/proposerSlashing.js";
Expand Down Expand Up @@ -77,7 +77,7 @@ export function getBeaconPoolApi({
metrics?.opPool.attestationPoolInsertOutcome.inc({insertOutcome});
}

chain.emitter.emit(routes.events.EventType.attestation, attestation);
chain.emitter.emit(routes.events.EventType.attestation, {data: attestation, version: ForkName.phase0});

const sentPeers = await network.publishBeaconAttestation(attestation, subnet);
metrics?.onPoolSubmitUnaggregatedAttestation(seenTimestampSec, indexedAttestation, subnet, sentPeers);
Expand Down
4 changes: 3 additions & 1 deletion packages/beacon-node/src/api/impl/validator/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -819,6 +819,7 @@ export function getValidatorApi({
const attEpoch = computeEpochAtSlot(slot);
const headBlockRootHex = chain.forkChoice.getHead().blockRoot;
const headBlockRoot = fromHexString(headBlockRootHex);
const fork = config.getForkSeq(slot);

const beaconBlockRoot =
slot >= headSlot
Expand Down Expand Up @@ -846,7 +847,7 @@ export function getValidatorApi({
return {
data: {
slot,
index: committeeIndex,
index: fork >= ForkSeq.electra ? 0 : committeeIndex,
beaconBlockRoot,
source: attEpochState.currentJustifiedCheckpoint,
target: {epoch: attEpoch, root: targetRoot},
Expand Down Expand Up @@ -1078,6 +1079,7 @@ export function getValidatorApi({

return {
data: aggregate,
version: config.getForkName(slot),
};
},

Expand Down
10 changes: 8 additions & 2 deletions packages/beacon-node/src/chain/blocks/importBlock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -426,12 +426,18 @@ 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, attestation);
this.emitter.emit(routes.events.EventType.attestation, {
version: this.config.getForkName(blockSlot),
data: attestation,
});
}
}
if (this.emitter.listenerCount(routes.events.EventType.attesterSlashing)) {
for (const attesterSlashing of block.message.body.attesterSlashings) {
this.emitter.emit(routes.events.EventType.attesterSlashing, attesterSlashing);
this.emitter.emit(routes.events.EventType.attesterSlashing, {
version: this.config.getForkName(blockSlot),
data: attesterSlashing,
});
}
}
if (this.emitter.listenerCount(routes.events.EventType.proposerSlashing)) {
Expand Down
12 changes: 11 additions & 1 deletion packages/beacon-node/src/chain/errors/attestationError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,14 @@ export enum AttestationErrorCode {
INVALID_SERIALIZED_BYTES = "ATTESTATION_ERROR_INVALID_SERIALIZED_BYTES",
/** Too many skipped slots. */
TOO_MANY_SKIPPED_SLOTS = "ATTESTATION_ERROR_TOO_MANY_SKIPPED_SLOTS",
/**
* Electra: The aggregated attestation doesn't have only one committee bit set.
*/
NOT_EXACTLY_ONE_COMMITTEE_BIT_SET = "ATTESTATION_ERROR_NOT_EXACTLY_ONE_COMMITTEE_BIT_SET",
/**
* Electra: Invalid attestationData index: is non-zero
*/
NON_ZERO_ATTESTATION_DATA_INDEX = "ATTESTATION_ERROR_NON_ZERO_ATTESTATION_DATA_INDEX",
}

export type AttestationErrorType =
Expand Down Expand Up @@ -160,7 +168,9 @@ export type AttestationErrorType =
| {code: AttestationErrorCode.INVALID_AGGREGATOR}
| {code: AttestationErrorCode.INVALID_INDEXED_ATTESTATION}
| {code: AttestationErrorCode.INVALID_SERIALIZED_BYTES}
| {code: AttestationErrorCode.TOO_MANY_SKIPPED_SLOTS; headBlockSlot: Slot; attestationSlot: Slot};
| {code: AttestationErrorCode.TOO_MANY_SKIPPED_SLOTS; headBlockSlot: Slot; attestationSlot: Slot}
| {code: AttestationErrorCode.NOT_EXACTLY_ONE_COMMITTEE_BIT_SET}
| {code: AttestationErrorCode.NON_ZERO_ATTESTATION_DATA_INDEX};

export class AttestationError extends GossipActionError<AttestationErrorType> {
getMetadata(): Record<string, string | number | null> {
Expand Down
Loading

0 comments on commit 838add9

Please sign in to comment.