@@ -84,7 +84,6 @@ def build_attestation_data(spec, state, slot, index, beacon_block_root=None, sha
8484 target = spec .Checkpoint (epoch = spec .compute_epoch_at_slot (slot ), root = epoch_boundary_root ),
8585 )
8686
87- # if spec.fork == SHARDING # TODO: add extra data for shard voting
8887 return data
8988
9089
@@ -95,27 +94,21 @@ def get_valid_attestation(spec,
9594 filter_participant_set = None ,
9695 beacon_block_root = None ,
9796 signed = False ):
98- # If filter_participant_set filters everything, the attestation has 0 participants, and cannot be signed.
99- # Thus strictly speaking invalid when no participant is added later.
97+ """
98+ Return a valid attestation at `slot` and committee index `index`.
99+
100+ If filter_participant_set filters everything, the attestation has 0 participants, and cannot be signed.
101+ Thus strictly speaking invalid when no participant is added later.
102+ """
100103 if slot is None :
101104 slot = state .slot
102105 if index is None :
103106 index = 0
104107
105108 attestation_data = build_attestation_data (spec , state , slot = slot , index = index , beacon_block_root = beacon_block_root )
106109
107- beacon_committee = spec .get_beacon_committee ( state , slot , index )
110+ attestation = spec .Attestation ( data = attestation_data )
108111
109- if is_post_eip7549 (spec ):
110- # will fill aggregation_bits later
111- attestation = spec .Attestation (data = attestation_data )
112- else :
113- committee_size = len (beacon_committee )
114- aggregation_bits = Bitlist [spec .MAX_VALIDATORS_PER_COMMITTEE ](* ([0 ] * committee_size ))
115- attestation = spec .Attestation (
116- aggregation_bits = aggregation_bits ,
117- data = attestation_data ,
118- )
119112 # fill the attestation with (optionally filtered) participants, and optionally sign it
120113 fill_aggregate_attestation (spec , state , attestation , signed = signed ,
121114 filter_participant_set = filter_participant_set , committee_index = index )
@@ -132,7 +125,7 @@ def sign_aggregate_attestation(spec, state, attestation_data, participants: List
132125 spec ,
133126 state ,
134127 attestation_data ,
135- privkey
128+ privkey ,
136129 )
137130 )
138131 return bls .Aggregate (signatures )
@@ -180,11 +173,16 @@ def fill_aggregate_attestation(spec, state, attestation, committee_index, signed
180173 if filter_participant_set is not None :
181174 participants = filter_participant_set (participants )
182175
176+ # initialize `aggregation_bits`
183177 if is_post_eip7549 (spec ):
184- attestation .committee_bits = spec .Bitvector [spec .MAX_COMMITTEES_PER_SLOT ]()
185178 attestation .committee_bits [committee_index ] = True
186179 attestation .aggregation_bits = get_empty_eip7549_aggregation_bits (
187180 spec , state , attestation .committee_bits , attestation .data .slot )
181+ else :
182+ committee_size = len (beacon_committee )
183+ attestation .aggregation_bits = Bitlist [spec .MAX_VALIDATORS_PER_COMMITTEE ](* ([0 ] * committee_size ))
184+
185+ # fill in the `aggregation_bits`
188186 for i in range (len (beacon_committee )):
189187 if is_post_eip7549 (spec ):
190188 offset = get_eip7549_aggregation_bits_offset (
@@ -205,15 +203,17 @@ def add_attestations_to_state(spec, state, attestations, slot):
205203 spec .process_attestation (state , attestation )
206204
207205
208- def get_valid_attestation_at_slot (state , spec , slot_to_attest , participation_fn = None , beacon_block_root = None ):
206+ def get_valid_attestations_at_slot (state , spec , slot_to_attest , participation_fn = None , beacon_block_root = None ):
207+ """
208+ Return attestations at slot `slot_to_attest`.
209+ """
209210 committees_per_slot = spec .get_committee_count_per_slot (state , spec .compute_epoch_at_slot (slot_to_attest ))
210211 for index in range (committees_per_slot ):
211212 def participants_filter (comm ):
212213 if participation_fn is None :
213214 return comm
214215 else :
215216 return participation_fn (state .slot , index , comm )
216- # if spec.fork == SHARDING: TODO: add shard data to attestation, include shard headers in block
217217 yield get_valid_attestation (
218218 spec ,
219219 state ,
@@ -225,6 +225,78 @@ def participants_filter(comm):
225225 )
226226
227227
228+ def _get_aggregate_committee_indices (spec , attestations ):
229+ """
230+ Aggregate all unique committee indices from the given attestations.
231+ """
232+ all_committee_indices = set ()
233+ for attestation in attestations :
234+ committee_indices = spec .get_committee_indices (attestation .committee_bits )
235+ assert len (committee_indices ) == 1
236+ all_committee_indices .add (committee_indices [0 ])
237+
238+ return all_committee_indices
239+
240+
241+ def _aggregate_aggregation_bits_and_signatures (spec , state , slot , aggregate , attestations ):
242+ """
243+ Aggregate the aggregation bits and signatures from the attestations,
244+ incorporating the calculation of aggregation bits offset directly.
245+ """
246+ # initialize aggregation bits for the aggregate attestation
247+ aggregate .aggregation_bits = get_empty_eip7549_aggregation_bits (
248+ spec , state , aggregate .committee_bits , slot )
249+
250+ signatures = []
251+
252+ offset = 0
253+ attestations = sorted (attestations , key = lambda att : spec .get_committee_indices (att .committee_bits )[0 ])
254+ for attestation in attestations :
255+ # retrieve the single committee index for the attestation.
256+ committee_index = spec .get_committee_indices (attestation .committee_bits )[0 ]
257+
258+ # update the aggregate's aggregation bits based on each attestation.
259+ for i , bit in enumerate (attestation .aggregation_bits ):
260+ aggregate .aggregation_bits [offset + i ] = bit
261+
262+ # collect signatures for aggregation.
263+ signatures .append (attestation .signature )
264+
265+ # update offset
266+ committee = spec .get_beacon_committee (state , slot , committee_index )
267+ offset += len (committee )
268+
269+ # aggregate signatures from all attestations.
270+ aggregate .signature = bls .Aggregate (signatures )
271+
272+
273+ def get_valid_attestation_at_slot (state , spec , slot_to_attest , participation_fn = None , beacon_block_root = None ):
274+ """
275+ Return the aggregate attestation post EIP-7549.
276+ Note: this EIP supports dense packing of on-chain aggregates so we can just return a single `Attestation`.
277+ """
278+ assert is_post_eip7549 (spec )
279+ attestations = list (get_valid_attestations_at_slot (
280+ state , spec , slot_to_attest ,
281+ participation_fn = participation_fn ,
282+ beacon_block_root = beacon_block_root ,
283+ ))
284+ if not attestations :
285+ return None
286+
287+ # initialize the aggregate attestation.
288+ aggregate = spec .Attestation (data = attestations [0 ].data )
289+
290+ # fill in committee_bits
291+ all_committee_indices = _get_aggregate_committee_indices (spec , attestations )
292+ for committee_index in all_committee_indices :
293+ aggregate .committee_bits [committee_index ] = True
294+
295+ _aggregate_aggregation_bits_and_signatures (spec , state , slot_to_attest , aggregate , attestations )
296+
297+ return aggregate
298+
299+
228300def next_slots_with_attestations (spec ,
229301 state ,
230302 slot_count ,
@@ -249,6 +321,26 @@ def next_slots_with_attestations(spec,
249321 return state , signed_blocks , post_state
250322
251323
324+ def _add_valid_attestations (spec , state , block , slot_to_attest , participation_fn = None ):
325+ if is_post_eip7549 (spec ):
326+ attestation = get_valid_attestation_at_slot (
327+ state ,
328+ spec ,
329+ slot_to_attest ,
330+ participation_fn = participation_fn ,
331+ )
332+ block .body .attestations .append (attestation )
333+ else :
334+ attestations = get_valid_attestations_at_slot (
335+ state ,
336+ spec ,
337+ slot_to_attest ,
338+ participation_fn = participation_fn ,
339+ )
340+ for attestation in attestations :
341+ block .body .attestations .append (attestation )
342+
343+
252344def next_epoch_with_attestations (spec ,
253345 state ,
254346 fill_cur_epoch ,
@@ -281,24 +373,10 @@ def state_transition_with_full_block(spec,
281373 if fill_cur_epoch and state .slot >= spec .MIN_ATTESTATION_INCLUSION_DELAY :
282374 slot_to_attest = state .slot - spec .MIN_ATTESTATION_INCLUSION_DELAY + 1
283375 if slot_to_attest >= spec .compute_start_slot_at_epoch (spec .get_current_epoch (state )):
284- attestations = get_valid_attestation_at_slot (
285- state ,
286- spec ,
287- slot_to_attest ,
288- participation_fn = participation_fn
289- )
290- for attestation in attestations :
291- block .body .attestations .append (attestation )
376+ _add_valid_attestations (spec , state , block , slot_to_attest , participation_fn = participation_fn )
292377 if fill_prev_epoch and state .slot >= spec .SLOTS_PER_EPOCH :
293378 slot_to_attest = state .slot - spec .SLOTS_PER_EPOCH + 1
294- attestations = get_valid_attestation_at_slot (
295- state ,
296- spec ,
297- slot_to_attest ,
298- participation_fn = participation_fn
299- )
300- for attestation in attestations :
301- block .body .attestations .append (attestation )
379+ _add_valid_attestations (spec , state , block , slot_to_attest , participation_fn = participation_fn )
302380 if sync_aggregate is not None :
303381 block .body .sync_aggregate = sync_aggregate
304382
@@ -319,7 +397,7 @@ def state_transition_with_full_attestations_block(spec, state, fill_cur_epoch, f
319397 slots = state .slot % spec .SLOTS_PER_EPOCH
320398 for slot_offset in range (slots ):
321399 target_slot = state .slot - slot_offset
322- attestations += get_valid_attestation_at_slot (
400+ attestations += get_valid_attestations_at_slot (
323401 state ,
324402 spec ,
325403 target_slot ,
@@ -330,7 +408,7 @@ def state_transition_with_full_attestations_block(spec, state, fill_cur_epoch, f
330408 slots = spec .SLOTS_PER_EPOCH - state .slot % spec .SLOTS_PER_EPOCH
331409 for slot_offset in range (1 , slots ):
332410 target_slot = state .slot - (state .slot % spec .SLOTS_PER_EPOCH ) - slot_offset
333- attestations += get_valid_attestation_at_slot (
411+ attestations += get_valid_attestations_at_slot (
334412 state ,
335413 spec ,
336414 target_slot ,
@@ -423,6 +501,9 @@ def get_empty_eip7549_aggregation_bits(spec, state, committee_bits, slot):
423501
424502
425503def get_eip7549_aggregation_bits_offset (spec , state , slot , committee_bits , committee_index ):
504+ """
505+ Calculate the offset for the aggregation bits based on the committee index.
506+ """
426507 committee_indices = spec .get_committee_indices (committee_bits )
427508 assert committee_index in committee_indices
428509 offset = 0
0 commit comments