From 1c35eb1c3303fe1e0b101323106d766d8f848cd6 Mon Sep 17 00:00:00 2001 From: dapplion <35266934+dapplion@users.noreply.github.com> Date: Fri, 10 Mar 2023 14:37:18 +0800 Subject: [PATCH 01/18] Lock voluntary_exit domain on capella --- specs/phase0/beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/phase0/beacon-chain.md b/specs/phase0/beacon-chain.md index 7e14fa951..e5eb12006 100644 --- a/specs/phase0/beacon-chain.md +++ b/specs/phase0/beacon-chain.md @@ -1903,7 +1903,7 @@ def process_voluntary_exit(state: BeaconState, signed_voluntary_exit: SignedVolu # Verify the validator has been active long enough assert get_current_epoch(state) >= validator.activation_epoch + SHARD_COMMITTEE_PERIOD # Verify signature - domain = get_domain(state, DOMAIN_VOLUNTARY_EXIT, voluntary_exit.epoch) + domain = get_domain(state, DOMAIN_VOLUNTARY_EXIT, min(voluntary_exit.epoch, CAPELLA_FORK_EPOCH)) signing_root = compute_signing_root(voluntary_exit, domain) assert bls.Verify(validator.pubkey, signing_root, signed_voluntary_exit.signature) # Initiate exit From 47f078fc1fdbcc6274d42ee891718ed73bc731d9 Mon Sep 17 00:00:00 2001 From: dapplion <35266934+dapplion@users.noreply.github.com> Date: Sat, 11 Mar 2023 20:28:49 +0800 Subject: [PATCH 02/18] Move change to deneb --- specs/deneb/beacon-chain.md | 44 +++++++++++++++++++++++++++++++++++- specs/phase0/beacon-chain.md | 2 +- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/specs/deneb/beacon-chain.md b/specs/deneb/beacon-chain.md index a0ac783b7..672d0a633 100644 --- a/specs/deneb/beacon-chain.md +++ b/specs/deneb/beacon-chain.md @@ -203,7 +203,7 @@ def process_block(state: BeaconState, block: BeaconBlock) -> None: process_execution_payload(state, block.body.execution_payload, EXECUTION_ENGINE) # [Modified in Deneb] process_randao(state, block.body) process_eth1_data(state, block.body) - process_operations(state, block.body) + process_operations(state, block.body) # [Modified in Deneb] process_sync_aggregate(state, block.body.sync_aggregate) process_blob_kzg_commitments(state, block.body) # [New in Deneb] ``` @@ -245,6 +245,48 @@ def process_execution_payload(state: BeaconState, payload: ExecutionPayload, exe ) ``` +#### Modified `process_operations` + +```python +def process_operations(state: BeaconState, body: BeaconBlockBody) -> None: + # Verify that outstanding deposits are processed up to the maximum number of deposits + assert len(body.deposits) == min(MAX_DEPOSITS, state.eth1_data.deposit_count - state.eth1_deposit_index) + + def for_ops(operations: Sequence[Any], fn: Callable[[BeaconState, Any], None]) -> None: + for operation in operations: + fn(state, operation) + + for_ops(body.proposer_slashings, process_proposer_slashing) + for_ops(body.attester_slashings, process_attester_slashing) + for_ops(body.attestations, process_attestation) + for_ops(body.deposits, process_deposit) + for_ops(body.voluntary_exits, process_voluntary_exit) # [Modified in Deneb] + for_ops(body.bls_to_execution_changes, process_bls_to_execution_change) +``` + +##### Modified `process_voluntary_exit` + +```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 + domain = get_domain(state, DOMAIN_VOLUNTARY_EXIT, min(voluntary_exit.epoch, CAPELLA_FORK_EPOCH)) # [Modified in Deneb] + 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) +``` + + #### Blob KZG commitments ```python diff --git a/specs/phase0/beacon-chain.md b/specs/phase0/beacon-chain.md index e5eb12006..7e14fa951 100644 --- a/specs/phase0/beacon-chain.md +++ b/specs/phase0/beacon-chain.md @@ -1903,7 +1903,7 @@ def process_voluntary_exit(state: BeaconState, signed_voluntary_exit: SignedVolu # Verify the validator has been active long enough assert get_current_epoch(state) >= validator.activation_epoch + SHARD_COMMITTEE_PERIOD # Verify signature - domain = get_domain(state, DOMAIN_VOLUNTARY_EXIT, min(voluntary_exit.epoch, CAPELLA_FORK_EPOCH)) + domain = get_domain(state, DOMAIN_VOLUNTARY_EXIT, voluntary_exit.epoch) signing_root = compute_signing_root(voluntary_exit, domain) assert bls.Verify(validator.pubkey, signing_root, signed_voluntary_exit.signature) # Initiate exit From df4ba47e633dff7707d7a531136cfa785a65f5b0 Mon Sep 17 00:00:00 2001 From: Lion - dapplion <35266934+dapplion@users.noreply.github.com> Date: Mon, 13 Mar 2023 08:51:02 +0800 Subject: [PATCH 03/18] Update beacon-chain.md --- specs/deneb/beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/deneb/beacon-chain.md b/specs/deneb/beacon-chain.md index 672d0a633..fd983f29e 100644 --- a/specs/deneb/beacon-chain.md +++ b/specs/deneb/beacon-chain.md @@ -279,7 +279,7 @@ def process_voluntary_exit(state: BeaconState, signed_voluntary_exit: SignedVolu # Verify the validator has been active long enough assert get_current_epoch(state) >= validator.activation_epoch + SHARD_COMMITTEE_PERIOD # Verify signature - domain = get_domain(state, DOMAIN_VOLUNTARY_EXIT, min(voluntary_exit.epoch, CAPELLA_FORK_EPOCH)) # [Modified in Deneb] + domain = compute_domain(DOMAIN_VOLUNTARY_EXIT, CAPELLA_FORK_VERSION, state.genesis_validators_root) # [Modified in Deneb] signing_root = compute_signing_root(voluntary_exit, domain) assert bls.Verify(validator.pubkey, signing_root, signed_voluntary_exit.signature) # Initiate exit From 680b026d59a6fc0b4e73817464fb478e7eb1d9bc Mon Sep 17 00:00:00 2001 From: dapplion <35266934+dapplion@users.noreply.github.com> Date: Thu, 30 Mar 2023 09:23:10 +0900 Subject: [PATCH 04/18] Add add_validator_to_registry fn --- specs/altair/beacon-chain.md | 47 ++++++++++++------------------------ specs/phase0/beacon-chain.md | 17 ++++++++----- 2 files changed, 26 insertions(+), 38 deletions(-) diff --git a/specs/altair/beacon-chain.md b/specs/altair/beacon-chain.md index 8c3a8877e..1de39d6fc 100644 --- a/specs/altair/beacon-chain.md +++ b/specs/altair/beacon-chain.md @@ -45,7 +45,7 @@ - [Modified `slash_validator`](#modified-slash_validator) - [Block processing](#block-processing) - [Modified `process_attestation`](#modified-process_attestation) - - [Modified `apply_deposit`](#modified-apply_deposit) + - [Modified `add_validator_to_registry`](#modified-add_validator_to_registry) - [Sync aggregate processing](#sync-aggregate-processing) - [Epoch processing](#epoch-processing) - [Justification and finalization](#justification-and-finalization) @@ -508,40 +508,23 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None: increase_balance(state, get_beacon_proposer_index(state), proposer_reward) ``` -#### Modified `apply_deposit` +#### Modified `add_validator_to_registry` -*Note*: The function `apply_deposit` is modified to initialize `inactivity_scores`, `previous_epoch_participation`, and `current_epoch_participation`. +*Note*: The function `add_validator_to_registry` is modified to initialize `inactivity_scores`, `previous_epoch_participation`, and `current_epoch_participation`. ```python -def apply_deposit(state: BeaconState, - pubkey: BLSPubkey, - withdrawal_credentials: Bytes32, - amount: uint64, - signature: BLSSignature) -> None: - validator_pubkeys = [validator.pubkey for validator in state.validators] - if pubkey not in validator_pubkeys: - # Verify the deposit signature (proof of possession) which is not checked by the deposit contract - deposit_message = DepositMessage( - pubkey=pubkey, - withdrawal_credentials=withdrawal_credentials, - amount=amount, - ) - domain = compute_domain(DOMAIN_DEPOSIT) # Fork-agnostic domain since deposits are valid across forks - signing_root = compute_signing_root(deposit_message, domain) - # Initialize validator if the deposit signature is valid - if bls.Verify(pubkey, signing_root, signature): - index = get_index_for_new_validator(state) - validator = get_validator_from_deposit(pubkey, withdrawal_credentials, amount) - set_or_append_list(state.validators, index, validator) - set_or_append_list(state.balances, index, amount) - # [New in Altair] - set_or_append_list(state.previous_epoch_participation, index, ParticipationFlags(0b0000_0000)) - set_or_append_list(state.current_epoch_participation, index, ParticipationFlags(0b0000_0000)) - set_or_append_list(state.inactivity_scores, index, uint64(0)) - else: - # Increase balance by deposit amount - index = ValidatorIndex(validator_pubkeys.index(pubkey)) - increase_balance(state, index, amount) +def add_validator_to_registry(state: BeaconState, + pubkey: BLSPubkey, + withdrawal_credentials: Bytes32, + amount: uint64) -> None: + index = get_index_for_new_validator(state) + validator = get_validator_from_deposit(pubkey, withdrawal_credentials, amount) + set_or_append_list(state.validators, index, validator) + set_or_append_list(state.balances, index, amount) + # [New in Altair] + set_or_append_list(state.previous_epoch_participation, index, ParticipationFlags(0b0000_0000)) + set_or_append_list(state.current_epoch_participation, index, ParticipationFlags(0b0000_0000)) + set_or_append_list(state.inactivity_scores, index, uint64(0)) ``` #### Sync aggregate processing diff --git a/specs/phase0/beacon-chain.md b/specs/phase0/beacon-chain.md index b77e017ab..214c0b77e 100644 --- a/specs/phase0/beacon-chain.md +++ b/specs/phase0/beacon-chain.md @@ -1849,6 +1849,15 @@ def get_validator_from_deposit(pubkey: BLSPubkey, withdrawal_credentials: Bytes3 ) ``` +```python +def add_validator_to_registry(state: BeaconState, + pubkey: BLSPubkey, + withdrawal_credentials: Bytes32, + amount: uint64) -> None: + state.validators.append(get_validator_from_deposit(pubkey, withdrawal_credentials, amount)) + state.balances.append(amount) +``` + ```python def apply_deposit(state: BeaconState, pubkey: BLSPubkey, @@ -1865,12 +1874,8 @@ def apply_deposit(state: BeaconState, ) domain = compute_domain(DOMAIN_DEPOSIT) # Fork-agnostic domain since deposits are valid across forks signing_root = compute_signing_root(deposit_message, domain) - if not bls.Verify(pubkey, signing_root, signature): - return - - # Add validator and balance entries - state.validators.append(get_validator_from_deposit(pubkey, withdrawal_credentials, amount)) - state.balances.append(amount) + if bls.Verify(pubkey, signing_root, signature): + add_validator_to_registry(state, pubkey, withdrawal_credentials, amount) else: # Increase balance by deposit amount index = ValidatorIndex(validator_pubkeys.index(pubkey)) From 32036d84a3542ce5b9bd4a2f4e288edfb6267a25 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Fri, 19 May 2023 23:32:49 +0800 Subject: [PATCH 05/18] Fix tests --- specs/deneb/beacon-chain.md | 5 ++- .../test_process_voluntary_exit.py | 28 +++++++----- .../test/deneb/block_processing/__init__.py | 0 .../test_process_voluntary_exit.py | 45 +++++++++++++++++++ .../test_process_voluntary_exit.py | 44 ++++++++++++++++++ .../eth2spec/test/helpers/voluntary_exits.py | 20 +++++---- tests/generators/operations/main.py | 6 ++- 7 files changed, 127 insertions(+), 21 deletions(-) create mode 100644 tests/core/pyspec/eth2spec/test/deneb/block_processing/__init__.py create mode 100644 tests/core/pyspec/eth2spec/test/deneb/block_processing/test_process_voluntary_exit.py create mode 100644 tests/core/pyspec/eth2spec/test/eip6110/block_processing/test_process_voluntary_exit.py diff --git a/specs/deneb/beacon-chain.md b/specs/deneb/beacon-chain.md index f1fe48e82..7d2f9bdc7 100644 --- a/specs/deneb/beacon-chain.md +++ b/specs/deneb/beacon-chain.md @@ -30,6 +30,8 @@ - [Block processing](#block-processing) - [Execution payload](#execution-payload) - [`process_execution_payload`](#process_execution_payload) + - [Modified `process_operations`](#modified-process_operations) + - [Modified `process_voluntary_exit`](#modified-process_voluntary_exit) - [Blob KZG commitments](#blob-kzg-commitments) - [Testing](#testing) @@ -281,7 +283,8 @@ def process_voluntary_exit(state: BeaconState, signed_voluntary_exit: SignedVolu # Verify the validator has been active long enough assert get_current_epoch(state) >= validator.activation_epoch + SHARD_COMMITTEE_PERIOD # Verify signature - domain = compute_domain(DOMAIN_VOLUNTARY_EXIT, CAPELLA_FORK_VERSION, state.genesis_validators_root) # [Modified in Deneb] + # [Modified in Deneb] + 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 diff --git a/tests/core/pyspec/eth2spec/test/bellatrix/block_processing/test_process_voluntary_exit.py b/tests/core/pyspec/eth2spec/test/bellatrix/block_processing/test_process_voluntary_exit.py index f4fcaac68..12b554da5 100644 --- a/tests/core/pyspec/eth2spec/test/bellatrix/block_processing/test_process_voluntary_exit.py +++ b/tests/core/pyspec/eth2spec/test/bellatrix/block_processing/test_process_voluntary_exit.py @@ -2,6 +2,12 @@ from eth2spec.test.context import ( spec_state_test, always_bls, with_bellatrix_and_later, + with_phases, +) +from eth2spec.test.helpers.constants import ( + BELLATRIX, + CAPELLA, + DENEB, ) from eth2spec.test.helpers.keys import pubkey_to_privkey from eth2spec.test.helpers.state import ( @@ -12,8 +18,10 @@ from eth2spec.test.helpers.voluntary_exits import ( sign_voluntary_exit, ) +BELLATRIX_AND_CAPELLA = [BELLATRIX, CAPELLA] -def _run_voluntary_exit_processing_test( + +def run_voluntary_exit_processing_test( spec, state, fork_version, @@ -51,7 +59,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, @@ -60,11 +68,11 @@ 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, @@ -72,13 +80,13 @@ def test_voluntary_exit_with_current_fork_version_not_is_before_fork_epoch(spec, ) -@with_bellatrix_and_later +@with_phases([BELLATRIX, CAPELLA, DENEB]) @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, @@ -86,13 +94,13 @@ def test_voluntary_exit_with_previous_fork_version_is_before_fork_epoch(spec, st ) -@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, @@ -107,7 +115,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, @@ -122,7 +130,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, diff --git a/tests/core/pyspec/eth2spec/test/deneb/block_processing/__init__.py b/tests/core/pyspec/eth2spec/test/deneb/block_processing/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/core/pyspec/eth2spec/test/deneb/block_processing/test_process_voluntary_exit.py b/tests/core/pyspec/eth2spec/test/deneb/block_processing/test_process_voluntary_exit.py new file mode 100644 index 000000000..371fcfed4 --- /dev/null +++ b/tests/core/pyspec/eth2spec/test/deneb/block_processing/test_process_voluntary_exit.py @@ -0,0 +1,45 @@ +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, +) + + +@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` + """ + yield from run_voluntary_exit_processing_test( + spec, + state, + fork_version=state.fork.current_version, + is_before_fork_epoch=False, + valid=False, + ) + + +@with_phases([DENEB]) +@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` + """ + assert state.fork.previous_version != state.fork.current_version + + yield from run_voluntary_exit_processing_test( + spec, + state, + fork_version=state.fork.previous_version, + is_before_fork_epoch=False, + ) diff --git a/tests/core/pyspec/eth2spec/test/eip6110/block_processing/test_process_voluntary_exit.py b/tests/core/pyspec/eth2spec/test/eip6110/block_processing/test_process_voluntary_exit.py new file mode 100644 index 000000000..4128a1181 --- /dev/null +++ b/tests/core/pyspec/eth2spec/test/eip6110/block_processing/test_process_voluntary_exit.py @@ -0,0 +1,44 @@ +from eth2spec.test.context import ( + always_bls, + spec_state_test, + with_eip6110_and_later, +) +from eth2spec.test.bellatrix.block_processing.test_process_voluntary_exit import ( + run_voluntary_exit_processing_test, +) + + +@with_eip6110_and_later +@spec_state_test +@always_bls +def test_invalid_voluntary_exit_with_previous_fork_version_not_is_before_fork_epoch(spec, state): + """ + Since Deneb, the VoluntaryExit domain is fixed to `CAPELLA_FORK_VERSION` + """ + assert state.fork.previous_version != state.fork.current_version + + yield from run_voluntary_exit_processing_test( + spec, + state, + fork_version=state.fork.previous_version, + is_before_fork_epoch=False, + valid=False, + ) + + +@with_eip6110_and_later +@spec_state_test +@always_bls +def test_invalid_voluntary_exit_with_previous_fork_version_is_before_fork_epoch(spec, state): + """ + Since Deneb, the VoluntaryExit domain is fixed to `CAPELLA_FORK_VERSION` + """ + assert state.fork.previous_version != state.fork.current_version + + yield from run_voluntary_exit_processing_test( + spec, + state, + fork_version=state.fork.previous_version, + is_before_fork_epoch=True, + valid=False, + ) diff --git a/tests/core/pyspec/eth2spec/test/helpers/voluntary_exits.py b/tests/core/pyspec/eth2spec/test/helpers/voluntary_exits.py index cac101dff..2e8139db6 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/voluntary_exits.py +++ b/tests/core/pyspec/eth2spec/test/helpers/voluntary_exits.py @@ -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) diff --git a/tests/generators/operations/main.py b/tests/generators/operations/main.py index fc2217917..2cff8bdf6 100644 --- a/tests/generators/operations/main.py +++ b/tests/generators/operations/main.py @@ -36,10 +36,14 @@ if __name__ == "__main__": ]} capella_mods = combine_mods(_new_capella_mods, bellatrix_mods) - deneb_mods = capella_mods + _new_deneb_mods = {key: 'eth2spec.test.deneb.block_processing.test_process_' + key for key in [ + 'voluntary_exit', + ]} + deneb_mods = combine_mods(_new_deneb_mods, capella_mods) _new_eip6110_mods = {key: 'eth2spec.test.eip6110.block_processing.test_process_' + key for key in [ 'deposit_receipt', + 'voluntary_exit', ]} eip6110_mods = combine_mods(_new_eip6110_mods, deneb_mods) From 653e03e70223335d270940cc81640ff875080c7d Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Tue, 30 May 2023 21:12:47 +0800 Subject: [PATCH 06/18] Fix ToC --- specs/deneb/beacon-chain.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/specs/deneb/beacon-chain.md b/specs/deneb/beacon-chain.md index a929db859..335723626 100644 --- a/specs/deneb/beacon-chain.md +++ b/specs/deneb/beacon-chain.md @@ -33,8 +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_voluntary_exit`](#modified-process_voluntary_exit) + - [Modified `process_execution_payload`](#modified-process_execution_payload) + - [Modified `process_voluntary_exit`](#modified-process_voluntary_exit) - [Testing](#testing) @@ -218,7 +218,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: @@ -262,7 +262,7 @@ def process_execution_payload(state: BeaconState, body: BeaconBlockBody, executi ) ``` -##### Modified `process_voluntary_exit` +#### Modified `process_voluntary_exit` ```python def process_voluntary_exit(state: BeaconState, signed_voluntary_exit: SignedVoluntaryExit) -> None: From bab93e8d44b27ccf41a0ddc4993eaae95b144939 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Wed, 7 Jun 2023 18:40:02 +0800 Subject: [PATCH 07/18] specially mark EIP7044 changes --- specs/deneb/beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/deneb/beacon-chain.md b/specs/deneb/beacon-chain.md index ccf0d6933..7ce8bb1e7 100644 --- a/specs/deneb/beacon-chain.md +++ b/specs/deneb/beacon-chain.md @@ -282,7 +282,7 @@ def process_voluntary_exit(state: BeaconState, signed_voluntary_exit: SignedVolu # Verify the validator has been active long enough assert get_current_epoch(state) >= validator.activation_epoch + SHARD_COMMITTEE_PERIOD # Verify signature - # [Modified in Deneb] + # [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) From 99f294cdd8a2d4461576eacaabbf4dc7ad60169c Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Mon, 12 Jun 2023 16:02:28 +0800 Subject: [PATCH 08/18] Add link to EIP PR7044. Need to change it to eips.ethereum.org path once the EIP is merged --- specs/deneb/beacon-chain.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/specs/deneb/beacon-chain.md b/specs/deneb/beacon-chain.md index e25751232..929ac39f6 100644 --- a/specs/deneb/beacon-chain.md +++ b/specs/deneb/beacon-chain.md @@ -43,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 From 530924020f84198675268830f54a879c21784d24 Mon Sep 17 00:00:00 2001 From: dapplion <35266934+dapplion@users.noreply.github.com> Date: Wed, 14 Jun 2023 15:29:59 +0300 Subject: [PATCH 09/18] Lock doctoc version --- .circleci/config.yml | 2 +- .github/workflows/run-tests.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index fcdf483d5..157c56ca5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -176,7 +176,7 @@ jobs: - checkout - run: name: Check table of contents - command: sudo npm install -g doctoc@2 && make check_toc + command: sudo npm install -g doctoc@2.2.0 && make check_toc codespell: docker: - image: circleci/python:3.9 diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 41a80ab92..b27c90765 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -50,7 +50,7 @@ jobs: with: ref: ${{ github.event.inputs.commitRef || env.DEFAULT_BRANCH }} - name: Check table of contents - run: sudo npm install -g doctoc@2 && make check_toc + run: sudo npm install -g doctoc@2.2.0 && make check_toc codespell: runs-on: self-hosted From 12fabf5854622cec2b01cf652a9277534ea6c59a Mon Sep 17 00:00:00 2001 From: Lion - dapplion <35266934+dapplion@users.noreply.github.com> Date: Wed, 14 Jun 2023 16:47:17 +0300 Subject: [PATCH 10/18] Update specs/deneb/beacon-chain.md Co-authored-by: Danny Ryan --- specs/deneb/beacon-chain.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/specs/deneb/beacon-chain.md b/specs/deneb/beacon-chain.md index 929ac39f6..0d71e4ae7 100644 --- a/specs/deneb/beacon-chain.md +++ b/specs/deneb/beacon-chain.md @@ -270,6 +270,8 @@ 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 + ```python def process_voluntary_exit(state: BeaconState, signed_voluntary_exit: SignedVoluntaryExit) -> None: voluntary_exit = signed_voluntary_exit.message From 420f8baf6777cbf35e63ffe4ceb9029b168f65ad Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Wed, 14 Jun 2023 23:04:46 +0800 Subject: [PATCH 11/18] Rework tests. Move all `process_voluntary_exit` tests to Deneb --- specs/deneb/beacon-chain.md | 2 +- .../test_process_voluntary_exit.py | 3 +- .../test_process_voluntary_exit.py | 54 ++++++++++++++++--- .../test_process_voluntary_exit.py | 44 --------------- tests/generators/operations/main.py | 1 - 5 files changed, 49 insertions(+), 55 deletions(-) delete mode 100644 tests/core/pyspec/eth2spec/test/eip6110/block_processing/test_process_voluntary_exit.py diff --git a/specs/deneb/beacon-chain.md b/specs/deneb/beacon-chain.md index 0d71e4ae7..3189ee190 100644 --- a/specs/deneb/beacon-chain.md +++ b/specs/deneb/beacon-chain.md @@ -270,7 +270,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: diff --git a/tests/core/pyspec/eth2spec/test/bellatrix/block_processing/test_process_voluntary_exit.py b/tests/core/pyspec/eth2spec/test/bellatrix/block_processing/test_process_voluntary_exit.py index 12b554da5..ea3b57a97 100644 --- a/tests/core/pyspec/eth2spec/test/bellatrix/block_processing/test_process_voluntary_exit.py +++ b/tests/core/pyspec/eth2spec/test/bellatrix/block_processing/test_process_voluntary_exit.py @@ -7,7 +7,6 @@ from eth2spec.test.context import ( from eth2spec.test.helpers.constants import ( BELLATRIX, CAPELLA, - DENEB, ) from eth2spec.test.helpers.keys import pubkey_to_privkey from eth2spec.test.helpers.state import ( @@ -80,7 +79,7 @@ def test_voluntary_exit_with_current_fork_version_not_is_before_fork_epoch(spec, ) -@with_phases([BELLATRIX, CAPELLA, DENEB]) +@with_phases([BELLATRIX, CAPELLA]) @spec_state_test @always_bls def test_voluntary_exit_with_previous_fork_version_is_before_fork_epoch(spec, state): diff --git a/tests/core/pyspec/eth2spec/test/deneb/block_processing/test_process_voluntary_exit.py b/tests/core/pyspec/eth2spec/test/deneb/block_processing/test_process_voluntary_exit.py index 371fcfed4..06a111c86 100644 --- a/tests/core/pyspec/eth2spec/test/deneb/block_processing/test_process_voluntary_exit.py +++ b/tests/core/pyspec/eth2spec/test/deneb/block_processing/test_process_voluntary_exit.py @@ -19,6 +19,7 @@ def test_invalid_voluntary_exit_with_current_fork_version_not_is_before_fork_epo """ 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, @@ -28,7 +29,7 @@ def test_invalid_voluntary_exit_with_current_fork_version_not_is_before_fork_epo ) -@with_phases([DENEB]) +@with_deneb_and_later @spec_state_test @always_bls def test_voluntary_exit_with_previous_fork_version_not_is_before_fork_epoch(spec, state): @@ -37,9 +38,48 @@ def test_voluntary_exit_with_previous_fork_version_not_is_before_fork_epoch(spec """ assert state.fork.previous_version != state.fork.current_version - yield from run_voluntary_exit_processing_test( - spec, - state, - fork_version=state.fork.previous_version, - is_before_fork_epoch=False, - ) + 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` + """ + 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, + ) diff --git a/tests/core/pyspec/eth2spec/test/eip6110/block_processing/test_process_voluntary_exit.py b/tests/core/pyspec/eth2spec/test/eip6110/block_processing/test_process_voluntary_exit.py deleted file mode 100644 index 4128a1181..000000000 --- a/tests/core/pyspec/eth2spec/test/eip6110/block_processing/test_process_voluntary_exit.py +++ /dev/null @@ -1,44 +0,0 @@ -from eth2spec.test.context import ( - always_bls, - spec_state_test, - with_eip6110_and_later, -) -from eth2spec.test.bellatrix.block_processing.test_process_voluntary_exit import ( - run_voluntary_exit_processing_test, -) - - -@with_eip6110_and_later -@spec_state_test -@always_bls -def test_invalid_voluntary_exit_with_previous_fork_version_not_is_before_fork_epoch(spec, state): - """ - Since Deneb, the VoluntaryExit domain is fixed to `CAPELLA_FORK_VERSION` - """ - assert state.fork.previous_version != state.fork.current_version - - yield from run_voluntary_exit_processing_test( - spec, - state, - fork_version=state.fork.previous_version, - is_before_fork_epoch=False, - valid=False, - ) - - -@with_eip6110_and_later -@spec_state_test -@always_bls -def test_invalid_voluntary_exit_with_previous_fork_version_is_before_fork_epoch(spec, state): - """ - Since Deneb, the VoluntaryExit domain is fixed to `CAPELLA_FORK_VERSION` - """ - assert state.fork.previous_version != state.fork.current_version - - yield from run_voluntary_exit_processing_test( - spec, - state, - fork_version=state.fork.previous_version, - is_before_fork_epoch=True, - valid=False, - ) diff --git a/tests/generators/operations/main.py b/tests/generators/operations/main.py index 3b4e5d1f7..053236c8d 100644 --- a/tests/generators/operations/main.py +++ b/tests/generators/operations/main.py @@ -45,7 +45,6 @@ if __name__ == "__main__": _new_eip6110_mods = {key: 'eth2spec.test.eip6110.block_processing.test_process_' + key for key in [ 'deposit_receipt', - 'voluntary_exit', ]} eip6110_mods = combine_mods(_new_eip6110_mods, deneb_mods) From 11ab19c90a6228a696dea3df27b8834674fef348 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Wed, 14 Jun 2023 09:25:54 -0600 Subject: [PATCH 12/18] Apply suggestions from code review --- .../deneb/block_processing/test_process_voluntary_exit.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/core/pyspec/eth2spec/test/deneb/block_processing/test_process_voluntary_exit.py b/tests/core/pyspec/eth2spec/test/deneb/block_processing/test_process_voluntary_exit.py index 06a111c86..711d27eb9 100644 --- a/tests/core/pyspec/eth2spec/test/deneb/block_processing/test_process_voluntary_exit.py +++ b/tests/core/pyspec/eth2spec/test/deneb/block_processing/test_process_voluntary_exit.py @@ -35,6 +35,8 @@ def test_invalid_voluntary_exit_with_current_fork_version_not_is_before_fork_epo 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 """ assert state.fork.previous_version != state.fork.current_version @@ -63,6 +65,8 @@ def test_voluntary_exit_with_previous_fork_version_not_is_before_fork_epoch(spec 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` + + Note: This test is valid for ``spec.fork == DENEB`` and invalid for subsequent forks """ assert state.fork.previous_version != state.fork.current_version From 7b132c20d1d4347df9bb262753be81407c410279 Mon Sep 17 00:00:00 2001 From: Lion - dapplion <35266934+dapplion@users.noreply.github.com> Date: Wed, 14 Jun 2023 18:52:22 +0300 Subject: [PATCH 13/18] Fix typos in get_shuffle_indices (#3426) --- specs/_features/whisk/beacon-chain.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/specs/_features/whisk/beacon-chain.md b/specs/_features/whisk/beacon-chain.md index bad818837..6c8db4cec 100644 --- a/specs/_features/whisk/beacon-chain.md +++ b/specs/_features/whisk/beacon-chain.md @@ -306,9 +306,10 @@ def get_shuffle_indices(randao_reveal: BLSSignature) -> Sequence[uint64]: Given a `randao_reveal` return the list of indices that got shuffled from the entire candidate set """ indices = [] - for i in WHISK_VALIDATORS_PER_SHUFFLE: + for i in range(0, WHISK_VALIDATORS_PER_SHUFFLE): # XXX ensure we are not suffering from modulo bias - shuffle_index = uint256(hash(randao_reveal + uint_to_bytes(i))) % WHISK_CANDIDATE_TRACKERS_COUNT + pre_image = randao_reveal + uint_to_bytes(uint64(i)) + shuffle_index = bytes_to_uint64(hash(pre_image)[0:8]) % WHISK_CANDIDATE_TRACKERS_COUNT indices.append(shuffle_index) return indices From 0ab160bc2801be1ecb5ca2f65a2202f632b75bf5 Mon Sep 17 00:00:00 2001 From: Lion - dapplion <35266934+dapplion@users.noreply.github.com> Date: Wed, 14 Jun 2023 18:55:07 +0300 Subject: [PATCH 14/18] Add initialize_beacon_state_from_eth1 (#3428) --- configs/mainnet.yaml | 3 +++ configs/minimal.yaml | 3 +++ specs/_features/whisk/beacon-chain.md | 23 +++++++++++++++++++++++ 3 files changed, 29 insertions(+) diff --git a/configs/mainnet.yaml b/configs/mainnet.yaml index 365bc1136..9206ab77d 100644 --- a/configs/mainnet.yaml +++ b/configs/mainnet.yaml @@ -53,6 +53,9 @@ DENEB_FORK_EPOCH: 18446744073709551615 # EIP6110 EIP6110_FORK_VERSION: 0x05000000 # temporary stub EIP6110_FORK_EPOCH: 18446744073709551615 +# WHISK +WHISK_FORK_VERSION: 0x06000000 # temporary stub +WHISK_FORK_EPOCH: 18446744073709551615 # Time parameters diff --git a/configs/minimal.yaml b/configs/minimal.yaml index b22a7165e..256a39d1c 100644 --- a/configs/minimal.yaml +++ b/configs/minimal.yaml @@ -52,6 +52,9 @@ DENEB_FORK_EPOCH: 18446744073709551615 # EIP6110 EIP6110_FORK_VERSION: 0x05000001 EIP6110_FORK_EPOCH: 18446744073709551615 +# WHISK +WHISK_FORK_VERSION: 0x06000001 +WHISK_FORK_EPOCH: 18446744073709551615 # Time parameters diff --git a/specs/_features/whisk/beacon-chain.md b/specs/_features/whisk/beacon-chain.md index 6c8db4cec..c8adf945d 100644 --- a/specs/_features/whisk/beacon-chain.md +++ b/specs/_features/whisk/beacon-chain.md @@ -23,6 +23,7 @@ - [`BeaconBlockBody`](#beaconblockbody) - [Deposits](#deposits) - [`get_beacon_proposer_index`](#get_beacon_proposer_index) +- [Testing](#testing) @@ -470,3 +471,25 @@ def get_beacon_proposer_index(state: BeaconState) -> ValidatorIndex: assert state.latest_block_header.slot == state.slot # sanity check `process_block_header` has been called return state.latest_block_header.proposer_index ``` + +## Testing + +*Note*: The function `initialize_beacon_state_from_eth1` is modified for pure Whisk testing only. + +```python +def initialize_beacon_state_from_eth1(eth1_block_hash: Hash32, + eth1_timestamp: uint64, + deposits: Sequence[Deposit], + execution_payload_header: ExecutionPayloadHeader=ExecutionPayloadHeader() + ) -> BeaconState: + state_capella = capella.initialize_beacon_state_from_eth1( + eth1_block_hash, + eth1_timestamp, + deposits, + execution_payload_header, + ) + state = upgrade_to_whisk(state_capella) + state.fork.previous_version = WHISK_FORK_VERSION + state.fork.current_version = WHISK_FORK_VERSION + return state +``` From 65a28b6d69a85135a8b9907b53d40f2f0182cb32 Mon Sep 17 00:00:00 2001 From: Lion - dapplion <35266934+dapplion@users.noreply.github.com> Date: Wed, 14 Jun 2023 18:58:57 +0300 Subject: [PATCH 15/18] Whisk: Move validator whisk trackers and commitments to state (#3407) * Move validator whisk trackers and commitments to state * Move comment --- specs/_features/whisk/beacon-chain.md | 77 ++++++++------------------- specs/_features/whisk/fork.md | 35 ++++++------ 2 files changed, 39 insertions(+), 73 deletions(-) diff --git a/specs/_features/whisk/beacon-chain.md b/specs/_features/whisk/beacon-chain.md index c8adf945d..34e79c1e4 100644 --- a/specs/_features/whisk/beacon-chain.md +++ b/specs/_features/whisk/beacon-chain.md @@ -15,7 +15,6 @@ - [Curdleproofs and opening proofs](#curdleproofs-and-opening-proofs) - [Epoch processing](#epoch-processing) - [`WhiskTracker`](#whisktracker) - - [`Validator`](#validator) - [`BeaconState`](#beaconstate) - [Block processing](#block-processing) - [Block header](#block-header) @@ -125,23 +124,6 @@ class WhiskTracker(Container): k_r_G: BLSG1Point # k * r * G ``` -### `Validator` - -```python -class Validator(Container): - pubkey: BLSPubkey - withdrawal_credentials: Bytes32 # Commitment to pubkey for withdrawals - effective_balance: Gwei # Balance at stake - slashed: boolean - # Status epochs - activation_eligibility_epoch: Epoch # When criteria for activation were met - activation_epoch: Epoch - exit_epoch: Epoch - withdrawable_epoch: Epoch # When validator can withdraw funds - whisk_tracker: WhiskTracker # Whisk tracker (r * G, k * r * G) [New in Whisk] - whisk_k_commitment: BLSG1Point # Whisk k commitment k * BLS_G1_GENERATOR [New in Whisk] -``` - ### `BeaconState` ```python @@ -187,8 +169,11 @@ class BeaconState(Container): next_withdrawal_validator_index: ValidatorIndex # Deep history valid from Capella onwards historical_summaries: List[HistoricalSummary, HISTORICAL_ROOTS_LIMIT] + # Whisk whisk_candidate_trackers: Vector[WhiskTracker, WHISK_CANDIDATE_TRACKERS_COUNT] # [New in Whisk] whisk_proposer_trackers: Vector[WhiskTracker, WHISK_PROPOSER_TRACKERS_COUNT] # [New in Whisk] + whisk_trackers: List[WhiskTracker, VALIDATOR_REGISTRY_LIMIT] # [New in Whisk] + whisk_k_commitments: List[BLSG1Point, VALIDATOR_REGISTRY_LIMIT] # [New in Whisk] ``` ```python @@ -204,7 +189,7 @@ def select_whisk_trackers(state: BeaconState, epoch: Epoch) -> None: for i in range(WHISK_CANDIDATE_TRACKERS_COUNT): seed = hash(get_seed(state, epoch, DOMAIN_WHISK_CANDIDATE_SELECTION) + uint_to_bytes(i)) candidate_index = compute_proposer_index(state, active_validator_indices, seed) # sample by effective balance - state.whisk_candidate_trackers[i] = state.validators[candidate_index].whisk_tracker + state.whisk_candidate_trackers[i] = state.whisk_trackers[candidate_index] ``` ```python @@ -238,7 +223,7 @@ def process_epoch(state: BeaconState) -> None: ```python def process_whisk_opening_proof(state: BeaconState, block: BeaconBlock) -> None: tracker = state.whisk_proposer_trackers[state.slot % WHISK_PROPOSER_TRACKERS_COUNT] - k_commitment = state.validators[block.proposer_index].whisk_k_commitment + k_commitment = state.whisk_k_commitments[block.proposer_index] assert IsValidWhiskOpeningProof(tracker, k_commitment, block.body.whisk_opening_proof) ``` @@ -298,7 +283,7 @@ class BeaconBlockBody(capella.BeaconBlockBody): whisk_shuffle_proof_M_commitment: BLSG1Point # [New in Whisk] whisk_registration_proof: WhiskTrackerProof # [New in Whisk] whisk_tracker: WhiskTracker # [New in Whisk] - whisk_k_commitment: BLSG1Point # [New in Whisk] + whisk_k_commitment: BLSG1Point # k * BLS_G1_GENERATOR [New in Whisk] ``` ```python @@ -343,7 +328,7 @@ def process_shuffled_trackers(state: BeaconState, body: BeaconBlockBody) -> None ```python def is_k_commitment_unique(state: BeaconState, k_commitment: BLSG1Point) -> bool: - return all([validator.whisk_k_commitment != k_commitment for validator in state.validators]) + return all([whisk_k_commitment != k_commitment for whisk_k_commitment in state.whisk_k_commitments]) ``` ```python @@ -384,49 +369,30 @@ def process_block(state: BeaconState, block: BeaconBlock) -> None: ### Deposits +```python +def get_initial_whisk_k(validator_index: ValidatorIndex, counter: int) -> BLSFieldElement: + # hash `validator_index || counter` + return BLSFieldElement(bytes_to_bls_field(hash(uint_to_bytes(validator_index) + uint_to_bytes(uint64(counter))))) +``` + ```python def get_unique_whisk_k(state: BeaconState, validator_index: ValidatorIndex) -> BLSFieldElement: counter = 0 while True: - # hash `validator_index || counter` - k = BLSFieldElement(bytes_to_bls_field(hash(uint_to_bytes(validator_index) + uint_to_bytes(uint64(counter))))) + k = get_initial_whisk_k(validator_index, counter) if is_k_commitment_unique(state, BLSG1ScalarMultiply(k, BLS_G1_GENERATOR)): return k # unique by trial and error counter += 1 ``` ```python -def get_initial_commitments(k: BLSFieldElement) -> Tuple[BLSG1Point, WhiskTracker]: - return ( - BLSG1ScalarMultiply(k, BLS_G1_GENERATOR), - WhiskTracker(r_G=BLS_G1_GENERATOR, k_r_G=BLSG1ScalarMultiply(k, BLS_G1_GENERATOR)) - ) +def get_k_commitment(k: BLSFieldElement) -> BLSG1Point: + return BLSG1ScalarMultiply(k, BLS_G1_GENERATOR) ``` ```python -def get_validator_from_deposit_whisk( - state: BeaconState, - pubkey: BLSPubkey, - withdrawal_credentials: Bytes32, - amount: uint64 -) -> Validator: - effective_balance = min(amount - amount % EFFECTIVE_BALANCE_INCREMENT, MAX_EFFECTIVE_BALANCE) - k = get_unique_whisk_k(state, ValidatorIndex(len(state.validators))) - whisk_k_commitment, whisk_tracker = get_initial_commitments(k) - - validator = Validator( - pubkey=pubkey, - withdrawal_credentials=withdrawal_credentials, - activation_eligibility_epoch=FAR_FUTURE_EPOCH, - activation_epoch=FAR_FUTURE_EPOCH, - exit_epoch=FAR_FUTURE_EPOCH, - withdrawable_epoch=FAR_FUTURE_EPOCH, - effective_balance=effective_balance, - # Whisk fields - whisk_tracker=whisk_tracker, - whisk_k_commitment=whisk_k_commitment, - ) - return validator +def get_initial_tracker(k: BLSFieldElement) -> WhiskTracker: + return WhiskTracker(r_G=BLS_G1_GENERATOR, k_r_G=BLSG1ScalarMultiply(k, BLS_G1_GENERATOR)) ``` ```python @@ -448,13 +414,16 @@ def apply_deposit(state: BeaconState, # Initialize validator if the deposit signature is valid if bls.Verify(pubkey, signing_root, signature): index = get_index_for_new_validator(state) - validator = get_validator_from_deposit_whisk(state, pubkey, withdrawal_credentials, amount) + validator = get_validator_from_deposit(pubkey, withdrawal_credentials, amount) set_or_append_list(state.validators, index, validator) set_or_append_list(state.balances, index, amount) - # [New in Altair] set_or_append_list(state.previous_epoch_participation, index, ParticipationFlags(0b0000_0000)) set_or_append_list(state.current_epoch_participation, index, ParticipationFlags(0b0000_0000)) set_or_append_list(state.inactivity_scores, index, uint64(0)) + # [New in Whisk] + k = get_unique_whisk_k(state, ValidatorIndex(len(state.validators) - 1)) + state.whisk_trackers.append(get_initial_tracker(k)) + state.whisk_k_commitments.append(get_k_commitment(k)) else: # Increase balance by deposit amount index = ValidatorIndex(validator_pubkeys.index(pubkey)) diff --git a/specs/_features/whisk/fork.md b/specs/_features/whisk/fork.md index 189d8c5b3..f66026037 100644 --- a/specs/_features/whisk/fork.md +++ b/specs/_features/whisk/fork.md @@ -68,6 +68,11 @@ def whisk_proposer_selection(state: BeaconState, epoch: Epoch) -> None: ```python def upgrade_to_whisk(pre: bellatrix.BeaconState) -> BeaconState: + # Compute initial unsafe trackers for all validators + ks = [get_initial_whisk_k(ValidatorIndex(validator_index), 0) for validator_index in range(len(pre.validators))] + whisk_k_commitments = [get_k_commitment(k) for k in ks] + whisk_trackers = [get_initial_tracker(k) for k in ks] + epoch = bellatrix.get_current_epoch(pre) post = BeaconState( # Versioning @@ -104,27 +109,19 @@ def upgrade_to_whisk(pre: bellatrix.BeaconState) -> BeaconState: current_justified_checkpoint=pre.current_justified_checkpoint, finalized_checkpoint=pre.finalized_checkpoint, # Inactivity - inactivity_scores=pre.inactivity_Scores, + 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=pre.latest_execution_payload_header, + # Whisk + whisk_proposer_trackers=[WhiskTracker() for _ in range(WHISK_PROPOSER_TRACKERS_COUNT)], # [New in Whisk] + whisk_candidate_trackers=[WhiskTracker() for _ in range(WHISK_CANDIDATE_TRACKERS_COUNT)], # [New in Whisk] + whisk_trackers=whisk_trackers, # [New in Whisk] + whisk_k_commitments=whisk_k_commitments, # [New in Whisk] ) - # Initialize all validators with predictable commitments - for val_index, pre_validator in enumerate(pre.validators): - whisk_commitment, whisk_tracker = get_initial_commitments(get_unique_whisk_k(post, ValidatorIndex(val_index))) - - post_validator = Validator( - pubkey=pre_validator.pubkey, - withdrawal_credentials=pre_validator.withdrawal_credentials, - effective_balance=pre_validator.effective_balance, - slashed=pre_validator.slashed, - activation_eligibility_epoch=pre_validator.activation_eligibility_epoch, - activation_epoch=pre_validator.activation_epoch, - exit_epoch=pre_validator.exit_epoch, - withdrawable_epoch=pre_validator.withdrawable_epoch, - whisk_commitment=whisk_commitment, - whisk_tracker=whisk_tracker, - ) - post.validators.append(post_validator) - # Do a candidate selection followed by a proposer selection so that we have proposers for the upcoming day # Use an old epoch when selecting candidates so that we don't get the same seed as in the next candidate selection whisk_candidate_selection(post, epoch - WHISK_PROPOSER_SELECTION_GAP - 1) From 9e50c74a372c0d8349764ef411b22cfa71f2a656 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Thu, 15 Jun 2023 00:45:43 +0800 Subject: [PATCH 16/18] Fix linter (#3430) --- .../test/deneb/block_processing/test_process_voluntary_exit.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/core/pyspec/eth2spec/test/deneb/block_processing/test_process_voluntary_exit.py b/tests/core/pyspec/eth2spec/test/deneb/block_processing/test_process_voluntary_exit.py index 711d27eb9..b01eaab0e 100644 --- a/tests/core/pyspec/eth2spec/test/deneb/block_processing/test_process_voluntary_exit.py +++ b/tests/core/pyspec/eth2spec/test/deneb/block_processing/test_process_voluntary_exit.py @@ -1,7 +1,6 @@ from eth2spec.test.context import ( always_bls, spec_state_test, - with_phases, with_deneb_and_later, ) from eth2spec.test.helpers.constants import ( From 834f6f70e7b864dde91641191f20e0dd1c3b0331 Mon Sep 17 00:00:00 2001 From: Lion - dapplion <35266934+dapplion@users.noreply.github.com> Date: Thu, 15 Jun 2023 14:33:28 +0300 Subject: [PATCH 17/18] Whisk: assert zeroed values during selection gap (#3425) * Assert zeroed values during selection gap * Update comment --- specs/_features/whisk/beacon-chain.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/specs/_features/whisk/beacon-chain.md b/specs/_features/whisk/beacon-chain.md index 34e79c1e4..b10611a8d 100644 --- a/specs/_features/whisk/beacon-chain.md +++ b/specs/_features/whisk/beacon-chain.md @@ -306,20 +306,23 @@ def process_shuffled_trackers(state: BeaconState, body: BeaconBlockBody) -> None # Check the shuffle proof shuffle_indices = get_shuffle_indices(body.randao_reveal) pre_shuffle_trackers = [state.whisk_candidate_trackers[i] for i in shuffle_indices] - post_shuffle_trackers = body.whisk_post_shuffle_trackers shuffle_epoch = get_current_epoch(state) % WHISK_EPOCHS_PER_SHUFFLING_PHASE if shuffle_epoch + WHISK_PROPOSER_SELECTION_GAP + 1 >= WHISK_EPOCHS_PER_SHUFFLING_PHASE: - # Require unchanged trackers during cooldown - assert pre_shuffle_trackers == post_shuffle_trackers + # Require trackers set to zero during cooldown + assert body.whisk_post_shuffle_trackers == Vector[WhiskTracker, WHISK_VALIDATORS_PER_SHUFFLE]() + assert body.whisk_shuffle_proof_M_commitment == BLSG1Point() + assert body.whisk_shuffle_proof == WhiskShuffleProof() + post_shuffle_trackers = pre_shuffle_trackers else: # Require shuffled trackers during shuffle assert IsValidWhiskShuffleProof( pre_shuffle_trackers, - post_shuffle_trackers, + body.whisk_post_shuffle_trackers, body.whisk_shuffle_proof_M_commitment, body.whisk_shuffle_proof, ) + post_shuffle_trackers = body.whisk_post_shuffle_trackers # Shuffle candidate trackers for i, shuffle_index in enumerate(shuffle_indices): From 781cd83f095e4805e7c24b430607ae7f2fcdbdff Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Fri, 16 Jun 2023 17:21:34 +0800 Subject: [PATCH 18/18] fix typo --- specs/phase0/p2p-interface.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specs/phase0/p2p-interface.md b/specs/phase0/p2p-interface.md index c0d18b08f..bbb4c4d42 100644 --- a/specs/phase0/p2p-interface.md +++ b/specs/phase0/p2p-interface.md @@ -55,7 +55,7 @@ It consists of four main sections: - [ENR structure](#enr-structure) - [Attestation subnet bitfield](#attestation-subnet-bitfield) - [`eth2` field](#eth2-field) - - [Attestation subnet subcription](#attestation-subnet-subcription) + - [Attestation subnet subscription](#attestation-subnet-subscription) - [Design decision rationale](#design-decision-rationale) - [Transport](#transport-1) - [Why are we defining specific transports?](#why-are-we-defining-specific-transports) @@ -1002,7 +1002,7 @@ Clients MAY connect to peers with the same `fork_digest` but a different `next_f Unless `ENRForkID` is manually updated to matching prior to the earlier `next_fork_epoch` of the two clients, these connecting clients will be unable to successfully interact starting at the earlier `next_fork_epoch`. -### Attestation subnet subcription +### Attestation subnet subscription Because Phase 0 does not have shards and thus does not have Shard Committees, there is no stable backbone to the attestation subnets (`beacon_attestation_{subnet_id}`). To provide this stability, each beacon node should: