Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EIP-7044: Lock voluntary exit domain on capella #3288

Merged
merged 14 commits into from
Jun 14, 2023
33 changes: 30 additions & 3 deletions specs/deneb/beacon-chain.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@
- [Modified `verify_and_notify_new_payload`](#modified-verify_and_notify_new_payload)
- [Block processing](#block-processing)
- [Execution payload](#execution-payload)
- [`process_execution_payload`](#process_execution_payload)
- [Modified `process_execution_payload`](#modified-process_execution_payload)
- [Modified `process_voluntary_exit`](#modified-process_voluntary_exit)
- [Testing](#testing)

<!-- END doctoc generated TOC please keep comment here to allow auto update -->
Expand All @@ -42,7 +43,8 @@
## Introduction

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-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

## Custom types

Expand Down Expand Up @@ -221,7 +223,7 @@ def verify_and_notify_new_payload(self: ExecutionEngine,

#### Execution payload

##### `process_execution_payload`
##### Modified `process_execution_payload`

```python
def process_execution_payload(state: BeaconState, body: BeaconBlockBody, execution_engine: ExecutionEngine) -> None:
Expand Down Expand Up @@ -266,6 +268,31 @@ def process_execution_payload(state: BeaconState, body: BeaconBlockBody, executi
)
```

#### Modified `process_voluntary_exit`
dapplion marked this conversation as resolved.
Show resolved Hide resolved

*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:
voluntary_exit = signed_voluntary_exit.message
validator = state.validators[voluntary_exit.validator_index]
# Verify the validator is active
assert is_active_validator(validator, get_current_epoch(state))
# Verify exit has not been initiated
assert validator.exit_epoch == FAR_FUTURE_EPOCH
# Exits must specify an epoch when they become valid; they are not valid before then
assert get_current_epoch(state) >= voluntary_exit.epoch
# Verify the validator has been active long enough
assert get_current_epoch(state) >= validator.activation_epoch + SHARD_COMMITTEE_PERIOD
# Verify signature
# [Modified in Deneb:EIP7044]
domain = compute_domain(DOMAIN_VOLUNTARY_EXIT, CAPELLA_FORK_VERSION, state.genesis_validators_root)
signing_root = compute_signing_root(voluntary_exit, domain)
assert bls.Verify(validator.pubkey, signing_root, signed_voluntary_exit.signature)
# Initiate exit
initiate_validator_exit(state, voluntary_exit.validator_index)
```

## Testing

*Note*: The function `initialize_beacon_state_from_eth1` is modified for pure Deneb testing only.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
spec_state_test,
always_bls,
with_bellatrix_and_later,
with_phases,
)
from eth2spec.test.helpers.constants import (
BELLATRIX,
CAPELLA,
)
from eth2spec.test.helpers.keys import pubkey_to_privkey
from eth2spec.test.helpers.state import (
Expand All @@ -12,8 +17,10 @@
sign_voluntary_exit,
)

BELLATRIX_AND_CAPELLA = [BELLATRIX, CAPELLA]


def _run_voluntary_exit_processing_test(
def run_voluntary_exit_processing_test(
spec,
state,
fork_version,
Expand Down Expand Up @@ -51,7 +58,7 @@ def _run_voluntary_exit_processing_test(
@spec_state_test
@always_bls
def test_invalid_voluntary_exit_with_current_fork_version_is_before_fork_epoch(spec, state):
yield from _run_voluntary_exit_processing_test(
yield from run_voluntary_exit_processing_test(
spec,
state,
fork_version=state.fork.current_version,
Expand All @@ -60,39 +67,39 @@ def test_invalid_voluntary_exit_with_current_fork_version_is_before_fork_epoch(s
)


@with_bellatrix_and_later
@with_phases(BELLATRIX_AND_CAPELLA)
@spec_state_test
@always_bls
def test_voluntary_exit_with_current_fork_version_not_is_before_fork_epoch(spec, state):
yield from _run_voluntary_exit_processing_test(
yield from run_voluntary_exit_processing_test(
spec,
state,
fork_version=state.fork.current_version,
is_before_fork_epoch=False,
)


@with_bellatrix_and_later
@with_phases([BELLATRIX, CAPELLA])
@spec_state_test
@always_bls
def test_voluntary_exit_with_previous_fork_version_is_before_fork_epoch(spec, state):
assert state.fork.previous_version != state.fork.current_version

yield from _run_voluntary_exit_processing_test(
yield from run_voluntary_exit_processing_test(
spec,
state,
fork_version=state.fork.previous_version,
is_before_fork_epoch=True,
)


@with_bellatrix_and_later
@with_phases(BELLATRIX_AND_CAPELLA)
@spec_state_test
@always_bls
def test_invalid_voluntary_exit_with_previous_fork_version_not_is_before_fork_epoch(spec, state):
assert state.fork.previous_version != state.fork.current_version

yield from _run_voluntary_exit_processing_test(
yield from run_voluntary_exit_processing_test(
spec,
state,
fork_version=state.fork.previous_version,
Expand All @@ -107,7 +114,7 @@ def test_invalid_voluntary_exit_with_previous_fork_version_not_is_before_fork_ep
def test_invalid_voluntary_exit_with_genesis_fork_version_is_before_fork_epoch(spec, state):
assert spec.config.GENESIS_FORK_VERSION not in (state.fork.previous_version, state.fork.current_version)

yield from _run_voluntary_exit_processing_test(
yield from run_voluntary_exit_processing_test(
spec,
state,
fork_version=spec.config.GENESIS_FORK_VERSION,
Expand All @@ -122,7 +129,7 @@ def test_invalid_voluntary_exit_with_genesis_fork_version_is_before_fork_epoch(s
def test_invalid_voluntary_exit_with_genesis_fork_version_not_is_before_fork_epoch(spec, state):
assert spec.config.GENESIS_FORK_VERSION not in (state.fork.previous_version, state.fork.current_version)

yield from _run_voluntary_exit_processing_test(
yield from run_voluntary_exit_processing_test(
spec,
state,
fork_version=spec.config.GENESIS_FORK_VERSION,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
from eth2spec.test.context import (
always_bls,
spec_state_test,
with_phases,
with_deneb_and_later,
)
from eth2spec.test.helpers.constants import (
DENEB,
)
from eth2spec.test.bellatrix.block_processing.test_process_voluntary_exit import (
run_voluntary_exit_processing_test,
)


djrtwo marked this conversation as resolved.
Show resolved Hide resolved
@with_deneb_and_later
@spec_state_test
@always_bls
def test_invalid_voluntary_exit_with_current_fork_version_not_is_before_fork_epoch(spec, state):
"""
Since Deneb, the VoluntaryExit domain is fixed to `CAPELLA_FORK_VERSION`
"""
assert state.fork.current_version != spec.config.CAPELLA_FORK_VERSION
yield from run_voluntary_exit_processing_test(
spec,
state,
fork_version=state.fork.current_version,
is_before_fork_epoch=False,
valid=False,
)


@with_deneb_and_later
@spec_state_test
@always_bls
def test_voluntary_exit_with_previous_fork_version_not_is_before_fork_epoch(spec, state):
"""
Since Deneb, the VoluntaryExit domain is fixed to `CAPELLA_FORK_VERSION`

Note: This test is valid for ``spec.fork == DENEB`` and invalid for subsequent forks
"""
djrtwo marked this conversation as resolved.
Show resolved Hide resolved
assert state.fork.previous_version != state.fork.current_version

if spec.fork == DENEB:
assert state.fork.previous_version == spec.config.CAPELLA_FORK_VERSION
yield from run_voluntary_exit_processing_test(
spec,
state,
fork_version=state.fork.previous_version,
is_before_fork_epoch=False,
)
else:
assert state.fork.previous_version != spec.config.CAPELLA_FORK_VERSION
yield from run_voluntary_exit_processing_test(
spec,
state,
fork_version=state.fork.previous_version,
is_before_fork_epoch=False,
valid=False,
)


@with_deneb_and_later
@spec_state_test
@always_bls
def test_voluntary_exit_with_previous_fork_version_is_before_fork_epoch(spec, state):
"""
Since Deneb, the VoluntaryExit domain is fixed to `CAPELLA_FORK_VERSION`
djrtwo marked this conversation as resolved.
Show resolved Hide resolved

Note: This test is valid for ``spec.fork == DENEB`` and invalid for subsequent forks
"""
assert state.fork.previous_version != state.fork.current_version

if spec.fork == DENEB:
assert state.fork.previous_version == spec.config.CAPELLA_FORK_VERSION
yield from run_voluntary_exit_processing_test(
spec,
state,
fork_version=state.fork.previous_version,
is_before_fork_epoch=True,
)
else:
assert state.fork.previous_version != spec.config.CAPELLA_FORK_VERSION
yield from run_voluntary_exit_processing_test(
spec,
state,
fork_version=state.fork.previous_version,
is_before_fork_epoch=True,
valid=False,
)
20 changes: 11 additions & 9 deletions tests/core/pyspec/eth2spec/test/helpers/voluntary_exits.py
Original file line number Diff line number Diff line change
@@ -1,29 +1,31 @@
from random import Random
from eth2spec.utils import bls
from eth2spec.test.context import expect_assertion_error
from eth2spec.test.helpers.forks import is_post_deneb
from eth2spec.test.helpers.keys import privkeys


def prepare_signed_exits(spec, state, indices, fork_version=None):
if fork_version is None:
domain = spec.get_domain(state, spec.DOMAIN_VOLUNTARY_EXIT)
else:
domain = spec.compute_domain(spec.DOMAIN_VOLUNTARY_EXIT, fork_version, state.genesis_validators_root)

def create_signed_exit(index):
exit = spec.VoluntaryExit(
voluntary_exit = spec.VoluntaryExit(
epoch=spec.get_current_epoch(state),
validator_index=index,
)
signing_root = spec.compute_signing_root(exit, domain)
return spec.SignedVoluntaryExit(message=exit, signature=bls.Sign(privkeys[index], signing_root))
return sign_voluntary_exit(spec, state, voluntary_exit, privkeys[index], fork_version=fork_version)

return [create_signed_exit(index) for index in indices]


def sign_voluntary_exit(spec, state, voluntary_exit, privkey, fork_version=None):
if fork_version is None:
domain = spec.get_domain(state, spec.DOMAIN_VOLUNTARY_EXIT, voluntary_exit.epoch)
if is_post_deneb(spec):
domain = spec.compute_domain(
spec.DOMAIN_VOLUNTARY_EXIT,
spec.config.CAPELLA_FORK_VERSION,
state.genesis_validators_root,
)
else:
domain = spec.get_domain(state, spec.DOMAIN_VOLUNTARY_EXIT, voluntary_exit.epoch)
else:
domain = spec.compute_domain(spec.DOMAIN_VOLUNTARY_EXIT, fork_version, state.genesis_validators_root)

Expand Down
1 change: 1 addition & 0 deletions tests/generators/operations/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@

_new_deneb_mods = {key: 'eth2spec.test.deneb.block_processing.test_process_' + key for key in [
'execution_payload',
'voluntary_exit',
]}
deneb_mods = combine_mods(_new_deneb_mods, capella_mods)

Expand Down