@@ -4,13 +4,15 @@ import {createChainForkConfig, defaultChainConfig} from "@lodestar/config";
44import {
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" ;
1214import { CachedBeaconStateAltair } from "@lodestar/state-transition/src/types.js" ;
13- import { phase0 , ssz } from "@lodestar/types" ;
15+ import { Attestation , phase0 , ssz } from "@lodestar/types" ;
1416import { afterEach , beforeAll , beforeEach , describe , expect , it , vi } from "vitest" ;
1517import {
1618 AggregatedAttestationPool ,
@@ -25,7 +27,7 @@ import {ZERO_HASH_HEX} from "../../../../src/constants/constants.js";
2527import { linspace } from "../../../../src/util/numpy.js" ;
2628import { MockedForkChoice , getMockedForkChoice } from "../../../mocks/mockedBeaconChain.js" ;
2729import { renderBitArray } from "../../../utils/render.js" ;
28- import { generateCachedAltairState } from "../../../utils/state.js" ;
30+ import { generateCachedAltairState , generateCachedElectraState } from "../../../utils/state.js" ;
2931import { generateProtoBlock } from "../../../utils/typeGenerator.js" ;
3032import { 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+
161284describe ( "MatchingDataAttestationGroup.add()" , ( ) => {
162285 const testCases : { id : string ; attestationsToAdd : { bits : number [ ] ; res : InsertOutcome ; isKept : boolean } [ ] } [ ] = [
163286 {
0 commit comments