From eec5503d4f8628b078719410f697234ce6737b72 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Fri, 15 Mar 2024 00:14:43 +0800 Subject: [PATCH 1/3] Fix tests and specs --- configs/mainnet.yaml | 3 + configs/minimal.yaml | 3 + presets/mainnet/eip7549.yaml | 8 + presets/minimal/eip7549.yaml | 8 + specs/_features/eip7549/beacon-chain.md | 44 ++++-- specs/_features/eip7549/fork.md | 141 ++++++++++++++++++ specs/_features/eip7549/p2p-interface.md | 4 +- specs/_features/eip7549/validator.md | 14 +- tests/core/pyspec/eth2spec/test/context.py | 3 +- .../eth2spec/test/helpers/attestations.py | 51 +++++-- .../test/helpers/attester_slashings.py | 8 + .../pyspec/eth2spec/test/helpers/constants.py | 3 + .../pyspec/eth2spec/test/helpers/forks.py | 6 +- .../eth2spec/test/helpers/multi_operations.py | 4 +- .../test_process_attestation.py | 20 ++- .../test/phase0/fork_choice/test_ex_ante.py | 18 ++- .../test/phase0/sanity/test_blocks.py | 13 +- .../fork_choice/test_on_attestation.py | 6 +- 18 files changed, 313 insertions(+), 44 deletions(-) create mode 100644 presets/mainnet/eip7549.yaml create mode 100644 presets/minimal/eip7549.yaml create mode 100644 specs/_features/eip7549/fork.md diff --git a/configs/mainnet.yaml b/configs/mainnet.yaml index 317daa1a45..a4d157d80e 100644 --- a/configs/mainnet.yaml +++ b/configs/mainnet.yaml @@ -56,6 +56,9 @@ EIP6110_FORK_EPOCH: 18446744073709551615 # EIP7002 EIP7002_FORK_VERSION: 0x05000000 # temporary stub EIP7002_FORK_EPOCH: 18446744073709551615 +# EIP7549 +EIP7549_FORK_VERSION: 0x05000000 # temporary stub +EIP7549_FORK_EPOCH: 18446744073709551615 # WHISK WHISK_FORK_VERSION: 0x06000000 # temporary stub WHISK_FORK_EPOCH: 18446744073709551615 diff --git a/configs/minimal.yaml b/configs/minimal.yaml index 6b2da84fdb..0525762820 100644 --- a/configs/minimal.yaml +++ b/configs/minimal.yaml @@ -55,6 +55,9 @@ EIP6110_FORK_EPOCH: 18446744073709551615 # EIP7002 EIP7002_FORK_VERSION: 0x05000001 EIP7002_FORK_EPOCH: 18446744073709551615 +# EIP7549 +EIP7549_FORK_VERSION: 0x05000000 # temporary stub +EIP7549_FORK_EPOCH: 18446744073709551615 # WHISK WHISK_FORK_VERSION: 0x06000001 WHISK_FORK_EPOCH: 18446744073709551615 diff --git a/presets/mainnet/eip7549.yaml b/presets/mainnet/eip7549.yaml new file mode 100644 index 0000000000..6d10f82e15 --- /dev/null +++ b/presets/mainnet/eip7549.yaml @@ -0,0 +1,8 @@ +# Mainnet preset - EIP7594 + +# # Max operations per block +# --------------------------------------------------------------- +# `uint64(2**0)` (= 1) +MAX_ATTESTER_SLASHINGS_EIP7549: 1 +# `uint64(2 * 3)` (= 8) +MAX_ATTESTATIONS_EIP7549: 8 diff --git a/presets/minimal/eip7549.yaml b/presets/minimal/eip7549.yaml new file mode 100644 index 0000000000..17e21652a3 --- /dev/null +++ b/presets/minimal/eip7549.yaml @@ -0,0 +1,8 @@ +# Minimal preset - EIP7594 + +# # Max operations per block +# --------------------------------------------------------------- +# `uint64(2**0)` (= 1) +MAX_ATTESTER_SLASHINGS_EIP7549: 1 +# `uint64(2 * 3)` (= 8) +MAX_ATTESTATIONS_EIP7549: 8 diff --git a/specs/_features/eip7549/beacon-chain.md b/specs/_features/eip7549/beacon-chain.md index 2382551e21..bf1d37c67e 100644 --- a/specs/_features/eip7549/beacon-chain.md +++ b/specs/_features/eip7549/beacon-chain.md @@ -12,6 +12,7 @@ - [Modified containers](#modified-containers) - [`Attestation`](#attestation) - [`IndexedAttestation`](#indexedattestation) + - [`BeaconBlockBody`](#beaconblockbody) - [Helper functions](#helper-functions) - [Misc](#misc) - [`get_committee_indices`](#get_committee_indices) @@ -33,8 +34,8 @@ This is the beacon chain specification to move the attestation committee index o | Name | Value | Description | | - | - | - | -| `MAX_ATTESTER_SLASHINGS` | `2**0` (= 1) | -| `MAX_ATTESTATIONS` | `2**3` (= 8) | +| `MAX_ATTESTER_SLASHINGS_EIP7549` | `2**0` (= 1) | +| `MAX_ATTESTATIONS_EIP7549` | `2**3` (= 8) | ## Containers @@ -44,7 +45,7 @@ This is the beacon chain specification to move the attestation committee index o ```python class Attestation(Container): - aggregation_bits: List[Bitlist[MAX_VALIDATORS_PER_COMMITTEE], MAX_COMMITTEES_PER_SLOT] # [Modified in EIP7549] + aggregation_bits_list: List[Bitlist[MAX_VALIDATORS_PER_COMMITTEE], MAX_COMMITTEES_PER_SLOT] # [Modified in EIP7549] data: AttestationData committee_bits: Bitvector[MAX_COMMITTEES_PER_SLOT] # [New in EIP7549] signature: BLSSignature @@ -60,6 +61,26 @@ class IndexedAttestation(Container): signature: BLSSignature ``` +#### `BeaconBlockBody` + +```python +class BeaconBlockBody(Container): + randao_reveal: BLSSignature + eth1_data: Eth1Data # Eth1 data vote + graffiti: Bytes32 # Arbitrary data + # Operations + proposer_slashings: List[ProposerSlashing, MAX_PROPOSER_SLASHINGS] + attester_slashings: List[AttesterSlashing, MAX_ATTESTER_SLASHINGS_EIP7549] # [Modified in EIP7549] + attestations: List[Attestation, MAX_ATTESTATIONS_EIP7549] # [Modified in EIP7549] + deposits: List[Deposit, MAX_DEPOSITS] + voluntary_exits: List[SignedVoluntaryExit, MAX_VOLUNTARY_EXITS] + sync_aggregate: SyncAggregate + # Execution + execution_payload: ExecutionPayload + bls_to_execution_changes: List[SignedBLSToExecutionChange, MAX_BLS_TO_EXECUTION_CHANGES] + blob_kzg_commitments: List[KZGCommitment, MAX_BLOB_COMMITMENTS_PER_BLOCK] +``` + ## Helper functions ### Misc @@ -67,8 +88,8 @@ class IndexedAttestation(Container): #### `get_committee_indices` ```python -def get_committee_indices(commitee_bits: Bitvector) -> List[CommitteeIndex]: - return [CommitteeIndex(index) for bit, index in enumerate(commitee_bits) if bit] +def get_committee_indices(commitee_bits: Bitvector) -> Sequence[CommitteeIndex]: + return [CommitteeIndex(index) for index, bit in enumerate(commitee_bits) if bit] ``` ### Beacon state accessors @@ -78,13 +99,14 @@ def get_committee_indices(commitee_bits: Bitvector) -> List[CommitteeIndex]: ```python def get_attesting_indices(state: BeaconState, attestation: Attestation) -> Set[ValidatorIndex]: """ - Return the set of attesting indices corresponding to ``aggregation_bits`` and ``committee_bits``. + Return the set of attesting indices corresponding to ``aggregation_bits_list`` and ``committee_bits``. """ - output = set() committee_indices = get_committee_indices(attestation.committee_bits) + aggregation_bits_list_index = 0 for index in committee_indices: - attesting_bits = attestation.aggregation_bits[index] + attesting_bits = attestation.aggregation_bits_list[aggregation_bits_list_index] + aggregation_bits_list_index += 1 committee = get_beacon_committee(state, attestation.data.slot, index) committee_attesters = set(index for i, index in enumerate(committee) if attesting_bits[i]) output = output.union(committee_attesters) @@ -106,11 +128,13 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None: # [Modified in EIP7549] assert data.index == 0 committee_indices = get_committee_indices(attestation.committee_bits) - assert len(committee_indices) == len(attestation.aggregation_bits) + assert len(committee_indices) == len(attestation.aggregation_bits_list) + aggregation_bits_list_index = 0 for index in committee_indices: assert index < get_committee_count_per_slot(state, data.target.epoch) committee = get_beacon_committee(state, data.slot, index) - assert len(attestation.aggregation_bits[index]) == len(committee) + assert len(attestation.aggregation_bits_list[aggregation_bits_list_index]) == len(committee) + aggregation_bits_list_index += 1 # Participation flag indices participation_flag_indices = get_attestation_participation_flag_indices(state, data, state.slot - data.slot) diff --git a/specs/_features/eip7549/fork.md b/specs/_features/eip7549/fork.md new file mode 100644 index 0000000000..c1da373d85 --- /dev/null +++ b/specs/_features/eip7549/fork.md @@ -0,0 +1,141 @@ +# EIP-7549 -- Fork Logic + +**Notice**: This document is a work-in-progress for researchers and implementers. + +## Table of contents + + + + +- [Introduction](#introduction) +- [Configuration](#configuration) +- [Helper functions](#helper-functions) + - [Misc](#misc) + - [Modified `compute_fork_version`](#modified-compute_fork_version) +- [Fork to EIP-7549](#fork-to-eip-7549) + - [Fork trigger](#fork-trigger) + - [Upgrading the state](#upgrading-the-state) + + + +## Introduction + +This document describes the process of EIP-7549 upgrade. + +## Configuration + +Warning: this configuration is not definitive. + +| Name | Value | +| - | - | +| `EIP7549_FORK_VERSION` | `Version('0x05000000')` | +| `EIP7549_FORK_EPOCH` | `Epoch(18446744073709551615)` **TBD** | + +## Helper functions + +### Misc + +#### Modified `compute_fork_version` + +```python +def compute_fork_version(epoch: Epoch) -> Version: + """ + Return the fork version at the given ``epoch``. + """ + if epoch >= EIP7549_FORK_EPOCH: + return EIP7549_FORK_VERSION + if epoch >= DENEB_FORK_EPOCH: + return DENEB_FORK_VERSION + if epoch >= CAPELLA_FORK_EPOCH: + return CAPELLA_FORK_VERSION + if epoch >= BELLATRIX_FORK_EPOCH: + return BELLATRIX_FORK_VERSION + if epoch >= ALTAIR_FORK_EPOCH: + return ALTAIR_FORK_VERSION + return GENESIS_FORK_VERSION +``` + +## Fork to EIP-7549 + +### Fork trigger + +TBD. This fork is defined for testing purposes, the EIP may be combined with other consensus-layer upgrade. +For now, we assume the condition will be triggered at epoch `EIP7549_FORK_EPOCH`. + +Note that for the pure EIP-7549 networks, we don't apply `upgrade_to_eip7549` since it starts with EIP-7549 version logic. + +### Upgrading the state + +If `state.slot % SLOTS_PER_EPOCH == 0` and `compute_epoch_at_slot(state.slot) == EIP7549_FORK_EPOCH`, +an irregular state change is made to upgrade to EIP-7549. + +```python +def upgrade_to_eip7549(pre: capella.BeaconState) -> BeaconState: + epoch = capella.get_current_epoch(pre) + latest_execution_payload_header = ExecutionPayloadHeader( + parent_hash=pre.latest_execution_payload_header.parent_hash, + fee_recipient=pre.latest_execution_payload_header.fee_recipient, + state_root=pre.latest_execution_payload_header.state_root, + receipts_root=pre.latest_execution_payload_header.receipts_root, + logs_bloom=pre.latest_execution_payload_header.logs_bloom, + prev_randao=pre.latest_execution_payload_header.prev_randao, + block_number=pre.latest_execution_payload_header.block_number, + gas_limit=pre.latest_execution_payload_header.gas_limit, + gas_used=pre.latest_execution_payload_header.gas_used, + timestamp=pre.latest_execution_payload_header.timestamp, + extra_data=pre.latest_execution_payload_header.extra_data, + base_fee_per_gas=pre.latest_execution_payload_header.base_fee_per_gas, + block_hash=pre.latest_execution_payload_header.block_hash, + transactions_root=pre.latest_execution_payload_header.transactions_root, + withdrawals_root=pre.latest_execution_payload_header.withdrawals_root, + ) + post = BeaconState( + # Versioning + genesis_time=pre.genesis_time, + genesis_validators_root=pre.genesis_validators_root, + slot=pre.slot, + fork=Fork( + previous_version=pre.fork.current_version, + current_version=EIP7549_FORK_VERSION, # [Modified in EIP-7549] + epoch=epoch, + ), + # History + latest_block_header=pre.latest_block_header, + block_roots=pre.block_roots, + state_roots=pre.state_roots, + historical_roots=pre.historical_roots, + # Eth1 + eth1_data=pre.eth1_data, + eth1_data_votes=pre.eth1_data_votes, + eth1_deposit_index=pre.eth1_deposit_index, + # Registry + validators=pre.validators, + balances=pre.balances, + # Randomness + randao_mixes=pre.randao_mixes, + # Slashings + slashings=pre.slashings, + # Participation + previous_epoch_participation=pre.previous_epoch_participation, + current_epoch_participation=pre.current_epoch_participation, + # Finality + justification_bits=pre.justification_bits, + previous_justified_checkpoint=pre.previous_justified_checkpoint, + current_justified_checkpoint=pre.current_justified_checkpoint, + finalized_checkpoint=pre.finalized_checkpoint, + # Inactivity + inactivity_scores=pre.inactivity_scores, + # Sync + current_sync_committee=pre.current_sync_committee, + next_sync_committee=pre.next_sync_committee, + # Execution-layer + latest_execution_payload_header=latest_execution_payload_header, + # Withdrawals + next_withdrawal_index=pre.next_withdrawal_index, + next_withdrawal_validator_index=pre.next_withdrawal_validator_index, + # Deep history valid from Capella onwards + historical_summaries=pre.historical_summaries, + ) + + return post +``` diff --git a/specs/_features/eip7549/p2p-interface.md b/specs/_features/eip7549/p2p-interface.md index c7413ea4b0..a93fb9bc9c 100644 --- a/specs/_features/eip7549/p2p-interface.md +++ b/specs/_features/eip7549/p2p-interface.md @@ -36,7 +36,7 @@ The `beacon_aggregate_and_proof` and `beacon_attestation_{subnet_id}` topics are The following convenience variables are re-defined - `index = get_committee_indices(aggregate.committee_bits)[0]` -- `aggregation_bits = aggregate.aggregation_bits[0]` +- `aggregation_bits = aggregate.aggregation_bits_list[0]` The following validations are added: * [REJECT] `len(committee_indices) == len(aggregate.attestation_bits) == 1`, where `committee_indices = get_committee_indices(aggregate)`. @@ -46,7 +46,7 @@ The following validations are added: The following convenience variables are re-defined - `index = get_committee_indices(attestation.committee_bits)[0]` -- `aggregation_bits = attestation.aggregation_bits[0]` +- `aggregation_bits = attestation.aggregation_bits_list[0]` The following validations are added: * [REJECT] `len(committee_indices) == len(attestation.attestation_bits) == 1`, where `committee_indices = get_committee_indices(attestation)`. diff --git a/specs/_features/eip7549/validator.md b/specs/_features/eip7549/validator.md index 6ae84aca63..ab296eb456 100644 --- a/specs/_features/eip7549/validator.md +++ b/specs/_features/eip7549/validator.md @@ -9,6 +9,7 @@ - [Modifications in EIP-7549](#modifications-in-eip-7549) - [Block proposal](#block-proposal) - [Constructing the `BeaconBlockBody`](#constructing-the-beaconblockbody) + - [Attester slashings](#attester-slashings) - [Attestations](#attestations) - [Attesting](#attesting) - [Construct attestation](#construct-attestation) @@ -24,8 +25,14 @@ #### Constructing the `BeaconBlockBody` +##### Attester slashings + +Changed the max attestations size to `MAX_ATTESTER_SLASHINGS_EIP7549`. + ##### Attestations +Changed the max attestations size to `MAX_ATTESTATIONS_EIP7549`. + Attestations received from aggregators with disjoint `committee_bits` sets and equal `AttestationData` SHOULD be consolidated into a single `Attestation` object. ### Attesting @@ -33,8 +40,8 @@ Attestations received from aggregators with disjoint `committee_bits` sets and e #### Construct attestation - Set `attestation_data.index = 0`. -- Let `aggregation_bits` be a `Bitlist[MAX_VALIDATORS_PER_COMMITTEE]` of length `len(committee)`, where the bit of the index of the validator in the `committee` is set to `0b1`. -- Set `attestation.aggregation_bits = [aggregation_bits]`, a list of length 1 +- Let `aggregation_bits_list` be a `Bitlist[MAX_VALIDATORS_PER_COMMITTEE]` of length `len(committee)`, where the bit of the index of the validator in the `committee` is set to `0b1`. +- Set `attestation.aggregation_bits_list = [aggregation_bits_list]`, a list of length 1 - Let `committee_bits` be a `Bitvector[MAX_COMMITTEES_PER_SLOT]`, where the bit at the index associated with the validator's committee is set to `0b1` - Set `attestation.committee_bits = committee_bits` @@ -46,6 +53,5 @@ Attestations received from aggregators with disjoint `committee_bits` sets and e - Set `attestation_data.index = 0`. - Let `aggregation_bits` be a `Bitlist[MAX_VALIDATORS_PER_COMMITTEE]` of length `len(committee)`, where each bit set from each individual attestation is set to `0b1`. -- Set `attestation.aggregation_bits = [aggregation_bits]`, a list of length 1 +- Set `attestation.aggregation_bits_list = [aggregation_bits]`, a list of length 1 - Set `attestation.committee_bits = committee_bits`, where `committee_bits` has the same value as in each individual attestation - diff --git a/tests/core/pyspec/eth2spec/test/context.py b/tests/core/pyspec/eth2spec/test/context.py index e940e24e72..43562512f6 100644 --- a/tests/core/pyspec/eth2spec/test/context.py +++ b/tests/core/pyspec/eth2spec/test/context.py @@ -8,7 +8,7 @@ from .exceptions import SkippedTest from .helpers.constants import ( PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB, - EIP6110, EIP7002, EIP7594, + EIP6110, EIP7002, EIP7549, EIP7594, WHISK, MINIMAL, ALL_PHASES, @@ -509,6 +509,7 @@ def wrapper(*args, spec: Spec, **kw): with_deneb_and_later = with_all_phases_from(DENEB) with_eip6110_and_later = with_all_phases_from(EIP6110) with_eip7002_and_later = with_all_phases_from(EIP7002) +with_eip7549_and_later = with_all_phases_from(EIP7549) with_whisk_and_later = with_all_phases_from(WHISK, all_phases=ALLOWED_TEST_RUNNER_FORKS) with_eip7594_and_later = with_all_phases_from(EIP7594, all_phases=ALLOWED_TEST_RUNNER_FORKS) diff --git a/tests/core/pyspec/eth2spec/test/helpers/attestations.py b/tests/core/pyspec/eth2spec/test/helpers/attestations.py index 9e549a74bf..000318de41 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/attestations.py +++ b/tests/core/pyspec/eth2spec/test/helpers/attestations.py @@ -5,7 +5,7 @@ from eth2spec.test.context import expect_assertion_error from eth2spec.test.helpers.state import state_transition_and_sign_block, next_epoch, next_slot from eth2spec.test.helpers.block import build_empty_block_for_next_slot -from eth2spec.test.helpers.forks import is_post_altair, is_post_deneb +from eth2spec.test.helpers.forks import is_post_altair, is_post_deneb, is_post_eip7549 from eth2spec.test.helpers.keys import privkeys from eth2spec.utils import bls from eth2spec.utils.ssz.ssz_typing import Bitlist @@ -78,7 +78,7 @@ def build_attestation_data(spec, state, slot, index, beacon_block_root=None, sha data = spec.AttestationData( slot=slot, - index=index, + index=0 if is_post_eip7549(spec) else index, beacon_block_root=beacon_block_root, source=spec.Checkpoint(epoch=source_epoch, root=source_root), target=spec.Checkpoint(epoch=spec.compute_epoch_at_slot(slot), root=epoch_boundary_root), @@ -108,12 +108,19 @@ def get_valid_attestation(spec, 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, - ) + if is_post_eip7549(spec): + attestation = spec.Attestation( + aggregation_bits_list=[aggregation_bits], + data=attestation_data, + ) + else: + 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) + fill_aggregate_attestation(spec, state, attestation, signed=signed, + filter_participant_set=filter_participant_set, committee_index=index) return attestation @@ -159,7 +166,7 @@ def compute_max_inclusion_slot(spec, attestation): return attestation.data.slot + spec.SLOTS_PER_EPOCH -def fill_aggregate_attestation(spec, state, attestation, signed=False, filter_participant_set=None): +def fill_aggregate_attestation(spec, state, attestation, committee_index, signed=False, filter_participant_set=None): """ `signed`: Signing is optional. `filter_participant_set`: Optional, filters the full committee indices set (default) to a subset that participates @@ -167,15 +174,24 @@ def fill_aggregate_attestation(spec, state, attestation, signed=False, filter_pa beacon_committee = spec.get_beacon_committee( state, attestation.data.slot, - attestation.data.index, + committee_index, ) # By default, have everyone participate participants = set(beacon_committee) # But optionally filter the participants to a smaller amount if filter_participant_set is not None: participants = filter_participant_set(participants) + + committee_bits = spec.Bitvector[spec.MAX_COMMITTEES_PER_SLOT]() for i in range(len(beacon_committee)): - attestation.aggregation_bits[i] = beacon_committee[i] in participants + if is_post_eip7549(spec): + committee_bits[committee_index] = True + attestation.aggregation_bits_list[0][i] = beacon_committee[i] in participants + else: + attestation.aggregation_bits[i] = beacon_committee[i] in participants + + if is_post_eip7549(spec): + attestation.committee_bits = committee_bits if signed and len(participants) > 0: sign_attestation(spec, state, attestation) @@ -349,8 +365,12 @@ def temp_participants_filter(comm): return participation_fn(state.slot, committee_index, comm) attestation = get_valid_attestation(spec, state, index=committee_index, filter_participant_set=temp_participants_filter, signed=True) - if any(attestation.aggregation_bits): # Only if there is at least 1 participant. - attestations.append(attestation) + if is_post_eip7549(spec): + if any(attestation.aggregation_bits_list): + attestations.append(attestation) + else: + if any(attestation.aggregation_bits): # Only if there is at least 1 participant. + attestations.append(attestation) # fill each created slot in state after inclusion delay if state.slot >= start_slot + spec.MIN_ATTESTATION_INCLUSION_DELAY: inclusion_slot = state.slot - spec.MIN_ATTESTATION_INCLUSION_DELAY @@ -384,3 +404,10 @@ def cached_prepare_state_with_attestations(spec, state): # Put the LRU cache result into the state view, as if we transitioned the original view state.set_backing(_prep_state_cache_dict[key]) + + +def get_max_attestations(spec): + if is_post_eip7549(spec): + return spec.MAX_ATTESTATIONS_EIP7549 + else: + return spec.MAX_ATTESTATIONS diff --git a/tests/core/pyspec/eth2spec/test/helpers/attester_slashings.py b/tests/core/pyspec/eth2spec/test/helpers/attester_slashings.py index 06e904805c..bdda3bf766 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/attester_slashings.py +++ b/tests/core/pyspec/eth2spec/test/helpers/attester_slashings.py @@ -1,4 +1,5 @@ from eth2spec.test.helpers.attestations import get_valid_attestation, sign_attestation, sign_indexed_attestation +from eth2spec.test.helpers.forks import is_post_eip7549 def get_valid_attester_slashing(spec, state, slot=None, signed_1=False, signed_2=False, filter_participant_set=None): @@ -62,3 +63,10 @@ def get_attestation_1_data(spec, att_slashing): def get_attestation_2_data(spec, att_slashing): return att_slashing.attestation_2.data + + +def get_max_attester_slashings(spec): + if is_post_eip7549(spec): + return spec.MAX_ATTESTER_SLASHINGS_EIP7549 + else: + return spec.MAX_ATTESTER_SLASHINGS diff --git a/tests/core/pyspec/eth2spec/test/helpers/constants.py b/tests/core/pyspec/eth2spec/test/helpers/constants.py index a51201906c..b44ba91481 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/constants.py +++ b/tests/core/pyspec/eth2spec/test/helpers/constants.py @@ -18,6 +18,7 @@ DAS = SpecForkName('das') EIP6110 = SpecForkName('eip6110') EIP7002 = SpecForkName('eip7002') +EIP7549 = SpecForkName('eip7549') WHISK = SpecForkName('whisk') EIP7594 = SpecForkName('eip7594') @@ -38,6 +39,7 @@ # Experimental patches EIP6110, EIP7002, + EIP7549, EIP7594, ) # The forks that have light client specs @@ -59,6 +61,7 @@ EIP6110: DENEB, WHISK: CAPELLA, EIP7002: CAPELLA, + EIP7549: DENEB, EIP7594: DENEB, } diff --git a/tests/core/pyspec/eth2spec/test/helpers/forks.py b/tests/core/pyspec/eth2spec/test/helpers/forks.py index d58743a7d2..f9d409b24f 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/forks.py +++ b/tests/core/pyspec/eth2spec/test/helpers/forks.py @@ -1,6 +1,6 @@ from .constants import ( PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB, - EIP6110, EIP7002, WHISK, + EIP6110, EIP7002, EIP7549, WHISK, PREVIOUS_FORK_OF, ) @@ -45,6 +45,10 @@ def is_post_eip7002(spec): return is_post_fork(spec.fork, EIP7002) +def is_post_eip7549(spec): + return is_post_fork(spec.fork, EIP7549) + + def is_post_whisk(spec): return is_post_fork(spec.fork, WHISK) diff --git a/tests/core/pyspec/eth2spec/test/helpers/multi_operations.py b/tests/core/pyspec/eth2spec/test/helpers/multi_operations.py index c11bb55842..9943fcdd45 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/multi_operations.py +++ b/tests/core/pyspec/eth2spec/test/helpers/multi_operations.py @@ -13,7 +13,7 @@ ) from eth2spec.test.helpers.proposer_slashings import get_valid_proposer_slashing from eth2spec.test.helpers.attester_slashings import get_valid_attester_slashing_by_indices -from eth2spec.test.helpers.attestations import get_valid_attestation +from eth2spec.test.helpers.attestations import get_valid_attestation, get_max_attestations from eth2spec.test.helpers.deposits import build_deposit, deposit_from_context from eth2spec.test.helpers.voluntary_exits import prepare_signed_exits from eth2spec.test.helpers.bls_to_execution_changes import get_signed_address_change @@ -101,7 +101,7 @@ def get_random_attester_slashings(spec, state, rng, slashed_indices=[]): def get_random_attestations(spec, state, rng): - num_attestations = rng.randrange(1, spec.MAX_ATTESTATIONS) + num_attestations = rng.randrange(1, get_max_attestations(spec)) attestations = [ get_valid_attestation( diff --git a/tests/core/pyspec/eth2spec/test/phase0/block_processing/test_process_attestation.py b/tests/core/pyspec/eth2spec/test/phase0/block_processing/test_process_attestation.py index 2cf314dc5f..56ee12026b 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/block_processing/test_process_attestation.py +++ b/tests/core/pyspec/eth2spec/test/phase0/block_processing/test_process_attestation.py @@ -14,6 +14,7 @@ sign_attestation, compute_max_inclusion_slot, ) +from eth2spec.test.helpers.forks import is_post_eip7549 from eth2spec.test.helpers.state import ( next_slots, next_epoch_via_block, @@ -346,7 +347,10 @@ def test_invalid_too_many_aggregation_bits(spec, state): next_slots(spec, state, spec.MIN_ATTESTATION_INCLUSION_DELAY) # one too many bits - attestation.aggregation_bits.append(0b0) + if is_post_eip7549(spec): + attestation.aggregation_bits_list[0].append(0b0) + else: + attestation.aggregation_bits.append(0b0) yield from run_attestation_processing(spec, state, attestation, valid=False) @@ -357,13 +361,21 @@ def test_invalid_too_few_aggregation_bits(spec, state): attestation = get_valid_attestation(spec, state) next_slots(spec, state, spec.MIN_ATTESTATION_INCLUSION_DELAY) - attestation.aggregation_bits = Bitlist[spec.MAX_VALIDATORS_PER_COMMITTEE]( - *([0b1] + [0b0] * (len(attestation.aggregation_bits) - 1))) + if is_post_eip7549(spec): + attestation.aggregation_bits_list[0] = Bitlist[spec.MAX_VALIDATORS_PER_COMMITTEE]( + *([0b1] + [0b0] * (len(attestation.aggregation_bits_list[0]) - 1))) + else: + attestation.aggregation_bits = Bitlist[spec.MAX_VALIDATORS_PER_COMMITTEE]( + *([0b1] + [0b0] * (len(attestation.aggregation_bits) - 1)) + ) sign_attestation(spec, state, attestation) # one too few bits - attestation.aggregation_bits = attestation.aggregation_bits[:-1] + if is_post_eip7549(spec): + attestation.aggregation_bits_list = attestation.aggregation_bits_list[0][:-1] + else: + attestation.aggregation_bits = attestation.aggregation_bits[:-1] yield from run_attestation_processing(spec, state, attestation, valid=False) diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_ex_ante.py b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_ex_ante.py index 9978f7d46c..744a47f89b 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_ex_ante.py +++ b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_ex_ante.py @@ -11,6 +11,7 @@ build_empty_block, ) from eth2spec.test.helpers.constants import MAINNET +from eth2spec.test.helpers.forks import is_post_eip7549 from eth2spec.test.helpers.fork_choice import ( get_genesis_forkchoice_store_and_block, on_tick_and_append_step, @@ -31,6 +32,14 @@ def _apply_base_block_a(spec, state, store, test_steps): assert spec.get_head(store) == signed_block_a.message.hash_tree_root() +def _get_aggregation_bits(spec, attestation): + if is_post_eip7549(spec): + aggregation_bits = attestation.aggregation_bits_list[0] + else: + aggregation_bits = attestation.aggregation_bits + return aggregation_bits + + @with_altair_and_later @spec_state_test def test_ex_ante_vanilla(spec, state): @@ -78,7 +87,8 @@ def _filter_participant_set(participants): spec, state_b, slot=state_b.slot, signed=False, filter_participant_set=_filter_participant_set ) attestation.data.beacon_block_root = signed_block_b.message.hash_tree_root() - assert len([i for i in attestation.aggregation_bits if i == 1]) == 1 + aggregation_bits = _get_aggregation_bits(spec, attestation) + assert len([i for i in aggregation_bits if i == 1]) == 1 sign_attestation(spec, state_b, attestation) # Block C received at N+2 — C is head @@ -180,7 +190,8 @@ def _filter_participant_set(participants): spec, state_b, slot=state_b.slot, signed=False, filter_participant_set=_filter_participant_set ) attestation.data.beacon_block_root = signed_block_b.message.hash_tree_root() - assert len([i for i in attestation.aggregation_bits if i == 1]) == participant_num + aggregation_bits = _get_aggregation_bits(spec, attestation) + assert len([i for i in aggregation_bits if i == 1]) == participant_num sign_attestation(spec, state_b, attestation) # Attestation_set_1 received at N+2 — B is head because B's attestation_score > C's proposer_score. @@ -304,7 +315,8 @@ def _filter_participant_set(participants): spec, state_c, slot=state_c.slot, signed=False, filter_participant_set=_filter_participant_set ) attestation.data.beacon_block_root = signed_block_c.message.hash_tree_root() - assert len([i for i in attestation.aggregation_bits if i == 1]) == 1 + aggregation_bits = _get_aggregation_bits(spec, attestation) + assert len([i for i in aggregation_bits if i == 1]) == 1 sign_attestation(spec, state_c, attestation) # Block D at slot `N + 3`, parent is B diff --git a/tests/core/pyspec/eth2spec/test/phase0/sanity/test_blocks.py b/tests/core/pyspec/eth2spec/test/phase0/sanity/test_blocks.py index d31955e827..e15c3a8f66 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/sanity/test_blocks.py +++ b/tests/core/pyspec/eth2spec/test/phase0/sanity/test_blocks.py @@ -15,6 +15,7 @@ get_valid_attester_slashing_by_indices, get_valid_attester_slashing, get_indexed_attestation_participants, + get_max_attester_slashings, ) from eth2spec.test.helpers.proposer_slashings import get_valid_proposer_slashing, check_proposer_slashing_effect from eth2spec.test.helpers.attestations import get_valid_attestation @@ -30,7 +31,11 @@ compute_sync_committee_participant_reward_and_penalty, ) from eth2spec.test.helpers.constants import PHASE0, MINIMAL -from eth2spec.test.helpers.forks import is_post_altair, is_post_bellatrix, is_post_capella +from eth2spec.test.helpers.forks import ( + is_post_altair, + is_post_bellatrix, + is_post_capella, +) from eth2spec.test.context import ( spec_test, spec_state_test, dump_skipping_message, with_phases, with_all_phases, single_phase, @@ -550,7 +555,7 @@ def test_attester_slashing(spec, state): @with_all_phases @spec_state_test def test_invalid_duplicate_attester_slashing_same_block(spec, state): - if spec.MAX_ATTESTER_SLASHINGS < 2: + if get_max_attester_slashings(spec) < 2: return dump_skipping_message("Skip test if config cannot handle multiple AttesterSlashings per block") attester_slashing = get_valid_attester_slashing(spec, state, signed_1=True, signed_2=True) @@ -578,7 +583,7 @@ def test_invalid_duplicate_attester_slashing_same_block(spec, state): @with_all_phases @spec_state_test def test_multiple_attester_slashings_no_overlap(spec, state): - if spec.MAX_ATTESTER_SLASHINGS < 2: + if get_max_attester_slashings(spec) < 2: return dump_skipping_message("Skip test if config cannot handle multiple AttesterSlashings per block") # copy for later balance lookups. @@ -618,7 +623,7 @@ def test_multiple_attester_slashings_no_overlap(spec, state): @with_all_phases @spec_state_test def test_multiple_attester_slashings_partial_overlap(spec, state): - if spec.MAX_ATTESTER_SLASHINGS < 2: + if get_max_attester_slashings(spec) < 2: return dump_skipping_message("Skip test if config cannot handle multiple AttesterSlashings per block") # copy for later balance lookups. diff --git a/tests/core/pyspec/eth2spec/test/phase0/unittests/fork_choice/test_on_attestation.py b/tests/core/pyspec/eth2spec/test/phase0/unittests/fork_choice/test_on_attestation.py index 6e545cef79..128c6bd704 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/unittests/fork_choice/test_on_attestation.py +++ b/tests/core/pyspec/eth2spec/test/phase0/unittests/fork_choice/test_on_attestation.py @@ -2,6 +2,7 @@ from eth2spec.test.helpers.block import build_empty_block_for_next_slot from eth2spec.test.helpers.attestations import get_valid_attestation, sign_attestation from eth2spec.test.helpers.constants import ALL_PHASES +from eth2spec.test.helpers.forks import is_post_eip7549 from eth2spec.test.helpers.state import transition_to, state_transition_and_sign_block, next_epoch, next_slot from eth2spec.test.helpers.fork_choice import get_genesis_forkchoice_store @@ -325,6 +326,9 @@ def test_on_attestation_invalid_attestation(spec, state): attestation = get_valid_attestation(spec, state, slot=block.slot, signed=True) # make invalid by using an invalid committee index - attestation.data.index = spec.MAX_COMMITTEES_PER_SLOT * spec.SLOTS_PER_EPOCH + if is_post_eip7549(spec): + attestation.committee_bits = spec.Bitvector[spec.MAX_COMMITTEES_PER_SLOT]() + else: + attestation.data.index = spec.MAX_COMMITTEES_PER_SLOT * spec.SLOTS_PER_EPOCH run_on_attestation(spec, state, store, attestation, False) From ce591f98c85695a4a3ae3129219937ceaafefe98 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Mon, 1 Apr 2024 10:43:38 +0900 Subject: [PATCH 2/3] Enable EIP7549 CI --- .circleci/config.yml | 16 ++++++++++++++++ .github/workflows/run-tests.yml | 2 +- Makefile | 2 +- specs/_features/eip7549/beacon-chain.md | 2 +- 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 0367da9dd9..ef2eff9e26 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -181,6 +181,19 @@ jobs: command: make citest fork=eip7002 - store_test_results: path: tests/core/pyspec/test-reports + test-eip7549: + docker: + - image: circleci/python:3.9 + working_directory: ~/specs-repo + steps: + - restore_cache: + key: v3-specs-repo-{{ .Branch }}-{{ .Revision }} + - restore_pyspec_cached_venv + - run: + name: Run py-tests + command: make citest fork=eip7549 + - store_test_results: + path: tests/core/pyspec/test-reports test-whisk: docker: - image: circleci/python:3.9 @@ -333,6 +346,9 @@ workflows: - test-eip7002: requires: - install_pyspec_test + - test-eip7549: + requires: + - install_pyspec_test - test-whisk: requires: - install_pyspec_test diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 2973afb068..9693217971 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -71,7 +71,7 @@ jobs: needs: [preclear,lint,codespell,table_of_contents] strategy: matrix: - version: ["phase0", "altair", "bellatrix", "capella", "deneb", "eip6110", "eip7002", "whisk", "eip7594"] + version: ["phase0", "altair", "bellatrix", "capella", "deneb", "eip6110", "eip7002", "eip7549", "whisk", "eip7594"] steps: - name: Checkout this repo uses: actions/checkout@v3.2.0 diff --git a/Makefile b/Makefile index d0d750e893..c6bc8dd842 100644 --- a/Makefile +++ b/Makefile @@ -35,7 +35,7 @@ MARKDOWN_FILES = $(wildcard $(SPEC_DIR)/*/*.md) \ $(wildcard $(SPEC_DIR)/_features/*/*/*.md) \ $(wildcard $(SSZ_DIR)/*.md) -ALL_EXECUTABLE_SPEC_NAMES = phase0 altair bellatrix capella deneb eip6110 eip7002 whisk +ALL_EXECUTABLE_SPEC_NAMES = phase0 altair bellatrix capella deneb eip6110 eip7002 eip7549 whisk # The parameters for commands. Use `foreach` to avoid listing specs again. COVERAGE_SCOPE := $(foreach S,$(ALL_EXECUTABLE_SPEC_NAMES), --cov=eth2spec.$S.$(TEST_PRESET_TYPE)) PYLINT_SCOPE := $(foreach S,$(ALL_EXECUTABLE_SPEC_NAMES), ./eth2spec/$S) diff --git a/specs/_features/eip7549/beacon-chain.md b/specs/_features/eip7549/beacon-chain.md index 3b20e0c632..9ee88e7def 100644 --- a/specs/_features/eip7549/beacon-chain.md +++ b/specs/_features/eip7549/beacon-chain.md @@ -101,7 +101,7 @@ def get_attesting_indices(state: BeaconState, attestation: Attestation) -> Set[V """ Return the set of attesting indices corresponding to ``aggregation_bits`` and ``committee_bits``. """ - output = set() + output: Set[ValidatorIndex] = set() committee_indices = get_committee_indices(attestation.committee_bits) committee_offset = 0 for index in committee_indices: From 44088378cc458c913e30dd684cbf7dc38b574c78 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Mon, 1 Apr 2024 10:55:39 +0900 Subject: [PATCH 3/3] Revert leftover --- .../block_processing/test_process_attestation.py | 3 +-- .../test/phase0/fork_choice/test_ex_ante.py | 14 +++----------- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/phase0/block_processing/test_process_attestation.py b/tests/core/pyspec/eth2spec/test/phase0/block_processing/test_process_attestation.py index 4c62803be4..2cf314dc5f 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/block_processing/test_process_attestation.py +++ b/tests/core/pyspec/eth2spec/test/phase0/block_processing/test_process_attestation.py @@ -358,8 +358,7 @@ def test_invalid_too_few_aggregation_bits(spec, state): next_slots(spec, state, spec.MIN_ATTESTATION_INCLUSION_DELAY) attestation.aggregation_bits = Bitlist[spec.MAX_VALIDATORS_PER_COMMITTEE]( - *([0b1] + [0b0] * (len(attestation.aggregation_bits) - 1)) - ) + *([0b1] + [0b0] * (len(attestation.aggregation_bits) - 1))) sign_attestation(spec, state, attestation) diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_ex_ante.py b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_ex_ante.py index 7e64e647aa..9978f7d46c 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_ex_ante.py +++ b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_ex_ante.py @@ -31,11 +31,6 @@ def _apply_base_block_a(spec, state, store, test_steps): assert spec.get_head(store) == signed_block_a.message.hash_tree_root() -def _get_aggregation_bits(spec, attestation): - aggregation_bits = attestation.aggregation_bits - return aggregation_bits - - @with_altair_and_later @spec_state_test def test_ex_ante_vanilla(spec, state): @@ -83,8 +78,7 @@ def _filter_participant_set(participants): spec, state_b, slot=state_b.slot, signed=False, filter_participant_set=_filter_participant_set ) attestation.data.beacon_block_root = signed_block_b.message.hash_tree_root() - aggregation_bits = _get_aggregation_bits(spec, attestation) - assert len([i for i in aggregation_bits if i == 1]) == 1 + assert len([i for i in attestation.aggregation_bits if i == 1]) == 1 sign_attestation(spec, state_b, attestation) # Block C received at N+2 — C is head @@ -186,8 +180,7 @@ def _filter_participant_set(participants): spec, state_b, slot=state_b.slot, signed=False, filter_participant_set=_filter_participant_set ) attestation.data.beacon_block_root = signed_block_b.message.hash_tree_root() - aggregation_bits = _get_aggregation_bits(spec, attestation) - assert len([i for i in aggregation_bits if i == 1]) == participant_num + assert len([i for i in attestation.aggregation_bits if i == 1]) == participant_num sign_attestation(spec, state_b, attestation) # Attestation_set_1 received at N+2 — B is head because B's attestation_score > C's proposer_score. @@ -311,8 +304,7 @@ def _filter_participant_set(participants): spec, state_c, slot=state_c.slot, signed=False, filter_participant_set=_filter_participant_set ) attestation.data.beacon_block_root = signed_block_c.message.hash_tree_root() - aggregation_bits = _get_aggregation_bits(spec, attestation) - assert len([i for i in aggregation_bits if i == 1]) == 1 + assert len([i for i in attestation.aggregation_bits if i == 1]) == 1 sign_attestation(spec, state_c, attestation) # Block D at slot `N + 3`, parent is B