Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
fc450a7
feat: placeholder PR for electra
g11tech Jan 24, 2024
082ad3f
feat: implement EIP-6110 (#6042)
ensi321 Feb 19, 2024
14c5126
chore: fix CI failure due to recent merge from `unstable` (#6646)
ensi321 Apr 12, 2024
30bdac7
feat: implement execution layer exits eip 7002 (#6651)
g11tech Apr 22, 2024
9d78ab7
chore: update spec test version for electra fork (#6717)
ensi321 May 1, 2024
46ae715
feat: add presets and ssz types for EIP-7549 (#6715)
ensi321 May 4, 2024
dde8635
chore: fix the rebase build (#6735)
g11tech May 4, 2024
c231c3d
feat: upgrade 7002 exits to withdrawal request (#6736)
g11tech May 5, 2024
b1efc6e
feat: implement maxEB EIP-7251 (#6539)
ensi321 May 7, 2024
738d708
feat: beacon node process electra attestations EIP-7549 (#6738)
ensi321 May 7, 2024
2ac48ef
feat: handle the EL payload sending data in deposit requests instead …
g11tech May 8, 2024
838add9
feat: implement EIP-7549 (#6689)
ensi321 May 8, 2024
59f3abe
fix: attestation pool for electra (#6744)
twoeths May 8, 2024
8648df1
feat: update engineapi endpoints to v4 (#6747)
g11tech May 8, 2024
2386c32
feat: rename deposit receipt to deposit request for Pectra (#6748)
ensi321 May 8, 2024
1945fd2
test: enable spec tests related to eip-7549 (#6741)
nazarhussain May 8, 2024
0797ff1
fix: fix e2e test in electra-fork (#6751)
ensi321 May 8, 2024
1c2ef4f
feat: get the basic integration working with the ethereumjs electra b…
g11tech May 8, 2024
d8b57e0
feat: apply some fixes and hacks to get the single node devnet workin…
g11tech May 9, 2024
def2ee4
chore: finalizedSync e2e test for electra
twoeths May 10, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
},
"dependencies": {
"@chainsafe/persistent-merkle-tree": "^0.7.1",
"@chainsafe/ssz": "^0.15.1",
"@chainsafe/ssz": "^0.16.0",
"@lodestar/config": "^1.18.0",
"@lodestar/params": "^1.18.0",
"@lodestar/types": "^1.18.0",
Expand Down
7 changes: 4 additions & 3 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, electra} 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: electra.Attestation[]
): Promise<ApiClientResponse<{[HttpStatusCode.OK]: void}, HttpStatusCode.BAD_REQUEST>>;

/**
Expand Down Expand Up @@ -178,7 +178,8 @@ export function getReqSerializers(): ReqSerializers<Api, ReqTypes> {
getPoolProposerSlashings: reqEmpty,
getPoolVoluntaryExits: reqEmpty,
getPoolBlsToExecutionChanges: reqEmpty,
submitPoolAttestations: reqOnlyBody(ArrayOf(ssz.phase0.Attestation), Schema.ObjectArray),
// TODO: need to handle both electra and phase0 attestations
submitPoolAttestations: reqOnlyBody(ArrayOf(ssz.electra.Attestation), Schema.ObjectArray),
submitPoolAttesterSlashings: reqOnlyBody(ssz.phase0.AttesterSlashing, Schema.Object),
submitPoolProposerSlashings: reqOnlyBody(ssz.phase0.ProposerSlashing, Schema.Object),
submitPoolVoluntaryExit: reqOnlyBody(ssz.phase0.SignedVoluntaryExit, Schema.Object),
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
22 changes: 13 additions & 9 deletions packages/api/src/beacon/routes/validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
SubcommitteeIndex,
Wei,
ProducedBlockSource,
electra,
} from "@lodestar/types";
import {ApiClientResponse} from "../../interfaces.js";
import {HttpStatusCode} from "../../utils/client/httpStatusCode.js";
Expand Down Expand Up @@ -338,23 +339,25 @@ export type Api = {
*/
getAggregatedAttestation(
attestationDataRoot: Root,
slot: Slot
slot: Slot,
index: CommitteeIndex
): Promise<
ApiClientResponse<
{[HttpStatusCode.OK]: {data: phase0.Attestation}},
{[HttpStatusCode.OK]: {data: allForks.Attestation; version: ForkName}},
HttpStatusCode.BAD_REQUEST | HttpStatusCode.NOT_FOUND
>
>;

/**
* Publish multiple aggregate and proofs
* Verifies given aggregate and proofs and publishes them on appropriate gossipsub topic.
* TODO: support both phase0 + electra
* @param requestBody
* @returns any Successful response
* @throws ApiError
*/
publishAggregateAndProofs(
signedAggregateAndProofs: phase0.SignedAggregateAndProof[]
signedAggregateAndProofs: electra.SignedAggregateAndProof[] // TODO Electra: Add version
): Promise<ApiClientResponse<{[HttpStatusCode.OK]: void}, HttpStatusCode.BAD_REQUEST>>;

publishContributionAndProofs(
Expand Down Expand Up @@ -498,7 +501,7 @@ export type ReqTypes = {
produceBlindedBlock: {params: {slot: number}; query: {randao_reveal: string; graffiti: string}};
produceAttestationData: {query: {slot: number; committee_index: number}};
produceSyncCommitteeContribution: {query: {slot: number; subcommittee_index: number; beacon_block_root: string}};
getAggregatedAttestation: {query: {attestation_data_root: string; slot: number}};
getAggregatedAttestation: {query: {attestation_data_root: string; slot: number; index: number}};
publishAggregateAndProofs: {body: unknown};
publishContributionAndProofs: {body: unknown};
prepareBeaconCommitteeSubnet: {body: unknown};
Expand Down Expand Up @@ -647,14 +650,15 @@ export function getReqSerializers(): ReqSerializers<Api, ReqTypes> {
},

getAggregatedAttestation: {
writeReq: (root, slot) => ({query: {attestation_data_root: toHexString(root), slot}}),
parseReq: ({query}) => [fromHexString(query.attestation_data_root), query.slot],
writeReq: (root, slot, index) => ({query: {attestation_data_root: toHexString(root), slot, index}}),
parseReq: ({query}) => [fromHexString(query.attestation_data_root), query.slot, query.index],
schema: {
query: {attestation_data_root: Schema.StringRequired, slot: Schema.UintRequired},
query: {attestation_data_root: Schema.StringRequired, slot: Schema.UintRequired, index: Schema.UintRequired},
},
},

publishAggregateAndProofs: reqOnlyBody(ArrayOf(ssz.phase0.SignedAggregateAndProof), Schema.ObjectArray),
// TODO: support both phase0 + electra
publishAggregateAndProofs: reqOnlyBody(ArrayOf(ssz.electra.SignedAggregateAndProof), Schema.ObjectArray),
publishContributionAndProofs: reqOnlyBody(ArrayOf(ssz.altair.SignedContributionAndProof), Schema.ObjectArray),
prepareBeaconCommitteeSubnet: reqOnlyBody(ArrayOf(BeaconCommitteeSubscription), Schema.ObjectArray),
prepareSyncCommitteeSubnets: reqOnlyBody(ArrayOf(SyncCommitteeSubscription), Schema.ObjectArray),
Expand Down Expand Up @@ -786,7 +790,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
98 changes: 52 additions & 46 deletions packages/api/test/unit/beacon/testData/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,21 @@ export const eventTestData: EventData = {
block: "0x9a2fefd2fdb57f74993c7780ea5b9030d2897b615b89f808011ca5aebed54eaf",
executionOptimistic: false,
},
[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.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.voluntaryExit]: ssz.phase0.SignedVoluntaryExit.fromJson({
message: {epoch: "1", validator_index: "1"},
signature:
Expand Down Expand Up @@ -72,44 +75,47 @@ export const eventTestData: EventData = {
"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
},
}),
[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",
[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",
},
},
signature:
"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
},
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",
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",
Expand Down
7 changes: 5 additions & 2 deletions packages/api/test/unit/beacon/testData/validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,11 @@ export const testData: GenericServerTestCases<Api> = {
res: {data: ssz.altair.SyncCommitteeContribution.defaultValue()},
},
getAggregatedAttestation: {
args: [ZERO_HASH, 32000],
res: {data: ssz.phase0.Attestation.defaultValue()},
args: [ZERO_HASH, 32000, 2],
res: {
data: ssz.phase0.Attestation.defaultValue(),
version: ForkName.altair,
},
},
publishAggregateAndProofs: {
args: [[ssz.phase0.SignedAggregateAndProof.defaultValue()]],
Expand Down
2 changes: 1 addition & 1 deletion packages/beacon-node/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@
"@chainsafe/libp2p-noise": "^15.0.0",
"@chainsafe/persistent-merkle-tree": "^0.7.1",
"@chainsafe/prometheus-gc-stats": "^1.0.0",
"@chainsafe/ssz": "^0.15.1",
"@chainsafe/ssz": "^0.16.0",
"@chainsafe/threads": "^1.11.1",
"@ethersproject/abi": "^5.7.0",
"@fastify/bearer-auth": "^9.0.0",
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
13 changes: 9 additions & 4 deletions 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 @@ -1062,22 +1063,26 @@ export function getValidatorApi({
};
},

async getAggregatedAttestation(attestationDataRoot, slot) {
async getAggregatedAttestation(attestationDataRoot, slot, committeeIndex) {
notWhileSyncing();

await waitForSlot(slot); // Must never request for a future slot > currentSlot

const dataRootHex = toHexString(attestationDataRoot);
const aggregate = chain.attestationPool.getAggregate(slot, dataRootHex);
const aggregate = chain.attestationPool.getAggregate(slot, committeeIndex, dataRootHex);

if (!aggregate) {
throw new ApiError(404, `No aggregated attestation for slot=${slot}, dataRoot=${dataRootHex}`);
throw new ApiError(
404,
`No aggregated attestation for slot=${slot} committeeIndex=${committeeIndex}, dataRoot=${dataRootHex}`
);
}

metrics?.production.producedAggregateParticipants.observe(aggregate.aggregationBits.getTrueBitIndexes().length);

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

Expand Down
14 changes: 11 additions & 3 deletions packages/beacon-node/src/chain/blocks/importBlock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ export async function importBlock(
const prevFinalizedEpoch = this.forkChoice.getFinalizedCheckpoint().epoch;
const blockDelaySec = (fullyVerifiedBlock.seenTimestampSec - postState.genesisTime) % this.config.SECONDS_PER_SLOT;
const recvToValLatency = Date.now() / 1000 - (opts.seenTimestampSec ?? Date.now() / 1000);
const fork = this.config.getForkSeq(blockSlot);

// this is just a type assertion since blockinput with blobsPromise type will not end up here
if (blockInput.type === BlockInputType.blobsPromise) {
Expand Down Expand Up @@ -146,7 +147,8 @@ export async function importBlock(

for (const attestation of attestations) {
try {
const indexedAttestation = postState.epochCtx.getIndexedAttestation(attestation);
// TODO Electra: figure out how to reuse the attesting indices computed from state transition
const indexedAttestation = postState.epochCtx.getIndexedAttestation(fork, attestation);
const {target, beaconBlockRoot} = attestation.data;

const attDataRoot = toHexString(ssz.phase0.AttestationData.hashTreeRoot(indexedAttestation.data));
Expand Down Expand Up @@ -424,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
Loading