From f37f9a367d4f55957ab70c59e6502993cf43ad3a Mon Sep 17 00:00:00 2001 From: protolambda Date: Wed, 5 May 2021 00:15:15 +0200 Subject: [PATCH 01/17] include merge in generators --- tests/generators/epoch_processing/main.py | 10 ++++++++-- tests/generators/finality/main.py | 9 ++++++--- tests/generators/fork_choice/main.py | 8 ++++++-- tests/generators/operations/main.py | 13 +++++++++++-- tests/generators/rewards/main.py | 11 +++++++++-- tests/generators/sanity/main.py | 11 +++++++++-- tests/generators/ssz_static/main.py | 2 +- 7 files changed, 50 insertions(+), 14 deletions(-) diff --git a/tests/generators/epoch_processing/main.py b/tests/generators/epoch_processing/main.py index 9efd96534b..2aa6381ffc 100644 --- a/tests/generators/epoch_processing/main.py +++ b/tests/generators/epoch_processing/main.py @@ -1,10 +1,11 @@ from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators from eth2spec.phase0 import spec as spec_phase0 from eth2spec.altair import spec as spec_altair -from eth2spec.test.helpers.constants import PHASE0, ALTAIR +from eth2spec.merge import spec as spec_merge +from eth2spec.test.helpers.constants import PHASE0, ALTAIR, MERGE -specs = (spec_phase0, spec_altair) +specs = (spec_phase0, spec_altair, spec_merge) if __name__ == "__main__": @@ -27,6 +28,10 @@ **phase_0_mods, } # also run the previous phase 0 tests + # No epoch-processing changes in Merge and previous testing repeats with new types, so no additional tests required. + # TODO: rebase onto Altair testing later. + merge_mods = phase_0_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', @@ -37,6 +42,7 @@ all_mods = { PHASE0: phase_0_mods, ALTAIR: altair_mods, + MERGE: merge_mods, } run_state_test_generators(runner_name="epoch_processing", specs=specs, all_mods=all_mods) diff --git a/tests/generators/finality/main.py b/tests/generators/finality/main.py index e50425da48..148ddef968 100644 --- a/tests/generators/finality/main.py +++ b/tests/generators/finality/main.py @@ -1,19 +1,22 @@ from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators from eth2spec.phase0 import spec as spec_phase0 from eth2spec.altair import spec as spec_altair -from eth2spec.test.helpers.constants import PHASE0, ALTAIR +from eth2spec.merge import spec as spec_merge +from eth2spec.test.helpers.constants import PHASE0, ALTAIR, MERGE -specs = (spec_phase0, spec_altair) +specs = (spec_phase0, spec_altair, spec_merge) if __name__ == "__main__": phase_0_mods = {'finality': 'eth2spec.test.phase0.finality.test_finality'} - altair_mods = phase_0_mods # No additional altair specific finality tests + altair_mods = phase_0_mods # No additional Altair specific finality tests + merge_mods = phase_0_mods # No additional Merge specific finality tests all_mods = { PHASE0: phase_0_mods, ALTAIR: altair_mods, + MERGE: spec_merge, } run_state_test_generators(runner_name="finality", specs=specs, all_mods=all_mods) diff --git a/tests/generators/fork_choice/main.py b/tests/generators/fork_choice/main.py index 445ee629c3..ae15caa1d8 100644 --- a/tests/generators/fork_choice/main.py +++ b/tests/generators/fork_choice/main.py @@ -1,10 +1,11 @@ from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators from eth2spec.phase0 import spec as spec_phase0 from eth2spec.altair import spec as spec_altair -from eth2spec.test.helpers.constants import PHASE0, ALTAIR +from eth2spec.merge import spec as spec_merge +from eth2spec.test.helpers.constants import PHASE0, ALTAIR, MERGE -specs = (spec_phase0, spec_altair) +specs = (spec_phase0, spec_altair, spec_merge) if __name__ == "__main__": @@ -13,10 +14,13 @@ ]} # No additional Altair specific finality tests, yet. altair_mods = phase_0_mods + # No specific Merge tests yet. TODO: rebase onto Altair testing later. + merge_mods = phase_0_mods all_mods = { PHASE0: phase_0_mods, ALTAIR: altair_mods, + MERGE: merge_mods, } run_state_test_generators(runner_name="fork_choice", specs=specs, all_mods=all_mods) diff --git a/tests/generators/operations/main.py b/tests/generators/operations/main.py index 0c1b84c240..316ccdf10c 100644 --- a/tests/generators/operations/main.py +++ b/tests/generators/operations/main.py @@ -1,10 +1,11 @@ from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators from eth2spec.phase0 import spec as spec_phase0 from eth2spec.altair import spec as spec_altair -from eth2spec.test.helpers.constants import PHASE0, ALTAIR +from eth2spec.merge import spec as spec_merge +from eth2spec.test.helpers.constants import PHASE0, ALTAIR, MERGE -specs = (spec_phase0, spec_altair) +specs = (spec_phase0, spec_altair, spec_merge) if __name__ == "__main__": @@ -23,6 +24,13 @@ **phase_0_mods, } # also run the previous phase 0 tests + merge_mods = { + **{key: 'eth2spec.test.merge.block_processing.test_process_' + key for key in [ + 'execution_payload', + ]}, + **phase_0_mods, # TODO: runs phase0 tests. Rebase to include `altair_mods` testing later. + } + # TODO Custody Game testgen is disabled for now # custody_game_mods = {**{key: 'eth2spec.test.custody_game.block_processing.test_process_' + key for key in [ # 'attestation', @@ -35,6 +43,7 @@ all_mods = { PHASE0: phase_0_mods, ALTAIR: altair_mods, + MERGE: merge_mods, } run_state_test_generators(runner_name="operations", specs=specs, all_mods=all_mods) diff --git a/tests/generators/rewards/main.py b/tests/generators/rewards/main.py index 2a0f14863c..8e50732e18 100644 --- a/tests/generators/rewards/main.py +++ b/tests/generators/rewards/main.py @@ -1,10 +1,11 @@ from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators from eth2spec.phase0 import spec as spec_phase0 from eth2spec.altair import spec as spec_altair -from eth2spec.test.helpers.constants import PHASE0, ALTAIR +from eth2spec.merge import spec as spec_merge +from eth2spec.test.helpers.constants import PHASE0, ALTAIR, MERGE -specs = (spec_phase0, spec_altair) +specs = (spec_phase0, spec_altair, spec_merge) if __name__ == "__main__": @@ -16,9 +17,15 @@ # No additional altair specific rewards tests, yet. altair_mods = phase_0_mods + # No additional merge specific rewards tests, yet. + # Note: Block rewards are non-epoch rewards and are tested as part of block processing tests. + # Transaction fees are part of the execution-layer. + merge_mods = phase_0_mods + all_mods = { PHASE0: phase_0_mods, ALTAIR: altair_mods, + MERGE: merge_mods, } run_state_test_generators(runner_name="rewards", specs=specs, all_mods=all_mods) diff --git a/tests/generators/sanity/main.py b/tests/generators/sanity/main.py index 7f7fecc676..3bed6672d5 100644 --- a/tests/generators/sanity/main.py +++ b/tests/generators/sanity/main.py @@ -1,11 +1,12 @@ from eth2spec.phase0 import spec as spec_phase0 from eth2spec.altair import spec as spec_altair -from eth2spec.test.helpers.constants import PHASE0, ALTAIR +from eth2spec.merge import spec as spec_merge +from eth2spec.test.helpers.constants import PHASE0, ALTAIR, MERGE from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators -specs = (spec_phase0, spec_altair) +specs = (spec_phase0, spec_altair, spec_merge) if __name__ == "__main__": @@ -17,9 +18,15 @@ 'blocks', ]}, **phase_0_mods} # also run the previous phase 0 tests + # Altair-specific test cases are ignored, but should be included after the Merge is rebased onto Altair work. + merge_mods = {**{key: 'eth2spec.test.merge.sanity.test_' + key for key in [ + 'blocks', + ]}, **phase_0_mods} # TODO: Merge inherits phase0 tests for now. + all_mods = { PHASE0: phase_0_mods, ALTAIR: altair_mods, + MERGE: merge_mods, } run_state_test_generators(runner_name="sanity", specs=specs, all_mods=all_mods) diff --git a/tests/generators/ssz_static/main.py b/tests/generators/ssz_static/main.py index 94ce023356..1e810f71e2 100644 --- a/tests/generators/ssz_static/main.py +++ b/tests/generators/ssz_static/main.py @@ -94,7 +94,7 @@ def cases_fn() -> Iterable[gen_typing.TestCase]: settings.append((seed, MAINNET, random_value.RandomizationMode.mode_random, False, 5)) seed += 1 # TODO: enable testing for the whole merge spec. - for fork in TESTGEN_FORKS + (MERGE,): + for fork in TESTGEN_FORKS: gen_runner.run_generator("ssz_static", [ create_provider(fork, config_name, seed, mode, chaos, cases_if_random) for (seed, config_name, mode, chaos, cases_if_random) in settings From ab693c972445a7ba362d2f527447461f377ce82a Mon Sep 17 00:00:00 2001 From: protolambda Date: Wed, 5 May 2021 00:16:22 +0200 Subject: [PATCH 02/17] update spec test constants for merge --- tests/core/pyspec/eth2spec/test/helpers/constants.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/helpers/constants.py b/tests/core/pyspec/eth2spec/test/helpers/constants.py index ccd7b20a20..d8f2a37baf 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/constants.py +++ b/tests/core/pyspec/eth2spec/test/helpers/constants.py @@ -7,21 +7,24 @@ # Some of the Spec module functionality is exposed here to deal with phase-specific changes. PHASE0 = SpecForkName('phase0') ALTAIR = SpecForkName('altair') +MERGE = SpecForkName('merge') # Experimental phases (not included in default "ALL_PHASES"): -MERGE = SpecForkName('merge') SHARDING = SpecForkName('sharding') CUSTODY_GAME = SpecForkName('custody_game') DAS = SpecForkName('das') # The forks that pytest runs with. -ALL_PHASES = (PHASE0, ALTAIR) +ALL_PHASES = (PHASE0, ALTAIR, MERGE) # The forks that output to the test vectors. -TESTGEN_FORKS = (PHASE0, ALTAIR) +TESTGEN_FORKS = (PHASE0, ALTAIR, MERGE) # TODO: everything runs in parallel to Altair. # After features are rebased on the Altair fork, this can be reduced to just PHASE0. FORKS_BEFORE_ALTAIR = (PHASE0, MERGE, SHARDING, CUSTODY_GAME, DAS) +# TODO: when rebasing Merge onto Altair, add ALTAIR to this tuple. +FORKS_BEFORE_MERGE = (PHASE0,) + # # Config # From 521cffc3e9c4de950489470eb1eefbf2871a5c33 Mon Sep 17 00:00:00 2001 From: protolambda Date: Wed, 5 May 2021 00:17:10 +0200 Subject: [PATCH 03/17] update execution-payload processing to isolate payload from block body --- specs/merge/beacon-chain.md | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/specs/merge/beacon-chain.md b/specs/merge/beacon-chain.md index b9c443962f..d665f1151f 100644 --- a/specs/merge/beacon-chain.md +++ b/specs/merge/beacon-chain.md @@ -146,8 +146,8 @@ def is_transition_completed(state: BeaconState) -> bool: #### `is_transition_block` ```python -def is_transition_block(state: BeaconState, block_body: BeaconBlockBody) -> bool: - return not is_transition_completed(state) and block_body.execution_payload != ExecutionPayload() +def is_transition_block(state: BeaconState, block: BeaconBlock) -> bool: + return not is_transition_completed(state) and block.body.execution_payload != ExecutionPayload() ``` #### `compute_time_at_slot` @@ -168,7 +168,9 @@ def process_block(state: BeaconState, block: BeaconBlock) -> None: process_randao(state, block.body) process_eth1_data(state, block.body) process_operations(state, block.body) - process_execution_payload(state, block.body) # [New in Merge] + # Pre-merge, skip execution payload processing + if is_transition_completed(state) or is_transition_block(state, block): + process_execution_payload(state, block.body.execution_payload) # [New in Merge] ``` #### Execution payload processing @@ -181,16 +183,10 @@ The body of the function is implementation dependent. ##### `process_execution_payload` ```python -def process_execution_payload(state: BeaconState, body: BeaconBlockBody) -> None: +def process_execution_payload(state: BeaconState, execution_payload: ExecutionPayload) -> None: """ Note: This function is designed to be able to be run in parallel with the other `process_block` sub-functions """ - # Pre-merge, skip processing - if not is_transition_completed(state) and not is_transition_block(state, body): - return - - execution_payload = body.execution_payload - if is_transition_completed(state): assert execution_payload.parent_hash == state.latest_execution_payload_header.block_hash assert execution_payload.number == state.latest_execution_payload_header.number + 1 From 25d0d673a9b0950c0aa2d0b2406f849c9dca0470 Mon Sep 17 00:00:00 2001 From: protolambda Date: Wed, 5 May 2021 00:18:01 +0200 Subject: [PATCH 04/17] start testing of merge functionality --- tests/core/pyspec/eth2spec/test/context.py | 8 +- .../pyspec/eth2spec/test/helpers/block.py | 6 +- .../test/helpers/execution_payload.py | 26 ++++ .../pyspec/eth2spec/test/merge/__init__.py | 0 .../test/merge/block_processing/__init__.py | 0 .../test_process_execution_payload.py | 147 ++++++++++++++++++ .../eth2spec/test/merge/sanity/__init__.py | 0 .../eth2spec/test/merge/sanity/test_blocks.py | 25 +++ 8 files changed, 210 insertions(+), 2 deletions(-) create mode 100644 tests/core/pyspec/eth2spec/test/helpers/execution_payload.py create mode 100644 tests/core/pyspec/eth2spec/test/merge/__init__.py create mode 100644 tests/core/pyspec/eth2spec/test/merge/block_processing/__init__.py create mode 100644 tests/core/pyspec/eth2spec/test/merge/block_processing/test_process_execution_payload.py create mode 100644 tests/core/pyspec/eth2spec/test/merge/sanity/__init__.py create mode 100644 tests/core/pyspec/eth2spec/test/merge/sanity/test_blocks.py diff --git a/tests/core/pyspec/eth2spec/test/context.py b/tests/core/pyspec/eth2spec/test/context.py index 438e611cf3..8699c65ad3 100644 --- a/tests/core/pyspec/eth2spec/test/context.py +++ b/tests/core/pyspec/eth2spec/test/context.py @@ -8,7 +8,7 @@ from .exceptions import SkippedTest from .helpers.constants import ( PHASE0, ALTAIR, - ALL_PHASES, FORKS_BEFORE_ALTAIR, + ALL_PHASES, FORKS_BEFORE_ALTAIR, FORKS_BEFORE_MERGE, ) from .helpers.genesis import create_genesis_state from .utils import vector_test, with_meta_tags @@ -365,3 +365,9 @@ def is_post_altair(spec): if spec.fork in FORKS_BEFORE_ALTAIR: return False return True + + +def is_post_merge(spec): + if spec.fork in FORKS_BEFORE_MERGE: + return False + return True diff --git a/tests/core/pyspec/eth2spec/test/helpers/block.py b/tests/core/pyspec/eth2spec/test/helpers/block.py index 6949b54bc4..64f4066331 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/block.py +++ b/tests/core/pyspec/eth2spec/test/helpers/block.py @@ -1,4 +1,5 @@ -from eth2spec.test.context import is_post_altair +from eth2spec.test.context import is_post_altair, is_post_merge +from eth2spec.test.helpers.execution_payload import build_empty_execution_payload from eth2spec.test.helpers.keys import privkeys from eth2spec.utils import bls from eth2spec.utils.bls import only_with_bls @@ -94,6 +95,9 @@ def build_empty_block(spec, state, slot=None): if is_post_altair(spec): empty_block.body.sync_aggregate.sync_committee_signature = spec.G2_POINT_AT_INFINITY + if is_post_merge(spec): + empty_block.body.execution_payload = build_empty_execution_payload(spec, state) + apply_randao_reveal(spec, state, empty_block) return empty_block diff --git a/tests/core/pyspec/eth2spec/test/helpers/execution_payload.py b/tests/core/pyspec/eth2spec/test/helpers/execution_payload.py new file mode 100644 index 0000000000..e829e07c76 --- /dev/null +++ b/tests/core/pyspec/eth2spec/test/helpers/execution_payload.py @@ -0,0 +1,26 @@ + +def build_empty_execution_payload(spec, state): + """ + Assuming a pre-state of the same slot, build a valid ExecutionPayload without any transactions. + """ + latest = state.latest_execution_payload_header + timestamp = spec.compute_time_at_slot(state, state.slot) + empty_txs = spec.List[spec.OpaqueTransaction, spec.MAX_EXECUTION_TRANSACTIONS]() + + payload = spec.ExecutionPayload( + block_hash=spec.Hash32(), + parent_hash=latest.block_hash, + coinbase=spec.Bytes20(), + state_root=latest.state_root, # no changes to the state + number=latest.number + 1, + gas_limit=latest.gas_limit, # retain same limit + gas_used=0, # empty block, 0 gas + timestamp=timestamp, + receipt_root=b"no receipts here" + b"\x00"*16, # TODO: root of empty MPT may be better. + logs_bloom=spec.ByteVector[spec.BYTES_PER_LOGS_BLOOM](), # TODO: zeroed logs bloom for empty logs ok? + transactions_root=empty_txs, + ) + # TODO: real RLP + block hash logic would be nice, requires RLP and keccak256 dependency however. + payload.block_hash = spec.Hash32(spec.hash(payload.hash_tree_root() + b"FAKE RLP HASH")) + + return payload diff --git a/tests/core/pyspec/eth2spec/test/merge/__init__.py b/tests/core/pyspec/eth2spec/test/merge/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/core/pyspec/eth2spec/test/merge/block_processing/__init__.py b/tests/core/pyspec/eth2spec/test/merge/block_processing/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/core/pyspec/eth2spec/test/merge/block_processing/test_process_execution_payload.py b/tests/core/pyspec/eth2spec/test/merge/block_processing/test_process_execution_payload.py new file mode 100644 index 0000000000..0cf5cef5f2 --- /dev/null +++ b/tests/core/pyspec/eth2spec/test/merge/block_processing/test_process_execution_payload.py @@ -0,0 +1,147 @@ +from eth2spec.test.helpers.constants import PHASE0, ALTAIR +from eth2spec.test.context import spec_state_test, expect_assertion_error, always_bls, with_all_phases_except + +with_merge_and_later = with_all_phases_except([PHASE0, ALTAIR]) + + +def run_execution_payload_processing(spec, state, execution_payload, valid=True, execution_valid=True): + """ + Run ``process_execution_payload``, yielding: + - pre-state ('pre') + - execution payload ('execution_payload') + - execution details, to mock EVM execution ('execution.yml', a dict with 'execution_valid' key and boolean value) + - post-state ('post'). + If ``valid == False``, run expecting ``AssertionError`` + """ + + yield 'pre', state + yield 'execution', {'execution_valid': execution_valid} + yield 'execution_payload', execution_payload + + if not valid: + expect_assertion_error(lambda: spec.process_execution_payload(state, execution_payload)) + yield 'post', None + return + + spec.process_execution_payload(state, execution_payload) + + yield 'post', state + + # TODO: any assertions to make? + + +@with_merge_and_later +@spec_state_test +def test_success_first_payload(spec, state): + assert not spec.is_transition_completed(state) + + # TODO: execution payload + execution_payload = spec.ExecutionPayload() + + yield from run_execution_payload_processing(spec, state, execution_payload) + + +@with_merge_and_later +@spec_state_test +def test_success_regular_payload(spec, state): + # TODO: setup state + assert spec.is_transition_completed(state) + + # TODO: execution payload + execution_payload = spec.ExecutionPayload() + + yield from run_execution_payload_processing(spec, state, execution_payload) + + +@with_merge_and_later +@spec_state_test +def test_success_first_payload_with_gap_slot(spec, state): + # TODO: transition gap slot + + assert not spec.is_transition_completed(state) + + # TODO: execution payload + execution_payload = spec.ExecutionPayload() + + yield from run_execution_payload_processing(spec, state, execution_payload) + + +@with_merge_and_later +@spec_state_test +def test_success_regular_payload_with_gap_slot(spec, state): + # TODO: setup state + assert spec.is_transition_completed(state) + # TODO: transition gap slot + + # TODO: execution payload + execution_payload = spec.ExecutionPayload() + + yield from run_execution_payload_processing(spec, state, execution_payload) + + +@with_merge_and_later +@spec_state_test +def test_bad_execution_first_payload(spec, state): + # completely valid payload, but execution itself fails (e.g. block exceeds gas limit) + + # TODO: execution payload. + execution_payload = spec.ExecutionPayload() + + yield from run_execution_payload_processing(spec, state, execution_payload, valid=False, execution_valid=False) + + +@with_merge_and_later +@spec_state_test +def test_bad_execution_regular_payload(spec, state): + # completely valid payload, but execution itself fails (e.g. block exceeds gas limit) + + # TODO: execution payload + execution_payload = spec.ExecutionPayload() + + yield from run_execution_payload_processing(spec, state, execution_payload, valid=False, execution_valid=False) + + +@with_merge_and_later +@spec_state_test +def test_bad_parent_hash_first_payload(spec, state): + # TODO: execution payload + execution_payload = spec.ExecutionPayload() + + yield from run_execution_payload_processing(spec, state, execution_payload, valid=False) + + +@with_merge_and_later +@spec_state_test +def test_bad_number_first_payload(spec, state): + # TODO: execution payload + execution_payload = spec.ExecutionPayload() + + yield from run_execution_payload_processing(spec, state, execution_payload, valid=False) + + +@with_merge_and_later +@spec_state_test +def test_bad_everything_first_payload(spec, state): + # TODO: execution payload + execution_payload = spec.ExecutionPayload() + + yield from run_execution_payload_processing(spec, state, execution_payload, valid=False) + + +@with_merge_and_later +@spec_state_test +def test_bad_timestamp_first_payload(spec, state): + # TODO: execution payload + execution_payload = spec.ExecutionPayload() + + yield from run_execution_payload_processing(spec, state, execution_payload, valid=False) + + +@with_merge_and_later +@spec_state_test +def test_bad_timestamp_regular_payload(spec, state): + # TODO: execution payload + execution_payload = spec.ExecutionPayload() + + yield from run_execution_payload_processing(spec, state, execution_payload, valid=False) + diff --git a/tests/core/pyspec/eth2spec/test/merge/sanity/__init__.py b/tests/core/pyspec/eth2spec/test/merge/sanity/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/core/pyspec/eth2spec/test/merge/sanity/test_blocks.py b/tests/core/pyspec/eth2spec/test/merge/sanity/test_blocks.py new file mode 100644 index 0000000000..1f9e691671 --- /dev/null +++ b/tests/core/pyspec/eth2spec/test/merge/sanity/test_blocks.py @@ -0,0 +1,25 @@ +from eth2spec.test.helpers.state import ( + state_transition_and_sign_block +) +from eth2spec.test.helpers.block import ( + build_empty_block_for_next_slot +) +from eth2spec.test.context import ( + with_all_phases, spec_state_test +) + + +@with_all_phases +@spec_state_test +def test_empty_block_transition(spec, state): + yield 'pre', state + + block = build_empty_block_for_next_slot(spec, state) + assert len(block.body.execution_payload.transactions) == 0 + + signed_block = state_transition_and_sign_block(spec, state, block) + + yield 'blocks', [signed_block] + yield 'post', state + +# TODO: tests with EVM, mock or replacement? From 36032fd115c1be0eef52ac4a68c3cc8672b5ed70 Mon Sep 17 00:00:00 2001 From: protolambda Date: Wed, 5 May 2021 00:37:00 +0200 Subject: [PATCH 05/17] update doc about format --- tests/formats/operations/README.md | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/tests/formats/operations/README.md b/tests/formats/operations/README.md index 033c5fdef6..f562a6f2aa 100644 --- a/tests/formats/operations/README.md +++ b/tests/formats/operations/README.md @@ -33,17 +33,23 @@ This excludes the other parts of the block-transition. Operations: -| *`operation-name`* | *`operation-object`* | *`input name`* | *`processing call`* | -|-------------------------|-----------------------|----------------------|-----------------------------------------------------------------| -| `attestation` | `Attestation` | `attestation` | `process_attestation(state, attestation)` | -| `attester_slashing` | `AttesterSlashing` | `attester_slashing` | `process_attester_slashing(state, attester_slashing)` | -| `block_header` | `BeaconBlock` | **`block`** | `process_block_header(state, block)` | -| `deposit` | `Deposit` | `deposit` | `process_deposit(state, deposit)` | -| `proposer_slashing` | `ProposerSlashing` | `proposer_slashing` | `process_proposer_slashing(state, proposer_slashing)` | -| `voluntary_exit` | `SignedVoluntaryExit` | `voluntary_exit` | `process_voluntary_exit(state, voluntary_exit)` | -| `sync_aggregate` | `SyncAggregate` | `sync_aggregate` | `process_sync_committee(state, sync_aggregate)` (new in Altair) | +| *`operation-name`* | *`operation-object`* | *`input name`* | *`processing call`* | +|-------------------------|-----------------------|----------------------|----------------------------------------------------------------------| +| `attestation` | `Attestation` | `attestation` | `process_attestation(state, attestation)` | +| `attester_slashing` | `AttesterSlashing` | `attester_slashing` | `process_attester_slashing(state, attester_slashing)` | +| `block_header` | `BeaconBlock` | **`block`** | `process_block_header(state, block)` | +| `deposit` | `Deposit` | `deposit` | `process_deposit(state, deposit)` | +| `proposer_slashing` | `ProposerSlashing` | `proposer_slashing` | `process_proposer_slashing(state, proposer_slashing)` | +| `voluntary_exit` | `SignedVoluntaryExit` | `voluntary_exit` | `process_voluntary_exit(state, voluntary_exit)` | +| `sync_aggregate` | `SyncAggregate` | `sync_aggregate` | `process_sync_committee(state, sync_aggregate)` (new in Altair) | +| `execution_payload` | `ExecutionPayload` | `execution_payload` | `process_execution_payload(state, execution_payload)` (new in Merge) | Note that `block_header` is not strictly an operation (and is a full `Block`), but processed in the same manner, and hence included here. +The `execution_payload` processing normally requires a `verify_execution_state_transition(execution_payload)`, +a responsibility of an (external) execution engine. +During testing this execution is mocked, an `execution.yml` is provided instead: +a dict containing an `execution_valid` boolean field with the verification result. + The resulting state should match the expected `post` state, or if the `post` state is left blank, the handler should reject the input operation as invalid. From 79fc41146d898d80358ff6c746217d827587ee2c Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Wed, 5 May 2021 13:37:07 +0600 Subject: [PATCH 06/17] Adjust is_transition_block call in fork-choice --- specs/merge/fork-choice.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/merge/fork-choice.md b/specs/merge/fork-choice.md index 34647f45d1..f478dd7e68 100644 --- a/specs/merge/fork-choice.md +++ b/specs/merge/fork-choice.md @@ -75,7 +75,7 @@ def on_block(store: Store, signed_block: SignedBeaconBlock) -> None: assert get_ancestor(store, block.parent_root, finalized_slot) == store.finalized_checkpoint.root # [New in Merge] - if is_transition_block(pre_state, block.body): + if is_transition_block(pre_state, block): # Delay consideration of block until PoW block is processed by the PoW node pow_block = get_pow_block(block.body.execution_payload.parent_hash) assert pow_block.is_processed From e2be7614cc2224c75ac6c0356dff48a9b31563e0 Mon Sep 17 00:00:00 2001 From: protolambda Date: Wed, 5 May 2021 15:35:36 +0200 Subject: [PATCH 07/17] introduce merge fork version --- specs/merge/beacon-chain.md | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/specs/merge/beacon-chain.md b/specs/merge/beacon-chain.md index d665f1151f..50b9551b0f 100644 --- a/specs/merge/beacon-chain.md +++ b/specs/merge/beacon-chain.md @@ -13,8 +13,8 @@ - [Introduction](#introduction) - [Custom types](#custom-types) - [Constants](#constants) - - [Transition](#transition) - [Execution](#execution) +- [Configuration](#configuration) - [Containers](#containers) - [Extended containers](#extended-containers) - [`BeaconBlockBody`](#beaconblockbody) @@ -50,12 +50,6 @@ We define the following Python custom types for type hinting and readability: ## Constants -### Transition - -| Name | Value | -| - | - | -| `TRANSITION_TOTAL_DIFFICULTY` | **TBD** | - ### Execution | Name | Value | @@ -64,6 +58,16 @@ We define the following Python custom types for type hinting and readability: | `MAX_EXECUTION_TRANSACTIONS` | `uint64(2**14)` (= 16,384) | | `BYTES_PER_LOGS_BLOOM` | `uint64(2**8)` (= 256) | +## Configuration + +Warning: this configuration is not definitive. + +| Name | Value | +| - | - | +| `MERGE_FORK_VERSION` | `Version('0x02000000')` | +| `MERGE_FORK_EPOCH` | `Epoch(18446744073709551615)` **TBD** | +| `TRANSITION_TOTAL_DIFFICULTY` | **TBD** | + ## Containers ### Extended containers From 470c6dcc6f38093a6991ee8bb78d1385d11d0d08 Mon Sep 17 00:00:00 2001 From: protolambda Date: Wed, 5 May 2021 15:35:52 +0200 Subject: [PATCH 08/17] update test runner to handle merge phase --- tests/core/pyspec/eth2spec/test/context.py | 9 +++++++-- tests/core/pyspec/eth2spec/test/helpers/genesis.py | 3 +++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/context.py b/tests/core/pyspec/eth2spec/test/context.py index 8699c65ad3..65201c0c43 100644 --- a/tests/core/pyspec/eth2spec/test/context.py +++ b/tests/core/pyspec/eth2spec/test/context.py @@ -7,7 +7,7 @@ from .exceptions import SkippedTest from .helpers.constants import ( - PHASE0, ALTAIR, + PHASE0, ALTAIR, MERGE, ALL_PHASES, FORKS_BEFORE_ALTAIR, FORKS_BEFORE_MERGE, ) from .helpers.genesis import create_genesis_state @@ -312,7 +312,7 @@ def wrapper(*args, **kw): return None run_phases = [phase] - if PHASE0 not in run_phases and ALTAIR not in run_phases: + if PHASE0 not in run_phases and ALTAIR not in run_phases and MERGE not in run_phases: dump_skipping_message("none of the recognized phases are executable, skipping test.") return None @@ -331,12 +331,17 @@ def wrapper(*args, **kw): if ALTAIR in available_phases: phase_dir[ALTAIR] = spec_altair + if MERGE in available_phases: + phase_dir[MERGE] = spec_merge + # return is ignored whenever multiple phases are ran. # This return is for test generators to emit python generators (yielding test vector outputs) if PHASE0 in run_phases: ret = fn(spec=spec_phase0, phases=phase_dir, *args, **kw) if ALTAIR in run_phases: ret = fn(spec=spec_altair, phases=phase_dir, *args, **kw) + if MERGE in run_phases: + ret = fn(spec=spec_merge, phases=phase_dir, *args, **kw) # TODO: merge, sharding, custody_game and das are not executable yet. # Tests that specify these features will not run, and get ignored for these specific phases. diff --git a/tests/core/pyspec/eth2spec/test/helpers/genesis.py b/tests/core/pyspec/eth2spec/test/helpers/genesis.py index 49af43ec1f..4a34a5eb3f 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/genesis.py +++ b/tests/core/pyspec/eth2spec/test/helpers/genesis.py @@ -1,6 +1,7 @@ from eth2spec.test.helpers.constants import ( ALTAIR, FORKS_BEFORE_ALTAIR, + MERGE, ) from eth2spec.test.helpers.keys import pubkeys @@ -28,6 +29,8 @@ def create_genesis_state(spec, validator_balances, activation_threshold): if spec.fork == ALTAIR: current_version = spec.ALTAIR_FORK_VERSION + elif spec.fork == MERGE: + current_version = spec.MERGE_FORK_VERSION state = spec.BeaconState( genesis_time=0, From ff3a82e0f38de19559bbba52e2693e01d4dad44f Mon Sep 17 00:00:00 2001 From: protolambda Date: Wed, 5 May 2021 15:40:56 +0200 Subject: [PATCH 09/17] fix transactions field in exec payload helper --- tests/core/pyspec/eth2spec/test/helpers/execution_payload.py | 2 +- .../merge/block_processing/test_process_execution_payload.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/helpers/execution_payload.py b/tests/core/pyspec/eth2spec/test/helpers/execution_payload.py index e829e07c76..0d035350dc 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/execution_payload.py +++ b/tests/core/pyspec/eth2spec/test/helpers/execution_payload.py @@ -18,7 +18,7 @@ def build_empty_execution_payload(spec, state): timestamp=timestamp, receipt_root=b"no receipts here" + b"\x00"*16, # TODO: root of empty MPT may be better. logs_bloom=spec.ByteVector[spec.BYTES_PER_LOGS_BLOOM](), # TODO: zeroed logs bloom for empty logs ok? - transactions_root=empty_txs, + transactions=empty_txs, ) # TODO: real RLP + block hash logic would be nice, requires RLP and keccak256 dependency however. payload.block_hash = spec.Hash32(spec.hash(payload.hash_tree_root() + b"FAKE RLP HASH")) diff --git a/tests/core/pyspec/eth2spec/test/merge/block_processing/test_process_execution_payload.py b/tests/core/pyspec/eth2spec/test/merge/block_processing/test_process_execution_payload.py index 0cf5cef5f2..f3f521c4b8 100644 --- a/tests/core/pyspec/eth2spec/test/merge/block_processing/test_process_execution_payload.py +++ b/tests/core/pyspec/eth2spec/test/merge/block_processing/test_process_execution_payload.py @@ -1,4 +1,5 @@ from eth2spec.test.helpers.constants import PHASE0, ALTAIR +from eth2spec.test.helpers.execution_payload import build_empty_execution_payload from eth2spec.test.context import spec_state_test, expect_assertion_error, always_bls, with_all_phases_except with_merge_and_later = with_all_phases_except([PHASE0, ALTAIR]) @@ -36,7 +37,7 @@ def test_success_first_payload(spec, state): assert not spec.is_transition_completed(state) # TODO: execution payload - execution_payload = spec.ExecutionPayload() + execution_payload = build_empty_execution_payload(spec, state) yield from run_execution_payload_processing(spec, state, execution_payload) From 865d7db5cad448e8d2ac37859a74eff9d8a2d633 Mon Sep 17 00:00:00 2001 From: protolambda Date: Wed, 5 May 2021 16:03:52 +0200 Subject: [PATCH 10/17] update altair tests to not collide with Merge + fix merge test triggers --- .../test_process_sync_committee.py | 20 +++++++++---------- .../test_process_sync_committee_updates.py | 8 ++++---- .../test/altair/sanity/test_blocks.py | 17 ++++++++-------- .../unittests/validator/test_validator.py | 8 ++++---- tests/core/pyspec/eth2spec/test/context.py | 8 ++++++++ .../pyspec/eth2spec/test/helpers/block.py | 2 ++ .../test_process_execution_payload.py | 5 +---- .../eth2spec/test/merge/sanity/test_blocks.py | 4 ++-- 8 files changed, 39 insertions(+), 33 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/altair/block_processing/test_process_sync_committee.py b/tests/core/pyspec/eth2spec/test/altair/block_processing/test_process_sync_committee.py index e0faf3d0d3..9b415cfbd8 100644 --- a/tests/core/pyspec/eth2spec/test/altair/block_processing/test_process_sync_committee.py +++ b/tests/core/pyspec/eth2spec/test/altair/block_processing/test_process_sync_committee.py @@ -17,7 +17,7 @@ ) from eth2spec.test.context import ( expect_assertion_error, - with_all_phases_except, + with_altair_and_later, with_configs, spec_state_test, always_bls, @@ -62,7 +62,7 @@ def get_committee_indices(spec, state, duplicates=False): state.randao_mixes[randao_index] = hash(state.randao_mixes[randao_index]) -@with_all_phases_except([PHASE0]) +@with_altair_and_later @spec_state_test @always_bls def test_invalid_signature_missing_participant(spec, state): @@ -84,7 +84,7 @@ def test_invalid_signature_missing_participant(spec, state): yield from run_sync_committee_processing(spec, state, block, expect_exception=True) -@with_all_phases_except([PHASE0]) +@with_altair_and_later @spec_state_test @always_bls def test_invalid_signature_extra_participant(spec, state): @@ -197,7 +197,7 @@ def run_successful_sync_committee_test(spec, state, committee, committee_bits): ) -@with_all_phases_except([PHASE0]) +@with_altair_and_later @with_configs([MINIMAL], reason="to create nonduplicate committee") @spec_state_test def test_sync_committee_rewards_nonduplicate_committee(spec, state): @@ -213,7 +213,7 @@ def test_sync_committee_rewards_nonduplicate_committee(spec, state): yield from run_successful_sync_committee_test(spec, state, committee, committee_bits) -@with_all_phases_except([PHASE0]) +@with_altair_and_later @with_configs([MAINNET], reason="to create duplicate committee") @spec_state_test def test_sync_committee_rewards_duplicate_committee(spec, state): @@ -229,7 +229,7 @@ def test_sync_committee_rewards_duplicate_committee(spec, state): yield from run_successful_sync_committee_test(spec, state, committee, committee_bits) -@with_all_phases_except([PHASE0]) +@with_altair_and_later @spec_state_test @always_bls def test_sync_committee_rewards_not_full_participants(spec, state): @@ -240,7 +240,7 @@ def test_sync_committee_rewards_not_full_participants(spec, state): yield from run_successful_sync_committee_test(spec, state, committee, committee_bits) -@with_all_phases_except([PHASE0]) +@with_altair_and_later @spec_state_test @always_bls def test_sync_committee_rewards_empty_participants(spec, state): @@ -250,7 +250,7 @@ def test_sync_committee_rewards_empty_participants(spec, state): yield from run_successful_sync_committee_test(spec, state, committee, committee_bits) -@with_all_phases_except([PHASE0]) +@with_altair_and_later @spec_state_test @always_bls def test_invalid_signature_past_block(spec, state): @@ -289,7 +289,7 @@ def test_invalid_signature_past_block(spec, state): yield from run_sync_committee_processing(spec, state, invalid_block, expect_exception=True) -@with_all_phases_except([PHASE0]) +@with_altair_and_later @with_configs([MINIMAL], reason="to produce different committee sets") @spec_state_test @always_bls @@ -326,7 +326,7 @@ def test_invalid_signature_previous_committee(spec, state): yield from run_sync_committee_processing(spec, state, block, expect_exception=True) -@with_all_phases_except([PHASE0]) +@with_altair_and_later @spec_state_test @always_bls @with_configs([MINIMAL], reason="too slow") diff --git a/tests/core/pyspec/eth2spec/test/altair/epoch_processing/test_process_sync_committee_updates.py b/tests/core/pyspec/eth2spec/test/altair/epoch_processing/test_process_sync_committee_updates.py index 8ffc515072..c914c476d8 100644 --- a/tests/core/pyspec/eth2spec/test/altair/epoch_processing/test_process_sync_committee_updates.py +++ b/tests/core/pyspec/eth2spec/test/altair/epoch_processing/test_process_sync_committee_updates.py @@ -2,7 +2,7 @@ always_bls, spec_state_test, spec_test, - with_all_phases_except, + with_altair_and_later, with_configs, with_custom_state, single_phase, @@ -49,7 +49,7 @@ def run_sync_committees_progress_test(spec, state): assert state.next_sync_committee == third_sync_committee -@with_all_phases_except([PHASE0]) +@with_altair_and_later @spec_state_test @always_bls @with_configs([MINIMAL], reason="too slow") @@ -60,7 +60,7 @@ def test_sync_committees_progress_genesis(spec, state): yield from run_sync_committees_progress_test(spec, state) -@with_all_phases_except([PHASE0]) +@with_altair_and_later @spec_state_test @always_bls @with_configs([MINIMAL], reason="too slow") @@ -73,7 +73,7 @@ def test_sync_committees_progress_not_genesis(spec, state): yield from run_sync_committees_progress_test(spec, state) -@with_all_phases_except([PHASE0]) +@with_altair_and_later @with_custom_state(balances_fn=misc_balances, threshold_fn=lambda spec: spec.EJECTION_BALANCE) @spec_test @single_phase diff --git a/tests/core/pyspec/eth2spec/test/altair/sanity/test_blocks.py b/tests/core/pyspec/eth2spec/test/altair/sanity/test_blocks.py index 48ab6956b2..ffe7435319 100644 --- a/tests/core/pyspec/eth2spec/test/altair/sanity/test_blocks.py +++ b/tests/core/pyspec/eth2spec/test/altair/sanity/test_blocks.py @@ -11,9 +11,8 @@ from eth2spec.test.helpers.sync_committee import ( compute_aggregate_sync_committee_signature, ) -from eth2spec.test.helpers.constants import PHASE0 from eth2spec.test.context import ( - with_all_phases_except, + with_altair_and_later, spec_state_test, ) @@ -40,46 +39,46 @@ def run_sync_committee_sanity_test(spec, state, fraction_full=1.0): yield 'post', state -@with_all_phases_except([PHASE0]) +@with_altair_and_later @spec_state_test def test_full_sync_committee_committee(spec, state): next_epoch(spec, state) yield from run_sync_committee_sanity_test(spec, state, fraction_full=1.0) -@with_all_phases_except([PHASE0]) +@with_altair_and_later @spec_state_test def test_half_sync_committee_committee(spec, state): next_epoch(spec, state) yield from run_sync_committee_sanity_test(spec, state, fraction_full=0.5) -@with_all_phases_except([PHASE0]) +@with_altair_and_later @spec_state_test def test_empty_sync_committee_committee(spec, state): next_epoch(spec, state) yield from run_sync_committee_sanity_test(spec, state, fraction_full=0.0) -@with_all_phases_except([PHASE0]) +@with_altair_and_later @spec_state_test def test_full_sync_committee_committee_genesis(spec, state): yield from run_sync_committee_sanity_test(spec, state, fraction_full=1.0) -@with_all_phases_except([PHASE0]) +@with_altair_and_later @spec_state_test def test_half_sync_committee_committee_genesis(spec, state): yield from run_sync_committee_sanity_test(spec, state, fraction_full=0.5) -@with_all_phases_except([PHASE0]) +@with_altair_and_later @spec_state_test def test_empty_sync_committee_committee_genesis(spec, state): yield from run_sync_committee_sanity_test(spec, state, fraction_full=0.0) -@with_all_phases_except([PHASE0]) +@with_altair_and_later @spec_state_test def test_inactivity_scores(spec, state): for _ in range(spec.MIN_EPOCHS_TO_INACTIVITY_PENALTY + 2): diff --git a/tests/core/pyspec/eth2spec/test/altair/unittests/validator/test_validator.py b/tests/core/pyspec/eth2spec/test/altair/unittests/validator/test_validator.py index 244e1aa7e9..e1f5d38604 100644 --- a/tests/core/pyspec/eth2spec/test/altair/unittests/validator/test_validator.py +++ b/tests/core/pyspec/eth2spec/test/altair/unittests/validator/test_validator.py @@ -8,7 +8,7 @@ from eth2spec.utils.bls import only_with_bls from eth2spec.test.context import ( PHASE0, - with_all_phases_except, + with_altair_and_later, with_state, ) @@ -25,7 +25,7 @@ def ensure_assignments_in_sync_committee( assert spec.is_assigned_to_sync_committee(state, epoch, validator_index) -@with_all_phases_except([PHASE0]) +@with_altair_and_later @with_state def test_is_assigned_to_sync_committee(phases, spec, state): epoch = spec.get_current_epoch(state) @@ -91,7 +91,7 @@ def _get_sync_committee_signature( @only_with_bls() -@with_all_phases_except([PHASE0]) +@with_altair_and_later @with_state def test_process_sync_committee_contributions(phases, spec, state): # skip over slots at genesis @@ -144,7 +144,7 @@ def _subnet_for_sync_committee_index(spec, i): return i // (spec.SYNC_COMMITTEE_SIZE // spec.SYNC_COMMITTEE_SUBNET_COUNT) -@with_all_phases_except([PHASE0]) +@with_altair_and_later @with_state def test_compute_subnets_for_sync_committee(state, spec, phases): some_sync_committee_members = list( diff --git a/tests/core/pyspec/eth2spec/test/context.py b/tests/core/pyspec/eth2spec/test/context.py index 65201c0c43..775efdcf3c 100644 --- a/tests/core/pyspec/eth2spec/test/context.py +++ b/tests/core/pyspec/eth2spec/test/context.py @@ -367,12 +367,20 @@ def wrapper(*args, spec: Spec, **kw): def is_post_altair(spec): + if spec.fork == MERGE: # TODO: remove parallel Altair-Merge condition after rebase. + return False if spec.fork in FORKS_BEFORE_ALTAIR: return False return True def is_post_merge(spec): + if spec.fork == ALTAIR: # TODO: remove parallel Altair-Merge condition after rebase. + return False if spec.fork in FORKS_BEFORE_MERGE: return False return True + + +with_altair_and_later = with_phases([ALTAIR]) # TODO: include Merge, but not until Merge work is rebased. +with_merge_and_later = with_phases([MERGE]) diff --git a/tests/core/pyspec/eth2spec/test/helpers/block.py b/tests/core/pyspec/eth2spec/test/helpers/block.py index 64f4066331..41d0a91316 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/block.py +++ b/tests/core/pyspec/eth2spec/test/helpers/block.py @@ -96,6 +96,8 @@ def build_empty_block(spec, state, slot=None): empty_block.body.sync_aggregate.sync_committee_signature = spec.G2_POINT_AT_INFINITY if is_post_merge(spec): + if not hasattr(state, 'latest_execution_payload_header'): + raise Exception("panic!!!") empty_block.body.execution_payload = build_empty_execution_payload(spec, state) apply_randao_reveal(spec, state, empty_block) diff --git a/tests/core/pyspec/eth2spec/test/merge/block_processing/test_process_execution_payload.py b/tests/core/pyspec/eth2spec/test/merge/block_processing/test_process_execution_payload.py index f3f521c4b8..96a657d7a9 100644 --- a/tests/core/pyspec/eth2spec/test/merge/block_processing/test_process_execution_payload.py +++ b/tests/core/pyspec/eth2spec/test/merge/block_processing/test_process_execution_payload.py @@ -1,8 +1,5 @@ -from eth2spec.test.helpers.constants import PHASE0, ALTAIR from eth2spec.test.helpers.execution_payload import build_empty_execution_payload -from eth2spec.test.context import spec_state_test, expect_assertion_error, always_bls, with_all_phases_except - -with_merge_and_later = with_all_phases_except([PHASE0, ALTAIR]) +from eth2spec.test.context import spec_state_test, expect_assertion_error, always_bls, with_merge_and_later def run_execution_payload_processing(spec, state, execution_payload, valid=True, execution_valid=True): diff --git a/tests/core/pyspec/eth2spec/test/merge/sanity/test_blocks.py b/tests/core/pyspec/eth2spec/test/merge/sanity/test_blocks.py index 1f9e691671..4a6db41068 100644 --- a/tests/core/pyspec/eth2spec/test/merge/sanity/test_blocks.py +++ b/tests/core/pyspec/eth2spec/test/merge/sanity/test_blocks.py @@ -5,11 +5,11 @@ build_empty_block_for_next_slot ) from eth2spec.test.context import ( - with_all_phases, spec_state_test + with_merge_and_later, spec_state_test ) -@with_all_phases +@with_merge_and_later @spec_state_test def test_empty_block_transition(spec, state): yield 'pre', state From cc11328f74d77bf3a914f71c6cbc606620bf0c24 Mon Sep 17 00:00:00 2001 From: protolambda Date: Wed, 5 May 2021 16:24:44 +0200 Subject: [PATCH 11/17] fix merge forkchoice tests with mock get_pow_block --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 37fbd77950..9e5a546fab 100644 --- a/setup.py +++ b/setup.py @@ -441,7 +441,7 @@ def sundry_functions(cls) -> str: def get_pow_block(hash: Bytes32) -> PowBlock: - pass + return PowBlock(block_hash=hash, is_valid=True, is_processed=True, total_difficulty=TRANSITION_TOTAL_DIFFICULTY) def get_execution_state(execution_state_root: Bytes32) -> ExecutionState: From 00cd1c3db76c77fe468721075347df683adb8aa3 Mon Sep 17 00:00:00 2001 From: protolambda Date: Wed, 5 May 2021 16:31:28 +0200 Subject: [PATCH 12/17] fix forkchoice unittest not recognizing merge spec --- .../test/phase0/unittests/fork_choice/test_on_attestation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/phase0/unittests/fork_choice/test_on_attestation.py b/tests/core/pyspec/eth2spec/test/phase0/unittests/fork_choice/test_on_attestation.py index fcc5e2d213..8ff6bbb389 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/unittests/fork_choice/test_on_attestation.py +++ b/tests/core/pyspec/eth2spec/test/phase0/unittests/fork_choice/test_on_attestation.py @@ -1,7 +1,7 @@ from eth2spec.test.context import with_all_phases, spec_state_test from eth2spec.test.helpers.block import build_empty_block_for_next_slot from eth2spec.test.helpers.attestations import get_valid_attestation, sign_attestation -from eth2spec.test.helpers.constants import PHASE0, ALTAIR +from eth2spec.test.helpers.constants import PHASE0, ALTAIR, MERGE from eth2spec.test.helpers.state import transition_to, state_transition_and_sign_block, next_epoch, next_slot from eth2spec.test.helpers.fork_choice import get_genesis_forkchoice_store @@ -19,7 +19,7 @@ def run_on_attestation(spec, state, store, attestation, valid=True): spec.on_attestation(store, attestation) sample_index = indexed_attestation.attesting_indices[0] - if spec.fork in (PHASE0, ALTAIR): + if spec.fork in (PHASE0, ALTAIR, MERGE): latest_message = spec.LatestMessage( epoch=attestation.data.target.epoch, root=attestation.data.beacon_block_root, From 2ef6291cbc74237c364c22c4f29f8debafe1d4b0 Mon Sep 17 00:00:00 2001 From: protolambda Date: Wed, 5 May 2021 16:41:59 +0200 Subject: [PATCH 13/17] Minimal execution payload test, more merge-specific testing in later PR --- .../test_process_execution_payload.py | 109 +----------------- 1 file changed, 2 insertions(+), 107 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/merge/block_processing/test_process_execution_payload.py b/tests/core/pyspec/eth2spec/test/merge/block_processing/test_process_execution_payload.py index 96a657d7a9..acb88b944b 100644 --- a/tests/core/pyspec/eth2spec/test/merge/block_processing/test_process_execution_payload.py +++ b/tests/core/pyspec/eth2spec/test/merge/block_processing/test_process_execution_payload.py @@ -1,6 +1,6 @@ from eth2spec.test.helpers.execution_payload import build_empty_execution_payload from eth2spec.test.context import spec_state_test, expect_assertion_error, always_bls, with_merge_and_later - +from eth2spec.test.helpers.state import next_slot def run_execution_payload_processing(spec, state, execution_payload, valid=True, execution_valid=True): """ @@ -31,115 +31,10 @@ def run_execution_payload_processing(spec, state, execution_payload, valid=True, @with_merge_and_later @spec_state_test def test_success_first_payload(spec, state): + next_slot(spec, state) assert not spec.is_transition_completed(state) - # TODO: execution payload execution_payload = build_empty_execution_payload(spec, state) yield from run_execution_payload_processing(spec, state, execution_payload) - -@with_merge_and_later -@spec_state_test -def test_success_regular_payload(spec, state): - # TODO: setup state - assert spec.is_transition_completed(state) - - # TODO: execution payload - execution_payload = spec.ExecutionPayload() - - yield from run_execution_payload_processing(spec, state, execution_payload) - - -@with_merge_and_later -@spec_state_test -def test_success_first_payload_with_gap_slot(spec, state): - # TODO: transition gap slot - - assert not spec.is_transition_completed(state) - - # TODO: execution payload - execution_payload = spec.ExecutionPayload() - - yield from run_execution_payload_processing(spec, state, execution_payload) - - -@with_merge_and_later -@spec_state_test -def test_success_regular_payload_with_gap_slot(spec, state): - # TODO: setup state - assert spec.is_transition_completed(state) - # TODO: transition gap slot - - # TODO: execution payload - execution_payload = spec.ExecutionPayload() - - yield from run_execution_payload_processing(spec, state, execution_payload) - - -@with_merge_and_later -@spec_state_test -def test_bad_execution_first_payload(spec, state): - # completely valid payload, but execution itself fails (e.g. block exceeds gas limit) - - # TODO: execution payload. - execution_payload = spec.ExecutionPayload() - - yield from run_execution_payload_processing(spec, state, execution_payload, valid=False, execution_valid=False) - - -@with_merge_and_later -@spec_state_test -def test_bad_execution_regular_payload(spec, state): - # completely valid payload, but execution itself fails (e.g. block exceeds gas limit) - - # TODO: execution payload - execution_payload = spec.ExecutionPayload() - - yield from run_execution_payload_processing(spec, state, execution_payload, valid=False, execution_valid=False) - - -@with_merge_and_later -@spec_state_test -def test_bad_parent_hash_first_payload(spec, state): - # TODO: execution payload - execution_payload = spec.ExecutionPayload() - - yield from run_execution_payload_processing(spec, state, execution_payload, valid=False) - - -@with_merge_and_later -@spec_state_test -def test_bad_number_first_payload(spec, state): - # TODO: execution payload - execution_payload = spec.ExecutionPayload() - - yield from run_execution_payload_processing(spec, state, execution_payload, valid=False) - - -@with_merge_and_later -@spec_state_test -def test_bad_everything_first_payload(spec, state): - # TODO: execution payload - execution_payload = spec.ExecutionPayload() - - yield from run_execution_payload_processing(spec, state, execution_payload, valid=False) - - -@with_merge_and_later -@spec_state_test -def test_bad_timestamp_first_payload(spec, state): - # TODO: execution payload - execution_payload = spec.ExecutionPayload() - - yield from run_execution_payload_processing(spec, state, execution_payload, valid=False) - - -@with_merge_and_later -@spec_state_test -def test_bad_timestamp_regular_payload(spec, state): - # TODO: execution payload - execution_payload = spec.ExecutionPayload() - - yield from run_execution_payload_processing(spec, state, execution_payload, valid=False) - From 56bcb630dbfa487a4797c718e5a0b5c3ac04abc2 Mon Sep 17 00:00:00 2001 From: protolambda Date: Wed, 5 May 2021 17:03:29 +0200 Subject: [PATCH 14/17] Lint fixes for merge testing update --- .../altair/block_processing/test_process_sync_committee.py | 1 - .../epoch_processing/test_process_sync_committee_updates.py | 5 +---- .../test/altair/unittests/validator/test_validator.py | 1 - tests/core/pyspec/eth2spec/test/helpers/execution_payload.py | 2 +- .../merge/block_processing/test_process_execution_payload.py | 4 ++-- 5 files changed, 4 insertions(+), 9 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/altair/block_processing/test_process_sync_committee.py b/tests/core/pyspec/eth2spec/test/altair/block_processing/test_process_sync_committee.py index 9b415cfbd8..db2664b21d 100644 --- a/tests/core/pyspec/eth2spec/test/altair/block_processing/test_process_sync_committee.py +++ b/tests/core/pyspec/eth2spec/test/altair/block_processing/test_process_sync_committee.py @@ -9,7 +9,6 @@ transition_to, ) from eth2spec.test.helpers.constants import ( - PHASE0, MAINNET, MINIMAL, ) from eth2spec.test.helpers.sync_committee import ( diff --git a/tests/core/pyspec/eth2spec/test/altair/epoch_processing/test_process_sync_committee_updates.py b/tests/core/pyspec/eth2spec/test/altair/epoch_processing/test_process_sync_committee_updates.py index c914c476d8..c909c791c5 100644 --- a/tests/core/pyspec/eth2spec/test/altair/epoch_processing/test_process_sync_committee_updates.py +++ b/tests/core/pyspec/eth2spec/test/altair/epoch_processing/test_process_sync_committee_updates.py @@ -8,10 +8,7 @@ single_phase, misc_balances, ) -from eth2spec.test.helpers.constants import ( - PHASE0, - MINIMAL, -) +from eth2spec.test.helpers.constants import MINIMAL from eth2spec.test.helpers.state import transition_to from eth2spec.test.helpers.epoch_processing import ( run_epoch_processing_with, diff --git a/tests/core/pyspec/eth2spec/test/altair/unittests/validator/test_validator.py b/tests/core/pyspec/eth2spec/test/altair/unittests/validator/test_validator.py index e1f5d38604..c8a894da68 100644 --- a/tests/core/pyspec/eth2spec/test/altair/unittests/validator/test_validator.py +++ b/tests/core/pyspec/eth2spec/test/altair/unittests/validator/test_validator.py @@ -7,7 +7,6 @@ from eth2spec.utils import bls from eth2spec.utils.bls import only_with_bls from eth2spec.test.context import ( - PHASE0, with_altair_and_later, with_state, ) diff --git a/tests/core/pyspec/eth2spec/test/helpers/execution_payload.py b/tests/core/pyspec/eth2spec/test/helpers/execution_payload.py index 0d035350dc..093b7cf2e2 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/execution_payload.py +++ b/tests/core/pyspec/eth2spec/test/helpers/execution_payload.py @@ -16,7 +16,7 @@ def build_empty_execution_payload(spec, state): gas_limit=latest.gas_limit, # retain same limit gas_used=0, # empty block, 0 gas timestamp=timestamp, - receipt_root=b"no receipts here" + b"\x00"*16, # TODO: root of empty MPT may be better. + receipt_root=b"no receipts here" + b"\x00" * 16, # TODO: root of empty MPT may be better. logs_bloom=spec.ByteVector[spec.BYTES_PER_LOGS_BLOOM](), # TODO: zeroed logs bloom for empty logs ok? transactions=empty_txs, ) diff --git a/tests/core/pyspec/eth2spec/test/merge/block_processing/test_process_execution_payload.py b/tests/core/pyspec/eth2spec/test/merge/block_processing/test_process_execution_payload.py index acb88b944b..d45a2689b7 100644 --- a/tests/core/pyspec/eth2spec/test/merge/block_processing/test_process_execution_payload.py +++ b/tests/core/pyspec/eth2spec/test/merge/block_processing/test_process_execution_payload.py @@ -1,7 +1,8 @@ from eth2spec.test.helpers.execution_payload import build_empty_execution_payload -from eth2spec.test.context import spec_state_test, expect_assertion_error, always_bls, with_merge_and_later +from eth2spec.test.context import spec_state_test, expect_assertion_error, with_merge_and_later from eth2spec.test.helpers.state import next_slot + def run_execution_payload_processing(spec, state, execution_payload, valid=True, execution_valid=True): """ Run ``process_execution_payload``, yielding: @@ -37,4 +38,3 @@ def test_success_first_payload(spec, state): execution_payload = build_empty_execution_payload(spec, state) yield from run_execution_payload_processing(spec, state, execution_payload) - From 8ac59b73170d452c5a8e16f65b8bb14bbe8c5c75 Mon Sep 17 00:00:00 2001 From: protolambda Date: Wed, 5 May 2021 22:38:16 +0200 Subject: [PATCH 15/17] fix old ssz-static todo comment --- tests/generators/ssz_static/main.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/generators/ssz_static/main.py b/tests/generators/ssz_static/main.py index 1e810f71e2..d86636e85e 100644 --- a/tests/generators/ssz_static/main.py +++ b/tests/generators/ssz_static/main.py @@ -93,7 +93,6 @@ def cases_fn() -> Iterable[gen_typing.TestCase]: seed += 1 settings.append((seed, MAINNET, random_value.RandomizationMode.mode_random, False, 5)) seed += 1 - # TODO: enable testing for the whole merge spec. for fork in TESTGEN_FORKS: gen_runner.run_generator("ssz_static", [ create_provider(fork, config_name, seed, mode, chaos, cases_if_random) From 76b5974d11692326cbbf513e9c04130aceabeba4 Mon Sep 17 00:00:00 2001 From: protolambda Date: Thu, 6 May 2021 02:21:52 +0200 Subject: [PATCH 16/17] is_execution_enabled function + misc review fixes Co-Authored-By: Danny Ryan --- specs/merge/beacon-chain.md | 10 +++++++++- tests/core/pyspec/eth2spec/test/context.py | 1 - .../block_processing/test_process_execution_payload.py | 5 ++++- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/specs/merge/beacon-chain.md b/specs/merge/beacon-chain.md index 50b9551b0f..626a86724c 100644 --- a/specs/merge/beacon-chain.md +++ b/specs/merge/beacon-chain.md @@ -24,6 +24,7 @@ - [`ExecutionPayloadHeader`](#executionpayloadheader) - [Helper functions](#helper-functions) - [Misc](#misc) + - [`is_execution_enabled`](#is_execution_enabled) - [`is_transition_completed`](#is_transition_completed) - [`is_transition_block`](#is_transition_block) - [`compute_time_at_slot`](#compute_time_at_slot) @@ -140,6 +141,13 @@ class ExecutionPayloadHeader(Container): ### Misc +#### `is_execution_enabled` + +```python +def is_execution_enabled(state: BeaconState, block: BeaconBlock) -> bool: + return is_transition_completed(state) or is_transition_block(state, block) +``` + #### `is_transition_completed` ```python @@ -173,7 +181,7 @@ def process_block(state: BeaconState, block: BeaconBlock) -> None: process_eth1_data(state, block.body) process_operations(state, block.body) # Pre-merge, skip execution payload processing - if is_transition_completed(state) or is_transition_block(state, block): + if is_execution_enabled(state, block): process_execution_payload(state, block.body.execution_payload) # [New in Merge] ``` diff --git a/tests/core/pyspec/eth2spec/test/context.py b/tests/core/pyspec/eth2spec/test/context.py index 775efdcf3c..7a2e61c22d 100644 --- a/tests/core/pyspec/eth2spec/test/context.py +++ b/tests/core/pyspec/eth2spec/test/context.py @@ -330,7 +330,6 @@ def wrapper(*args, **kw): phase_dir[PHASE0] = spec_phase0 if ALTAIR in available_phases: phase_dir[ALTAIR] = spec_altair - if MERGE in available_phases: phase_dir[MERGE] = spec_merge diff --git a/tests/core/pyspec/eth2spec/test/merge/block_processing/test_process_execution_payload.py b/tests/core/pyspec/eth2spec/test/merge/block_processing/test_process_execution_payload.py index d45a2689b7..fb1da87587 100644 --- a/tests/core/pyspec/eth2spec/test/merge/block_processing/test_process_execution_payload.py +++ b/tests/core/pyspec/eth2spec/test/merge/block_processing/test_process_execution_payload.py @@ -13,6 +13,8 @@ def run_execution_payload_processing(spec, state, execution_payload, valid=True, If ``valid == False``, run expecting ``AssertionError`` """ + pre_exec_header = state.latest_execution_payload_header.copy() + yield 'pre', state yield 'execution', {'execution_valid': execution_valid} yield 'execution_payload', execution_payload @@ -26,7 +28,8 @@ def run_execution_payload_processing(spec, state, execution_payload, valid=True, yield 'post', state - # TODO: any assertions to make? + assert pre_exec_header != state.latest_execution_payload_header + # TODO: any more assertions to make? @with_merge_and_later From 42733b7e34fa9f0e7fbd9eda0415171e843d0de7 Mon Sep 17 00:00:00 2001 From: protolambda Date: Thu, 6 May 2021 02:27:05 +0200 Subject: [PATCH 17/17] remove merge-test exec-payload trigger debug helper --- tests/core/pyspec/eth2spec/test/helpers/block.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/helpers/block.py b/tests/core/pyspec/eth2spec/test/helpers/block.py index 41d0a91316..64f4066331 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/block.py +++ b/tests/core/pyspec/eth2spec/test/helpers/block.py @@ -96,8 +96,6 @@ def build_empty_block(spec, state, slot=None): empty_block.body.sync_aggregate.sync_committee_signature = spec.G2_POINT_AT_INFINITY if is_post_merge(spec): - if not hasattr(state, 'latest_execution_payload_header'): - raise Exception("panic!!!") empty_block.body.execution_payload = build_empty_execution_payload(spec, state) apply_randao_reveal(spec, state, empty_block)