Skip to content

Commit

Permalink
Merge pull request #3360 from ethereum/att-slot-range
Browse files Browse the repository at this point in the history
EIP-7045: Increase max attestation inclusion slot
  • Loading branch information
djrtwo committed Jun 21, 2023
2 parents c90d724 + ecefe2d commit 95f36d9
Show file tree
Hide file tree
Showing 10 changed files with 222 additions and 24 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ venv
.venvs
.venv
/.pytest_cache
*.swp

build/
output/
Expand Down
6 changes: 4 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -776,7 +776,6 @@ def imports(cls, preset_name: str):
from eth2spec.deneb import {preset_name} as deneb
'''


#
# WhiskSpecBuilder
#
Expand All @@ -801,7 +800,10 @@ def hardcoded_custom_type_dep_constants(cls, spec_object) -> str:

spec_builders = {
builder.fork: builder
for builder in (Phase0SpecBuilder, AltairSpecBuilder, BellatrixSpecBuilder, CapellaSpecBuilder, DenebSpecBuilder, EIP6110SpecBuilder, WhiskSpecBuilder)
for builder in (
Phase0SpecBuilder, AltairSpecBuilder, BellatrixSpecBuilder, CapellaSpecBuilder, DenebSpecBuilder,
EIP6110SpecBuilder, WhiskSpecBuilder,
)
}


Expand Down
83 changes: 82 additions & 1 deletion specs/deneb/beacon-chain.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
- [Helper functions](#helper-functions)
- [Misc](#misc)
- [`kzg_commitment_to_versioned_hash`](#kzg_commitment_to_versioned_hash)
- [Beacon state accessors](#beacon-state-accessors)
- [Modified `get_attestation_participation_flag_indices`](#modified-get_attestation_participation_flag_indices)
- [Beacon chain state transition function](#beacon-chain-state-transition-function)
- [Execution engine](#execution-engine)
- [Request data](#request-data)
Expand All @@ -32,6 +34,7 @@
- [`is_valid_versioned_hashes`](#is_valid_versioned_hashes)
- [Modified `verify_and_notify_new_payload`](#modified-verify_and_notify_new_payload)
- [Block processing](#block-processing)
- [Modified `process_attestation`](#modified-process_attestation)
- [Execution payload](#execution-payload)
- [Modified `process_execution_payload`](#modified-process_execution_payload)
- [Modified `process_voluntary_exit`](#modified-process_voluntary_exit)
Expand All @@ -45,6 +48,7 @@
Deneb is a consensus-layer upgrade containing a number of features. Including:
* [EIP-4844](https://eips.ethereum.org/EIPS/eip-4844): Shard Blob Transactions scale data-availability of Ethereum in a simple, forwards-compatible manner
* [EIP-7044](https://github.com/ethereum/EIPs/pull/7044): Perpetually Valid Signed Voluntary Exits
* [EIP-7045](https://eips.ethereum.org/EIPS/eip-7045): Increase Max Attestation Inclusion Slot

## Custom types

Expand Down Expand Up @@ -170,6 +174,41 @@ def kzg_commitment_to_versioned_hash(kzg_commitment: KZGCommitment) -> Versioned
return VERSIONED_HASH_VERSION_KZG + hash(kzg_commitment)[1:]
```

### Beacon state accessors

#### Modified `get_attestation_participation_flag_indices`

*Note:* The function `get_attestation_participation_flag_indices` is modified to set the `TIMELY_TARGET_FLAG` for any correct target attestation, regardless of `inclusion_delay` as a baseline reward for any speed of inclusion of an attestation that contributes to justification of the contained chain for EIP-7045.

```python
def get_attestation_participation_flag_indices(state: BeaconState,
data: AttestationData,
inclusion_delay: uint64) -> Sequence[int]:
"""
Return the flag indices that are satisfied by an attestation.
"""
if data.target.epoch == get_current_epoch(state):
justified_checkpoint = state.current_justified_checkpoint
else:
justified_checkpoint = state.previous_justified_checkpoint

# Matching roots
is_matching_source = data.source == justified_checkpoint
is_matching_target = is_matching_source and data.target.root == get_block_root(state, data.target.epoch)
is_matching_head = is_matching_target and data.beacon_block_root == get_block_root_at_slot(state, data.slot)
assert is_matching_source

participation_flag_indices = []
if is_matching_source and inclusion_delay <= integer_squareroot(SLOTS_PER_EPOCH):
participation_flag_indices.append(TIMELY_SOURCE_FLAG_INDEX)
if is_matching_target: # [Modified in Deneb:EIP7045]
participation_flag_indices.append(TIMELY_TARGET_FLAG_INDEX)
if is_matching_head and inclusion_delay == MIN_ATTESTATION_INCLUSION_DELAY:
participation_flag_indices.append(TIMELY_HEAD_FLAG_INDEX)

return participation_flag_indices
```

## Beacon chain state transition function

### Execution engine
Expand Down Expand Up @@ -221,10 +260,52 @@ def verify_and_notify_new_payload(self: ExecutionEngine,

### Block processing

#### Modified `process_attestation`

*Note*: The function `process_attestation` is modified to expand valid slots for inclusion to those in both `target.epoch` epoch and `target.epoch + 1` epoch for EIP-7045. Additionally, it utilizes an updated version of `get_attestation_participation_flag_indices` to ensure rewards are available for the extended attestation inclusion range for EIP-7045.

```python
def process_attestation(state: BeaconState, attestation: Attestation) -> None:
data = attestation.data
assert data.target.epoch in (get_previous_epoch(state), get_current_epoch(state))
assert data.target.epoch == compute_epoch_at_slot(data.slot)
assert data.slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot # [Modified in Deneb:EIP7045]
assert data.index < get_committee_count_per_slot(state, data.target.epoch)

committee = get_beacon_committee(state, data.slot, data.index)
assert len(attestation.aggregation_bits) == len(committee)

# Participation flag indices
participation_flag_indices = get_attestation_participation_flag_indices(state, data, state.slot - data.slot)

# Verify signature
assert is_valid_indexed_attestation(state, get_indexed_attestation(state, attestation))

# Update epoch participation flags
if data.target.epoch == get_current_epoch(state):
epoch_participation = state.current_epoch_participation
else:
epoch_participation = state.previous_epoch_participation

proposer_reward_numerator = 0
for index in get_attesting_indices(state, data, attestation.aggregation_bits):
for flag_index, weight in enumerate(PARTICIPATION_FLAG_WEIGHTS):
if flag_index in participation_flag_indices and not has_flag(epoch_participation[index], flag_index):
epoch_participation[index] = add_flag(epoch_participation[index], flag_index)
proposer_reward_numerator += get_base_reward(state, index) * weight

# Reward proposer
proposer_reward_denominator = (WEIGHT_DENOMINATOR - PROPOSER_WEIGHT) * WEIGHT_DENOMINATOR // PROPOSER_WEIGHT
proposer_reward = Gwei(proposer_reward_numerator // proposer_reward_denominator)
increase_balance(state, get_beacon_proposer_index(state), proposer_reward)
```

#### Execution payload

##### Modified `process_execution_payload`

*Note*: The function `process_execution_payload` is modified to pass `versioned_hashes` into `execution_engine.verify_and_notify_new_payload` and to assign the new fields in `ExecutionPayloadHeader` for EIP-4844.

```python
def process_execution_payload(state: BeaconState, body: BeaconBlockBody, execution_engine: ExecutionEngine) -> None:
payload = body.execution_payload
Expand Down Expand Up @@ -270,7 +351,7 @@ def process_execution_payload(state: BeaconState, body: BeaconBlockBody, executi

#### Modified `process_voluntary_exit`

*Note*: The function `process_voluntary_exit` is modified to use the a fixed fork version -- `CAPELLA_FORK_VERSION` -- for EIP-7044
*Note*: The function `process_voluntary_exit` is modified to use the a fixed fork version -- `CAPELLA_FORK_VERSION` -- for EIP-7044.

```python
def process_voluntary_exit(state: BeaconState, signed_voluntary_exit: SignedVoluntaryExit) -> None:
Expand Down
2 changes: 1 addition & 1 deletion specs/deneb/fork.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def compute_fork_version(epoch: Epoch) -> Version:

### Fork trigger

TBD. This fork is defined for testing purposes, the EIP may be combined with other consensus-layer upgrade.
TBD. This fork is defined for testing purposes.
For now, we assume the condition will be triggered at epoch `DENEB_FORK_EPOCH`.

Note that for the pure Deneb networks, we don't apply `upgrade_to_deneb` since it starts with Deneb version logic.
Expand Down
48 changes: 46 additions & 2 deletions specs/deneb/p2p-interface.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ The specification of these changes continues in the same format as the network s
- [Global topics](#global-topics)
- [`beacon_block`](#beacon_block)
- [`blob_sidecar_{subnet_id}`](#blob_sidecar_subnet_id)
- [`beacon_aggregate_and_proof`](#beacon_aggregate_and_proof)
- [Attestation subnets](#attestation-subnets)
- [`beacon_attestation_{subnet_id}`](#beacon_attestation_subnet_id)
- [Transitioning the gossip](#transitioning-the-gossip)
- [The Req/Resp domain](#the-reqresp-domain)
- [Messages](#messages)
Expand Down Expand Up @@ -106,7 +109,11 @@ Some gossip meshes are upgraded in the fork of Deneb to support upgraded types.

Topics follow the same specification as in prior upgrades.

The `beacon_block` topic is modified to also support deneb blocks and new topics are added per table below. All other topics remain stable.
The `beacon_block` topic is modified to also support Deneb blocks and new topics are added per table below.

The `voluntary_exit` topic is implicitly modified due to the lock-in use of `CAPELLA_FORK_VERSION` for this message signature validation for EIP-7044.

The `beacon_aggregate_and_proof` and `beacon_attestation_{subnet_id}` topics are modified to support the gossip of attestations created in epoch `N` to be gossiped through the entire range of slots in epoch `N+1` rather than only through one epoch of slots for EIP-7045.

The specification around the creation, validation, and dissemination of messages has not changed from the Capella document unless explicitly noted here.

Expand All @@ -124,7 +131,9 @@ Deneb introduces new global topics for blob sidecars.

###### `beacon_block`

The *type* of the payload of this topic changes to the (modified) `SignedBeaconBlock` found in deneb.
The *type* of the payload of this topic changes to the (modified) `SignedBeaconBlock` found in Deneb.

*[Modified in Deneb:EIP4844]*

New validation:

Expand All @@ -150,6 +159,41 @@ The following validations MUST pass before forwarding the `signed_blob_sidecar`
- _[REJECT]_ The sidecar is proposed by the expected `proposer_index` for the block's slot in the context of the current shuffling (defined by `block_parent_root`/`slot`).
If the `proposer_index` cannot immediately be verified against the expected shuffling, the sidecar MAY be queued for later processing while proposers for the block's branch are calculated -- in such a case _do not_ `REJECT`, instead `IGNORE` this message.

###### `beacon_aggregate_and_proof`

*[Modified in Deneb:EIP7045]*

The following validation is removed:
* _[IGNORE]_ `aggregate.data.slot` is within the last `ATTESTATION_PROPAGATION_SLOT_RANGE` slots (with a `MAXIMUM_GOSSIP_CLOCK_DISPARITY` allowance) --
i.e. `aggregate.data.slot + ATTESTATION_PROPAGATION_SLOT_RANGE >= current_slot >= aggregate.data.slot`
(a client MAY queue future aggregates for processing at the appropriate slot).

The following validations are added in its place:
* _[IGNORE]_ `aggregate.data.slot` is equal to or earlier than the `current_slot` (with a `MAXIMUM_GOSSIP_CLOCK_DISPARITY` allowance) --
i.e. `aggregate.data.slot <= current_slot`
(a client MAY queue future aggregates for processing at the appropriate slot).
* _[IGNORE]_ the epoch of `aggregate.data.slot` is either the current or previous epoch
(with a `MAXIMUM_GOSSIP_CLOCK_DISPARITY` allowance) --
i.e. `compute_epoch_at_slot(aggregate.data.slot) in (get_previous_epoch(state), get_current_epoch(state))`

##### Attestation subnets

###### `beacon_attestation_{subnet_id}`

*[Modified in Deneb:EIP7045]*

The following validation is removed:
* _[IGNORE]_ `attestation.data.slot` is within the last `ATTESTATION_PROPAGATION_SLOT_RANGE` slots (with a `MAXIMUM_GOSSIP_CLOCK_DISPARITY` allowance) --
i.e. `attestation.data.slot + ATTESTATION_PROPAGATION_SLOT_RANGE >= current_slot >= attestation.data.slot`
(a client MAY queue future attestations for processing at the appropriate slot).

The following validations are added in its place:
* _[IGNORE]_ `attestation.data.slot` is equal to or earlier than the `current_slot` (with a `MAXIMUM_GOSSIP_CLOCK_DISPARITY` allowance) --
i.e. `attestation.data.slot <= current_slot`
(a client MAY queue future attestation for processing at the appropriate slot).
* _[IGNORE]_ the epoch of `attestation.data.slot` is either the current or previous epoch
(with a `MAXIMUM_GOSSIP_CLOCK_DISPARITY` allowance) --
i.e. `compute_epoch_at_slot(attestation.data.slot) in (get_previous_epoch(state), get_current_epoch(state))`

#### Transitioning the gossip

Expand Down
3 changes: 2 additions & 1 deletion tests/core/pyspec/eth2spec/test/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -534,12 +534,13 @@ def wrapper(*args, spec: Spec, **kw):
return decorator


with_light_client = with_phases(LIGHT_CLIENT_TESTING_FORKS)

with_altair_and_later = with_all_phases_from(ALTAIR)
with_bellatrix_and_later = with_all_phases_from(BELLATRIX)
with_capella_and_later = with_all_phases_from(CAPELLA)
with_deneb_and_later = with_all_phases_from(DENEB)
with_eip6110_and_later = with_all_phases_from(EIP6110)
with_light_client = with_phases(LIGHT_CLIENT_TESTING_FORKS)


class quoted_str(str):
Expand Down
44 changes: 42 additions & 2 deletions tests/core/pyspec/eth2spec/test/deneb/sanity/test_blocks.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
from eth2spec.test.helpers.state import (
state_transition_and_sign_block
state_transition_and_sign_block,
next_epoch_via_block,
transition_to,
)
from eth2spec.test.helpers.block import (
build_empty_block_for_next_slot
build_empty_block_for_next_slot,
)
from eth2spec.test.context import (
DENEB,
spec_state_test,
spec_configured_state_test,
with_deneb_and_later,
with_phases,
)
from eth2spec.test.helpers.execution_payload import (
compute_el_block_hash,
)
from eth2spec.test.helpers.attestations import (
get_valid_attestation,
)
from eth2spec.test.helpers.sharding import (
get_sample_opaque_tx,
)
Expand Down Expand Up @@ -58,3 +66,35 @@ def test_max_blobs_per_block(spec, state):
@spec_state_test
def test_invalid_exceed_max_blobs_per_block(spec, state):
yield from run_block_with_blobs(spec, state, blob_count=spec.MAX_BLOBS_PER_BLOCK + 1, valid=False)


@with_phases([DENEB])
@spec_configured_state_test({
'DENEB_FORK_EPOCH': 2,
})
def test_include_attestation_from_previous_fork_with_new_range(spec, state):
# Transition to the epoch prior to the fork epoch
next_epoch_via_block(spec, state)

# Generate an attestation for slot 0 of this epoch
attestation = get_valid_attestation(spec, state, signed=True)

# Transition to second to last slot in `DENEB_FORK_EPOCH`
next_epoch_via_block(spec, state)
current_epoch = spec.get_current_epoch(state)
assert current_epoch == spec.config.DENEB_FORK_EPOCH
penultimate_slot = spec.compute_start_slot_at_epoch(current_epoch + 1) - 2
transition_to(spec, state, penultimate_slot)

# Ensure the new state is in the increased EIP-7045 slot inclusion range
assert penultimate_slot - attestation.data.slot > spec.SLOTS_PER_EPOCH

block = build_empty_block_for_next_slot(spec, state)
block.body.attestations.append(attestation)

yield 'pre', state

signed_block = state_transition_and_sign_block(spec, state, block)

yield 'blocks', [signed_block]
yield 'post', state
10 changes: 9 additions & 1 deletion tests/core/pyspec/eth2spec/test/helpers/attestations.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
from eth2spec.test.helpers.forks import is_post_altair, is_post_deneb
from eth2spec.test.helpers.keys import privkeys
from eth2spec.utils import bls
from eth2spec.utils.ssz.ssz_typing import Bitlist
Expand Down Expand Up @@ -158,6 +158,14 @@ def get_attestation_signature(spec, state, attestation_data, privkey):
return bls.Sign(privkey, signing_root)


def compute_max_inclusion_slot(spec, attestation):
if is_post_deneb(spec):
next_epoch = spec.compute_epoch_at_slot(attestation.data.slot) + 1
end_of_next_epoch = spec.compute_start_slot_at_epoch(next_epoch + 1) - 1
return end_of_next_epoch
return attestation.data.slot + spec.SLOTS_PER_EPOCH


def fill_aggregate_attestation(spec, state, attestation, signed=False, filter_participant_set=None):
"""
`signed`: Signing is optional.
Expand Down
2 changes: 1 addition & 1 deletion tests/core/pyspec/eth2spec/test/helpers/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
# Formal forks
*MAINNET_FORKS,
DENEB,
# Experimental features
# Experimental patches
EIP6110,
)
# The forks that have light client specs
Expand Down
Loading

0 comments on commit 95f36d9

Please sign in to comment.