From 70f90c5296b9ecc95b6f63b8854d103c5d6a18c7 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Mon, 19 Sep 2022 11:05:47 -0600 Subject: [PATCH 01/35] rmove withdrawn_epoch --- specs/capella/beacon-chain.md | 16 +-- specs/capella/fork.md | 16 +-- .../test_process_bls_to_execution_change.py | 16 ++- .../block_processing/test_process_deposit.py | 30 +++++ .../test_process_full_withdrawals.py | 123 +++++++++++++++--- .../eth2spec/test/helpers/capella/fork.py | 5 +- .../pyspec/eth2spec/test/helpers/deposits.py | 15 ++- .../pyspec/eth2spec/test/helpers/genesis.py | 3 - .../eth2spec/test/helpers/withdrawals.py | 17 +++ .../block_processing/test_process_deposit.py | 46 ++++++- 10 files changed, 228 insertions(+), 59 deletions(-) create mode 100644 tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_deposit.py create mode 100644 tests/core/pyspec/eth2spec/test/helpers/withdrawals.py diff --git a/specs/capella/beacon-chain.md b/specs/capella/beacon-chain.md index 40592a9bde..25588c1e8e 100644 --- a/specs/capella/beacon-chain.md +++ b/specs/capella/beacon-chain.md @@ -193,7 +193,6 @@ class Validator(Container): activation_epoch: Epoch exit_epoch: Epoch withdrawable_epoch: Epoch # When validator can withdraw funds - fully_withdrawn_epoch: Epoch # [New in Capella] ``` #### `BeaconBlockBody` @@ -297,13 +296,14 @@ def has_eth1_withdrawal_credential(validator: Validator) -> bool: #### `is_fully_withdrawable_validator` ```python -def is_fully_withdrawable_validator(validator: Validator, epoch: Epoch) -> bool: +def is_fully_withdrawable_validator(validator: Validator, balance: Gwei, epoch: Epoch) -> bool: """ Check if ``validator`` is fully withdrawable. """ return ( has_eth1_withdrawal_credential(validator) - and validator.withdrawable_epoch <= epoch < validator.fully_withdrawn_epoch + and validator.withdrawable_epoch <= epoch + and balance > 0 ) ``` @@ -349,11 +349,11 @@ def process_epoch(state: BeaconState) -> None: ```python def process_full_withdrawals(state: BeaconState) -> None: current_epoch = get_current_epoch(state) - for index, validator in enumerate(state.validators): - if is_fully_withdrawable_validator(validator, current_epoch): - # TODO, consider the zero-balance case - withdraw_balance(state, ValidatorIndex(index), state.balances[index]) - validator.fully_withdrawn_epoch = current_epoch + for index in range(len(state.validators)): + balance = state.balances[index] + validator = state.validators[index] + if is_fully_withdrawable_validator(validator, balance, current_epoch): + withdraw_balance(state, ValidatorIndex(index), balance) ``` #### Partial withdrawals diff --git a/specs/capella/fork.md b/specs/capella/fork.md index c22387ee72..34d373be06 100644 --- a/specs/capella/fork.md +++ b/specs/capella/fork.md @@ -90,7 +90,7 @@ def upgrade_to_capella(pre: bellatrix.BeaconState) -> BeaconState: eth1_data_votes=pre.eth1_data_votes, eth1_deposit_index=pre.eth1_deposit_index, # Registry - validators=[], + validators=pre.validators, balances=pre.balances, # Randomness randao_mixes=pre.randao_mixes, @@ -117,19 +117,5 @@ def upgrade_to_capella(pre: bellatrix.BeaconState) -> BeaconState: next_partial_withdrawal_validator_index=ValidatorIndex(0), ) - for pre_validator in pre.validators: - 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, - fully_withdrawn_epoch=FAR_FUTURE_EPOCH, - ) - post.validators.append(post_validator) - return post ``` diff --git a/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_bls_to_execution_change.py b/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_bls_to_execution_change.py index 4b69e04a63..abcbec0f19 100644 --- a/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_bls_to_execution_change.py +++ b/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_bls_to_execution_change.py @@ -82,7 +82,9 @@ def test_success_not_activated(spec, state): signed_address_change = get_signed_address_change(spec, state) yield from run_bls_to_execution_change_processing(spec, state, signed_address_change) - assert not spec.is_fully_withdrawable_validator(state.validators[validator_index], spec.get_current_epoch(state)) + validator = state.validators[validator_index] + balance = state.balances[validator_index] + assert not spec.is_fully_withdrawable_validator(validator, balance, spec.get_current_epoch(state)) @with_capella_and_later @@ -98,7 +100,9 @@ def test_success_in_activation_queue(spec, state): signed_address_change = get_signed_address_change(spec, state) yield from run_bls_to_execution_change_processing(spec, state, signed_address_change) - assert not spec.is_fully_withdrawable_validator(state.validators[validator_index], spec.get_current_epoch(state)) + validator = state.validators[validator_index] + balance = state.balances[validator_index] + assert not spec.is_fully_withdrawable_validator(validator, balance, spec.get_current_epoch(state)) @with_capella_and_later @@ -126,7 +130,9 @@ def test_success_exited(spec, state): signed_address_change = get_signed_address_change(spec, state, validator_index=validator_index) yield from run_bls_to_execution_change_processing(spec, state, signed_address_change) - assert not spec.is_fully_withdrawable_validator(state.validators[validator_index], spec.get_current_epoch(state)) + validator = state.validators[validator_index] + balance = state.balances[validator_index] + assert not spec.is_fully_withdrawable_validator(validator, balance, spec.get_current_epoch(state)) @with_capella_and_later @@ -142,7 +148,9 @@ def test_success_withdrawable(spec, state): signed_address_change = get_signed_address_change(spec, state, validator_index=validator_index) yield from run_bls_to_execution_change_processing(spec, state, signed_address_change) - assert spec.is_fully_withdrawable_validator(state.validators[validator_index], spec.get_current_epoch(state)) + validator = state.validators[validator_index] + balance = state.balances[validator_index] + assert spec.is_fully_withdrawable_validator(validator, balance, spec.get_current_epoch(state)) @with_capella_and_later diff --git a/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_deposit.py b/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_deposit.py new file mode 100644 index 0000000000..34c58ef39c --- /dev/null +++ b/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_deposit.py @@ -0,0 +1,30 @@ +from eth2spec.test.context import ( + spec_state_test, + with_capella_and_later, +) +from eth2spec.test.helpers.state import next_epoch_via_block +from eth2spec.test.helpers.deposits import ( + prepare_state_and_deposit, + run_deposit_processing, +) +from eth2spec.test.helpers.withdrawals import set_validator_fully_withdrawable + + +@with_capella_and_later +@spec_state_test +def test_success_top_up_to_withdrawn_validator(spec, state): + validator_index = 0 + + # Fully withdraw validator + set_validator_fully_withdrawable(spec, state, validator_index) + assert state.balances[validator_index] > 0 + next_epoch_via_block(spec, state) + assert state.balances[validator_index] == 0 + + # Make a top-up balance to validator + amount = spec.MAX_EFFECTIVE_BALANCE // 4 + deposit = prepare_state_and_deposit(spec, state, validator_index, amount, signed=True) + + yield from run_deposit_processing(spec, state, deposit, validator_index) + + state.balances[validator_index] == amount diff --git a/tests/core/pyspec/eth2spec/test/capella/epoch_processing/test_process_full_withdrawals.py b/tests/core/pyspec/eth2spec/test/capella/epoch_processing/test_process_full_withdrawals.py index 1498666bb6..35d2968cb9 100644 --- a/tests/core/pyspec/eth2spec/test/capella/epoch_processing/test_process_full_withdrawals.py +++ b/tests/core/pyspec/eth2spec/test/capella/epoch_processing/test_process_full_withdrawals.py @@ -1,27 +1,28 @@ +from random import Random + from eth2spec.test.context import ( with_capella_and_later, spec_state_test, ) -from eth2spec.test.helpers.epoch_processing import run_epoch_processing_with - - -def set_validator_withdrawable(spec, state, index, withdrawable_epoch=None): - if withdrawable_epoch is None: - withdrawable_epoch = spec.get_current_epoch(state) - - validator = state.validators[index] - validator.withdrawable_epoch = withdrawable_epoch - validator.withdrawal_credentials = spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + validator.withdrawal_credentials[1:] - - assert spec.is_fully_withdrawable_validator(validator, withdrawable_epoch) +from eth2spec.test.helpers.random import ( + randomize_state, +) +from eth2spec.test.helpers.epoch_processing import ( + run_epoch_processing_to, +) +from eth2spec.test.helpers.withdrawals import ( + set_validator_fully_withdrawable, +) def run_process_full_withdrawals(spec, state, num_expected_withdrawals=None): + run_epoch_processing_to(spec, state, 'process_full_withdrawals') + pre_next_withdrawal_index = state.next_withdrawal_index pre_withdrawal_queue = state.withdrawal_queue.copy() to_be_withdrawn_indices = [ index for index, validator in enumerate(state.validators) - if spec.is_fully_withdrawable_validator(validator, spec.get_current_epoch(state)) + if spec.is_fully_withdrawable_validator(validator, state.balances[index], spec.get_current_epoch(state)) ] if num_expected_withdrawals is not None: @@ -29,11 +30,11 @@ def run_process_full_withdrawals(spec, state, num_expected_withdrawals=None): else: num_expected_withdrawals = len(to_be_withdrawn_indices) - yield from run_epoch_processing_with(spec, state, 'process_full_withdrawals') + yield 'pre', state + spec.process_full_withdrawals(state) + yield 'post', state for index in to_be_withdrawn_indices: - validator = state.validators[index] - assert validator.fully_withdrawn_epoch == spec.get_current_epoch(state) assert state.balances[index] == 0 assert len(state.withdrawal_queue) == len(pre_withdrawal_queue) + num_expected_withdrawals @@ -42,13 +43,49 @@ def run_process_full_withdrawals(spec, state, num_expected_withdrawals=None): @with_capella_and_later @spec_state_test -def test_no_withdrawals(spec, state): +def test_no_withdrawable_validators(spec, state): pre_validators = state.validators.copy() yield from run_process_full_withdrawals(spec, state, 0) assert pre_validators == state.validators +@with_capella_and_later +@spec_state_test +def test_withdrawable_epoch_but_0_balance(spec, state): + current_epoch = spec.get_current_epoch(state) + set_validator_fully_withdrawable(spec, state, 0, current_epoch) + + state.validators[0].effective_balance = 10000000000 + state.balances[0] = 0 + + yield from run_process_full_withdrawals(spec, state, 0) + + +@with_capella_and_later +@spec_state_test +def test_withdrawable_epoch_but_0_effective_balance_0_balance(spec, state): + current_epoch = spec.get_current_epoch(state) + set_validator_fully_withdrawable(spec, state, 0, current_epoch) + + state.validators[0].effective_balance = 0 + state.balances[0] = 0 + + yield from run_process_full_withdrawals(spec, state, 0) + + +@with_capella_and_later +@spec_state_test +def test_withdrawable_epoch_but_0_effective_balance_nonzero_balance(spec, state): + current_epoch = spec.get_current_epoch(state) + set_validator_fully_withdrawable(spec, state, 0, current_epoch) + + state.validators[0].effective_balance = 0 + state.balances[0] = 100000000 + + yield from run_process_full_withdrawals(spec, state, 1) + + @with_capella_and_later @spec_state_test def test_no_withdrawals_but_some_next_epoch(spec, state): @@ -56,7 +93,7 @@ def test_no_withdrawals_but_some_next_epoch(spec, state): # Make a few validators withdrawable at the *next* epoch for index in range(3): - set_validator_withdrawable(spec, state, index, current_epoch + 1) + set_validator_fully_withdrawable(spec, state, index, current_epoch + 1) yield from run_process_full_withdrawals(spec, state, 0) @@ -65,7 +102,7 @@ def test_no_withdrawals_but_some_next_epoch(spec, state): @spec_state_test def test_single_withdrawal(spec, state): # Make one validator withdrawable - set_validator_withdrawable(spec, state, 0) + set_validator_fully_withdrawable(spec, state, 0) assert state.next_withdrawal_index == 0 yield from run_process_full_withdrawals(spec, state, 1) @@ -78,7 +115,7 @@ def test_single_withdrawal(spec, state): def test_multi_withdrawal(spec, state): # Make a few validators withdrawable for index in range(3): - set_validator_withdrawable(spec, state, index) + set_validator_fully_withdrawable(spec, state, index) yield from run_process_full_withdrawals(spec, state, 3) @@ -88,6 +125,50 @@ def test_multi_withdrawal(spec, state): def test_all_withdrawal(spec, state): # Make all validators withdrawable for index in range(len(state.validators)): - set_validator_withdrawable(spec, state, index) + set_validator_fully_withdrawable(spec, state, index) yield from run_process_full_withdrawals(spec, state, len(state.validators)) + + +def run_random_full_withdrawals_test(spec, state, rng): + randomize_state(spec, state, rng) + for index in range(len(state.validators)): + # 50% withdrawable + if rng.choice([True, False]): + set_validator_fully_withdrawable(spec, state, index) + validator = state.validators[index] + # 12.5% unset credentials + if rng.randint(0, 7) == 0: + validator.withdrawal_credentials = spec.BLS_WITHDRAWAL_PREFIX + validator.withdrawal_credentials[1:] + # 12.5% not enough balance + if rng.randint(0, 7) == 0: + state.balances[index] = 0 + # 12.5% not close enough epoch + if rng.randint(0, 7) == 0: + validator.withdrawable_epoch += 1 + + yield from run_process_full_withdrawals(spec, state, None) + + +@with_capella_and_later +@spec_state_test +def test_random_withdrawals_0(spec, state): + yield from run_random_full_withdrawals_test(spec, state, Random(444)) + + +@with_capella_and_later +@spec_state_test +def test_random_withdrawals_1(spec, state): + yield from run_random_full_withdrawals_test(spec, state, Random(420)) + + +@with_capella_and_later +@spec_state_test +def test_random_withdrawals_2(spec, state): + yield from run_random_full_withdrawals_test(spec, state, Random(200)) + + +@with_capella_and_later +@spec_state_test +def test_random_withdrawals_3(spec, state): + yield from run_random_full_withdrawals_test(spec, state, Random(2000000)) diff --git a/tests/core/pyspec/eth2spec/test/helpers/capella/fork.py b/tests/core/pyspec/eth2spec/test/helpers/capella/fork.py index 41975904fe..8e0aec9c6e 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/capella/fork.py +++ b/tests/core/pyspec/eth2spec/test/helpers/capella/fork.py @@ -16,7 +16,7 @@ def run_fork_test(post_spec, pre_state): # Eth1 'eth1_data', 'eth1_data_votes', 'eth1_deposit_index', # Registry - 'balances', + 'validators', 'balances', # Randomness 'randao_mixes', # Slashings @@ -36,7 +36,7 @@ def run_fork_test(post_spec, pre_state): assert getattr(pre_state, field) == getattr(post_state, field) # Modified fields - modified_fields = ['fork', 'validators'] + modified_fields = ['fork'] for field in modified_fields: assert getattr(pre_state, field) != getattr(post_state, field) @@ -50,7 +50,6 @@ def run_fork_test(post_spec, pre_state): ] for field in stable_validator_fields: assert getattr(pre_validator, field) == getattr(post_validator, field) - assert post_validator.fully_withdrawn_epoch == post_spec.FAR_FUTURE_EPOCH assert pre_state.fork.current_version == post_state.fork.previous_version assert post_state.fork.current_version == post_spec.config.CAPELLA_FORK_VERSION diff --git a/tests/core/pyspec/eth2spec/test/helpers/deposits.py b/tests/core/pyspec/eth2spec/test/helpers/deposits.py index 18929b1d79..4639b22dc3 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/deposits.py +++ b/tests/core/pyspec/eth2spec/test/helpers/deposits.py @@ -188,8 +188,12 @@ def run_deposit_processing(spec, state, deposit, validator_index, valid=True, ef """ pre_validator_count = len(state.validators) pre_balance = 0 + is_top_up = False + # is a top-up if validator_index < pre_validator_count: + is_top_up = True pre_balance = get_balance(state, validator_index) + pre_effective_balance = state.validators[validator_index].effective_balance yield 'pre', state yield 'deposit', deposit @@ -219,9 +223,12 @@ def run_deposit_processing(spec, state, deposit, validator_index, valid=True, ef assert len(state.balances) == pre_validator_count + 1 assert get_balance(state, validator_index) == pre_balance + deposit.data.amount - effective = min(spec.MAX_EFFECTIVE_BALANCE, - pre_balance + deposit.data.amount) - effective -= effective % spec.EFFECTIVE_BALANCE_INCREMENT - assert state.validators[validator_index].effective_balance == effective + if is_top_up: + # Top-ups do not change effective balance + assert state.validators[validator_index].effective_balance == pre_effective_balance + else: + effective_balance = min(spec.MAX_EFFECTIVE_BALANCE, deposit.data.amount) + effective_balance -= effective_balance % spec.EFFECTIVE_BALANCE_INCREMENT + assert state.validators[validator_index].effective_balance == effective_balance assert state.eth1_deposit_index == state.eth1_data.deposit_count diff --git a/tests/core/pyspec/eth2spec/test/helpers/genesis.py b/tests/core/pyspec/eth2spec/test/helpers/genesis.py index b7b9411257..e497a0ce2a 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/genesis.py +++ b/tests/core/pyspec/eth2spec/test/helpers/genesis.py @@ -20,9 +20,6 @@ def build_mock_validator(spec, i: int, balance: int): effective_balance=min(balance - balance % spec.EFFECTIVE_BALANCE_INCREMENT, spec.MAX_EFFECTIVE_BALANCE) ) - if spec.fork in (CAPELLA): - validator.fully_withdrawn_epoch = spec.FAR_FUTURE_EPOCH - return validator diff --git a/tests/core/pyspec/eth2spec/test/helpers/withdrawals.py b/tests/core/pyspec/eth2spec/test/helpers/withdrawals.py new file mode 100644 index 0000000000..739f5eb063 --- /dev/null +++ b/tests/core/pyspec/eth2spec/test/helpers/withdrawals.py @@ -0,0 +1,17 @@ +def set_validator_fully_withdrawable(spec, state, index, withdrawable_epoch=None): + if withdrawable_epoch is None: + withdrawable_epoch = spec.get_current_epoch(state) + + validator = state.validators[index] + validator.withdrawable_epoch = withdrawable_epoch + # set exit epoch as well to avoid interactions with other epoch process, e.g. forced ejecions + if validator.exit_epoch > withdrawable_epoch: + validator.exit_epoch = withdrawable_epoch + + if validator.withdrawal_credentials[0:1] == spec.BLS_WITHDRAWAL_PREFIX: + validator.withdrawal_credentials = spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + validator.withdrawal_credentials[1:] + + if state.balances[index] == 0: + state.balances[index] = 10000000000 + + assert spec.is_fully_withdrawable_validator(validator, state.balances[index], withdrawable_epoch) diff --git a/tests/core/pyspec/eth2spec/test/phase0/block_processing/test_process_deposit.py b/tests/core/pyspec/eth2spec/test/phase0/block_processing/test_process_deposit.py index 8922032b41..20d8d7d746 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/block_processing/test_process_deposit.py +++ b/tests/core/pyspec/eth2spec/test/phase0/block_processing/test_process_deposit.py @@ -141,13 +141,57 @@ def test_invalid_sig_new_deposit(spec, state): @with_all_phases @spec_state_test -def test_success_top_up(spec, state): +def test_success_top_up__max_effective_balance(spec, state): validator_index = 0 amount = spec.MAX_EFFECTIVE_BALANCE // 4 deposit = prepare_state_and_deposit(spec, state, validator_index, amount, signed=True) + state.balances[validator_index] = spec.MAX_EFFECTIVE_BALANCE + state.validators[validator_index].effective_balance = spec.MAX_EFFECTIVE_BALANCE + + yield from run_deposit_processing(spec, state, deposit, validator_index) + + assert state.balances[validator_index] == spec.MAX_EFFECTIVE_BALANCE + amount + assert state.validators[validator_index].effective_balance == spec.MAX_EFFECTIVE_BALANCE + + +@with_all_phases +@spec_state_test +def test_success_top_up__less_effective_balance(spec, state): + validator_index = 0 + amount = spec.MAX_EFFECTIVE_BALANCE // 4 + deposit = prepare_state_and_deposit(spec, state, validator_index, amount, signed=True) + + initial_balance = spec.MAX_EFFECTIVE_BALANCE - 1000 + initial_effective_balance = spec.MAX_EFFECTIVE_BALANCE - spec.EFFECTIVE_BALANCE_INCREMENT + state.balances[validator_index] = initial_balance + state.validators[validator_index].effective_balance = initial_effective_balance + yield from run_deposit_processing(spec, state, deposit, validator_index) + assert state.balances[validator_index] == initial_balance + amount + # unchanged effective balance + assert state.validators[validator_index].effective_balance == initial_effective_balance + + +@with_all_phases +@spec_state_test +def test_success_top_up__zero_balance(spec, state): + validator_index = 0 + amount = spec.MAX_EFFECTIVE_BALANCE // 4 + deposit = prepare_state_and_deposit(spec, state, validator_index, amount, signed=True) + + initial_balance = 0 + initial_effective_balance = 0 + state.balances[validator_index] = initial_balance + state.validators[validator_index].effective_balance = initial_effective_balance + + yield from run_deposit_processing(spec, state, deposit, validator_index) + + assert state.balances[validator_index] == initial_balance + amount + # unchanged effective balance + assert state.validators[validator_index].effective_balance == initial_effective_balance + @with_all_phases @spec_state_test From 91ea9e6959b64640fad2f617c2f006e8eb6d9b04 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Tue, 20 Sep 2022 08:18:42 -0600 Subject: [PATCH 02/35] review suggestion from @hwwhww Co-authored-by: Hsiao-Wei Wang --- .../test/capella/block_processing/test_process_deposit.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_deposit.py b/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_deposit.py index 34c58ef39c..93dd655fcc 100644 --- a/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_deposit.py +++ b/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_deposit.py @@ -28,3 +28,9 @@ def test_success_top_up_to_withdrawn_validator(spec, state): yield from run_deposit_processing(spec, state, deposit, validator_index) state.balances[validator_index] == amount + state.validators[validator_index].effective_balance == 0 + + validator = state.validators[validator_index] + balance = state.balances[validator_index] + current_epoch = spec.get_current_epoch(state) + assert spec.is_fully_withdrawable_validator(validator, balance, current_epoch) From ceee7bfbf1b5b6c6b59cf28ee277803781839bf2 Mon Sep 17 00:00:00 2001 From: inphi Date: Wed, 21 Sep 2022 09:10:37 -0400 Subject: [PATCH 03/35] EIP-4844: Fee market updates to execution --- specs/eip4844/beacon-chain.md | 85 +++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/specs/eip4844/beacon-chain.md b/specs/eip4844/beacon-chain.md index a1385d9e2b..4cf9535933 100644 --- a/specs/eip4844/beacon-chain.md +++ b/specs/eip4844/beacon-chain.md @@ -19,6 +19,8 @@ - [Containers](#containers) - [Extended containers](#extended-containers) - [`BeaconBlockBody`](#beaconblockbody) + - [`ExecutionPayload`](#executionpayload) + - [`ExecutionPayloadHeader`](#executionpayloadheader) - [Helper functions](#helper-functions) - [Misc](#misc) - [`kzg_commitment_to_versioned_hash`](#kzg_commitment_to_versioned_hash) @@ -26,6 +28,8 @@ - [`verify_kzg_commitments_against_transactions`](#verify_kzg_commitments_against_transactions) - [Beacon chain state transition function](#beacon-chain-state-transition-function) - [Block processing](#block-processing) + - [Execution payload](#execution-payload) + - [`process_execution_payload`](#process_execution_payload) - [Blob KZG commitments](#blob-kzg-commitments) - [Testing](#testing) @@ -96,6 +100,52 @@ class BeaconBlockBody(Container): blob_kzg_commitments: List[KZGCommitment, MAX_BLOBS_PER_BLOCK] # [New in EIP-4844] ``` +#### `ExecutionPayload` + +```python +class ExecutionPayload(Container): + # Execution block header fields + parent_hash: Hash32 + fee_recipient: ExecutionAddress # 'beneficiary' in the yellow paper + state_root: Bytes32 + receipts_root: Bytes32 + logs_bloom: ByteVector[BYTES_PER_LOGS_BLOOM] + prev_randao: Bytes32 # 'difficulty' in the yellow paper + block_number: uint64 # 'number' in the yellow paper + gas_limit: uint64 + gas_used: uint64 + timestamp: uint64 + extra_data: ByteList[MAX_EXTRA_DATA_BYTES] + base_fee_per_gas: uint256 + excess_blobs: uint64 # [New in EIP-4844] + # Extra payload fields + block_hash: Hash32 # Hash of execution block + transactions: List[Transaction, MAX_TRANSACTIONS_PER_PAYLOAD] +``` + +#### `ExecutionPayloadHeader` + +```python +class ExecutionPayloadHeader(Container): + # Execution block header fields + parent_hash: Hash32 + fee_recipient: ExecutionAddress + state_root: Bytes32 + receipts_root: Bytes32 + logs_bloom: ByteVector[BYTES_PER_LOGS_BLOOM] + prev_randao: Bytes32 + block_number: uint64 + gas_limit: uint64 + gas_used: uint64 + timestamp: uint64 + extra_data: ByteList[MAX_EXTRA_DATA_BYTES] + base_fee_per_gas: uint256 + excess_blobs: uint64 # [New in EIP-4844] + # Extra payload fields + block_hash: Hash32 # Hash of execution block + transactions_root: Root +``` + ## Helper functions ### Misc @@ -156,6 +206,41 @@ def process_block(state: BeaconState, block: BeaconBlock) -> None: process_blob_kzg_commitments(state, block.body) # [New in EIP-4844] ``` +#### Execution payload + +##### `process_execution_payload` + +```python +def process_execution_payload(state: BeaconState, payload: ExecutionPayload, execution_engine: ExecutionEngine) -> None: + # Verify consistency of the parent hash with respect to the previous execution payload header + if is_merge_transition_complete(state): + assert payload.parent_hash == state.latest_execution_payload_header.block_hash + # Verify prev_randao + assert payload.prev_randao == get_randao_mix(state, get_current_epoch(state)) + # Verify timestamp + assert payload.timestamp == compute_timestamp_at_slot(state, state.slot) + # Verify the execution payload is valid + assert execution_engine.notify_new_payload(payload) + # Cache execution payload header + state.latest_execution_payload_header = ExecutionPayloadHeader( + parent_hash=payload.parent_hash, + fee_recipient=payload.fee_recipient, + state_root=payload.state_root, + receipts_root=payload.receipts_root, + logs_bloom=payload.logs_bloom, + prev_randao=payload.prev_randao, + block_number=payload.block_number, + gas_limit=payload.gas_limit, + gas_used=payload.gas_used, + timestamp=payload.timestamp, + extra_data=payload.extra_data, + base_fee_per_gas=payload.base_fee_per_gas, + excess_blobs=payload.excess_blobs, # [New in EIP-4844] + block_hash=payload.block_hash, + transactions_root=hash_tree_root(payload.transactions), + ) +``` + #### Blob KZG commitments ```python From bfca7f9a96a6847940c73bd2508b18318577de45 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Wed, 21 Sep 2022 07:33:05 -0600 Subject: [PATCH 04/35] Apply suggestions from code review Co-authored-by: Alex Stokes --- .../test/capella/block_processing/test_process_deposit.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_deposit.py b/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_deposit.py index 93dd655fcc..e0603d301f 100644 --- a/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_deposit.py +++ b/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_deposit.py @@ -20,6 +20,9 @@ def test_success_top_up_to_withdrawn_validator(spec, state): assert state.balances[validator_index] > 0 next_epoch_via_block(spec, state) assert state.balances[validator_index] == 0 + assert state.validators[validator_index].effective_balance > 0 + next_epoch_via_block(spec, state) + assert state.validators[validator_index].effective_balance == 0 # Make a top-up balance to validator amount = spec.MAX_EFFECTIVE_BALANCE // 4 @@ -27,8 +30,8 @@ def test_success_top_up_to_withdrawn_validator(spec, state): yield from run_deposit_processing(spec, state, deposit, validator_index) - state.balances[validator_index] == amount - state.validators[validator_index].effective_balance == 0 + assert state.balances[validator_index] == amount + assert state.validators[validator_index].effective_balance == 0 validator = state.validators[validator_index] balance = state.balances[validator_index] From fdb5c7dcf3ac3e051a0abeb7a2d71f99e4c767e7 Mon Sep 17 00:00:00 2001 From: Justin Traglia Date: Thu, 22 Sep 2022 15:19:22 -0500 Subject: [PATCH 05/35] Add partial withdrawal balance tests --- .../test_process_bls_to_execution_change.py | 2 +- .../test_process_withdrawals.py | 2 +- .../test_process_partial_withdrawals.py | 58 +++++++++++++++++-- 3 files changed, 55 insertions(+), 7 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_bls_to_execution_change.py b/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_bls_to_execution_change.py index 4b69e04a63..4ad1bca86c 100644 --- a/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_bls_to_execution_change.py +++ b/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_bls_to_execution_change.py @@ -185,7 +185,7 @@ def test_fail_incorrect_from_bls_pubkey(spec, state): @always_bls def test_fail_bad_signature(spec, state): signed_address_change = get_signed_address_change(spec, state) - # Mutate sigature + # Mutate signature signed_address_change.signature = spec.BLSSignature(b'\x42' * 96) yield from run_bls_to_execution_change_processing(spec, state, signed_address_change, valid=False) diff --git a/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_withdrawals.py b/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_withdrawals.py index 26ace24b3a..4932bc60df 100644 --- a/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_withdrawals.py +++ b/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_withdrawals.py @@ -235,7 +235,7 @@ def test_fail_many_dequeued_incorrectly(spec, state): if i % 3 == 0: withdrawal.index += 1 elif i % 3 == 1: - withdrawal.address = (i).to_bytes(20, 'big') + withdrawal.address = i.to_bytes(20, 'big') else: withdrawal.amount += 1 diff --git a/tests/core/pyspec/eth2spec/test/capella/epoch_processing/test_process_partial_withdrawals.py b/tests/core/pyspec/eth2spec/test/capella/epoch_processing/test_process_partial_withdrawals.py index bf2e73fa18..431c2f2d33 100644 --- a/tests/core/pyspec/eth2spec/test/capella/epoch_processing/test_process_partial_withdrawals.py +++ b/tests/core/pyspec/eth2spec/test/capella/epoch_processing/test_process_partial_withdrawals.py @@ -10,13 +10,18 @@ from eth2spec.test.helpers.random import randomize_state -def set_validator_partially_withdrawable(spec, state, index, rng=random.Random(666)): +def set_eth1_withdrawal_credential_with_balance(spec, state, index, balance): validator = state.validators[index] validator.withdrawal_credentials = spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + validator.withdrawal_credentials[1:] - validator.effective_balance = spec.MAX_EFFECTIVE_BALANCE - state.balances[index] = spec.MAX_EFFECTIVE_BALANCE + rng.randint(1, 100000000) + validator.effective_balance = min(balance, spec.MAX_EFFECTIVE_BALANCE) + state.balances[index] = balance + + +def set_validator_partially_withdrawable(spec, state, index, rng=random.Random(666)): + balance = spec.MAX_EFFECTIVE_BALANCE + rng.randint(1, 100000000) + set_eth1_withdrawal_credential_with_balance(spec, state, index, balance) - assert spec.is_partially_withdrawable_validator(validator, state.balances[index]) + assert spec.is_partially_withdrawable_validator(state.validators[index], state.balances[index]) def run_process_partial_withdrawals(spec, state, num_expected_withdrawals=None): @@ -62,6 +67,49 @@ def test_success_no_withdrawable(spec, state): assert pre_validators == state.validators +@with_capella_and_later +@spec_state_test +def test_success_no_max_effective_balance(spec, state): + validator_index = len(state.validators) // 2 + # To be partially withdrawable, the validator's effective balance must be maxed out + set_eth1_withdrawal_credential_with_balance(spec, state, validator_index, spec.MAX_EFFECTIVE_BALANCE - 1) + validator = state.validators[validator_index] + + assert validator.effective_balance < spec.MAX_EFFECTIVE_BALANCE + assert not spec.is_partially_withdrawable_validator(validator, state.balances[validator_index]) + + yield from run_process_partial_withdrawals(spec, state, 0) + + +@with_capella_and_later +@spec_state_test +def test_success_no_excess_balance(spec, state): + validator_index = len(state.validators) // 2 + # To be partially withdrawable, the validator needs an excess balance + set_eth1_withdrawal_credential_with_balance(spec, state, validator_index, spec.MAX_EFFECTIVE_BALANCE) + validator = state.validators[validator_index] + + assert validator.effective_balance == spec.MAX_EFFECTIVE_BALANCE + assert not spec.is_partially_withdrawable_validator(validator, state.balances[validator_index]) + + yield from run_process_partial_withdrawals(spec, state, 0) + + +@with_capella_and_later +@spec_state_test +def test_success_excess_balance_but_no_max_effective_balance(spec, state): + validator_index = len(state.validators) // 2 + set_validator_partially_withdrawable(spec, state, validator_index) + validator = state.validators[validator_index] + + # To be partially withdrawable, the validator needs both a maxed out effective balance and an excess balance + validator.effective_balance = spec.MAX_EFFECTIVE_BALANCE - 1 + + assert not spec.is_partially_withdrawable_validator(validator, state.balances[validator_index]) + + yield from run_process_partial_withdrawals(spec, state, 0) + + @with_capella_and_later @spec_state_test def test_success_one_partial_withdrawable(spec, state): @@ -155,7 +203,7 @@ def test_success_max_partial_withdrawable(spec, state): @with_capella_and_later -@with_presets([MINIMAL], reason="not no enough validators with mainnet config") +@with_presets([MINIMAL], reason="not enough validators with mainnet config") @spec_state_test def test_success_max_plus_one_withdrawable(spec, state): # Sanity check that this test works for this state From f4ba8b55ee833cc9e1be014fb2fd06ea682f2d53 Mon Sep 17 00:00:00 2001 From: George Kadianakis Date: Mon, 26 Sep 2022 16:39:16 +0300 Subject: [PATCH 06/35] EIP4844: Implement reverse bit ordering in KZG commitments Co-authored-by: Dankrad Feist Co-authored-by: Hsiao-Wei Wang --- setup.py | 9 +++- specs/eip4844/polynomial-commitments.md | 59 ++++++++++++++++++++++--- 2 files changed, 62 insertions(+), 6 deletions(-) diff --git a/setup.py b/setup.py index 72b011f479..34aca0aed7 100644 --- a/setup.py +++ b/setup.py @@ -589,9 +589,16 @@ def imports(cls, preset_name: str): from eth2spec.utils.ssz.ssz_impl import serialize as ssz_serialize ''' + + @classmethod + def preparations(cls): + return super().preparations() + '\n' + ''' +T = TypeVar('T') # For generic function +''' + @classmethod def sundry_functions(cls) -> str: - return super().sundry_functions() + ''' + return super().sundry_functions() + '\n\n' + ''' # TODO: for mainnet, load pre-generated trusted setup file to reduce building time. # TESTING_FIELD_ELEMENTS_PER_BLOB is hardcoded copy from minimal presets TESTING_FIELD_ELEMENTS_PER_BLOB = 4 diff --git a/specs/eip4844/polynomial-commitments.md b/specs/eip4844/polynomial-commitments.md index 6ebd3fd3a1..66a2e9e119 100644 --- a/specs/eip4844/polynomial-commitments.md +++ b/specs/eip4844/polynomial-commitments.md @@ -12,6 +12,10 @@ - [Preset](#preset) - [Trusted setup](#trusted-setup) - [Helper functions](#helper-functions) + - [Bit-reversal permutation](#bit-reversal-permutation) + - [`is_power_of_two`](#is_power_of_two) + - [`reverse_bits`](#reverse_bits) + - [`bit_reversal_permutation`](#bit_reversal_permutation) - [BLS12-381 helpers](#bls12-381-helpers) - [`bls_modular_inverse`](#bls_modular_inverse) - [`div`](#div) @@ -64,6 +68,47 @@ but reusing the `mainnet` settings in public networks is a critical security req ## Helper functions +### Bit-reversal permutation + +All polynomials (which are always given in Lagrange form) should be interpreted as being in +bit-reversal permutation. In practice, clients can implement this by storing the lists +`KZG_SETUP_LAGRANGE` and `ROOTS_OF_UNITY` in bit-reversal permutation, so these functions only +have to be called once at startup. + +#### `is_power_of_two` + +```python +def is_power_of_two(value: int) -> bool: + """ + Check if ``value`` is a power of two integer. + """ + return (value > 0) and (value & (value - 1) == 0) +``` + +#### `reverse_bits` + +```python +def reverse_bits(n: int, order: int) -> int: + """ + Reverse the bit order of an integer n + """ + assert is_power_of_two(order) + # Convert n to binary with the same number of bits as "order" - 1, then reverse its bit order + return int(('{:0' + str(order.bit_length() - 1) + 'b}').format(n)[::-1], 2) +``` + +#### `bit_reversal_permutation` + +```python +def bit_reversal_permutation(l: Sequence[T]) -> Sequence[T]: + """ + Return a copy with bit-reversed permutation. This operation is idempotent. + + The input and output are a sequence of generic type ``T`` objects. + """ + return [l[reverse_bits(i, len(l))] for i in range(len(l))] +``` + ### BLS12-381 helpers #### `bls_modular_inverse` @@ -123,7 +168,7 @@ KZG core functions. These are also defined in EIP-4844 execution specs. ```python def blob_to_kzg_commitment(blob: Blob) -> KZGCommitment: - return g1_lincomb(KZG_SETUP_LAGRANGE, blob) + return g1_lincomb(bit_reversal_permutation(KZG_SETUP_LAGRANGE), blob) ``` #### `verify_kzg_proof` @@ -149,7 +194,9 @@ def verify_kzg_proof(polynomial_kzg: KZGCommitment, ```python def compute_kzg_proof(polynomial: Sequence[BLSFieldElement], z: BLSFieldElement) -> KZGProof: - """Compute KZG proof at point `z` with `polynomial` being in evaluation form""" + """ + Compute KZG proof at point `z` with `polynomial` being in evaluation form + """ # To avoid SSZ overflow/underflow, convert element into int polynomial = [int(i) for i in polynomial] @@ -161,11 +208,11 @@ def compute_kzg_proof(polynomial: Sequence[BLSFieldElement], z: BLSFieldElement) # Make sure we won't divide by zero during division assert z not in ROOTS_OF_UNITY - denominator_poly = [(x - z) % BLS_MODULUS for x in ROOTS_OF_UNITY] + denominator_poly = [(x - z) % BLS_MODULUS for x in bit_reversal_permutation(ROOTS_OF_UNITY)] # Calculate quotient polynomial by doing point-by-point division quotient_polynomial = [div(a, b) for a, b in zip(polynomial_shifted, denominator_poly)] - return KZGProof(g1_lincomb(KZG_SETUP_LAGRANGE, quotient_polynomial)) + return KZGProof(g1_lincomb(bit_reversal_permutation(KZG_SETUP_LAGRANGE), quotient_polynomial)) ``` ### Polynomials @@ -187,9 +234,11 @@ def evaluate_polynomial_in_evaluation_form(polynomial: Sequence[BLSFieldElement] # Make sure we won't divide by zero during division assert z not in ROOTS_OF_UNITY + roots_of_unity_brp = bit_reversal_permutation(ROOTS_OF_UNITY) + result = 0 for i in range(width): - result += div(int(polynomial[i]) * int(ROOTS_OF_UNITY[i]), (z - ROOTS_OF_UNITY[i])) + result += div(int(polynomial[i]) * int(roots_of_unity_brp[i]), (z - roots_of_unity_brp[i])) result = result * (pow(z, width, BLS_MODULUS) - 1) * inverse_width % BLS_MODULUS return result ``` From d197ed1451eb3d441438da5bc6d8b27d9e6c5776 Mon Sep 17 00:00:00 2001 From: George Kadianakis Date: Mon, 26 Sep 2022 18:57:00 +0300 Subject: [PATCH 07/35] EIP4844: Introduce bytes_to_bls_field() helper Improves separation between BLS cryptography and Ethereum SSZ logic. Now the BLS library just implements bytes_to_bls_field(). Then hash_to_bls_field() does the Ethereum SSZ magic and calls bytes_to_bls_field(). --- specs/eip4844/polynomial-commitments.md | 11 +++++++++++ specs/eip4844/validator.md | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/specs/eip4844/polynomial-commitments.md b/specs/eip4844/polynomial-commitments.md index 66a2e9e119..45f942b0f5 100644 --- a/specs/eip4844/polynomial-commitments.md +++ b/specs/eip4844/polynomial-commitments.md @@ -17,6 +17,7 @@ - [`reverse_bits`](#reverse_bits) - [`bit_reversal_permutation`](#bit_reversal_permutation) - [BLS12-381 helpers](#bls12-381-helpers) + - [`bytes_to_bls_field`](#bytes_to_bls_field) - [`bls_modular_inverse`](#bls_modular_inverse) - [`div`](#div) - [`g1_lincomb`](#g1_lincomb) @@ -111,6 +112,16 @@ def bit_reversal_permutation(l: Sequence[T]) -> Sequence[T]: ### BLS12-381 helpers +#### `bytes_to_bls_field` + +```python +def bytes_to_bls_field(b: Bytes32) -> BLSFieldElement: + """ + Convert bytes to a BLS field scalar. The output is not uniform over the BLS field. + """ + return int.from_bytes(b, "little") % BLS_MODULUS +``` + #### `bls_modular_inverse` ```python diff --git a/specs/eip4844/validator.md b/specs/eip4844/validator.md index f624c5157f..a1faffa255 100644 --- a/specs/eip4844/validator.md +++ b/specs/eip4844/validator.md @@ -96,7 +96,7 @@ def hash_to_bls_field(x: Container) -> BLSFieldElement: Compute 32-byte hash of serialized container and convert it to BLS field. The output is not uniform over the BLS field. """ - return int.from_bytes(hash(ssz_serialize(x)), "little") % BLS_MODULUS + return bytes_to_bls_field(hash(ssz_serialize(x))) ``` ### `compute_powers` From 9ac605cc5ed0602f16b9b322b854f572e2ee1b62 Mon Sep 17 00:00:00 2001 From: Ramana Kumar Date: Tue, 27 Sep 2022 12:13:56 +0100 Subject: [PATCH 08/35] Fix incorrect comment bit-reversal permutation is not idempotent --- specs/eip4844/polynomial-commitments.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/eip4844/polynomial-commitments.md b/specs/eip4844/polynomial-commitments.md index 45f942b0f5..69cb5e0e6a 100644 --- a/specs/eip4844/polynomial-commitments.md +++ b/specs/eip4844/polynomial-commitments.md @@ -103,7 +103,7 @@ def reverse_bits(n: int, order: int) -> int: ```python def bit_reversal_permutation(l: Sequence[T]) -> Sequence[T]: """ - Return a copy with bit-reversed permutation. This operation is idempotent. + Return a copy with bit-reversed permutation. The permutation is an involution (inverts itself). The input and output are a sequence of generic type ``T`` objects. """ From 44424c408038221896780854b93a3c227b2d1201 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Wed, 28 Sep 2022 12:22:53 +0800 Subject: [PATCH 09/35] Bump dep packages version and fix lint issues --- Makefile | 2 +- setup.py | 38 ++++++++++------- specs/eip4844/polynomial-commitments.md | 4 +- .../test/bellatrix/sync/test_optimistic.py | 4 +- tests/generators/bls/main.py | 42 +++++++++---------- 5 files changed, 48 insertions(+), 42 deletions(-) diff --git a/Makefile b/Makefile index 725e64de87..5f0706dd99 100644 --- a/Makefile +++ b/Makefile @@ -143,7 +143,7 @@ lint: pyspec lint_generators: pyspec . venv/bin/activate; cd $(TEST_GENERATORS_DIR); \ - flake8 --config $(LINTER_CONFIG_FILE) + flake8 --config $(LINTER_CONFIG_FILE) compile_deposit_contract: @cd $(SOLIDITY_DEPOSIT_CONTRACT_DIR) diff --git a/setup.py b/setup.py index 34aca0aed7..6db4aa8707 100644 --- a/setup.py +++ b/setup.py @@ -20,7 +20,7 @@ def installPackage(package: str): subprocess.check_call([sys.executable, '-m', 'pip', 'install', package]) -RUAMEL_YAML_VERSION = "ruamel.yaml==0.16.5" +RUAMEL_YAML_VERSION = "ruamel.yaml==0.17.21" try: import ruamel.yaml except ImportError: @@ -78,6 +78,7 @@ class VariableDefinition(NamedTuple): type_name: Optional[str] value: str comment: Optional[str] # e.g. "noqa: E501" + type_hint: Optional[str] # e.g., "Final" class SpecObject(NamedTuple): @@ -152,18 +153,18 @@ def _get_eth2_spec_comment(child: LinkRefDef) -> Optional[str]: return title[len(ETH2_SPEC_COMMENT_PREFIX):].strip() -def _parse_value(name: str, typed_value: str) -> VariableDefinition: +def _parse_value(name: str, typed_value: str, type_hint: Optional[str]=None) -> VariableDefinition: comment = None if name == "BLS12_381_Q": comment = "noqa: E501" typed_value = typed_value.strip() if '(' not in typed_value: - return VariableDefinition(type_name=None, value=typed_value, comment=comment) + return VariableDefinition(type_name=None, value=typed_value, comment=comment, type_hint=type_hint) i = typed_value.index('(') type_name = typed_value[:i] - return VariableDefinition(type_name=type_name, value=typed_value[i+1:-1], comment=comment) + return VariableDefinition(type_name=type_name, value=typed_value[i+1:-1], comment=comment, type_hint=type_hint) def get_spec(file_name: Path, preset: Dict[str, str], config: Dict[str, str]) -> SpecObject: @@ -241,10 +242,13 @@ def get_spec(file_name: Path, preset: Dict[str, str], config: Dict[str, str]) -> value_def = _parse_value(name, value) if name in preset: - preset_vars[name] = VariableDefinition(value_def.type_name, preset[name], value_def.comment) + preset_vars[name] = VariableDefinition(value_def.type_name, preset[name], value_def.comment, None) elif name in config: - config_vars[name] = VariableDefinition(value_def.type_name, config[name], value_def.comment) + config_vars[name] = VariableDefinition(value_def.type_name, config[name], value_def.comment, None) else: + if name == 'ENDIANNESS': + # Deal with mypy Literal typing check + value_def = _parse_value(name, value, type_hint='Final') constant_vars[name] = value_def elif isinstance(child, LinkRefDef): @@ -337,7 +341,7 @@ def imports(cls, preset_name: str) -> str: field, ) from typing import ( - Any, Callable, Dict, Set, Sequence, Tuple, Optional, TypeVar, NamedTuple + Any, Callable, Dict, Set, Sequence, Tuple, Optional, TypeVar, NamedTuple, Final ) from eth2spec.utils.ssz.ssz_impl import hash_tree_root, copy, uint_to_bytes @@ -703,7 +707,10 @@ def format_config_var(name: str, vardef: VariableDefinition) -> str: def format_constant(name: str, vardef: VariableDefinition) -> str: if vardef.type_name is None: - out = f'{name} = {vardef.value}' + if vardef.type_hint is None: + out = f'{name} = {vardef.value}' + else: + out = f'{name}: {vardef.type_hint} = {vardef.value}' else: out = f'{name} = {vardef.type_name}({vardef.value})' if vardef.comment is not None: @@ -1115,19 +1122,18 @@ def run(self): python_requires=">=3.8, <4", extras_require={ "test": ["pytest>=4.4", "pytest-cov", "pytest-xdist"], - "lint": ["flake8==3.7.7", "mypy==0.812", "pylint==2.12.2"], - "generator": ["python-snappy==0.5.4", "filelock"], + "lint": ["flake8==5.0.4", "mypy==0.981", "pylint==2.15.3"], + "generator": ["python-snappy==0.6.1", "filelock"], }, install_requires=[ - "eth-utils>=1.3.0,<2", - "eth-typing>=2.1.0,<3.0.0", - "pycryptodome==3.9.4", - "py_ecc==5.2.0", + "eth-utils>=2.0.0,<3", + "eth-typing>=3.2.0,<4.0.0", + "pycryptodome==3.15.0", + "py_ecc==6.0.0", "milagro_bls_binding==1.9.0", - "dataclasses==0.6", "remerkleable==0.1.24", RUAMEL_YAML_VERSION, - "lru-dict==1.1.6", + "lru-dict==1.1.8", MARKO_VERSION, ] ) diff --git a/specs/eip4844/polynomial-commitments.md b/specs/eip4844/polynomial-commitments.md index 45f942b0f5..9d4f641b59 100644 --- a/specs/eip4844/polynomial-commitments.md +++ b/specs/eip4844/polynomial-commitments.md @@ -101,13 +101,13 @@ def reverse_bits(n: int, order: int) -> int: #### `bit_reversal_permutation` ```python -def bit_reversal_permutation(l: Sequence[T]) -> Sequence[T]: +def bit_reversal_permutation(sequence: Sequence[T]) -> Sequence[T]: """ Return a copy with bit-reversed permutation. This operation is idempotent. The input and output are a sequence of generic type ``T`` objects. """ - return [l[reverse_bits(i, len(l))] for i in range(len(l))] + return [sequence[reverse_bits(i, len(sequence))] for i in range(len(sequence))] ``` ### BLS12-381 helpers diff --git a/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py b/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py index 4fb8adbaf1..c849ccbd80 100644 --- a/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py +++ b/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py @@ -46,7 +46,7 @@ def test_from_syncing_to_invalid(spec, state): # Block 0 block_0 = build_empty_block_for_next_slot(spec, state) - block_0.body.execution_payload.block_hash = spec.hash(bytes(f'block_0', 'UTF-8')) + block_0.body.execution_payload.block_hash = spec.hash(bytes('block_0', 'UTF-8')) signed_block = state_transition_and_sign_block(spec, state, block_0) yield from add_optimistic_block(spec, mega_store, signed_block, test_steps, status=PayloadStatusV1Status.VALID) assert spec.get_head(mega_store.fc_store) == mega_store.opt_store.head_block_root @@ -84,7 +84,7 @@ def test_from_syncing_to_invalid(spec, state): # Now add block 4 to chain `b` with INVALID block = build_empty_block_for_next_slot(spec, state) - block.body.execution_payload.block_hash = spec.hash(bytes(f'chain_b_3', 'UTF-8')) + block.body.execution_payload.block_hash = spec.hash(bytes('chain_b_3', 'UTF-8')) block.body.execution_payload.parent_hash = signed_blocks_b[-1].message.body.execution_payload.block_hash signed_block = state_transition_and_sign_block(spec, state, block) payload_status = PayloadStatusV1( diff --git a/tests/generators/bls/main.py b/tests/generators/bls/main.py index 60c31d9d98..dca5ae7dd0 100644 --- a/tests/generators/bls/main.py +++ b/tests/generators/bls/main.py @@ -89,7 +89,7 @@ def case01_sign(): # Edge case: privkey == 0 expect_exception(bls.Sign, ZERO_PRIVKEY, message) expect_exception(milagro_bls.Sign, ZERO_PRIVKEY_BYTES, message) - yield f'sign_case_zero_privkey', { + yield 'sign_case_zero_privkey', { 'input': { 'privkey': encode_hex(ZERO_PRIVKEY_BYTES), 'message': encode_hex(message), @@ -153,7 +153,7 @@ def case02_verify(): # Invalid pubkey and signature with the point at infinity assert not bls.Verify(G1_POINT_AT_INFINITY, SAMPLE_MESSAGE, G2_POINT_AT_INFINITY) assert not milagro_bls.Verify(G1_POINT_AT_INFINITY, SAMPLE_MESSAGE, G2_POINT_AT_INFINITY) - yield f'verify_infinity_pubkey_and_infinity_signature', { + yield 'verify_infinity_pubkey_and_infinity_signature', { 'input': { 'pubkey': encode_hex(G1_POINT_AT_INFINITY), 'message': encode_hex(SAMPLE_MESSAGE), @@ -177,7 +177,7 @@ def case03_aggregate(): expect_exception(bls.Aggregate, []) # No signatures to aggregate. Follow IETF BLS spec, return `None` to represent INVALID. # https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-04#section-2.8 - yield f'aggregate_na_signatures', { + yield 'aggregate_na_signatures', { 'input': [], 'output': None, } @@ -185,7 +185,7 @@ def case03_aggregate(): # Valid to aggregate G2 point at infinity aggregate_sig = bls.Aggregate([G2_POINT_AT_INFINITY]) assert aggregate_sig == milagro_bls.Aggregate([G2_POINT_AT_INFINITY]) == G2_POINT_AT_INFINITY - yield f'aggregate_infinity_signature', { + yield 'aggregate_infinity_signature', { 'input': [encode_hex(G2_POINT_AT_INFINITY)], 'output': encode_hex(aggregate_sig), } @@ -244,7 +244,7 @@ def case04_fast_aggregate_verify(): # Invalid pubkeys and signature -- len(pubkeys) == 0 and signature == Z1_SIGNATURE assert not bls.FastAggregateVerify([], message, G2_POINT_AT_INFINITY) assert not milagro_bls.FastAggregateVerify([], message, G2_POINT_AT_INFINITY) - yield f'fast_aggregate_verify_na_pubkeys_and_infinity_signature', { + yield 'fast_aggregate_verify_na_pubkeys_and_infinity_signature', { 'input': { 'pubkeys': [], 'message': encode_hex(message), @@ -256,7 +256,7 @@ def case04_fast_aggregate_verify(): # Invalid pubkeys and signature -- len(pubkeys) == 0 and signature == 0x00... assert not bls.FastAggregateVerify([], message, ZERO_SIGNATURE) assert not milagro_bls.FastAggregateVerify([], message, ZERO_SIGNATURE) - yield f'fast_aggregate_verify_na_pubkeys_and_zero_signature', { + yield 'fast_aggregate_verify_na_pubkeys_and_zero_signature', { 'input': { 'pubkeys': [], 'message': encode_hex(message), @@ -272,7 +272,7 @@ def case04_fast_aggregate_verify(): aggregate_signature = bls.Aggregate(signatures) assert not bls.FastAggregateVerify(pubkeys_with_infinity, SAMPLE_MESSAGE, aggregate_signature) assert not milagro_bls.FastAggregateVerify(pubkeys_with_infinity, SAMPLE_MESSAGE, aggregate_signature) - yield f'fast_aggregate_verify_infinity_pubkey', { + yield 'fast_aggregate_verify_infinity_pubkey', { 'input': { 'pubkeys': [encode_hex(pubkey) for pubkey in pubkeys_with_infinity], 'message': encode_hex(SAMPLE_MESSAGE), @@ -300,7 +300,7 @@ def case05_aggregate_verify(): aggregate_signature = bls.Aggregate(sigs) assert bls.AggregateVerify(pubkeys, messages, aggregate_signature) assert milagro_bls.AggregateVerify(pubkeys, messages, aggregate_signature) - yield f'aggregate_verify_valid', { + yield 'aggregate_verify_valid', { 'input': { 'pubkeys': pubkeys_serial, 'messages': messages_serial, @@ -312,7 +312,7 @@ def case05_aggregate_verify(): tampered_signature = aggregate_signature[:4] + b'\xff\xff\xff\xff' assert not bls.AggregateVerify(pubkey, messages, tampered_signature) assert not milagro_bls.AggregateVerify(pubkeys, messages, tampered_signature) - yield f'aggregate_verify_tampered_signature', { + yield 'aggregate_verify_tampered_signature', { 'input': { 'pubkeys': pubkeys_serial, 'messages': messages_serial, @@ -324,7 +324,7 @@ def case05_aggregate_verify(): # Invalid pubkeys and signature -- len(pubkeys) == 0 and signature == Z1_SIGNATURE assert not bls.AggregateVerify([], [], G2_POINT_AT_INFINITY) assert not milagro_bls.AggregateVerify([], [], G2_POINT_AT_INFINITY) - yield f'aggregate_verify_na_pubkeys_and_infinity_signature', { + yield 'aggregate_verify_na_pubkeys_and_infinity_signature', { 'input': { 'pubkeys': [], 'messages': [], @@ -336,7 +336,7 @@ def case05_aggregate_verify(): # Invalid pubkeys and signature -- len(pubkeys) == 0 and signature == 0x00... assert not bls.AggregateVerify([], [], ZERO_SIGNATURE) assert not milagro_bls.AggregateVerify([], [], ZERO_SIGNATURE) - yield f'aggregate_verify_na_pubkeys_and_zero_signature', { + yield 'aggregate_verify_na_pubkeys_and_zero_signature', { 'input': { 'pubkeys': [], 'messages': [], @@ -350,7 +350,7 @@ def case05_aggregate_verify(): messages_with_sample = messages + [SAMPLE_MESSAGE] assert not bls.AggregateVerify(pubkeys_with_infinity, messages_with_sample, aggregate_signature) assert not milagro_bls.AggregateVerify(pubkeys_with_infinity, messages_with_sample, aggregate_signature) - yield f'aggregate_verify_infinity_pubkey', { + yield 'aggregate_verify_infinity_pubkey', { 'input': { 'pubkeys': [encode_hex(pubkey) for pubkey in pubkeys_with_infinity], 'messages': [encode_hex(message) for message in messages_with_sample], @@ -375,7 +375,7 @@ def case06_eth_aggregate_pubkeys(): # Valid pubkeys aggregate_pubkey = spec.eth_aggregate_pubkeys(PUBKEYS) assert aggregate_pubkey == milagro_bls._AggregatePKs(PUBKEYS) - yield f'eth_aggregate_pubkeys_valid_pubkeys', { + yield 'eth_aggregate_pubkeys_valid_pubkeys', { 'input': [encode_hex(pubkey) for pubkey in PUBKEYS], 'output': encode_hex(aggregate_pubkey), } @@ -383,7 +383,7 @@ def case06_eth_aggregate_pubkeys(): # Invalid pubkeys -- len(pubkeys) == 0 expect_exception(spec.eth_aggregate_pubkeys, []) expect_exception(milagro_bls._AggregatePKs, []) - yield f'eth_aggregate_pubkeys_empty_list', { + yield 'eth_aggregate_pubkeys_empty_list', { 'input': [], 'output': None, } @@ -391,7 +391,7 @@ def case06_eth_aggregate_pubkeys(): # Invalid pubkeys -- [ZERO_PUBKEY] expect_exception(spec.eth_aggregate_pubkeys, [ZERO_PUBKEY]) expect_exception(milagro_bls._AggregatePKs, [ZERO_PUBKEY]) - yield f'eth_aggregate_pubkeys_zero_pubkey', { + yield 'eth_aggregate_pubkeys_zero_pubkey', { 'input': [encode_hex(ZERO_PUBKEY)], 'output': None, } @@ -399,7 +399,7 @@ def case06_eth_aggregate_pubkeys(): # Invalid pubkeys -- G1 point at infinity expect_exception(spec.eth_aggregate_pubkeys, [G1_POINT_AT_INFINITY]) expect_exception(milagro_bls._AggregatePKs, [G1_POINT_AT_INFINITY]) - yield f'eth_aggregate_pubkeys_infinity_pubkey', { + yield 'eth_aggregate_pubkeys_infinity_pubkey', { 'input': [encode_hex(G1_POINT_AT_INFINITY)], 'output': None, } @@ -408,7 +408,7 @@ def case06_eth_aggregate_pubkeys(): x40_pubkey = b'\x40' + b'\00' * 47 expect_exception(spec.eth_aggregate_pubkeys, [x40_pubkey]) expect_exception(milagro_bls._AggregatePKs, [x40_pubkey]) - yield f'eth_aggregate_pubkeys_x40_pubkey', { + yield 'eth_aggregate_pubkeys_x40_pubkey', { 'input': [encode_hex(x40_pubkey)], 'output': None, } @@ -455,7 +455,7 @@ def case07_eth_fast_aggregate_verify(): tampered_signature = aggregate_signature[:-4] + b'\xff\xff\xff\xff' identifier = f'{pubkeys_serial}_{encode_hex(message)}' assert not spec.eth_fast_aggregate_verify(pubkeys, message, tampered_signature) - yield f'eth_fast_aggregate_verify_tampered_signature_{(hash(bytes(identifier, "utf-8"))[:8]).hex()}', { + yield 'eth_fast_aggregate_verify_tampered_signature_{(hash(bytes(identifier, "utf-8"))[:8]).hex()}', { 'input': { 'pubkeys': pubkeys_serial, 'message': encode_hex(message), @@ -466,7 +466,7 @@ def case07_eth_fast_aggregate_verify(): # NOTE: Unlike `FastAggregateVerify`, len(pubkeys) == 0 and signature == G2_POINT_AT_INFINITY is VALID assert spec.eth_fast_aggregate_verify([], message, G2_POINT_AT_INFINITY) - yield f'eth_fast_aggregate_verify_na_pubkeys_and_infinity_signature', { + yield 'eth_fast_aggregate_verify_na_pubkeys_and_infinity_signature', { 'input': { 'pubkeys': [], 'message': encode_hex(message), @@ -477,7 +477,7 @@ def case07_eth_fast_aggregate_verify(): # Invalid pubkeys and signature -- len(pubkeys) == 0 and signature == 0x00... assert not spec.eth_fast_aggregate_verify([], message, ZERO_SIGNATURE) - yield f'eth_fast_aggregate_verify_na_pubkeys_and_zero_signature', { + yield 'eth_fast_aggregate_verify_na_pubkeys_and_zero_signature', { 'input': { 'pubkeys': [], 'message': encode_hex(message), @@ -492,7 +492,7 @@ def case07_eth_fast_aggregate_verify(): signatures = [bls.Sign(privkey, SAMPLE_MESSAGE) for privkey in PRIVKEYS] aggregate_signature = bls.Aggregate(signatures) assert not spec.eth_fast_aggregate_verify(pubkeys_with_infinity, SAMPLE_MESSAGE, aggregate_signature) - yield f'eth_fast_aggregate_verify_infinity_pubkey', { + yield 'eth_fast_aggregate_verify_infinity_pubkey', { 'input': { 'pubkeys': [encode_hex(pubkey) for pubkey in pubkeys_with_infinity], 'message': encode_hex(SAMPLE_MESSAGE), From 93cb17dc7f14114e382e1657841d22cdc885c0ff Mon Sep 17 00:00:00 2001 From: terence tsao Date: Thu, 29 Sep 2022 07:36:39 -0700 Subject: [PATCH 10/35] Eip4844: various fixes --- specs/eip4844/p2p-interface.md | 13 +++++-------- specs/eip4844/validator.md | 2 +- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/specs/eip4844/p2p-interface.md b/specs/eip4844/p2p-interface.md index 5c9c46f2c2..081337dbdf 100644 --- a/specs/eip4844/p2p-interface.md +++ b/specs/eip4844/p2p-interface.md @@ -33,10 +33,10 @@ The specification of these changes continues in the same format as the network s ## Configuration -| Name | Value | Description | -|------------------------------------------|-------------------------------|---------------------------------------------------------------------| -| `MAX_REQUEST_BLOBS_SIDECARS` | `2**7` (= 128) | Maximum number of blobs sidecars in a single request | -| `MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS` | `2**13` (= 8192, ~1.2 months) | The minimum epoch range over which a node must serve blobs sidecars | +| Name | Value | Description | +|------------------------------------------|------------------------------------|---------------------------------------------------------------------| +| `MAX_REQUEST_BLOBS_SIDECARS` | `2**7` (= 128) | Maximum number of blobs sidecars in a single request | +| `MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS` | `2**13epoch` (= 8192, ~1.2 months) | The minimum epoch range over which a node must serve blobs sidecars | ## Containers @@ -207,10 +207,7 @@ or disconnected at any time. *Note*: The above requirement implies that nodes that start from a recent weak subjectivity checkpoint MUST backfill the local blobs database to at least epoch `current_epoch - MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS` -to be fully compliant with `BlobsSidecarsByRange` requests. To safely perform such a -backfill of blocks to the recent state, the node MUST validate both (1) the -proposer signatures and (2) that the blocks form a valid chain up to the most -recent block referenced in the weak subjectivity state. +to be fully compliant with `BlobsSidecarsByRange` requests. *Note*: Although clients that bootstrap from a weak subjectivity checkpoint can begin participating in the networking immediately, other peers MAY diff --git a/specs/eip4844/validator.md b/specs/eip4844/validator.md index a1faffa255..c29a7c414b 100644 --- a/specs/eip4844/validator.md +++ b/specs/eip4844/validator.md @@ -245,7 +245,7 @@ def get_signed_blobs_sidecar(state: BeaconState, blobs_sidecar: BlobsSidecar, pr return SignedBlobsSidecar(message=blobs_sidecar, signature=signature) ``` -This `signed_blobs_sidecar` is then published to the global `blobs_sidecar` topic as soon as the `beacon_block` is published. +This `signed_blobs_sidecar` is then published to the global `blobs_sidecar` topic as soon as the `signed_beacon_block` is published. After publishing the sidecar peers on the network may request the sidecar through sync-requests, or a local user may be interested. The validator MUST hold on to blobs for `MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS` epochs and serve when capable, From 06c91e4843ea3675b4de7e10e1fd5e662e677741 Mon Sep 17 00:00:00 2001 From: terence tsao Date: Thu, 29 Sep 2022 07:53:52 -0700 Subject: [PATCH 11/35] Try a different format for epoch --- specs/eip4844/p2p-interface.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/specs/eip4844/p2p-interface.md b/specs/eip4844/p2p-interface.md index 081337dbdf..7be539b406 100644 --- a/specs/eip4844/p2p-interface.md +++ b/specs/eip4844/p2p-interface.md @@ -33,10 +33,10 @@ The specification of these changes continues in the same format as the network s ## Configuration -| Name | Value | Description | -|------------------------------------------|------------------------------------|---------------------------------------------------------------------| -| `MAX_REQUEST_BLOBS_SIDECARS` | `2**7` (= 128) | Maximum number of blobs sidecars in a single request | -| `MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS` | `2**13epoch` (= 8192, ~1.2 months) | The minimum epoch range over which a node must serve blobs sidecars | +| Name | Value | Description | +|------------------------------------------|-------------------------------------|---------------------------------------------------------------------| +| `MAX_REQUEST_BLOBS_SIDECARS` | `2**7` (= 128) | Maximum number of blobs sidecars in a single request | +| `MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS` | `2**13` (= 8192epochs, ~1.2 months) | The minimum epoch range over which a node must serve blobs sidecars | ## Containers From 779e6e7b7e18781418f11df423e6ad18955a93bd Mon Sep 17 00:00:00 2001 From: terencechain Date: Thu, 29 Sep 2022 13:43:28 -0700 Subject: [PATCH 12/35] Update specs/eip4844/p2p-interface.md Co-authored-by: Lion - dapplion <35266934+dapplion@users.noreply.github.com> --- specs/eip4844/p2p-interface.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/eip4844/p2p-interface.md b/specs/eip4844/p2p-interface.md index 7be539b406..eab8815a4a 100644 --- a/specs/eip4844/p2p-interface.md +++ b/specs/eip4844/p2p-interface.md @@ -36,7 +36,7 @@ The specification of these changes continues in the same format as the network s | Name | Value | Description | |------------------------------------------|-------------------------------------|---------------------------------------------------------------------| | `MAX_REQUEST_BLOBS_SIDECARS` | `2**7` (= 128) | Maximum number of blobs sidecars in a single request | -| `MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS` | `2**13` (= 8192epochs, ~1.2 months) | The minimum epoch range over which a node must serve blobs sidecars | +| `MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS` | `2**13` (= 8192 epochs, ~1.2 months) | The minimum epoch range over which a node must serve blobs sidecars | ## Containers From 807650e8a165c3930fd5b6604f511ad7f0779373 Mon Sep 17 00:00:00 2001 From: Ramana Kumar Date: Sat, 1 Oct 2022 15:25:47 +0100 Subject: [PATCH 13/35] Fix some type annotations for blobs blobs should not be Sequence[BLSFieldElement], it should be Sequence[Sequence[BLSFieldElement]]. But we can be more specific and use Sequence[Blob]. --- specs/eip4844/validator.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/specs/eip4844/validator.md b/specs/eip4844/validator.md index c29a7c414b..e1bcf9564a 100644 --- a/specs/eip4844/validator.md +++ b/specs/eip4844/validator.md @@ -117,7 +117,7 @@ def compute_powers(x: BLSFieldElement, n: uint64) -> Sequence[BLSFieldElement]: ```python def compute_aggregated_poly_and_commitment( - blobs: Sequence[Sequence[BLSFieldElement]], + blobs: Sequence[Blob], kzg_commitments: Sequence[KZGCommitment]) -> Tuple[Polynomial, KZGCommitment]: """ Return the aggregated polynomial and aggregated KZG commitment. @@ -167,7 +167,7 @@ def validate_blobs_sidecar(slot: Slot, ### `compute_proof_from_blobs` ```python -def compute_proof_from_blobs(blobs: Sequence[BLSFieldElement]) -> KZGProof: +def compute_proof_from_blobs(blobs: Sequence[Blob]) -> KZGProof: commitments = [blob_to_kzg_commitment(blob) for blob in blobs] aggregated_poly, aggregated_poly_commitment = compute_aggregated_poly_and_commitment(blobs, commitments) x = hash_to_bls_field(PolynomialAndCommitment( @@ -206,7 +206,7 @@ use the `payload_id` to retrieve `blobs` and `blob_kzg_commitments` via `get_blo ```python def validate_blobs_and_kzg_commitments(execution_payload: ExecutionPayload, - blobs: Sequence[BLSFieldElement], + blobs: Sequence[Blob], blob_kzg_commitments: Sequence[KZGCommitment]) -> None: # Optionally sanity-check that the KZG commitments match the versioned hashes in the transactions assert verify_kzg_commitments_against_transactions(execution_payload.transactions, blob_kzg_commitments) From fda2a69ff7b9809cd6b3fdfb7c9a5dc68f01d55e Mon Sep 17 00:00:00 2001 From: Lion - dapplion <35266934+dapplion@users.noreply.github.com> Date: Sun, 2 Oct 2022 12:19:55 +0200 Subject: [PATCH 14/35] Typo in sync committee duties description I assume it refers to producing a signature for the last slot of phase0 fork --- specs/altair/validator.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/altair/validator.md b/specs/altair/validator.md index 6265353749..013a516490 100644 --- a/specs/altair/validator.md +++ b/specs/altair/validator.md @@ -146,7 +146,7 @@ This function is a predicate indicating the presence or absence of the validator *Note*: Being assigned to a sync committee for a given `slot` means that the validator produces and broadcasts signatures for `slot - 1` for inclusion in `slot`. This means that when assigned to an `epoch` sync committee signatures must be produced and broadcast for slots on range `[compute_start_slot_at_epoch(epoch) - 1, compute_start_slot_at_epoch(epoch) + SLOTS_PER_EPOCH - 1)` rather than for the range `[compute_start_slot_at_epoch(epoch), compute_start_slot_at_epoch(epoch) + SLOTS_PER_EPOCH)`. -To reduce complexity during the Altair fork, sync committees are not expected to produce signatures for `compute_epoch_at_slot(ALTAIR_FORK_EPOCH) - 1`. +To reduce complexity during the Altair fork, sync committees are not expected to produce signatures for `compute_start_slot_at_epoch(ALTAIR_FORK_EPOCH) - 1`. ```python def compute_sync_committee_period(epoch: Epoch) -> uint64: From 491f14c76f376fe242669a90681943b5a3c484d0 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Mon, 19 Sep 2022 11:27:09 -0600 Subject: [PATCH 15/35] some capella sanity tests --- presets/minimal/capella.yaml | 2 +- .../test_process_bls_to_execution_change.py | 29 +--- .../test_process_partial_withdrawals.py | 20 +-- .../test/capella/sanity/test_blocks.py | 135 ++++++++++++++++++ .../test/helpers/bls_to_execution_changes.py | 27 ++++ .../eth2spec/test/helpers/withdrawals.py | 14 ++ 6 files changed, 184 insertions(+), 43 deletions(-) create mode 100644 tests/core/pyspec/eth2spec/test/capella/sanity/test_blocks.py create mode 100644 tests/core/pyspec/eth2spec/test/helpers/bls_to_execution_changes.py diff --git a/presets/minimal/capella.yaml b/presets/minimal/capella.yaml index bf78685cdf..0476172a10 100644 --- a/presets/minimal/capella.yaml +++ b/presets/minimal/capella.yaml @@ -21,4 +21,4 @@ MAX_BLS_TO_EXECUTION_CHANGES: 16 # Execution # --------------------------------------------------------------- # [customized] Lower than MAX_PARTIAL_WITHDRAWALS_PER_EPOCH so not all processed in one block -MAX_WITHDRAWALS_PER_PAYLOAD: 16 +MAX_WITHDRAWALS_PER_PAYLOAD: 8 diff --git a/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_bls_to_execution_change.py b/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_bls_to_execution_change.py index bbac7fd366..8ff02489cb 100644 --- a/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_bls_to_execution_change.py +++ b/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_bls_to_execution_change.py @@ -1,5 +1,5 @@ -from eth2spec.utils import bls -from eth2spec.test.helpers.keys import pubkeys, privkeys, pubkey_to_privkey +from eth2spec.test.helpers.keys import pubkeys +from eth2spec.test.helpers.bls_to_execution_changes import get_signed_address_change from eth2spec.test.context import spec_state_test, expect_assertion_error, with_capella_and_later, always_bls @@ -37,31 +37,6 @@ def run_bls_to_execution_change_processing(spec, state, signed_address_change, v yield 'post', state -def get_signed_address_change(spec, state, validator_index=None, withdrawal_pubkey=None): - if validator_index is None: - validator_index = 0 - - if withdrawal_pubkey is None: - key_index = -1 - validator_index - withdrawal_pubkey = pubkeys[key_index] - withdrawal_privkey = privkeys[key_index] - else: - withdrawal_privkey = pubkey_to_privkey[withdrawal_pubkey] - - domain = spec.get_domain(state, spec.DOMAIN_BLS_TO_EXECUTION_CHANGE) - address_change = spec.BLSToExecutionChange( - validator_index=validator_index, - from_bls_pubkey=withdrawal_pubkey, - to_execution_address=b'\x42' * 20, - ) - - signing_root = spec.compute_signing_root(address_change, domain) - return spec.SignedBLSToExecutionChange( - message=address_change, - signature=bls.Sign(withdrawal_privkey, signing_root), - ) - - @with_capella_and_later @spec_state_test def test_success(spec, state): diff --git a/tests/core/pyspec/eth2spec/test/capella/epoch_processing/test_process_partial_withdrawals.py b/tests/core/pyspec/eth2spec/test/capella/epoch_processing/test_process_partial_withdrawals.py index 431c2f2d33..7569d28621 100644 --- a/tests/core/pyspec/eth2spec/test/capella/epoch_processing/test_process_partial_withdrawals.py +++ b/tests/core/pyspec/eth2spec/test/capella/epoch_processing/test_process_partial_withdrawals.py @@ -8,20 +8,10 @@ from eth2spec.test.helpers.epoch_processing import run_epoch_processing_to from eth2spec.test.helpers.state import next_epoch from eth2spec.test.helpers.random import randomize_state - - -def set_eth1_withdrawal_credential_with_balance(spec, state, index, balance): - validator = state.validators[index] - validator.withdrawal_credentials = spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + validator.withdrawal_credentials[1:] - validator.effective_balance = min(balance, spec.MAX_EFFECTIVE_BALANCE) - state.balances[index] = balance - - -def set_validator_partially_withdrawable(spec, state, index, rng=random.Random(666)): - balance = spec.MAX_EFFECTIVE_BALANCE + rng.randint(1, 100000000) - set_eth1_withdrawal_credential_with_balance(spec, state, index, balance) - - assert spec.is_partially_withdrawable_validator(state.validators[index], state.balances[index]) +from eth2spec.test.helpers.withdrawals import ( + set_validator_partially_withdrawable, + set_eth1_withdrawal_credential_with_balance, +) def run_process_partial_withdrawals(spec, state, num_expected_withdrawals=None): @@ -228,7 +218,7 @@ def run_random_partial_withdrawals_test(spec, state, rng): num_partially_withdrawable = rng.randint(0, num_validators - 1) partially_withdrawable_indices = rng.sample(range(num_validators), num_partially_withdrawable) for index in partially_withdrawable_indices: - set_validator_partially_withdrawable(spec, state, index) + set_validator_partially_withdrawable(spec, state, index, excess_balance=rng.randint(1, 1000000000)) # Note: due to the randomness and other epoch processing, some of these set as "partially withdrawable" # may not be partially withdrawable once we get to ``process_partial_withdrawals``, diff --git a/tests/core/pyspec/eth2spec/test/capella/sanity/test_blocks.py b/tests/core/pyspec/eth2spec/test/capella/sanity/test_blocks.py new file mode 100644 index 0000000000..28c20a2cd3 --- /dev/null +++ b/tests/core/pyspec/eth2spec/test/capella/sanity/test_blocks.py @@ -0,0 +1,135 @@ +from eth2spec.test.context import ( + with_capella_and_later, spec_state_test +) + +from eth2spec.test.helpers.state import ( + state_transition_and_sign_block, +) +from eth2spec.test.helpers.block import ( + build_empty_block_for_next_slot, build_empty_block, +) +from eth2spec.test.helpers.bls_to_execution_changes import get_signed_address_change +from eth2spec.test.helpers.withdrawals import ( + set_validator_fully_withdrawable, + set_validator_partially_withdrawable, +) +from eth2spec.test.helpers.voluntary_exits import prepare_signed_exits + + +@with_capella_and_later +@spec_state_test +def test_successful_bls_change(spec, state): + index = 0 + signed_address_change = get_signed_address_change(spec, state, validator_index=index) + pre_credentials = state.validators[index].withdrawal_credentials + yield 'pre', state + + block = build_empty_block_for_next_slot(spec, state) + block.body.bls_to_execution_changes.append(signed_address_change) + + signed_block = state_transition_and_sign_block(spec, state, block) + + yield 'blocks', [signed_block] + yield 'post', state + + post_credentials = state.validators[index].withdrawal_credentials + assert pre_credentials != post_credentials + assert post_credentials[:1] == spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + assert post_credentials[1:12] == b'\x00' * 11 + assert post_credentials[12:] == signed_address_change.message.to_execution_address + + +@with_capella_and_later +@spec_state_test +def test_full_withdrawal_in_epoch_transition(spec, state): + index = 0 + current_epoch = spec.get_current_epoch(state) + set_validator_fully_withdrawable(spec, state, index, current_epoch) + yield 'pre', state + + # trigger epoch transition + block = build_empty_block(spec, state, state.slot + spec.SLOTS_PER_EPOCH) + signed_block = state_transition_and_sign_block(spec, state, block) + + yield 'blocks', [signed_block] + yield 'post', state + + assert state.balances[index] == 0 + + +@with_capella_and_later +@spec_state_test +def test_partial_withdrawal_in_epoch_transition(spec, state): + index = state.next_withdrawal_index + set_validator_partially_withdrawable(spec, state, index, excess_balance=1000000000000) + pre_balance = state.balances[index] + pre_withdrawal_queue_len = len(state.withdrawal_queue) + + yield 'pre', state + + # trigger epoch transition + block = build_empty_block(spec, state, state.slot + spec.SLOTS_PER_EPOCH) + signed_block = state_transition_and_sign_block(spec, state, block) + + yield 'blocks', [signed_block] + yield 'post', state + + assert state.balances[index] < pre_balance + # Potentially less than due to sync committee penalty + assert state.balances[index] <= spec.MAX_EFFECTIVE_BALANCE + # Withdrawal is processed within the context of the block so queue empty + assert len(state.withdrawal_queue) == pre_withdrawal_queue_len + + +@with_capella_and_later +@spec_state_test +def test_many_partial_withdrawals_in_epoch_transition(spec, state): + assert len(state.validators) > spec.MAX_WITHDRAWALS_PER_PAYLOAD + assert spec.MAX_PARTIAL_WITHDRAWALS_PER_EPOCH > spec.MAX_WITHDRAWALS_PER_PAYLOAD + + for i in range(spec.MAX_WITHDRAWALS_PER_PAYLOAD + 1): + index = (i + state.next_withdrawal_index) % len(state.validators) + set_validator_partially_withdrawable(spec, state, index, excess_balance=1000000000000) + + pre_withdrawal_queue_len = len(state.withdrawal_queue) + + yield 'pre', state + + # trigger epoch transition + block = build_empty_block(spec, state, state.slot + spec.SLOTS_PER_EPOCH) + signed_block = state_transition_and_sign_block(spec, state, block) + + yield 'blocks', [signed_block] + yield 'post', state + + # All new partial withdrawals processed except 1 + assert len(state.withdrawal_queue) == pre_withdrawal_queue_len + 1 + + +@with_capella_and_later +@spec_state_test +def test_exit_and_bls_change(spec, state): + # move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit + state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH + + index = 0 + signed_address_change = get_signed_address_change(spec, state, validator_index=index) + signed_exit = prepare_signed_exits(spec, state, [index])[0] + + yield 'pre', state + + block = build_empty_block_for_next_slot(spec, state) + block.body.voluntary_exits.append(signed_exit) + block.body.bls_to_execution_changes.append(signed_address_change) + + signed_block = state_transition_and_sign_block(spec, state, block) + + yield 'blocks', [signed_block] + yield 'post', state + + validator = state.validators[index] + balance = state.balances[index] + current_epoch = spec.get_current_epoch(state) + assert not spec.is_fully_withdrawable_validator(validator, balance, current_epoch) + assert validator.withdrawable_epoch < spec.FAR_FUTURE_EPOCH + assert spec.is_fully_withdrawable_validator(validator, balance, validator.withdrawable_epoch) diff --git a/tests/core/pyspec/eth2spec/test/helpers/bls_to_execution_changes.py b/tests/core/pyspec/eth2spec/test/helpers/bls_to_execution_changes.py new file mode 100644 index 0000000000..61c84b5152 --- /dev/null +++ b/tests/core/pyspec/eth2spec/test/helpers/bls_to_execution_changes.py @@ -0,0 +1,27 @@ +from eth2spec.utils import bls +from eth2spec.test.helpers.keys import pubkeys, privkeys, pubkey_to_privkey + + +def get_signed_address_change(spec, state, validator_index=None, withdrawal_pubkey=None): + if validator_index is None: + validator_index = 0 + + if withdrawal_pubkey is None: + key_index = -1 - validator_index + withdrawal_pubkey = pubkeys[key_index] + withdrawal_privkey = privkeys[key_index] + else: + withdrawal_privkey = pubkey_to_privkey[withdrawal_pubkey] + + domain = spec.get_domain(state, spec.DOMAIN_BLS_TO_EXECUTION_CHANGE) + address_change = spec.BLSToExecutionChange( + validator_index=validator_index, + from_bls_pubkey=withdrawal_pubkey, + to_execution_address=b'\x42' * 20, + ) + + signing_root = spec.compute_signing_root(address_change, domain) + return spec.SignedBLSToExecutionChange( + message=address_change, + signature=bls.Sign(withdrawal_privkey, signing_root), + ) diff --git a/tests/core/pyspec/eth2spec/test/helpers/withdrawals.py b/tests/core/pyspec/eth2spec/test/helpers/withdrawals.py index 739f5eb063..526ac0caa9 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/withdrawals.py +++ b/tests/core/pyspec/eth2spec/test/helpers/withdrawals.py @@ -15,3 +15,17 @@ def set_validator_fully_withdrawable(spec, state, index, withdrawable_epoch=None state.balances[index] = 10000000000 assert spec.is_fully_withdrawable_validator(validator, state.balances[index], withdrawable_epoch) + + +def set_eth1_withdrawal_credential_with_balance(spec, state, index, balance): + validator = state.validators[index] + validator.withdrawal_credentials = spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + validator.withdrawal_credentials[1:] + validator.effective_balance = min(balance, spec.MAX_EFFECTIVE_BALANCE) + state.balances[index] = balance + + +def set_validator_partially_withdrawable(spec, state, index, excess_balance=1000000000): + set_eth1_withdrawal_credential_with_balance(spec, state, index, spec.MAX_EFFECTIVE_BALANCE + excess_balance) + validator = state.validators[index] + + assert spec.is_partially_withdrawable_validator(validator, state.balances[index]) From 092617ec1e236a485b65f555bbfd99d1bcbb2f45 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Sun, 9 Oct 2022 17:02:50 -0500 Subject: [PATCH 16/35] Remove duplicated definition of `Validator` --- specs/capella/beacon-chain.md | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/specs/capella/beacon-chain.md b/specs/capella/beacon-chain.md index 88c21d3fef..09d568f18c 100644 --- a/specs/capella/beacon-chain.md +++ b/specs/capella/beacon-chain.md @@ -24,7 +24,6 @@ - [Extended Containers](#extended-containers) - [`ExecutionPayload`](#executionpayload) - [`ExecutionPayloadHeader`](#executionpayloadheader) - - [`Validator`](#validator) - [`BeaconBlockBody`](#beaconblockbody) - [`BeaconState`](#beaconstate) - [Helpers](#helpers) @@ -180,21 +179,6 @@ class ExecutionPayloadHeader(Container): withdrawals_root: Root # [New in Capella] ``` -#### `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 -``` - #### `BeaconBlockBody` ```python From 24f8ec170b6572b1798af768f7ae5549438e0207 Mon Sep 17 00:00:00 2001 From: ethosdev <79124435+ethosdev@users.noreply.github.com> Date: Fri, 14 Oct 2022 21:05:45 +0000 Subject: [PATCH 17/35] Remove work-in-progress notes in Bellatrix specs (#3033) * Remove the work-in-progress note in Bellatrix spec Bellatrix is done and released. * Remove work-in-progress notes in Bellatrix specs * Remove work-in-progress notes in Bellatrix specs * Remove work-in-progress notes in Bellatrix specs --- specs/bellatrix/beacon-chain.md | 2 -- specs/bellatrix/fork-choice.md | 2 -- specs/bellatrix/fork.md | 2 -- specs/bellatrix/validator.md | 2 -- 4 files changed, 8 deletions(-) diff --git a/specs/bellatrix/beacon-chain.md b/specs/bellatrix/beacon-chain.md index 6d39c1ae89..1133cba06c 100644 --- a/specs/bellatrix/beacon-chain.md +++ b/specs/bellatrix/beacon-chain.md @@ -1,7 +1,5 @@ # Bellatrix -- The Beacon Chain -**Notice**: This document is a work-in-progress for researchers and implementers. - ## Table of contents diff --git a/specs/bellatrix/fork-choice.md b/specs/bellatrix/fork-choice.md index 312768e447..94d0688273 100644 --- a/specs/bellatrix/fork-choice.md +++ b/specs/bellatrix/fork-choice.md @@ -1,7 +1,5 @@ # Bellatrix -- Fork Choice -**Notice**: This document is a work-in-progress for researchers and implementers. - ## Table of contents diff --git a/specs/bellatrix/fork.md b/specs/bellatrix/fork.md index 19700e3839..a114e5a5fa 100644 --- a/specs/bellatrix/fork.md +++ b/specs/bellatrix/fork.md @@ -1,7 +1,5 @@ # Bellatrix -- Fork Logic -**Notice**: This document is a work-in-progress for researchers and implementers. - ## Table of contents diff --git a/specs/bellatrix/validator.md b/specs/bellatrix/validator.md index c88aa9babe..a176d7534e 100644 --- a/specs/bellatrix/validator.md +++ b/specs/bellatrix/validator.md @@ -1,7 +1,5 @@ # Bellatrix -- Honest Validator -**Notice**: This document is a work-in-progress for researchers and implementers. - ## Table of contents From 6181035d5ddef4b668d0fcfee460da9888009dd9 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Sat, 15 Oct 2022 12:40:17 +0800 Subject: [PATCH 18/35] Fix typo (#3039) --- .../pyspec/eth2spec/test/phase0/fork_choice/test_ex_ante.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 d931011565..0a145dfa52 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 @@ -196,7 +196,7 @@ def _filter_participant_set(participants): def test_ex_ante_sandwich_without_attestations(spec, state): """ Simple Sandwich test with boost and no attestations. - Obejcts: + Objects: Block A - slot N Block B (parent A) - slot N+1 Block C (parent A) - slot N+2 From 03ced9c2639269ef9503c18d36c2c7a3646fcf67 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Thu, 13 Oct 2022 14:53:40 -0500 Subject: [PATCH 19/35] Add Capella tests --- tests/core/pyspec/eth2spec/test/capella/__init__.py | 0 .../eth2spec/test/capella/block_processing/__init__.py | 0 .../eth2spec/test/capella/epoch_processing/__init__.py | 0 .../core/pyspec/eth2spec/test/capella/fork/__init__.py | 0 .../pyspec/eth2spec/test/capella/sanity/__init__.py | 0 tests/core/pyspec/eth2spec/test/helpers/constants.py | 5 +++-- tests/generators/epoch_processing/main.py | 9 ++++++++- tests/generators/finality/main.py | 8 +++++--- tests/generators/fork_choice/main.py | 5 +++-- tests/generators/forks/main.py | 8 +++++++- tests/generators/genesis/main.py | 7 ++++--- tests/generators/light_client/main.py | 4 +++- tests/generators/operations/main.py | 10 +++++++++- tests/generators/random/main.py | 6 +++++- tests/generators/rewards/main.py | 4 +++- tests/generators/sanity/main.py | 8 +++++++- tests/generators/sync/main.py | 4 +++- 17 files changed, 60 insertions(+), 18 deletions(-) create mode 100644 tests/core/pyspec/eth2spec/test/capella/__init__.py create mode 100644 tests/core/pyspec/eth2spec/test/capella/block_processing/__init__.py create mode 100644 tests/core/pyspec/eth2spec/test/capella/epoch_processing/__init__.py create mode 100644 tests/core/pyspec/eth2spec/test/capella/fork/__init__.py create mode 100644 tests/core/pyspec/eth2spec/test/capella/sanity/__init__.py diff --git a/tests/core/pyspec/eth2spec/test/capella/__init__.py b/tests/core/pyspec/eth2spec/test/capella/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/core/pyspec/eth2spec/test/capella/block_processing/__init__.py b/tests/core/pyspec/eth2spec/test/capella/block_processing/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/core/pyspec/eth2spec/test/capella/epoch_processing/__init__.py b/tests/core/pyspec/eth2spec/test/capella/epoch_processing/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/core/pyspec/eth2spec/test/capella/fork/__init__.py b/tests/core/pyspec/eth2spec/test/capella/fork/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/core/pyspec/eth2spec/test/capella/sanity/__init__.py b/tests/core/pyspec/eth2spec/test/capella/sanity/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/core/pyspec/eth2spec/test/helpers/constants.py b/tests/core/pyspec/eth2spec/test/helpers/constants.py index b1463b97bd..0763c42cc3 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/constants.py +++ b/tests/core/pyspec/eth2spec/test/helpers/constants.py @@ -24,11 +24,12 @@ EIP4844, ) # The forks that output to the test vectors. -TESTGEN_FORKS = (PHASE0, ALTAIR, BELLATRIX) +TESTGEN_FORKS = (PHASE0, ALTAIR, BELLATRIX, CAPELLA, EIP4844) FORKS_BEFORE_ALTAIR = (PHASE0,) FORKS_BEFORE_BELLATRIX = (PHASE0, ALTAIR) -FORKS_BEFORE_CAPELLA = (PHASE0, ALTAIR, BELLATRIX) + +# TODO: no EIP4844 fork tests now. Should add when we figure out the content of Capella. ALL_FORK_UPGRADES = { # pre_fork_name: post_fork_name PHASE0: ALTAIR, diff --git a/tests/generators/epoch_processing/main.py b/tests/generators/epoch_processing/main.py index 7ba2709290..946a6c2c01 100644 --- a/tests/generators/epoch_processing/main.py +++ b/tests/generators/epoch_processing/main.py @@ -1,5 +1,5 @@ from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators, combine_mods -from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX +from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA if __name__ == "__main__": @@ -27,6 +27,12 @@ # so no additional tests required. bellatrix_mods = altair_mods + _new_capella_mods = {key: 'eth2spec.test.capella.epoch_processing.test_process_' + key for key in [ + 'full_withdrawals', + 'partial_withdrawals', + ]} + capella_mods = combine_mods(_new_capella_mods, altair_mods) + # TODO Custody Game testgen is disabled for now # custody_game_mods = {**{key: 'eth2spec.test.custody_game.epoch_processing.test_process_' + key for key in [ # 'reveal_deadlines', @@ -38,6 +44,7 @@ PHASE0: phase_0_mods, ALTAIR: altair_mods, BELLATRIX: bellatrix_mods, + CAPELLA: capella_mods, } run_state_test_generators(runner_name="epoch_processing", all_mods=all_mods) diff --git a/tests/generators/finality/main.py b/tests/generators/finality/main.py index 24c7d0c6ee..cb40e7cc9a 100644 --- a/tests/generators/finality/main.py +++ b/tests/generators/finality/main.py @@ -1,16 +1,18 @@ from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators -from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX +from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA if __name__ == "__main__": phase_0_mods = {'finality': 'eth2spec.test.phase0.finality.test_finality'} - altair_mods = phase_0_mods # No additional Altair specific finality tests - bellatrix_mods = altair_mods # No additional Bellatrix specific finality tests + altair_mods = phase_0_mods # No additional Altair specific finality tests + bellatrix_mods = altair_mods # No additional Bellatrix specific finality tests + capella_mods = bellatrix_mods # No additional Capella specific finality tests all_mods = { PHASE0: phase_0_mods, ALTAIR: altair_mods, BELLATRIX: bellatrix_mods, + CAPELLA: capella_mods, } run_state_test_generators(runner_name="finality", all_mods=all_mods) diff --git a/tests/generators/fork_choice/main.py b/tests/generators/fork_choice/main.py index 87d3a3fdf3..2c95b70e55 100644 --- a/tests/generators/fork_choice/main.py +++ b/tests/generators/fork_choice/main.py @@ -1,5 +1,5 @@ from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators, combine_mods -from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX +from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA if __name__ == "__main__": @@ -17,11 +17,12 @@ 'on_merge_block', ]} bellatrix_mods = combine_mods(_new_bellatrix_mods, altair_mods) - + capella_mods = bellatrix_mods all_mods = { PHASE0: phase_0_mods, ALTAIR: altair_mods, BELLATRIX: bellatrix_mods, + CAPELLA: capella_mods, } run_state_test_generators(runner_name="fork_choice", all_mods=all_mods) diff --git a/tests/generators/forks/main.py b/tests/generators/forks/main.py index fbf75a2211..6281149795 100644 --- a/tests/generators/forks/main.py +++ b/tests/generators/forks/main.py @@ -1,9 +1,13 @@ from typing import Iterable -from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, MINIMAL, MAINNET +from eth2spec.test.helpers.constants import ( + PHASE0, ALTAIR, BELLATRIX, CAPELLA, + MINIMAL, MAINNET, +) from eth2spec.test.helpers.typing import SpecForkName, PresetBaseName from eth2spec.test.altair.fork import test_altair_fork_basic, test_altair_fork_random from eth2spec.test.bellatrix.fork import test_bellatrix_fork_basic, test_bellatrix_fork_random +from eth2spec.test.capella.fork import test_capella_fork_basic, test_capella_fork_random from eth2spec.gen_helpers.gen_base import gen_runner, gen_typing from eth2spec.gen_helpers.gen_from_tests.gen import generate_from_tests @@ -33,6 +37,8 @@ def _get_fork_tests_providers(): yield create_provider(test_altair_fork_random, preset, PHASE0, ALTAIR) yield create_provider(test_bellatrix_fork_basic, preset, ALTAIR, BELLATRIX) yield create_provider(test_bellatrix_fork_random, preset, ALTAIR, BELLATRIX) + yield create_provider(test_capella_fork_basic, preset, BELLATRIX, CAPELLA) + yield create_provider(test_capella_fork_random, preset, BELLATRIX, CAPELLA) if __name__ == "__main__": diff --git a/tests/generators/genesis/main.py b/tests/generators/genesis/main.py index 272eebb4e9..e71b154012 100644 --- a/tests/generators/genesis/main.py +++ b/tests/generators/genesis/main.py @@ -1,5 +1,5 @@ from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators, combine_mods -from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX +from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA if __name__ == "__main__": @@ -8,18 +8,19 @@ 'validity', ]} - # we have new unconditional lines in `initialize_beacon_state_from_eth1` and we want to test it altair_mods = phase_0_mods + # we have new unconditional lines in `initialize_beacon_state_from_eth1` and we want to test it _new_bellatrix_mods = {key: 'eth2spec.test.bellatrix.genesis.test_' + key for key in [ 'initialization', ]} bellatrix_mods = combine_mods(_new_bellatrix_mods, altair_mods) - + capella_mods = bellatrix_mods all_mods = { PHASE0: phase_0_mods, ALTAIR: altair_mods, BELLATRIX: bellatrix_mods, + CAPELLA: capella_mods, } run_state_test_generators(runner_name="genesis", all_mods=all_mods) diff --git a/tests/generators/light_client/main.py b/tests/generators/light_client/main.py index 68a0da5297..0ff3fe7866 100644 --- a/tests/generators/light_client/main.py +++ b/tests/generators/light_client/main.py @@ -1,4 +1,4 @@ -from eth2spec.test.helpers.constants import ALTAIR, BELLATRIX +from eth2spec.test.helpers.constants import ALTAIR, BELLATRIX, CAPELLA from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators @@ -9,10 +9,12 @@ 'update_ranking', ]} bellatrix_mods = altair_mods + capella_mods = bellatrix_mods all_mods = { ALTAIR: altair_mods, BELLATRIX: bellatrix_mods, + CAPELLA: capella_mods, } run_state_test_generators(runner_name="light_client", all_mods=all_mods) diff --git a/tests/generators/operations/main.py b/tests/generators/operations/main.py index 2a634cd5b1..1977ec217b 100644 --- a/tests/generators/operations/main.py +++ b/tests/generators/operations/main.py @@ -1,5 +1,5 @@ from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators, combine_mods -from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX +from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA if __name__ == "__main__": @@ -24,6 +24,13 @@ ]} bellatrix_mods = combine_mods(_new_bellatrix_mods, altair_mods) + _new_capella_mods = {key: 'eth2spec.test.capella.block_processing.test_process_' + key for key in [ + 'deposit', + 'bls_to_execution_change', + 'withdrawals', + ]} + capella_mods = combine_mods(_new_capella_mods, bellatrix_mods) + # TODO Custody Game testgen is disabled for now # _new_custody_game_mods = {key: 'eth2spec.test.custody_game.block_processing.test_process_' + key for key in [ # 'attestation', @@ -38,6 +45,7 @@ PHASE0: phase_0_mods, ALTAIR: altair_mods, BELLATRIX: bellatrix_mods, + CAPELLA: capella_mods, } run_state_test_generators(runner_name="operations", all_mods=all_mods) diff --git a/tests/generators/random/main.py b/tests/generators/random/main.py index 1791da83b9..9983da96c4 100644 --- a/tests/generators/random/main.py +++ b/tests/generators/random/main.py @@ -1,4 +1,4 @@ -from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX +from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators @@ -12,11 +12,15 @@ bellatrix_mods = {key: 'eth2spec.test.bellatrix.random.test_' + key for key in [ 'random', ]} + capella_mods = {key: 'eth2spec.test.capella.random.test_' + key for key in [ + 'random', + ]} all_mods = { PHASE0: phase_0_mods, ALTAIR: altair_mods, BELLATRIX: bellatrix_mods, + CAPELLA: capella_mods, } run_state_test_generators(runner_name="random", all_mods=all_mods) diff --git a/tests/generators/rewards/main.py b/tests/generators/rewards/main.py index 22b4be5c3b..33763144bd 100644 --- a/tests/generators/rewards/main.py +++ b/tests/generators/rewards/main.py @@ -1,5 +1,5 @@ from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators -from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX +from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA if __name__ == "__main__": @@ -15,11 +15,13 @@ # Note: Block rewards are non-epoch rewards and are tested as part of block processing tests. # Transaction fees are part of the execution-layer. bellatrix_mods = altair_mods + capella_mods = bellatrix_mods all_mods = { PHASE0: phase_0_mods, ALTAIR: altair_mods, BELLATRIX: bellatrix_mods, + CAPELLA: capella_mods, } run_state_test_generators(runner_name="rewards", all_mods=all_mods) diff --git a/tests/generators/sanity/main.py b/tests/generators/sanity/main.py index 259e790592..bb3c954c2a 100644 --- a/tests/generators/sanity/main.py +++ b/tests/generators/sanity/main.py @@ -1,4 +1,4 @@ -from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX +from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators, combine_mods @@ -18,10 +18,16 @@ ]} bellatrix_mods = combine_mods(_new_bellatrix_mods, altair_mods) + _new_capella_mods = {key: 'eth2spec.test.capella.sanity.test_' + key for key in [ + 'blocks', + ]} + capella_mods = combine_mods(_new_capella_mods, bellatrix_mods) + all_mods = { PHASE0: phase_0_mods, ALTAIR: altair_mods, BELLATRIX: bellatrix_mods, + CAPELLA: capella_mods, } run_state_test_generators(runner_name="sanity", all_mods=all_mods) diff --git a/tests/generators/sync/main.py b/tests/generators/sync/main.py index ad83b78a09..eba3ac1527 100644 --- a/tests/generators/sync/main.py +++ b/tests/generators/sync/main.py @@ -1,14 +1,16 @@ from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators -from eth2spec.test.helpers.constants import BELLATRIX +from eth2spec.test.helpers.constants import BELLATRIX, CAPELLA if __name__ == "__main__": bellatrix_mods = {key: 'eth2spec.test.bellatrix.sync.test_' + key for key in [ 'optimistic', ]} + capella_mods = bellatrix_mods all_mods = { BELLATRIX: bellatrix_mods, + CAPELLA: capella_mods, } run_state_test_generators(runner_name="sync", all_mods=all_mods) From b90436c98837924c3a929652b57b07ac9d3c2254 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Fri, 14 Oct 2022 18:14:50 -0500 Subject: [PATCH 20/35] Fix capella random & fork --- Makefile | 2 + specs/capella/fork.md | 19 +- .../eth2spec/test/capella/random/__init__.py | 0 .../test/capella/random/test_random.py | 438 ++++++++++++++++++ .../eth2spec/test/helpers/capella/__init__.py | 0 .../test/utils/randomized_block_tests.py | 14 + tests/generators/random/Makefile | 2 + tests/generators/random/generate.py | 11 +- 8 files changed, 484 insertions(+), 2 deletions(-) create mode 100644 tests/core/pyspec/eth2spec/test/capella/random/__init__.py create mode 100644 tests/core/pyspec/eth2spec/test/capella/random/test_random.py create mode 100644 tests/core/pyspec/eth2spec/test/helpers/capella/__init__.py diff --git a/Makefile b/Makefile index 5f0706dd99..639fae9530 100644 --- a/Makefile +++ b/Makefile @@ -64,6 +64,8 @@ partial_clean: rm -rf $(ETH2SPEC_MODULE_DIR)/phase0 rm -rf $(ETH2SPEC_MODULE_DIR)/altair rm -rf $(ETH2SPEC_MODULE_DIR)/bellatrix + rm -rf $(ETH2SPEC_MODULE_DIR)/capella + rm -rf $(ETH2SPEC_MODULE_DIR)/eip4844 rm -rf $(COV_HTML_OUT_DIR) rm -rf $(TEST_REPORT_DIR) rm -rf eth2spec.egg-info dist build diff --git a/specs/capella/fork.md b/specs/capella/fork.md index e6e70f7974..bc3c95aed9 100644 --- a/specs/capella/fork.md +++ b/specs/capella/fork.md @@ -70,6 +70,23 @@ In particular, the outer `state_transition` function defined in the Phase 0 docu ```python def upgrade_to_capella(pre: bellatrix.BeaconState) -> BeaconState: epoch = bellatrix.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=Root(), # [New in Capella] + ) post = BeaconState( # Versioning genesis_time=pre.genesis_time, @@ -110,7 +127,7 @@ def upgrade_to_capella(pre: bellatrix.BeaconState) -> BeaconState: 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, + latest_execution_payload_header=latest_execution_payload_header, # Withdrawals withdrawal_queue=[], next_withdrawal_index=WithdrawalIndex(0), diff --git a/tests/core/pyspec/eth2spec/test/capella/random/__init__.py b/tests/core/pyspec/eth2spec/test/capella/random/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/core/pyspec/eth2spec/test/capella/random/test_random.py b/tests/core/pyspec/eth2spec/test/capella/random/test_random.py new file mode 100644 index 0000000000..47ee93187f --- /dev/null +++ b/tests/core/pyspec/eth2spec/test/capella/random/test_random.py @@ -0,0 +1,438 @@ +""" +This module is generated from the ``random`` test generator. +Please do not edit this file manually. +See the README for that generator for more information. +""" + +from eth2spec.test.helpers.constants import CAPELLA +from eth2spec.test.context import ( + misc_balances_in_default_range_with_many_validators, + with_phases, + zero_activation_threshold, + only_generator, +) +from eth2spec.test.context import ( + always_bls, + spec_test, + with_custom_state, + single_phase, +) +from eth2spec.test.utils.randomized_block_tests import ( + run_generated_randomized_test, +) + + +@only_generator("randomized test for broad coverage, not point-to-point CI") +@with_phases([CAPELLA]) +@with_custom_state( + balances_fn=misc_balances_in_default_range_with_many_validators, + threshold_fn=zero_activation_threshold +) +@spec_test +@single_phase +@always_bls +def test_randomized_0(spec, state): + # scenario as high-level, informal text: + # epochs:0,slots:0,with-block:no_block + # epochs:0,slots:0,with-block:no_block + # epochs:0,slots:0,with-block:no_block + # epochs:0,slots:0,with-block:random_block_capella + # epochs:1,slots:0,with-block:no_block + # epochs:0,slots:random_slot_in_epoch,with-block:no_block + # epochs:0,slots:0,with-block:random_block_capella + scenario = {'transitions': [{'validation': 'validate_is_not_leaking', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block'}, {'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 0, 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_capella', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}, {'epochs_to_skip': 1, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'random_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_capella', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_capella'} # noqa: E501 + yield from run_generated_randomized_test( + spec, + state, + scenario, + ) + + +@only_generator("randomized test for broad coverage, not point-to-point CI") +@with_phases([CAPELLA]) +@with_custom_state( + balances_fn=misc_balances_in_default_range_with_many_validators, + threshold_fn=zero_activation_threshold +) +@spec_test +@single_phase +@always_bls +def test_randomized_1(spec, state): + # scenario as high-level, informal text: + # epochs:0,slots:0,with-block:no_block + # epochs:1,slots:0,with-block:no_block + # epochs:0,slots:random_slot_in_epoch,with-block:no_block + # epochs:0,slots:0,with-block:random_block_capella + # epochs:0,slots:0,with-block:no_block + # epochs:0,slots:0,with-block:no_block + # epochs:0,slots:0,with-block:random_block_capella + scenario = {'transitions': [{'validation': 'validate_is_not_leaking', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block'}, {'epochs_to_skip': 1, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'random_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_capella', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}, {'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 0, 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_capella', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_capella'} # noqa: E501 + yield from run_generated_randomized_test( + spec, + state, + scenario, + ) + + +@only_generator("randomized test for broad coverage, not point-to-point CI") +@with_phases([CAPELLA]) +@with_custom_state( + balances_fn=misc_balances_in_default_range_with_many_validators, + threshold_fn=zero_activation_threshold +) +@spec_test +@single_phase +@always_bls +def test_randomized_2(spec, state): + # scenario as high-level, informal text: + # epochs:0,slots:0,with-block:no_block + # epochs:0,slots:0,with-block:no_block + # epochs:0,slots:penultimate_slot_in_epoch,with-block:no_block + # epochs:0,slots:0,with-block:random_block_capella + # epochs:0,slots:0,with-block:no_block + # epochs:0,slots:last_slot_in_epoch,with-block:no_block + # epochs:0,slots:0,with-block:random_block_capella + scenario = {'transitions': [{'validation': 'validate_is_not_leaking', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block'}, {'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'penultimate_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_capella', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}, {'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'last_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_capella', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_capella'} # noqa: E501 + yield from run_generated_randomized_test( + spec, + state, + scenario, + ) + + +@only_generator("randomized test for broad coverage, not point-to-point CI") +@with_phases([CAPELLA]) +@with_custom_state( + balances_fn=misc_balances_in_default_range_with_many_validators, + threshold_fn=zero_activation_threshold +) +@spec_test +@single_phase +@always_bls +def test_randomized_3(spec, state): + # scenario as high-level, informal text: + # epochs:0,slots:0,with-block:no_block + # epochs:0,slots:0,with-block:no_block + # epochs:0,slots:last_slot_in_epoch,with-block:no_block + # epochs:0,slots:0,with-block:random_block_capella + # epochs:1,slots:0,with-block:no_block + # epochs:0,slots:last_slot_in_epoch,with-block:no_block + # epochs:0,slots:0,with-block:random_block_capella + scenario = {'transitions': [{'validation': 'validate_is_not_leaking', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block'}, {'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'last_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_capella', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}, {'epochs_to_skip': 1, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'last_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_capella', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_capella'} # noqa: E501 + yield from run_generated_randomized_test( + spec, + state, + scenario, + ) + + +@only_generator("randomized test for broad coverage, not point-to-point CI") +@with_phases([CAPELLA]) +@with_custom_state( + balances_fn=misc_balances_in_default_range_with_many_validators, + threshold_fn=zero_activation_threshold +) +@spec_test +@single_phase +@always_bls +def test_randomized_4(spec, state): + # scenario as high-level, informal text: + # epochs:0,slots:0,with-block:no_block + # epochs:1,slots:0,with-block:no_block + # epochs:0,slots:last_slot_in_epoch,with-block:no_block + # epochs:0,slots:0,with-block:random_block_capella + # epochs:1,slots:0,with-block:no_block + # epochs:0,slots:penultimate_slot_in_epoch,with-block:no_block + # epochs:0,slots:0,with-block:random_block_capella + scenario = {'transitions': [{'validation': 'validate_is_not_leaking', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block'}, {'epochs_to_skip': 1, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'last_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_capella', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}, {'epochs_to_skip': 1, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'penultimate_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_capella', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_capella'} # noqa: E501 + yield from run_generated_randomized_test( + spec, + state, + scenario, + ) + + +@only_generator("randomized test for broad coverage, not point-to-point CI") +@with_phases([CAPELLA]) +@with_custom_state( + balances_fn=misc_balances_in_default_range_with_many_validators, + threshold_fn=zero_activation_threshold +) +@spec_test +@single_phase +@always_bls +def test_randomized_5(spec, state): + # scenario as high-level, informal text: + # epochs:0,slots:0,with-block:no_block + # epochs:0,slots:0,with-block:no_block + # epochs:0,slots:random_slot_in_epoch,with-block:no_block + # epochs:0,slots:0,with-block:random_block_capella + # epochs:0,slots:0,with-block:no_block + # epochs:0,slots:random_slot_in_epoch,with-block:no_block + # epochs:0,slots:0,with-block:random_block_capella + scenario = {'transitions': [{'validation': 'validate_is_not_leaking', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block'}, {'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'random_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_capella', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}, {'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'random_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_capella', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_capella'} # noqa: E501 + yield from run_generated_randomized_test( + spec, + state, + scenario, + ) + + +@only_generator("randomized test for broad coverage, not point-to-point CI") +@with_phases([CAPELLA]) +@with_custom_state( + balances_fn=misc_balances_in_default_range_with_many_validators, + threshold_fn=zero_activation_threshold +) +@spec_test +@single_phase +@always_bls +def test_randomized_6(spec, state): + # scenario as high-level, informal text: + # epochs:0,slots:0,with-block:no_block + # epochs:1,slots:0,with-block:no_block + # epochs:0,slots:penultimate_slot_in_epoch,with-block:no_block + # epochs:0,slots:0,with-block:random_block_capella + # epochs:0,slots:0,with-block:no_block + # epochs:0,slots:penultimate_slot_in_epoch,with-block:no_block + # epochs:0,slots:0,with-block:random_block_capella + scenario = {'transitions': [{'validation': 'validate_is_not_leaking', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block'}, {'epochs_to_skip': 1, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'penultimate_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_capella', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}, {'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'penultimate_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_capella', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_capella'} # noqa: E501 + yield from run_generated_randomized_test( + spec, + state, + scenario, + ) + + +@only_generator("randomized test for broad coverage, not point-to-point CI") +@with_phases([CAPELLA]) +@with_custom_state( + balances_fn=misc_balances_in_default_range_with_many_validators, + threshold_fn=zero_activation_threshold +) +@spec_test +@single_phase +@always_bls +def test_randomized_7(spec, state): + # scenario as high-level, informal text: + # epochs:0,slots:0,with-block:no_block + # epochs:1,slots:0,with-block:no_block + # epochs:0,slots:0,with-block:no_block + # epochs:0,slots:0,with-block:random_block_capella + # epochs:1,slots:0,with-block:no_block + # epochs:0,slots:0,with-block:no_block + # epochs:0,slots:0,with-block:random_block_capella + scenario = {'transitions': [{'validation': 'validate_is_not_leaking', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block'}, {'epochs_to_skip': 1, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 0, 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_capella', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}, {'epochs_to_skip': 1, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 0, 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_capella', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_capella'} # noqa: E501 + yield from run_generated_randomized_test( + spec, + state, + scenario, + ) + + +@only_generator("randomized test for broad coverage, not point-to-point CI") +@with_phases([CAPELLA]) +@with_custom_state( + balances_fn=misc_balances_in_default_range_with_many_validators, + threshold_fn=zero_activation_threshold +) +@spec_test +@single_phase +@always_bls +def test_randomized_8(spec, state): + # scenario as high-level, informal text: + # epochs:epochs_until_leak,slots:0,with-block:no_block + # epochs:0,slots:0,with-block:no_block + # epochs:0,slots:0,with-block:no_block + # epochs:0,slots:0,with-block:random_block_capella + # epochs:1,slots:0,with-block:no_block + # epochs:0,slots:random_slot_in_epoch,with-block:no_block + # epochs:0,slots:0,with-block:random_block_capella + scenario = {'transitions': [{'epochs_to_skip': 'epochs_until_leak', 'validation': 'validate_is_leaking', 'slots_to_skip': 0, 'block_producer': 'no_block'}, {'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 0, 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_capella', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}, {'epochs_to_skip': 1, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'random_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_capella', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_capella'} # noqa: E501 + yield from run_generated_randomized_test( + spec, + state, + scenario, + ) + + +@only_generator("randomized test for broad coverage, not point-to-point CI") +@with_phases([CAPELLA]) +@with_custom_state( + balances_fn=misc_balances_in_default_range_with_many_validators, + threshold_fn=zero_activation_threshold +) +@spec_test +@single_phase +@always_bls +def test_randomized_9(spec, state): + # scenario as high-level, informal text: + # epochs:epochs_until_leak,slots:0,with-block:no_block + # epochs:1,slots:0,with-block:no_block + # epochs:0,slots:random_slot_in_epoch,with-block:no_block + # epochs:0,slots:0,with-block:random_block_capella + # epochs:0,slots:0,with-block:no_block + # epochs:0,slots:0,with-block:no_block + # epochs:0,slots:0,with-block:random_block_capella + scenario = {'transitions': [{'epochs_to_skip': 'epochs_until_leak', 'validation': 'validate_is_leaking', 'slots_to_skip': 0, 'block_producer': 'no_block'}, {'epochs_to_skip': 1, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'random_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_capella', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}, {'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 0, 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_capella', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_capella'} # noqa: E501 + yield from run_generated_randomized_test( + spec, + state, + scenario, + ) + + +@only_generator("randomized test for broad coverage, not point-to-point CI") +@with_phases([CAPELLA]) +@with_custom_state( + balances_fn=misc_balances_in_default_range_with_many_validators, + threshold_fn=zero_activation_threshold +) +@spec_test +@single_phase +@always_bls +def test_randomized_10(spec, state): + # scenario as high-level, informal text: + # epochs:epochs_until_leak,slots:0,with-block:no_block + # epochs:0,slots:0,with-block:no_block + # epochs:0,slots:penultimate_slot_in_epoch,with-block:no_block + # epochs:0,slots:0,with-block:random_block_capella + # epochs:0,slots:0,with-block:no_block + # epochs:0,slots:last_slot_in_epoch,with-block:no_block + # epochs:0,slots:0,with-block:random_block_capella + scenario = {'transitions': [{'epochs_to_skip': 'epochs_until_leak', 'validation': 'validate_is_leaking', 'slots_to_skip': 0, 'block_producer': 'no_block'}, {'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'penultimate_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_capella', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}, {'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'last_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_capella', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_capella'} # noqa: E501 + yield from run_generated_randomized_test( + spec, + state, + scenario, + ) + + +@only_generator("randomized test for broad coverage, not point-to-point CI") +@with_phases([CAPELLA]) +@with_custom_state( + balances_fn=misc_balances_in_default_range_with_many_validators, + threshold_fn=zero_activation_threshold +) +@spec_test +@single_phase +@always_bls +def test_randomized_11(spec, state): + # scenario as high-level, informal text: + # epochs:epochs_until_leak,slots:0,with-block:no_block + # epochs:0,slots:0,with-block:no_block + # epochs:0,slots:last_slot_in_epoch,with-block:no_block + # epochs:0,slots:0,with-block:random_block_capella + # epochs:1,slots:0,with-block:no_block + # epochs:0,slots:last_slot_in_epoch,with-block:no_block + # epochs:0,slots:0,with-block:random_block_capella + scenario = {'transitions': [{'epochs_to_skip': 'epochs_until_leak', 'validation': 'validate_is_leaking', 'slots_to_skip': 0, 'block_producer': 'no_block'}, {'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'last_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_capella', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}, {'epochs_to_skip': 1, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'last_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_capella', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_capella'} # noqa: E501 + yield from run_generated_randomized_test( + spec, + state, + scenario, + ) + + +@only_generator("randomized test for broad coverage, not point-to-point CI") +@with_phases([CAPELLA]) +@with_custom_state( + balances_fn=misc_balances_in_default_range_with_many_validators, + threshold_fn=zero_activation_threshold +) +@spec_test +@single_phase +@always_bls +def test_randomized_12(spec, state): + # scenario as high-level, informal text: + # epochs:epochs_until_leak,slots:0,with-block:no_block + # epochs:1,slots:0,with-block:no_block + # epochs:0,slots:last_slot_in_epoch,with-block:no_block + # epochs:0,slots:0,with-block:random_block_capella + # epochs:1,slots:0,with-block:no_block + # epochs:0,slots:penultimate_slot_in_epoch,with-block:no_block + # epochs:0,slots:0,with-block:random_block_capella + scenario = {'transitions': [{'epochs_to_skip': 'epochs_until_leak', 'validation': 'validate_is_leaking', 'slots_to_skip': 0, 'block_producer': 'no_block'}, {'epochs_to_skip': 1, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'last_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_capella', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}, {'epochs_to_skip': 1, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'penultimate_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_capella', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_capella'} # noqa: E501 + yield from run_generated_randomized_test( + spec, + state, + scenario, + ) + + +@only_generator("randomized test for broad coverage, not point-to-point CI") +@with_phases([CAPELLA]) +@with_custom_state( + balances_fn=misc_balances_in_default_range_with_many_validators, + threshold_fn=zero_activation_threshold +) +@spec_test +@single_phase +@always_bls +def test_randomized_13(spec, state): + # scenario as high-level, informal text: + # epochs:epochs_until_leak,slots:0,with-block:no_block + # epochs:0,slots:0,with-block:no_block + # epochs:0,slots:random_slot_in_epoch,with-block:no_block + # epochs:0,slots:0,with-block:random_block_capella + # epochs:0,slots:0,with-block:no_block + # epochs:0,slots:random_slot_in_epoch,with-block:no_block + # epochs:0,slots:0,with-block:random_block_capella + scenario = {'transitions': [{'epochs_to_skip': 'epochs_until_leak', 'validation': 'validate_is_leaking', 'slots_to_skip': 0, 'block_producer': 'no_block'}, {'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'random_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_capella', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}, {'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'random_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_capella', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_capella'} # noqa: E501 + yield from run_generated_randomized_test( + spec, + state, + scenario, + ) + + +@only_generator("randomized test for broad coverage, not point-to-point CI") +@with_phases([CAPELLA]) +@with_custom_state( + balances_fn=misc_balances_in_default_range_with_many_validators, + threshold_fn=zero_activation_threshold +) +@spec_test +@single_phase +@always_bls +def test_randomized_14(spec, state): + # scenario as high-level, informal text: + # epochs:epochs_until_leak,slots:0,with-block:no_block + # epochs:1,slots:0,with-block:no_block + # epochs:0,slots:penultimate_slot_in_epoch,with-block:no_block + # epochs:0,slots:0,with-block:random_block_capella + # epochs:0,slots:0,with-block:no_block + # epochs:0,slots:penultimate_slot_in_epoch,with-block:no_block + # epochs:0,slots:0,with-block:random_block_capella + scenario = {'transitions': [{'epochs_to_skip': 'epochs_until_leak', 'validation': 'validate_is_leaking', 'slots_to_skip': 0, 'block_producer': 'no_block'}, {'epochs_to_skip': 1, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'penultimate_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_capella', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}, {'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'penultimate_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_capella', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_capella'} # noqa: E501 + yield from run_generated_randomized_test( + spec, + state, + scenario, + ) + + +@only_generator("randomized test for broad coverage, not point-to-point CI") +@with_phases([CAPELLA]) +@with_custom_state( + balances_fn=misc_balances_in_default_range_with_many_validators, + threshold_fn=zero_activation_threshold +) +@spec_test +@single_phase +@always_bls +def test_randomized_15(spec, state): + # scenario as high-level, informal text: + # epochs:epochs_until_leak,slots:0,with-block:no_block + # epochs:1,slots:0,with-block:no_block + # epochs:0,slots:0,with-block:no_block + # epochs:0,slots:0,with-block:random_block_capella + # epochs:1,slots:0,with-block:no_block + # epochs:0,slots:0,with-block:no_block + # epochs:0,slots:0,with-block:random_block_capella + scenario = {'transitions': [{'epochs_to_skip': 'epochs_until_leak', 'validation': 'validate_is_leaking', 'slots_to_skip': 0, 'block_producer': 'no_block'}, {'epochs_to_skip': 1, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 0, 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_capella', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}, {'epochs_to_skip': 1, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 0, 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_capella', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_capella'} # noqa: E501 + yield from run_generated_randomized_test( + spec, + state, + scenario, + ) diff --git a/tests/core/pyspec/eth2spec/test/helpers/capella/__init__.py b/tests/core/pyspec/eth2spec/test/helpers/capella/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/core/pyspec/eth2spec/test/utils/randomized_block_tests.py b/tests/core/pyspec/eth2spec/test/utils/randomized_block_tests.py index 386b4c8b23..c66970ba72 100644 --- a/tests/core/pyspec/eth2spec/test/utils/randomized_block_tests.py +++ b/tests/core/pyspec/eth2spec/test/utils/randomized_block_tests.py @@ -71,6 +71,15 @@ def randomize_state_bellatrix(spec, state, stats, exit_fraction=0.1, slash_fract return scenario_state +def randomize_state_capella(spec, state, stats, exit_fraction=0.1, slash_fraction=0.1): + scenario_state = randomize_state_bellatrix(spec, + state, + stats, + exit_fraction=exit_fraction, + slash_fraction=slash_fraction) + return scenario_state + + # epochs def epochs_until_leak(spec): @@ -195,6 +204,11 @@ def random_block_bellatrix(spec, state, signed_blocks, scenario_state): return block +def random_block_capella(spec, state, signed_blocks, scenario_state): + block = random_block_bellatrix(spec, state, signed_blocks, scenario_state) + return block + + # validations def no_op_validation(_spec, _state): diff --git a/tests/generators/random/Makefile b/tests/generators/random/Makefile index 607d69a470..8b77a36177 100644 --- a/tests/generators/random/Makefile +++ b/tests/generators/random/Makefile @@ -5,6 +5,8 @@ all: rm -f ../../core/pyspec/eth2spec/test/phase0/random/test_random.py rm -f ../../core/pyspec/eth2spec/test/altair/random/test_random.py rm -f ../../core/pyspec/eth2spec/test/bellatrix/random/test_random.py + rm -f ../../core/pyspec/eth2spec/test/capella/random/test_random.py python3 generate.py phase0 > ../../core/pyspec/eth2spec/test/phase0/random/test_random.py python3 generate.py altair > ../../core/pyspec/eth2spec/test/altair/random/test_random.py python3 generate.py bellatrix > ../../core/pyspec/eth2spec/test/bellatrix/random/test_random.py + python3 generate.py capella > ../../core/pyspec/eth2spec/test/capella/random/test_random.py diff --git a/tests/generators/random/generate.py b/tests/generators/random/generate.py index f71595c02e..39d43b0aa0 100644 --- a/tests/generators/random/generate.py +++ b/tests/generators/random/generate.py @@ -20,9 +20,11 @@ randomize_state, randomize_state_altair, randomize_state_bellatrix, + randomize_state_capella, random_block, random_block_altair_with_cycling_sync_committee_participation, random_block_bellatrix, + random_block_capella, last_slot_in_epoch, random_slot_in_epoch, penultimate_slot_in_epoch, @@ -32,7 +34,7 @@ transition_to_leaking, transition_without_leak, ) -from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX +from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA # Ensure this many blocks are present in *each* randomized scenario @@ -263,5 +265,12 @@ def run_generate_tests_to_std_out(phase, state_randomizer, block_randomizer): state_randomizer=randomize_state_bellatrix, block_randomizer=random_block_bellatrix, ) + if CAPELLA in sys.argv: + did_generate = True + run_generate_tests_to_std_out( + CAPELLA, + state_randomizer=randomize_state_capella, + block_randomizer=random_block_capella, + ) if not did_generate: warnings.warn("no phase given for test generation") From 940fc207587ae7c6f3db946928ec7c5900aa02ab Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Mon, 17 Oct 2022 23:21:15 +0800 Subject: [PATCH 21/35] Apply suggestions from code review Co-authored-by: Danny Ryan --- tests/generators/fork_choice/main.py | 2 +- tests/generators/genesis/main.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/generators/fork_choice/main.py b/tests/generators/fork_choice/main.py index 2c95b70e55..7982902c54 100644 --- a/tests/generators/fork_choice/main.py +++ b/tests/generators/fork_choice/main.py @@ -17,7 +17,7 @@ 'on_merge_block', ]} bellatrix_mods = combine_mods(_new_bellatrix_mods, altair_mods) - capella_mods = bellatrix_mods + capella_mods = bellatrix_mods # No additional Capella specific fork choice tests all_mods = { PHASE0: phase_0_mods, ALTAIR: altair_mods, diff --git a/tests/generators/genesis/main.py b/tests/generators/genesis/main.py index e71b154012..c8f5a83d54 100644 --- a/tests/generators/genesis/main.py +++ b/tests/generators/genesis/main.py @@ -15,7 +15,7 @@ 'initialization', ]} bellatrix_mods = combine_mods(_new_bellatrix_mods, altair_mods) - capella_mods = bellatrix_mods + capella_mods = bellatrix_mods # No additional Capella specific genesis tests all_mods = { PHASE0: phase_0_mods, ALTAIR: altair_mods, From eb5a2c2741aa49ffb545181657780d0e75380c78 Mon Sep 17 00:00:00 2001 From: terence tsao Date: Mon, 17 Oct 2022 15:16:38 -0700 Subject: [PATCH 22/35] Couple beacon block and blob sidecar for p2p --- specs/eip4844/p2p-interface.md | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/specs/eip4844/p2p-interface.md b/specs/eip4844/p2p-interface.md index eab8815a4a..7757ed9c2d 100644 --- a/specs/eip4844/p2p-interface.md +++ b/specs/eip4844/p2p-interface.md @@ -58,6 +58,14 @@ class SignedBlobsSidecar(Container): signature: BLSSignature ``` +### `SignedBeaconBlockAndBlobsSidecar` + +```python +class SignedBeaconBlockAndBlobsSidecar(Container): + beacon_block: SignedBeaconBlock + blobs_sidecar: SignedBlobsSidecar +``` + ## The gossip domain: gossipsub Some gossip meshes are upgraded in the fork of EIP4844 to support upgraded types. @@ -75,33 +83,25 @@ The new topics along with the type of the `data` field of a gossipsub message ar | Name | Message Type | | - | - | -| `beacon_block` | `SignedBeaconBlock` (modified) | -| `blobs_sidecar` | `SignedBlobsSidecar` (new) | +| `beacon_block_and_blob_sidecar` | `SignedBeaconBlockAndBlobsSidecar` (new) | -Note that the `ForkDigestValue` path segment of the topic separates the old and the new `beacon_block` topics. #### Global topics -EIP4844 changes the type of the global beacon block topic and introduces a new global topic for blobs-sidecars. +EIP4844 introduces a new global topic for beacon block and blobs-sidecars. -##### `beacon_block` +##### `beacon_block_and_blob_sidecar` -The *type* of the payload of this topic changes to the (modified) `SignedBeaconBlock` found in EIP4844. +This topic is used to propagate new signed and coupled beacon blocks and blobs sidecars to all nodes on the networks. -In addition to the gossip validations for this topic from prior specifications, -the following validations MUST pass before forwarding the `signed_beacon_block` on the network. -Alias `block = signed_beacon_block.message`, `execution_payload = block.body.execution_payload`. +The following validations MUST pass before forwarding the `signed_beacon_block_and_blobs_sidecar` on the network; +Alias `signed_beacon_block = signed_beacon_block_and_blobs_sidecar.beacon_block`, `block = signed_beacon_block.message`, `execution_payload = block.body.execution_payload`. - _[REJECT]_ The KZG commitments of the blobs are all correctly encoded compressed BLS G1 Points. -- i.e. `all(bls.KeyValidate(commitment) for commitment in block.body.blob_kzg_commitments)` - _[REJECT]_ The KZG commitments correspond to the versioned hashes in the transactions list. -- i.e. `verify_kzg_commitments_against_transactions(block.body.execution_payload.transactions, block.body.blob_kzg_commitments)` -##### `blobs_sidecar` - -This topic is used to propagate data blobs included in any given beacon block. - -The following validations MUST pass before forwarding the `signed_blobs_sidecar` on the network; -Alias `sidecar = signed_blobs_sidecar.message`. +Alias `sidecar = signed_beacon_block_and_blobs_sidecar.blobs_sidecar.message`. - _[IGNORE]_ the `sidecar.beacon_block_slot` is for the current slot (with a `MAXIMUM_GOSSIP_CLOCK_DISPARITY` allowance) -- i.e. `sidecar.beacon_block_slot == current_slot`. - _[REJECT]_ the `sidecar.blobs` are all well formatted, i.e. the `BLSFieldElement` in valid range (`x < BLS_MODULUS`). - _[REJECT]_ The KZG proof is a correctly encoded compressed BLS G1 Point -- i.e. `bls.KeyValidate(blobs_sidecar.kzg_aggregated_proof)` @@ -113,10 +113,9 @@ Alias `sidecar = signed_blobs_sidecar.message`. - _[IGNORE]_ The sidecar is the first sidecar with valid signature received for the `(proposer_index, sidecar.beacon_block_slot)` combination, where `proposer_index` is the validator index of the beacon block proposer of `sidecar.beacon_block_slot` -Note that a sidecar may be propagated before or after the corresponding beacon block. - Once both sidecar and beacon block are received, `validate_blobs_sidecar` can unlock the data-availability fork-choice dependency. + ### Transitioning the gossip See gossip transition details found in the [Altair document](../altair/p2p-interface.md#transitioning-the-gossip) for From 96b90200b79cd4dab7821c544821abb8c197aa26 Mon Sep 17 00:00:00 2001 From: terence tsao Date: Mon, 17 Oct 2022 15:59:48 -0700 Subject: [PATCH 23/35] Fix ToC --- specs/eip4844/p2p-interface.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/specs/eip4844/p2p-interface.md b/specs/eip4844/p2p-interface.md index 7757ed9c2d..7993707489 100644 --- a/specs/eip4844/p2p-interface.md +++ b/specs/eip4844/p2p-interface.md @@ -14,11 +14,11 @@ The specification of these changes continues in the same format as the network s - [Containers](#containers) - [`BlobsSidecar`](#blobssidecar) - [`SignedBlobsSidecar`](#signedblobssidecar) + - [`SignedBeaconBlockAndBlobsSidecar`](#signedbeaconblockandblobssidecar) - [The gossip domain: gossipsub](#the-gossip-domain-gossipsub) - [Topics and messages](#topics-and-messages) - [Global topics](#global-topics) - - [`beacon_block`](#beacon_block) - - [`blobs_sidecar`](#blobs_sidecar) + - [`beacon_block_and_blob_sidecar`](#beacon_block_and_blob_sidecar) - [Transitioning the gossip](#transitioning-the-gossip) - [The Req/Resp domain](#the-reqresp-domain) - [Messages](#messages) @@ -94,14 +94,14 @@ EIP4844 introduces a new global topic for beacon block and blobs-sidecars. This topic is used to propagate new signed and coupled beacon blocks and blobs sidecars to all nodes on the networks. -The following validations MUST pass before forwarding the `signed_beacon_block_and_blobs_sidecar` on the network; +The following validations MUST pass before forwarding the `signed_beacon_block_and_blobs_sidecar` on the network. Alias `signed_beacon_block = signed_beacon_block_and_blobs_sidecar.beacon_block`, `block = signed_beacon_block.message`, `execution_payload = block.body.execution_payload`. - _[REJECT]_ The KZG commitments of the blobs are all correctly encoded compressed BLS G1 Points. -- i.e. `all(bls.KeyValidate(commitment) for commitment in block.body.blob_kzg_commitments)` - _[REJECT]_ The KZG commitments correspond to the versioned hashes in the transactions list. -- i.e. `verify_kzg_commitments_against_transactions(block.body.execution_payload.transactions, block.body.blob_kzg_commitments)` -Alias `sidecar = signed_beacon_block_and_blobs_sidecar.blobs_sidecar.message`. +Alias `signed_blobs_sidecar = signed_beacon_block_and_blobs_sidecar.blobs_sidecar`, `sidecar = signed_blobs_sidecar.message`. - _[IGNORE]_ the `sidecar.beacon_block_slot` is for the current slot (with a `MAXIMUM_GOSSIP_CLOCK_DISPARITY` allowance) -- i.e. `sidecar.beacon_block_slot == current_slot`. - _[REJECT]_ the `sidecar.blobs` are all well formatted, i.e. the `BLSFieldElement` in valid range (`x < BLS_MODULUS`). - _[REJECT]_ The KZG proof is a correctly encoded compressed BLS G1 Point -- i.e. `bls.KeyValidate(blobs_sidecar.kzg_aggregated_proof)` @@ -115,7 +115,6 @@ Alias `sidecar = signed_beacon_block_and_blobs_sidecar.blobs_sidecar.message`. Once both sidecar and beacon block are received, `validate_blobs_sidecar` can unlock the data-availability fork-choice dependency. - ### Transitioning the gossip See gossip transition details found in the [Altair document](../altair/p2p-interface.md#transitioning-the-gossip) for From b128148a17f04df8c37ac4c66907d686d266496e Mon Sep 17 00:00:00 2001 From: terence tsao Date: Mon, 17 Oct 2022 16:24:19 -0700 Subject: [PATCH 24/35] Modify `MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS` to 18 days --- specs/eip4844/p2p-interface.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/specs/eip4844/p2p-interface.md b/specs/eip4844/p2p-interface.md index eab8815a4a..5f9f1fb1d7 100644 --- a/specs/eip4844/p2p-interface.md +++ b/specs/eip4844/p2p-interface.md @@ -33,10 +33,10 @@ The specification of these changes continues in the same format as the network s ## Configuration -| Name | Value | Description | -|------------------------------------------|-------------------------------------|---------------------------------------------------------------------| -| `MAX_REQUEST_BLOBS_SIDECARS` | `2**7` (= 128) | Maximum number of blobs sidecars in a single request | -| `MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS` | `2**13` (= 8192 epochs, ~1.2 months) | The minimum epoch range over which a node must serve blobs sidecars | +| Name | Value | Description | +|------------------------------------------|-----------------------------------|---------------------------------------------------------------------| +| `MAX_REQUEST_BLOBS_SIDECARS` | `2**7` (= 128) | Maximum number of blobs sidecars in a single request | +| `MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS` | `2**12` (= 4096 epochs, ~18 days) | The minimum epoch range over which a node must serve blobs sidecars | ## Containers From 0345b2284a8756acd58a3dd0b0029c459c41f710 Mon Sep 17 00:00:00 2001 From: Jim McDonald Date: Tue, 18 Oct 2022 15:35:06 +0100 Subject: [PATCH 25/35] Add validator_index to Withdrawal. --- specs/capella/beacon-chain.md | 2 ++ .../test/capella/block_processing/test_process_withdrawals.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/specs/capella/beacon-chain.md b/specs/capella/beacon-chain.md index 40592a9bde..5a5fd42e8b 100644 --- a/specs/capella/beacon-chain.md +++ b/specs/capella/beacon-chain.md @@ -111,6 +111,7 @@ We define the following Python custom types for type hinting and readability: ```python class Withdrawal(Container): index: WithdrawalIndex + validator_index: ValidatorIndex address: ExecutionAddress amount: Gwei ``` @@ -275,6 +276,7 @@ def withdraw_balance(state: BeaconState, validator_index: ValidatorIndex, amount # Create a corresponding withdrawal receipt withdrawal = Withdrawal( index=state.next_withdrawal_index, + validator_index=validator_index, address=ExecutionAddress(state.validators[validator_index].withdrawal_credentials[12:]), amount=amount, ) diff --git a/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_withdrawals.py b/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_withdrawals.py index 26ace24b3a..29b1bb7c9b 100644 --- a/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_withdrawals.py +++ b/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_withdrawals.py @@ -13,6 +13,7 @@ def prepare_withdrawal_queue(spec, state, num_withdrawals): for i in range(num_withdrawals): withdrawal = spec.Withdrawal( index=i + 5, + validator_index=i + 1000, address=b'\x42' * 20, amount=200000 + i, ) @@ -110,6 +111,7 @@ def test_fail_empty_queue_non_empty_withdrawals(spec, state): execution_payload = build_empty_execution_payload(spec, state) withdrawal = spec.Withdrawal( index=0, + validator_index=0, address=b'\x30' * 20, amount=420, ) From 779d9be66a3eb1231a35a52299216253b8ed4d6a Mon Sep 17 00:00:00 2001 From: terencechain Date: Tue, 18 Oct 2022 11:22:43 -0700 Subject: [PATCH 26/35] Compare sidecar slot with block slot Co-authored-by: protolambda --- specs/eip4844/p2p-interface.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/eip4844/p2p-interface.md b/specs/eip4844/p2p-interface.md index 7993707489..4856dd59c9 100644 --- a/specs/eip4844/p2p-interface.md +++ b/specs/eip4844/p2p-interface.md @@ -102,7 +102,7 @@ Alias `signed_beacon_block = signed_beacon_block_and_blobs_sidecar.beacon_block` -- i.e. `verify_kzg_commitments_against_transactions(block.body.execution_payload.transactions, block.body.blob_kzg_commitments)` Alias `signed_blobs_sidecar = signed_beacon_block_and_blobs_sidecar.blobs_sidecar`, `sidecar = signed_blobs_sidecar.message`. -- _[IGNORE]_ the `sidecar.beacon_block_slot` is for the current slot (with a `MAXIMUM_GOSSIP_CLOCK_DISPARITY` allowance) -- i.e. `sidecar.beacon_block_slot == current_slot`. +- _[IGNORE]_ the `sidecar.beacon_block_slot` is for the current slot (with a `MAXIMUM_GOSSIP_CLOCK_DISPARITY` allowance) -- i.e. `sidecar.beacon_block_slot == block.slot`. - _[REJECT]_ the `sidecar.blobs` are all well formatted, i.e. the `BLSFieldElement` in valid range (`x < BLS_MODULUS`). - _[REJECT]_ The KZG proof is a correctly encoded compressed BLS G1 Point -- i.e. `bls.KeyValidate(blobs_sidecar.kzg_aggregated_proof)` - _[REJECT]_ the beacon proposer signature, `signed_blobs_sidecar.signature`, is valid -- i.e. From cab2e3ef9a5978e87b773894118ab8496c5fc79b Mon Sep 17 00:00:00 2001 From: terencechain Date: Tue, 18 Oct 2022 11:23:19 -0700 Subject: [PATCH 27/35] Describe sidecar and block are recieved together Co-authored-by: protolambda --- specs/eip4844/p2p-interface.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/eip4844/p2p-interface.md b/specs/eip4844/p2p-interface.md index 4856dd59c9..42aa9e9070 100644 --- a/specs/eip4844/p2p-interface.md +++ b/specs/eip4844/p2p-interface.md @@ -113,7 +113,7 @@ Alias `signed_blobs_sidecar = signed_beacon_block_and_blobs_sidecar.blobs_sideca - _[IGNORE]_ The sidecar is the first sidecar with valid signature received for the `(proposer_index, sidecar.beacon_block_slot)` combination, where `proposer_index` is the validator index of the beacon block proposer of `sidecar.beacon_block_slot` -Once both sidecar and beacon block are received, `validate_blobs_sidecar` can unlock the data-availability fork-choice dependency. +Once the sidecar and beacon block are received together, `validate_blobs_sidecar` can unlock the data-availability fork-choice dependency. ### Transitioning the gossip From dfa5ac80080475e05fc1e9d0c236c719ed853557 Mon Sep 17 00:00:00 2001 From: terence tsao Date: Tue, 18 Oct 2022 11:34:14 -0700 Subject: [PATCH 28/35] @protolambda's feedback, `SignedBlobsSidecar` -> `BlobsSidecar` --- specs/eip4844/p2p-interface.md | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/specs/eip4844/p2p-interface.md b/specs/eip4844/p2p-interface.md index 42aa9e9070..735230027f 100644 --- a/specs/eip4844/p2p-interface.md +++ b/specs/eip4844/p2p-interface.md @@ -63,7 +63,7 @@ class SignedBlobsSidecar(Container): ```python class SignedBeaconBlockAndBlobsSidecar(Container): beacon_block: SignedBeaconBlock - blobs_sidecar: SignedBlobsSidecar + blobs_sidecar: BlobsSidecar ``` ## The gossip domain: gossipsub @@ -101,17 +101,10 @@ Alias `signed_beacon_block = signed_beacon_block_and_blobs_sidecar.beacon_block` - _[REJECT]_ The KZG commitments correspond to the versioned hashes in the transactions list. -- i.e. `verify_kzg_commitments_against_transactions(block.body.execution_payload.transactions, block.body.blob_kzg_commitments)` -Alias `signed_blobs_sidecar = signed_beacon_block_and_blobs_sidecar.blobs_sidecar`, `sidecar = signed_blobs_sidecar.message`. +Alias `sidecar = signed_beacon_block_and_blobs_sidecar.blobs_sidecar`. - _[IGNORE]_ the `sidecar.beacon_block_slot` is for the current slot (with a `MAXIMUM_GOSSIP_CLOCK_DISPARITY` allowance) -- i.e. `sidecar.beacon_block_slot == block.slot`. - _[REJECT]_ the `sidecar.blobs` are all well formatted, i.e. the `BLSFieldElement` in valid range (`x < BLS_MODULUS`). - _[REJECT]_ The KZG proof is a correctly encoded compressed BLS G1 Point -- i.e. `bls.KeyValidate(blobs_sidecar.kzg_aggregated_proof)` -- _[REJECT]_ the beacon proposer signature, `signed_blobs_sidecar.signature`, is valid -- i.e. - - Let `domain = get_domain(state, DOMAIN_BLOBS_SIDECAR, sidecar.beacon_block_slot // SLOTS_PER_EPOCH)` - - Let `signing_root = compute_signing_root(sidecar, domain)` - - Verify `bls.Verify(proposer_pubkey, signing_root, signed_blobs_sidecar.signature) is True`, - where `proposer_pubkey` is the pubkey of the beacon block proposer of `sidecar.beacon_block_slot` -- _[IGNORE]_ The sidecar is the first sidecar with valid signature received for the `(proposer_index, sidecar.beacon_block_slot)` combination, - where `proposer_index` is the validator index of the beacon block proposer of `sidecar.beacon_block_slot` Once the sidecar and beacon block are received together, `validate_blobs_sidecar` can unlock the data-availability fork-choice dependency. From d0133796288e893c3c69cc21f4a01c80a7de5ecb Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Wed, 19 Oct 2022 10:27:01 -0500 Subject: [PATCH 29/35] Ensure `validator_index` is in validator set --- .../test/capella/block_processing/test_process_withdrawals.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_withdrawals.py b/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_withdrawals.py index 73ee9fb398..9bf70b56dd 100644 --- a/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_withdrawals.py +++ b/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_withdrawals.py @@ -9,11 +9,11 @@ def prepare_withdrawal_queue(spec, state, num_withdrawals): pre_queue_len = len(state.withdrawal_queue) - + validator_len = len(state.validators) for i in range(num_withdrawals): withdrawal = spec.Withdrawal( index=i + 5, - validator_index=i + 1000, + validator_index=(i + 1000) % validator_len, address=b'\x42' * 20, amount=200000 + i, ) From 0e2c3d89e020f43e0c2af1c246ec772ec54ee250 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Thu, 20 Oct 2022 22:22:04 -0500 Subject: [PATCH 30/35] Fill `bls_to_execution_changes` field in Capella random tests --- .../eth2spec/test/helpers/multi_operations.py | 19 ++++++++++++++++++- .../test/utils/randomized_block_tests.py | 8 +++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/helpers/multi_operations.py b/tests/core/pyspec/eth2spec/test/helpers/multi_operations.py index 44ed0ae897..c11bb55842 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/multi_operations.py +++ b/tests/core/pyspec/eth2spec/test/helpers/multi_operations.py @@ -16,6 +16,7 @@ from eth2spec.test.helpers.attestations import get_valid_attestation 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 def run_slash_and_exit(spec, state, slash_index, exit_index, valid=True): @@ -126,13 +127,15 @@ def get_random_deposits(spec, state, rng, num_deposits=None): # First build deposit data leaves for i in range(num_deposits): index = len(state.validators) + i + withdrawal_pubkey = pubkeys[-1 - index] + withdrawal_credentials = spec.BLS_WITHDRAWAL_PREFIX + spec.hash(withdrawal_pubkey)[1:] _, root, deposit_data_leaves = build_deposit( spec, deposit_data_leaves, pubkeys[index], privkeys[index], spec.MAX_EFFECTIVE_BALANCE, - withdrawal_credentials=b'\x00' * 32, + withdrawal_credentials=withdrawal_credentials, signed=True, ) @@ -200,6 +203,20 @@ def get_random_sync_aggregate(spec, state, slot, block_root=None, fraction_parti ) +def get_random_bls_to_execution_changes(spec, state, rng=Random(2188), num_address_changes=0): + bls_indices = [ + index + for index, validator in enumerate(state.validators) + if validator.withdrawal_credentials[:1] == spec.BLS_WITHDRAWAL_PREFIX + ] + assert len(bls_indices) > 0 + + return [ + get_signed_address_change(spec, state, validator_index=validator_index) + for validator_index in rng.sample(bls_indices, min(num_address_changes, len(bls_indices))) + ] + + def build_random_block_from_state_for_next_slot(spec, state, rng=Random(2188), deposits=None): block = build_empty_block_for_next_slot(spec, state) proposer_slashings = get_random_proposer_slashings(spec, state, rng) diff --git a/tests/core/pyspec/eth2spec/test/utils/randomized_block_tests.py b/tests/core/pyspec/eth2spec/test/utils/randomized_block_tests.py index c66970ba72..5504a53d7b 100644 --- a/tests/core/pyspec/eth2spec/test/utils/randomized_block_tests.py +++ b/tests/core/pyspec/eth2spec/test/utils/randomized_block_tests.py @@ -9,6 +9,7 @@ from eth2spec.test.helpers.multi_operations import ( build_random_block_from_state_for_next_slot, + get_random_bls_to_execution_changes, get_random_sync_aggregate, prepare_state_and_get_random_deposits, ) @@ -204,8 +205,13 @@ def random_block_bellatrix(spec, state, signed_blocks, scenario_state): return block -def random_block_capella(spec, state, signed_blocks, scenario_state): +def random_block_capella(spec, state, signed_blocks, scenario_state, rng=Random(3456)): block = random_block_bellatrix(spec, state, signed_blocks, scenario_state) + block.body.bls_to_execution_changes = get_random_bls_to_execution_changes( + spec, + state, + num_address_changes=rng.randint(1, spec.MAX_BLS_TO_EXECUTION_CHANGES) + ) return block From f48d6b324d8c23204b9868b1af6bf7cec935127b Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Thu, 20 Oct 2022 22:27:49 -0500 Subject: [PATCH 31/35] Update test formats --- tests/formats/epoch_processing/README.md | 2 ++ tests/formats/operations/README.md | 1 + 2 files changed, 3 insertions(+) diff --git a/tests/formats/epoch_processing/README.md b/tests/formats/epoch_processing/README.md index e0e6dc3675..41d2a102a3 100644 --- a/tests/formats/epoch_processing/README.md +++ b/tests/formats/epoch_processing/README.md @@ -45,5 +45,7 @@ Sub-transitions: - `participation_record_updates` (Phase 0 only) - `participation_flag_updates` (Altair) - `sync_committee_updates` (Altair) +- `full_withdrawals` (Capella) +- `partial_withdrawals` (Capella) The resulting state should match the expected `post` state. diff --git a/tests/formats/operations/README.md b/tests/formats/operations/README.md index e532c5047c..ba2c64b3a6 100644 --- a/tests/formats/operations/README.md +++ b/tests/formats/operations/README.md @@ -43,6 +43,7 @@ Operations: | `voluntary_exit` | `SignedVoluntaryExit` | `voluntary_exit` | `process_voluntary_exit(state, voluntary_exit)` | | `sync_aggregate` | `SyncAggregate` | `sync_aggregate` | `process_sync_aggregate(state, sync_aggregate)` (new in Altair) | | `execution_payload` | `ExecutionPayload` | `execution_payload` | `process_execution_payload(state, execution_payload)` (new in Bellatrix) | +| `bls_to_execution_change` | `SignedBLSToExecutionChange` | `signed_address_change` | `process_bls_to_execution_change(state, signed_address_change)` (new in Capella) | Note that `block_header` is not strictly an operation (and is a full `Block`), but processed in the same manner, and hence included here. From 9f4ae4c6eeb281a76788900ae2552a59a7f55eb1 Mon Sep 17 00:00:00 2001 From: terence tsao Date: Fri, 21 Oct 2022 17:33:55 -0700 Subject: [PATCH 32/35] Update validator spec with `SignedBeaconBlockAndBlobsSidecar` --- specs/eip4844/p2p-interface.md | 6 +++--- specs/eip4844/validator.md | 33 +++++++++++++-------------------- 2 files changed, 16 insertions(+), 23 deletions(-) diff --git a/specs/eip4844/p2p-interface.md b/specs/eip4844/p2p-interface.md index 735230027f..2162ab1611 100644 --- a/specs/eip4844/p2p-interface.md +++ b/specs/eip4844/p2p-interface.md @@ -18,7 +18,7 @@ The specification of these changes continues in the same format as the network s - [The gossip domain: gossipsub](#the-gossip-domain-gossipsub) - [Topics and messages](#topics-and-messages) - [Global topics](#global-topics) - - [`beacon_block_and_blob_sidecar`](#beacon_block_and_blob_sidecar) + - [`beacon_block_and_blobs_sidecar`](#beacon_block_and_blobs_sidecar) - [Transitioning the gossip](#transitioning-the-gossip) - [The Req/Resp domain](#the-reqresp-domain) - [Messages](#messages) @@ -83,14 +83,14 @@ The new topics along with the type of the `data` field of a gossipsub message ar | Name | Message Type | | - | - | -| `beacon_block_and_blob_sidecar` | `SignedBeaconBlockAndBlobsSidecar` (new) | +| `beacon_block_and_blobs_sidecar` | `SignedBeaconBlockAndBlobsSidecar` (new) | #### Global topics EIP4844 introduces a new global topic for beacon block and blobs-sidecars. -##### `beacon_block_and_blob_sidecar` +##### `beacon_block_and_blobs_sidecar` This topic is used to propagate new signed and coupled beacon blocks and blobs sidecars to all nodes on the networks. diff --git a/specs/eip4844/validator.md b/specs/eip4844/validator.md index e1bcf9564a..18623659ad 100644 --- a/specs/eip4844/validator.md +++ b/specs/eip4844/validator.md @@ -192,9 +192,9 @@ def get_blobs_and_kzg_commitments(payload_id: PayloadId) -> Tuple[Sequence[BLSFi ## Beacon chain responsibilities All validator responsibilities remain unchanged other than those noted below. -Namely, the blob handling and the addition of `BlobsSidecar`. +Namely, the blob handling and the addition of `SignedBeaconBlockAndBlobsSidecar`. -### Block proposal +### Block and sidecar proposal #### Constructing the `BeaconBlockBody` @@ -218,13 +218,16 @@ def validate_blobs_and_kzg_commitments(execution_payload: ExecutionPayload, 3. If valid, set `block.body.blob_kzg_commitments = blob_kzg_commitments`. -Note that the `blobs` should be held with the block in preparation of publishing. -Without the `blobs`, the published block will effectively be ignored by honest validators. +#### Constructing the `SignedBeaconBlockAndBlobsSidecar` +To construct a `SignedBeaconBlockAndBlobsSidecar`, a `signed_beacon_block_and_blobs_sidecar` is defined with the necessary context for block and sidecar proposal. -### Beacon Block publishing time +##### Block +Set `signed_beacon_block_and_blobs_sidecar.beacon_block = block` where `block` is obtained above. -Before publishing a prepared beacon block proposal, the corresponding blobs are packaged into a sidecar object for distribution to the network: +##### Sidecar +Coupled with block, the corresponding blobs are packaged into a sidecar object for distribution to the network. +Set `signed_beacon_block_and_blobs_sidecar.blobs_sidecar = sidecar` where `sidecar` is obtained from: ```python def get_blobs_sidecar(block: BeaconBlock, blobs: Sequence[Blob]) -> BlobsSidecar: return BlobsSidecar( @@ -235,20 +238,10 @@ def get_blobs_sidecar(block: BeaconBlock, blobs: Sequence[Blob]) -> BlobsSidecar ) ``` -And then signed: +This `signed_beacon_block_and_blobs_sidecar` is then published to the global `beacon_block_and_blobs_sidecar` topic. -```python -def get_signed_blobs_sidecar(state: BeaconState, blobs_sidecar: BlobsSidecar, privkey: int) -> SignedBlobsSidecar: - domain = get_domain(state, DOMAIN_BLOBS_SIDECAR, blobs_sidecar.beacon_block_slot // SLOTS_PER_EPOCH) - signing_root = compute_signing_root(blobs_sidecar, domain) - signature = bls.Sign(privkey, signing_root) - return SignedBlobsSidecar(message=blobs_sidecar, signature=signature) -``` - -This `signed_blobs_sidecar` is then published to the global `blobs_sidecar` topic as soon as the `signed_beacon_block` is published. - -After publishing the sidecar peers on the network may request the sidecar through sync-requests, or a local user may be interested. -The validator MUST hold on to blobs for `MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS` epochs and serve when capable, +After publishing the peers on the network may request the sidecar through sync-requests, or a local user may be interested. +The validator MUST hold on to sidecars for `MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS` epochs and serve when capable, to ensure the data-availability of these blobs throughout the network. -After `MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS` nodes MAY prune the blobs and/or stop serving them. +After `MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS` nodes MAY prune the sidecars and/or stop serving them. From e2d0a4fa9a64ee5f08e116a2b31f38c30527dab5 Mon Sep 17 00:00:00 2001 From: terence tsao Date: Sat, 22 Oct 2022 08:13:14 -0700 Subject: [PATCH 33/35] Fix ToC and test --- specs/eip4844/validator.md | 6 ++++-- .../test/eip4844/unittests/validator/test_validator.py | 3 --- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/specs/eip4844/validator.md b/specs/eip4844/validator.md index 18623659ad..0cd420637f 100644 --- a/specs/eip4844/validator.md +++ b/specs/eip4844/validator.md @@ -23,10 +23,12 @@ - [`compute_proof_from_blobs`](#compute_proof_from_blobs) - [`get_blobs_and_kzg_commitments`](#get_blobs_and_kzg_commitments) - [Beacon chain responsibilities](#beacon-chain-responsibilities) - - [Block proposal](#block-proposal) + - [Block and sidecar proposal](#block-and-sidecar-proposal) - [Constructing the `BeaconBlockBody`](#constructing-the-beaconblockbody) - [Blob KZG commitments](#blob-kzg-commitments) - - [Beacon Block publishing time](#beacon-block-publishing-time) + - [Constructing the `SignedBeaconBlockAndBlobsSidecar`](#constructing-the-signedbeaconblockandblobssidecar) + - [Block](#block) + - [Sidecar](#sidecar) diff --git a/tests/core/pyspec/eth2spec/test/eip4844/unittests/validator/test_validator.py b/tests/core/pyspec/eth2spec/test/eip4844/unittests/validator/test_validator.py index d0250f3df7..680a0a9c4c 100644 --- a/tests/core/pyspec/eth2spec/test/eip4844/unittests/validator/test_validator.py +++ b/tests/core/pyspec/eth2spec/test/eip4844/unittests/validator/test_validator.py @@ -12,7 +12,6 @@ get_sample_opaque_tx, get_sample_blob, ) -from eth2spec.test.helpers.keys import privkeys @with_eip4844_and_later @@ -38,8 +37,6 @@ def _run_validate_blobs_sidecar_test(spec, state, blob_count): state_transition_and_sign_block(spec, state, block) blobs_sidecar = spec.get_blobs_sidecar(block, blobs) - privkey = privkeys[1] - spec.get_signed_blobs_sidecar(state, blobs_sidecar, privkey) expected_commitments = [spec.blob_to_kzg_commitment(blobs[i]) for i in range(blob_count)] spec.validate_blobs_sidecar(block.slot, block.hash_tree_root(), expected_commitments, blobs_sidecar) From 3c7c3a9c973935850c63e35e52e6acbd75b30e0d Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Tue, 25 Oct 2022 11:45:04 +0800 Subject: [PATCH 34/35] bump VERSION.txt to 1.3.0-alpha.0 (#3056) --- tests/core/pyspec/eth2spec/VERSION.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/core/pyspec/eth2spec/VERSION.txt b/tests/core/pyspec/eth2spec/VERSION.txt index 26aaba0e86..000e3207c2 100644 --- a/tests/core/pyspec/eth2spec/VERSION.txt +++ b/tests/core/pyspec/eth2spec/VERSION.txt @@ -1 +1 @@ -1.2.0 +1.3.0-alpha.0 From 6c7922df672aac25a3a0238db25de37a63eaacf5 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Wed, 26 Oct 2022 11:03:16 -0500 Subject: [PATCH 35/35] Fix bls test case file name --- tests/generators/bls/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/generators/bls/main.py b/tests/generators/bls/main.py index dca5ae7dd0..8c187c68c1 100644 --- a/tests/generators/bls/main.py +++ b/tests/generators/bls/main.py @@ -455,7 +455,7 @@ def case07_eth_fast_aggregate_verify(): tampered_signature = aggregate_signature[:-4] + b'\xff\xff\xff\xff' identifier = f'{pubkeys_serial}_{encode_hex(message)}' assert not spec.eth_fast_aggregate_verify(pubkeys, message, tampered_signature) - yield 'eth_fast_aggregate_verify_tampered_signature_{(hash(bytes(identifier, "utf-8"))[:8]).hex()}', { + yield f'eth_fast_aggregate_verify_tampered_signature_{(hash(bytes(identifier, "utf-8"))[:8]).hex()}', { 'input': { 'pubkeys': pubkeys_serial, 'message': encode_hex(message),