Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: get attestations for electra block #6732

Merged
merged 6 commits into from
May 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 5 additions & 2 deletions packages/beacon-node/src/chain/blocks/importBlock.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {toHexString} from "@chainsafe/ssz";
import {capella, ssz, allForks, altair} from "@lodestar/types";
import {ForkName, ForkSeq, INTERVALS_PER_SLOT, MAX_SEED_LOOKAHEAD, SLOTS_PER_EPOCH} from "@lodestar/params";
import {ForkSeq, INTERVALS_PER_SLOT, MAX_SEED_LOOKAHEAD, SLOTS_PER_EPOCH} from "@lodestar/params";
import {
CachedBeaconStateAltair,
computeEpochAtSlot,
Expand Down Expand Up @@ -433,7 +433,10 @@ export async function importBlock(
}
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, {
version: this.config.getForkName(blockSlot),
data: attesterSlashing,
});
}
}
if (this.emitter.listenerCount(routes.events.EventType.proposerSlashing)) {
Expand Down
368 changes: 268 additions & 100 deletions packages/beacon-node/src/chain/opPools/aggregatedAttestationPool.ts

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ 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";
Expand Down Expand Up @@ -62,7 +63,10 @@ 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, ssz.phase0.Attestation.defaultValue());
chainEventEmmitter.emit(routes.events.EventType.attestation, {
version: ForkName.phase0,
data: ssz.phase0.Attestation.defaultValue(),
});
chainEventEmmitter.emit(routes.events.EventType.head, headEventData);

