Skip to content

Commit b785bbd

Browse files
ensi321nflaig
andauthored
chore: add unit test for getAttestationsForBlockElectra (#7464)
Follow up on #7455 (comment) to add unit test of the changes introduced in #7455. --------- Co-authored-by: Nico Flaig <nflaig@protonmail.com>
1 parent 32186d0 commit b785bbd

File tree

2 files changed

+129
-6
lines changed

2 files changed

+129
-6
lines changed

packages/beacon-node/test/unit/chain/opPools/aggregatedAttestationPool.test.ts

Lines changed: 127 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@ import {createChainForkConfig, defaultChainConfig} from "@lodestar/config";
44
import {
55
FAR_FUTURE_EPOCH,
66
ForkName,
7+
ForkPostElectra,
78
MAX_COMMITTEES_PER_SLOT,
89
MAX_EFFECTIVE_BALANCE,
10+
MAX_VALIDATORS_PER_COMMITTEE,
911
SLOTS_PER_EPOCH,
1012
} from "@lodestar/params";
11-
import {CachedBeaconStateAllForks, newFilledArray} from "@lodestar/state-transition";
13+
import {CachedBeaconStateAllForks, CachedBeaconStateElectra, newFilledArray} from "@lodestar/state-transition";
1214
import {CachedBeaconStateAltair} from "@lodestar/state-transition/src/types.js";
13-
import {phase0, ssz} from "@lodestar/types";
15+
import {Attestation, phase0, ssz} from "@lodestar/types";
1416
import {afterEach, beforeAll, beforeEach, describe, expect, it, vi} from "vitest";
1517
import {
1618
AggregatedAttestationPool,
@@ -25,7 +27,7 @@ import {ZERO_HASH_HEX} from "../../../../src/constants/constants.js";
2527
import {linspace} from "../../../../src/util/numpy.js";
2628
import {MockedForkChoice, getMockedForkChoice} from "../../../mocks/mockedBeaconChain.js";
2729
import {renderBitArray} from "../../../utils/render.js";
28-
import {generateCachedAltairState} from "../../../utils/state.js";
30+
import {generateCachedAltairState, generateCachedElectraState} from "../../../utils/state.js";
2931
import {generateProtoBlock} from "../../../utils/typeGenerator.js";
3032
import {generateValidators} from "../../../utils/validator.js";
3133

@@ -34,7 +36,7 @@ const validSignature = fromHexString(
3436
"0xb2afb700f6c561ce5e1b4fedaec9d7c06b822d38c720cf588adfda748860a940adf51634b6788f298c552de40183b5a203b2bbe8b7dd147f0bb5bc97080a12efbb631c8888cb31a99cc4706eb3711865b8ea818c10126e4d818b542e9dbf9ae8"
3537
);
3638

37-
describe("AggregatedAttestationPool", () => {
39+
describe("AggregatedAttestationPool - Altair", () => {
3840
let pool: AggregatedAttestationPool;
3941
const fork = ForkName.altair;
4042
const config = createChainForkConfig({
@@ -158,6 +160,127 @@ describe("AggregatedAttestationPool", () => {
158160
});
159161
});
160162

163+
describe("AggregatedAttestationPool - Electra", () => {
164+
let pool: AggregatedAttestationPool;
165+
const fork = ForkName.electra;
166+
const electraForkEpoch = 2020;
167+
const config = createChainForkConfig({
168+
...defaultChainConfig,
169+
ALTAIR_FORK_EPOCH: 0,
170+
BELLATRIX_FORK_EPOCH: 0,
171+
CAPELLA_FORK_EPOCH: 0,
172+
DENEB_FORK_EPOCH: 0,
173+
ELECTRA_FORK_EPOCH: electraForkEpoch,
174+
});
175+
const currentEpoch = electraForkEpoch + 10;
176+
const currentSlot = SLOTS_PER_EPOCH * currentEpoch;
177+
178+
const committeeIndices = [0, 1, 2, 3];
179+
const attestation = ssz.electra.Attestation.defaultValue();
180+
attestation.data.slot = currentSlot;
181+
attestation.data.index = 0; // Must be zero post-electra
182+
attestation.data.target.epoch = currentEpoch;
183+
attestation.signature = validSignature;
184+
const attDataRootHex = toHexString(ssz.phase0.AttestationData.hashTreeRoot(attestation.data));
185+
186+
const validatorOpts = {
187+
activationEpoch: 0,
188+
effectiveBalance: MAX_EFFECTIVE_BALANCE,
189+
withdrawableEpoch: FAR_FUTURE_EPOCH,
190+
exitEpoch: FAR_FUTURE_EPOCH,
191+
};
192+
// this makes a committee length of 4
193+
const vc = 1024;
194+
const committeeLength = 32;
195+
const validators = generateValidators(vc, validatorOpts);
196+
const originalState = generateCachedElectraState({slot: currentSlot + 1, validators}, electraForkEpoch);
197+
expect(originalState.epochCtx.getCommitteeCountPerSlot(currentEpoch)).toEqual(committeeIndices.length);
198+
199+
const committees = originalState.epochCtx.getBeaconCommittees(currentSlot, committeeIndices);
200+
201+
const epochParticipation = newFilledArray(vc, 0b000);
202+
for (const committee of committees) {
203+
expect(committee.length).toEqual(committeeLength);
204+
}
205+
206+
(originalState as CachedBeaconStateElectra).previousEpochParticipation =
207+
ssz.altair.EpochParticipation.toViewDU(epochParticipation);
208+
(originalState as CachedBeaconStateElectra).currentEpochParticipation =
209+
ssz.altair.EpochParticipation.toViewDU(epochParticipation);
210+
originalState.commit();
211+
let electraState: CachedBeaconStateAllForks;
212+
213+
let forkchoiceStub: MockedForkChoice;
214+
215+
beforeEach(() => {
216+
pool = new AggregatedAttestationPool(config);
217+
electraState = originalState.clone();
218+
forkchoiceStub = getMockedForkChoice();
219+
});
220+
221+
afterEach(() => {
222+
vi.clearAllMocks();
223+
});
224+
225+
it("Multiple attestations with same attestation data different committee", () => {
226+
// Attestation from committee 0
227+
const committeeBits0 = BitArray.fromSingleBit(MAX_COMMITTEES_PER_SLOT, 0);
228+
229+
const attestation0: Attestation<ForkPostElectra> = {
230+
...attestation,
231+
aggregationBits: new BitArray(new Uint8Array(committeeLength / 8).fill(1), committeeLength),
232+
committeeBits: committeeBits0,
233+
};
234+
235+
// Attestation from committee 1
236+
const committeeBits1 = BitArray.fromSingleBit(MAX_COMMITTEES_PER_SLOT, 1);
237+
238+
const attestation1: Attestation<ForkPostElectra> = {
239+
...attestation,
240+
aggregationBits: new BitArray(new Uint8Array(committeeLength / 8).fill(1), committeeLength),
241+
committeeBits: committeeBits1,
242+
};
243+
// Attestation from committee 2
244+
const committeeBits2 = BitArray.fromSingleBit(MAX_COMMITTEES_PER_SLOT, 2);
245+
246+
const attestation2: Attestation<ForkPostElectra> = {
247+
...attestation,
248+
aggregationBits: new BitArray(new Uint8Array(committeeLength / 8).fill(1), committeeLength),
249+
committeeBits: committeeBits2,
250+
};
251+
252+
// Attestation from committee 3
253+
const committeeBits3 = BitArray.fromSingleBit(MAX_COMMITTEES_PER_SLOT, 3);
254+
255+
const attestation3: Attestation<ForkPostElectra> = {
256+
...attestation,
257+
aggregationBits: new BitArray(new Uint8Array(committeeLength / 8).fill(1), committeeLength),
258+
committeeBits: committeeBits3,
259+
};
260+
261+
pool.add(attestation0, attDataRootHex, attestation0.aggregationBits.getTrueBitIndexes().length, committees[0]);
262+
263+
pool.add(attestation1, attDataRootHex, attestation1.aggregationBits.getTrueBitIndexes().length, committees[1]);
264+
265+
pool.add(attestation2, attDataRootHex, attestation2.aggregationBits.getTrueBitIndexes().length, committees[2]);
266+
267+
pool.add(attestation3, attDataRootHex, attestation3.aggregationBits.getTrueBitIndexes().length, committees[3]);
268+
269+
forkchoiceStub.getBlockHex.mockReturnValue(generateProtoBlock());
270+
forkchoiceStub.getDependentRoot.mockReturnValue(ZERO_HASH_HEX);
271+
272+
const blockAttestations = pool.getAttestationsForBlock(fork, forkchoiceStub, electraState);
273+
274+
expect(blockAttestations.length).toBe(1); // Expect attestations from committee 0, 1, 2 and 3 to be aggregated into one
275+
expect((blockAttestations[0] as Attestation<ForkPostElectra>).committeeBits.getTrueBitIndexes()).toStrictEqual(
276+
committeeIndices
277+
);
278+
expect((blockAttestations[0] as Attestation<ForkPostElectra>).aggregationBits.bitLen).toStrictEqual(
279+
committeeLength * 4
280+
);
281+
});
282+
});
283+
161284
describe("MatchingDataAttestationGroup.add()", () => {
162285
const testCases: {id: string; attestationsToAdd: {bits: number[]; res: InsertOutcome; isKept: boolean}[]}[] = [
163286
{

packages/beacon-node/test/utils/state.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,8 +150,8 @@ export function generateCachedBellatrixState(opts?: TestBeaconState): CachedBeac
150150
/**
151151
* This generates state with default pubkey
152152
*/
153-
export function generateCachedElectraState(opts?: TestBeaconState): CachedBeaconStateElectra {
154-
const config = getConfig(ForkName.electra);
153+
export function generateCachedElectraState(opts?: TestBeaconState, electraForkEpoch = 0): CachedBeaconStateElectra {
154+
const config = getConfig(ForkName.electra, electraForkEpoch);
155155
const state = generateState(opts, config);
156156
return createCachedBeaconState(state as BeaconStateElectra, {
157157
config: createBeaconConfig(config, state.genesisValidatorsRoot),

0 commit comments

Comments
 (0)