Add EIP-7549 aggregation logic to testing tools

This commit is contained in:
Hsiao-Wei Wang 2024-04-04 17:01:08 +09:00
parent 44088378cc
commit a12e16b739
No known key found for this signature in database
GPG Key ID: AE3D6B174F971DE4
4 changed files with 95 additions and 46 deletions

View File

@ -7,7 +7,7 @@ from eth2spec.test.helpers.constants import (
MINIMAL,
)
from eth2spec.test.helpers.attestations import (
get_valid_attestation_at_slot,
get_valid_attestations_at_slot,
)
from eth2spec.test.helpers.block import (
build_empty_block_for_next_slot,
@ -109,7 +109,7 @@ def test_should_override_forkchoice_update__true(spec, state):
# Fill a slot with attestations to its parent
block = build_empty_block_for_next_slot(spec, state)
parent_block_slot = block.slot - 1
block.body.attestations = get_valid_attestation_at_slot(
block.body.attestations = get_valid_attestations_at_slot(
state,
spec,
parent_block_slot,
@ -135,7 +135,7 @@ def test_should_override_forkchoice_update__true(spec, state):
# Add attestations to the parent block
temp_state = state.copy()
next_slot(spec, temp_state)
attestations = get_valid_attestation_at_slot(
attestations = get_valid_attestations_at_slot(
temp_state,
spec,
slot_to_attest=temp_state.slot - 1,

View File

@ -84,7 +84,6 @@ def build_attestation_data(spec, state, slot, index, beacon_block_root=None, sha
target=spec.Checkpoint(epoch=spec.compute_epoch_at_slot(slot), root=epoch_boundary_root),
)
# if spec.fork == SHARDING # TODO: add extra data for shard voting
return data
@ -95,8 +94,12 @@ def get_valid_attestation(spec,
filter_participant_set=None,
beacon_block_root=None,
signed=False):
# If filter_participant_set filters everything, the attestation has 0 participants, and cannot be signed.
# Thus strictly speaking invalid when no participant is added later.
"""
Return a valid attestation at `slot` and committee index `index`.
If filter_participant_set filters everything, the attestation has 0 participants, and cannot be signed.
Thus strictly speaking invalid when no participant is added later.
"""
if slot is None:
slot = state.slot
if index is None:
@ -104,18 +107,8 @@ def get_valid_attestation(spec,
attestation_data = build_attestation_data(spec, state, slot=slot, index=index, beacon_block_root=beacon_block_root)
beacon_committee = spec.get_beacon_committee(state, slot, index)
attestation = spec.Attestation(data=attestation_data)
if is_post_eip7549(spec):
# will fill aggregation_bits later
attestation = spec.Attestation(data=attestation_data)
else:
committee_size = len(beacon_committee)
aggregation_bits = Bitlist[spec.MAX_VALIDATORS_PER_COMMITTEE](*([0] * committee_size))
attestation = spec.Attestation(
aggregation_bits=aggregation_bits,
data=attestation_data,
)
# fill the attestation with (optionally filtered) participants, and optionally sign it
fill_aggregate_attestation(spec, state, attestation, signed=signed,
filter_participant_set=filter_participant_set, committee_index=index)
@ -132,7 +125,7 @@ def sign_aggregate_attestation(spec, state, attestation_data, participants: List
spec,
state,
attestation_data,
privkey
privkey,
)
)
return bls.Aggregate(signatures)
@ -180,11 +173,16 @@ def fill_aggregate_attestation(spec, state, attestation, committee_index, signed
if filter_participant_set is not None:
participants = filter_participant_set(participants)
# initialize `aggregation_bits`
if is_post_eip7549(spec):
attestation.committee_bits = spec.Bitvector[spec.MAX_COMMITTEES_PER_SLOT]()
attestation.committee_bits[committee_index] = True
attestation.aggregation_bits = get_empty_eip7549_aggregation_bits(
spec, state, attestation.committee_bits, attestation.data.slot)
else:
committee_size = len(beacon_committee)
attestation.aggregation_bits = Bitlist[spec.MAX_VALIDATORS_PER_COMMITTEE](*([0] * committee_size))
# fill in the `aggregation_bits`
for i in range(len(beacon_committee)):
if is_post_eip7549(spec):
offset = get_eip7549_aggregation_bits_offset(
@ -205,7 +203,10 @@ def add_attestations_to_state(spec, state, attestations, slot):
spec.process_attestation(state, attestation)
def get_valid_attestation_at_slot(state, spec, slot_to_attest, participation_fn=None, beacon_block_root=None):
def get_valid_attestations_at_slot(state, spec, slot_to_attest, participation_fn=None, beacon_block_root=None):
"""
Return attestations at slot `slot_to_attest`.
"""
committees_per_slot = spec.get_committee_count_per_slot(state, spec.compute_epoch_at_slot(slot_to_attest))
for index in range(committees_per_slot):
def participants_filter(comm):
@ -213,7 +214,6 @@ def get_valid_attestation_at_slot(state, spec, slot_to_attest, participation_fn=
return comm
else:
return participation_fn(state.slot, index, comm)
# if spec.fork == SHARDING: TODO: add shard data to attestation, include shard headers in block
yield get_valid_attestation(
spec,
state,
@ -225,6 +225,48 @@ def get_valid_attestation_at_slot(state, spec, slot_to_attest, participation_fn=
)
def get_valid_attestation_at_slot(state, spec, slot_to_attest, participation_fn=None, beacon_block_root=None):
"""
Return the aggregate attestation
"""
assert is_post_eip7549(spec)
attestations = list(get_valid_attestations_at_slot(
state, spec, slot_to_attest,
participation_fn=participation_fn,
beacon_block_root=beacon_block_root,
))
if len(attestations) == 0:
return None
aggregate = spec.Attestation(data=attestations[0].data)
# fill in committee_bits
all_committee_indices = []
for attestation in attestations:
committee_indices_of_attestation = spec.get_committee_indices(attestation.committee_bits)
assert len(committee_indices_of_attestation) == 1
all_committee_indices += committee_indices_of_attestation
all_committee_indices = set(all_committee_indices)
for committee_index in all_committee_indices:
aggregate.committee_bits[committee_index] = True
# aggregate the attestation data and sigs
aggregate.aggregation_bits = get_empty_eip7549_aggregation_bits(
spec, state, aggregate.committee_bits, slot_to_attest)
for attestation in attestations:
committee_indices_of_attestation = spec.get_committee_indices(attestation.committee_bits)
assert len(committee_indices_of_attestation) == 1
committee_index = committee_indices_of_attestation[0]
offset = get_eip7549_aggregation_bits_offset(
spec, state, attestation.data.slot, aggregate.committee_bits, committee_index)
for i in range(len(attestation.aggregation_bits)):
aggregation_bits_index = offset + i
aggregate.aggregation_bits[aggregation_bits_index] = attestation.aggregation_bits[i]
aggregate.signature = bls.Aggregate([attestation.signature for attestation in attestations])
return aggregate
def next_slots_with_attestations(spec,
state,
slot_count,
@ -249,6 +291,26 @@ def next_slots_with_attestations(spec,
return state, signed_blocks, post_state
def _add_valid_attestations(spec, state, block, slot_to_attest, participation_fn=None):
if is_post_eip7549(spec):
attestation = get_valid_attestation_at_slot(
state,
spec,
slot_to_attest,
participation_fn=participation_fn,
)
block.body.attestations.append(attestation)
else:
attestations = get_valid_attestations_at_slot(
state,
spec,
slot_to_attest,
participation_fn=participation_fn,
)
for attestation in attestations:
block.body.attestations.append(attestation)
def next_epoch_with_attestations(spec,
state,
fill_cur_epoch,
@ -281,24 +343,10 @@ def state_transition_with_full_block(spec,
if fill_cur_epoch and state.slot >= spec.MIN_ATTESTATION_INCLUSION_DELAY:
slot_to_attest = state.slot - spec.MIN_ATTESTATION_INCLUSION_DELAY + 1
if slot_to_attest >= spec.compute_start_slot_at_epoch(spec.get_current_epoch(state)):
attestations = get_valid_attestation_at_slot(
state,
spec,
slot_to_attest,
participation_fn=participation_fn
)
for attestation in attestations:
block.body.attestations.append(attestation)
_add_valid_attestations(spec, state, block, slot_to_attest, participation_fn=participation_fn)
if fill_prev_epoch and state.slot >= spec.SLOTS_PER_EPOCH:
slot_to_attest = state.slot - spec.SLOTS_PER_EPOCH + 1
attestations = get_valid_attestation_at_slot(
state,
spec,
slot_to_attest,
participation_fn=participation_fn
)
for attestation in attestations:
block.body.attestations.append(attestation)
_add_valid_attestations(spec, state, block, slot_to_attest, participation_fn=participation_fn)
if sync_aggregate is not None:
block.body.sync_aggregate = sync_aggregate
@ -319,7 +367,7 @@ def state_transition_with_full_attestations_block(spec, state, fill_cur_epoch, f
slots = state.slot % spec.SLOTS_PER_EPOCH
for slot_offset in range(slots):
target_slot = state.slot - slot_offset
attestations += get_valid_attestation_at_slot(
attestations += get_valid_attestations_at_slot(
state,
spec,
target_slot,
@ -330,7 +378,7 @@ def state_transition_with_full_attestations_block(spec, state, fill_cur_epoch, f
slots = spec.SLOTS_PER_EPOCH - state.slot % spec.SLOTS_PER_EPOCH
for slot_offset in range(1, slots):
target_slot = state.slot - (state.slot % spec.SLOTS_PER_EPOCH) - slot_offset
attestations += get_valid_attestation_at_slot(
attestations += get_valid_attestations_at_slot(
state,
spec,
target_slot,
@ -423,6 +471,7 @@ def get_empty_eip7549_aggregation_bits(spec, state, committee_bits, slot):
def get_eip7549_aggregation_bits_offset(spec, state, slot, committee_bits, committee_index):
# FIXME: it's not efficient to use it as an aggregator
committee_indices = spec.get_committee_indices(committee_bits)
assert committee_index in committee_indices
offset = 0

View File

@ -4,7 +4,7 @@ from eth2spec.test.context import (
with_altair_and_later,
)
from eth2spec.test.helpers.attestations import (
get_valid_attestation_at_slot,
get_valid_attestations_at_slot,
)
from eth2spec.test.helpers.block import (
build_empty_block_for_next_slot,
@ -101,7 +101,7 @@ def test_basic_is_parent_root(spec, state):
# Fill a slot with attestations to its parent
block = build_empty_block_for_next_slot(spec, state)
parent_block_slot = block.slot - 1
block.body.attestations = get_valid_attestation_at_slot(
block.body.attestations = get_valid_attestations_at_slot(
state,
spec,
parent_block_slot,
@ -128,7 +128,7 @@ def test_basic_is_parent_root(spec, state):
slot = state.slot
# Add attestations to the parent block
attestations = get_valid_attestation_at_slot(
attestations = get_valid_attestations_at_slot(
state,
spec,
slot_to_attest=slot - 1,

View File

@ -9,7 +9,7 @@ from eth2spec.test.helpers.constants import (
from eth2spec.test.helpers.attestations import (
state_transition_with_full_block,
get_valid_attestation,
get_valid_attestation_at_slot,
get_valid_attestations_at_slot,
)
from eth2spec.test.helpers.block import (
build_empty_block,
@ -218,7 +218,7 @@ def _run_delayed_justification(spec, state, attemped_reorg, is_justifying_previo
# add attestations of y
temp_state = state.copy()
next_slot(spec, temp_state)
attestations_for_y = list(get_valid_attestation_at_slot(temp_state, spec, signed_block_y.message.slot))
attestations_for_y = list(get_valid_attestations_at_slot(temp_state, spec, signed_block_y.message.slot))
current_time = temp_state.slot * spec.config.SECONDS_PER_SLOT + store.genesis_time
on_tick_and_append_step(spec, store, current_time, test_steps)
yield from add_attestations(spec, store, attestations_for_y, test_steps)
@ -345,10 +345,10 @@ def _run_include_votes_of_another_empty_chain(spec, state, enough_ffg, is_justif
# create 2/3 votes for the empty chain
attestations_for_y = []
# target_is_current = not is_justifying_previous_epoch
attestations = list(get_valid_attestation_at_slot(state, spec, state_a.slot))
attestations = list(get_valid_attestations_at_slot(state, spec, state_a.slot))
attestations_for_y.append(attestations)
for state in states_of_empty_chain:
attestations = list(get_valid_attestation_at_slot(state, spec, state.slot))
attestations = list(get_valid_attestations_at_slot(state, spec, state.slot))
attestations_for_y.append(attestations)
state = state_a.copy()