expect(events).toHaveLength(1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,21 @@ import bls from "@chainsafe/bls";
import {BitArray, fromHexString, toHexString} from "@chainsafe/ssz";
import {describe, it, expect, beforeEach, beforeAll, afterEach, vi} from "vitest";
import {CachedBeaconStateAllForks, newFilledArray} from "@lodestar/state-transition";
import {FAR_FUTURE_EPOCH, ForkName, MAX_EFFECTIVE_BALANCE, SLOTS_PER_EPOCH} from "@lodestar/params";
import {
FAR_FUTURE_EPOCH,
ForkName,
MAX_COMMITTEES_PER_SLOT,
MAX_EFFECTIVE_BALANCE,
SLOTS_PER_EPOCH,
} from "@lodestar/params";
import {ssz, phase0} from "@lodestar/types";
import {CachedBeaconStateAltair} from "@lodestar/state-transition/src/types.js";
import {MockedForkChoice, getMockedForkChoice} from "../../../mocks/mockedBeaconChain.js";
import {
aggregateConsolidation,
AggregatedAttestationPool,
aggregateInto,
AttestationsConsolidation,
getNotSeenValidatorsFn,
MatchingDataAttestationGroup,
} from "../../../../src/chain/opPools/aggregatedAttestationPool.js";
Expand Down Expand Up @@ -81,11 +89,11 @@ describe("AggregatedAttestationPool", function () {
vi.clearAllMocks();
});

it("getParticipationFn", () => {
it("getNotSeenValidatorsFn", () => {
// previousEpochParticipation and currentEpochParticipation is created inside generateCachedState
// 0 and 1 are fully participated
const notSeenValidatorFn = getNotSeenValidatorsFn(altairState);
const participation = notSeenValidatorFn(currentEpoch, committee);
const participation = notSeenValidatorFn(currentEpoch, currentSlot, committeeIndex);
// seen attesting indices are 0, 1 => not seen are 2, 3
expect(participation).toEqual(
// {
Expand Down Expand Up @@ -280,6 +288,7 @@ describe("MatchingDataAttestationGroup.getAttestationsForBlock", () => {
}
}
const attestationsForBlock = attestationGroup.getAttestationsForBlock(
ForkName.phase0,
// notSeenValidatorIndices,
notSeenAttestingIndices
);
Expand Down Expand Up @@ -319,3 +328,75 @@ describe("MatchingDataAttestationGroup aggregateInto", function () {
expect(aggregatedSignature.verifyAggregate([sk1.toPublicKey(), sk2.toPublicKey()], attestationDataRoot)).toBe(true);
});
});

describe("aggregateConsolidation", function () {
const sk0 = bls.SecretKey.fromBytes(Buffer.alloc(32, 1));
const sk1 = bls.SecretKey.fromBytes(Buffer.alloc(32, 2));
const sk2 = bls.SecretKey.fromBytes(Buffer.alloc(32, 3));
const skArr = [sk0, sk1, sk2];
const testCases: {
name: string;
committeeIndices: number[];
aggregationBitsArr: Array<number>[];
expectedAggregationBits: Array<number>;
expectedCommitteeBits: Array<boolean>;
}[] = [
// note that bit index starts from the right
{
name: "test case 0",
committeeIndices: [0, 1, 2],
aggregationBitsArr: [[0b111], [0b011], [0b111]],
expectedAggregationBits: [0b11011111, 0b1],
expectedCommitteeBits: [true, true, true, false],
},
{
name: "test case 1",
committeeIndices: [2, 3, 1],
aggregationBitsArr: [[0b100], [0b010], [0b001]],
expectedAggregationBits: [0b10100001, 0b0],
expectedCommitteeBits: [false, true, true, true],
},
];
for (const {
name,
committeeIndices,
aggregationBitsArr,
expectedAggregationBits,
expectedCommitteeBits,
} of testCases) {
it(name, () => {
const attData = ssz.phase0.AttestationData.defaultValue();
const consolidation: AttestationsConsolidation = {
byCommittee: new Map(),
attData: attData,
totalNotSeenCount: 0,
score: 0,
};
// to simplify, instead of signing the signingRoot, just sign the attData root
const sigArr = skArr.map((sk) => sk.sign(ssz.phase0.AttestationData.hashTreeRoot(attData)));
const attestationSeed = ssz.electra.Attestation.defaultValue();
for (let i = 0; i < committeeIndices.length; i++) {
const committeeIndex = committeeIndices[i];
const commiteeBits = BitArray.fromBoolArray(
Array.from({length: MAX_COMMITTEES_PER_SLOT}, (_, i) => i === committeeIndex)
);
const aggAttestation = {
...attestationSeed,
aggregationBits: new BitArray(new Uint8Array(aggregationBitsArr[i]), 3),
committeeBits: commiteeBits,
signature: sigArr[i].toBytes(),
};
consolidation.byCommittee.set(committeeIndex, {
attestation: aggAttestation,
notSeenAttesterCount: aggregationBitsArr[i].filter((item) => item).length,
});
}

const finalAttestation = aggregateConsolidation(consolidation);
expect(finalAttestation.aggregationBits.uint8Array).toEqual(new Uint8Array(expectedAggregationBits));
expect(finalAttestation.committeeBits.toBoolArray()).toEqual(expectedCommitteeBits);
expect(finalAttestation.data).toEqual(attData);
expect(finalAttestation.signature).toEqual(bls.Signature.aggregate(sigArr).toBytes());
});
}
});
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export function getAttesterSlashingsSignatureSets(
/** Get signature sets from a single AttesterSlashing object */
export function getAttesterSlashingSignatureSets(
state: CachedBeaconStateAllForks,
attesterSlashing: allForks.AttesterSlashing,
attesterSlashing: allForks.AttesterSlashing
): ISignatureSet[] {
return [attesterSlashing.attestation1, attesterSlashing.attestation2].map((attestation) =>
getIndexedAttestationBigintSignatureSet(state, attestation)
Expand Down
17 changes: 0 additions & 17 deletions packages/types/src/electra/sszTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -367,20 +367,3 @@ export const SSEPayloadAttributes = new ContainerType(
},
{typeName: "SSEPayloadAttributes", jsonCase: "eth2"}
);

export const AggregateAndProof = new ContainerType(
{
aggregatorIndex: ValidatorIndex,
aggregate: Attestation,
selectionProof: BLSSignature,
},
{typeName: "AggregateAndProof", jsonCase: "eth2", cachePermanentRootStruct: true}
);

export const SignedAggregateAndProof = new ContainerType(
{
message: AggregateAndProof,
signature: BLSSignature,
},
{typeName: "SignedAggregateAndProof", jsonCase: "eth2"}
);
5 changes: 0 additions & 5 deletions packages/types/src/electra/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,3 @@ export type LightClientUpdate = ValueOf<typeof ssz.LightClientUpdate>;
export type LightClientFinalityUpdate = ValueOf<typeof ssz.LightClientFinalityUpdate>;
export type LightClientOptimisticUpdate = ValueOf<typeof ssz.LightClientOptimisticUpdate>;
export type LightClientStore = ValueOf<typeof ssz.LightClientStore>;

export type AggregateAndProof = ValueOf<typeof ssz.AggregateAndProof>;
export type SignedAggregateAndProof = ValueOf<typeof ssz.SignedAggregateAndProof>;
export type AttesterSlashing = ValueOf<typeof ssz.AttesterSlashing>
export type IndexedAttestationBigint = ValueOf<typeof ssz.IndexedAttestationBigint>
Loading