From 69d8c96de4478bd4c78b61dbfe8d486547c0093f Mon Sep 17 00:00:00 2001 From: Michael Sproul Date: Thu, 11 May 2023 13:16:52 +1000 Subject: [PATCH 01/46] Apply proposer boost to first block in case of equivocation --- specs/bellatrix/fork-choice.md | 3 ++- specs/deneb/fork-choice.md | 3 ++- specs/phase0/fork-choice.md | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/specs/bellatrix/fork-choice.md b/specs/bellatrix/fork-choice.md index 68519ff908..c8475195fc 100644 --- a/specs/bellatrix/fork-choice.md +++ b/specs/bellatrix/fork-choice.md @@ -194,7 +194,8 @@ def on_block(store: Store, signed_block: SignedBeaconBlock) -> None: # Add proposer score boost if the block is timely time_into_slot = (store.time - store.genesis_time) % SECONDS_PER_SLOT is_before_attesting_interval = time_into_slot < SECONDS_PER_SLOT // INTERVALS_PER_SLOT - if get_current_slot(store) == block.slot and is_before_attesting_interval: + is_first_block = store.proposer_boost_root == Root() + if get_current_slot(store) == block.slot and is_before_attesting_interval and is_first_block: store.proposer_boost_root = hash_tree_root(block) # Update checkpoints in store if necessary diff --git a/specs/deneb/fork-choice.md b/specs/deneb/fork-choice.md index 9faa11077f..8790ea1baa 100644 --- a/specs/deneb/fork-choice.md +++ b/specs/deneb/fork-choice.md @@ -111,7 +111,8 @@ def on_block(store: Store, signed_block: SignedBeaconBlock) -> None: # Add proposer score boost if the block is timely time_into_slot = (store.time - store.genesis_time) % SECONDS_PER_SLOT is_before_attesting_interval = time_into_slot < SECONDS_PER_SLOT // INTERVALS_PER_SLOT - if get_current_slot(store) == block.slot and is_before_attesting_interval: + is_first_block = store.proposer_boost_root == Root() + if get_current_slot(store) == block.slot and is_before_attesting_interval and is_first_block: store.proposer_boost_root = hash_tree_root(block) # Update checkpoints in store if necessary diff --git a/specs/phase0/fork-choice.md b/specs/phase0/fork-choice.md index be6edca643..8b52186dda 100644 --- a/specs/phase0/fork-choice.md +++ b/specs/phase0/fork-choice.md @@ -539,7 +539,8 @@ def on_block(store: Store, signed_block: SignedBeaconBlock) -> None: # Add proposer score boost if the block is timely time_into_slot = (store.time - store.genesis_time) % SECONDS_PER_SLOT is_before_attesting_interval = time_into_slot < SECONDS_PER_SLOT // INTERVALS_PER_SLOT - if get_current_slot(store) == block.slot and is_before_attesting_interval: + is_first_block = store.proposer_boost_root == Root() + if get_current_slot(store) == block.slot and is_before_attesting_interval and is_first_block: store.proposer_boost_root = hash_tree_root(block) # Update checkpoints in store if necessary From 2210cea734c625567cc481c9e9ea352d2ca3a327 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Thu, 1 Jun 2023 23:56:14 +0800 Subject: [PATCH 02/46] Add deneb fc tests and update test format --- .../test/deneb/fork_choice/__init__.py | 0 .../test/deneb/fork_choice/test_on_block.py | 105 ++++++++++++++++++ .../eth2spec/test/helpers/fork_choice.py | 78 +++++++++++-- .../test/phase0/fork_choice/test_get_head.py | 5 +- tests/formats/fork_choice/README.md | 38 ++++++- tests/generators/fork_choice/main.py | 8 +- 6 files changed, 215 insertions(+), 19 deletions(-) create mode 100644 tests/core/pyspec/eth2spec/test/deneb/fork_choice/__init__.py create mode 100644 tests/core/pyspec/eth2spec/test/deneb/fork_choice/test_on_block.py diff --git a/tests/core/pyspec/eth2spec/test/deneb/fork_choice/__init__.py b/tests/core/pyspec/eth2spec/test/deneb/fork_choice/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/core/pyspec/eth2spec/test/deneb/fork_choice/test_on_block.py b/tests/core/pyspec/eth2spec/test/deneb/fork_choice/test_on_block.py new file mode 100644 index 0000000000..85610675c5 --- /dev/null +++ b/tests/core/pyspec/eth2spec/test/deneb/fork_choice/test_on_block.py @@ -0,0 +1,105 @@ +from random import Random + +from eth2spec.test.context import ( + spec_state_test, + with_deneb_and_later, +) + +from eth2spec.test.helpers.block import ( + build_empty_block_for_next_slot, +) +from eth2spec.test.helpers.fork_choice import ( + BlobData, + get_genesis_forkchoice_store_and_block, + on_tick_and_append_step, + tick_and_add_block, + with_blob_data, +) +from eth2spec.test.helpers.state import ( + state_transition_and_sign_block, +) +from eth2spec.test.helpers.sharding import ( + get_sample_opaque_tx +) + + +def get_block_with_blob(spec, state, rng=None): + block = build_empty_block_for_next_slot(spec, state) + opaque_tx, blobs, blob_kzg_commitments, blob_kzg_proofs = get_sample_opaque_tx(spec, blob_count=1, rng=rng) + block.body.execution_payload.transactions = [opaque_tx] + # block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) + block.body.blob_kzg_commitments = blob_kzg_commitments + return block, blobs, blob_kzg_proofs + + +@with_deneb_and_later +@spec_state_test +def test_simple_blob_data(spec, state): + rng = Random(1234) + + test_steps = [] + # Initialization + store, anchor_block = get_genesis_forkchoice_store_and_block(spec, state) + yield 'anchor_state', state + yield 'anchor_block', anchor_block + current_time = state.slot * spec.config.SECONDS_PER_SLOT + store.genesis_time + on_tick_and_append_step(spec, store, current_time, test_steps) + assert store.time == current_time + + # On receiving a block of `GENESIS_SLOT + 1` slot + block, blobs, blob_kzg_proofs = get_block_with_blob(spec, state, rng=rng) + signed_block = state_transition_and_sign_block(spec, state, block) + blob_data = BlobData(blobs, blob_kzg_proofs) + + def run_func_1(): + yield from tick_and_add_block(spec, store, signed_block, test_steps, blob_data=blob_data) + + yield from with_blob_data(spec, blob_data, run_func_1) + + assert spec.get_head(store) == signed_block.message.hash_tree_root() + + # On receiving a block of next epoch + store.time = current_time + spec.config.SECONDS_PER_SLOT * spec.SLOTS_PER_EPOCH + block, blobs, blob_kzg_proofs = get_block_with_blob(spec, state, rng=rng) + signed_block = state_transition_and_sign_block(spec, state, block) + blob_data = BlobData(blobs, blob_kzg_proofs) + + def run_func_2(): + yield from tick_and_add_block(spec, store, signed_block, test_steps, blob_data=blob_data) + + yield from with_blob_data(spec, blob_data, run_func_2) + + assert spec.get_head(store) == signed_block.message.hash_tree_root() + + yield 'steps', test_steps + + +@with_deneb_and_later +@spec_state_test +def test_invalid_incorrect_proof(spec, state): + rng = Random(1234) + + test_steps = [] + # Initialization + store, anchor_block = get_genesis_forkchoice_store_and_block(spec, state) + yield 'anchor_state', state + yield 'anchor_block', anchor_block + current_time = state.slot * spec.config.SECONDS_PER_SLOT + store.genesis_time + on_tick_and_append_step(spec, store, current_time, test_steps) + assert store.time == current_time + + # On receiving a block of `GENESIS_SLOT + 1` slot + block, blobs, blob_kzg_proofs = get_block_with_blob(spec, state, rng=rng) + signed_block = state_transition_and_sign_block(spec, state, block) + # Insert incorrect proof + blob_kzg_proofs = [b'\xc0' + b'\x00' * 47] + blob_data = BlobData(blobs, blob_kzg_proofs) + + def run_func_1(): + yield from tick_and_add_block(spec, store, signed_block, test_steps, blob_data=blob_data, valid=False) + + yield from with_blob_data(spec, blob_data, run_func_1) + + assert spec.get_head(store) != signed_block.message.hash_tree_root() + + yield 'steps', test_steps diff --git a/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py b/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py index af231d87ff..d73a9a01b7 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py +++ b/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py @@ -1,3 +1,5 @@ +from typing import NamedTuple, Sequence, Any + from eth_utils import encode_hex from eth2spec.test.exceptions import BlockNotFoundException from eth2spec.test.helpers.attestations import ( @@ -7,6 +9,33 @@ ) +class BlobData(NamedTuple): + blobs: Sequence[Any] + proofs: Sequence[bytes] + + +def with_blob_data(spec, blob_data, func): + def retrieve_blobs_and_proofs(beacon_block_root): + return blob_data.blobs, blob_data.proofs + + retrieve_blobs_and_proofs_backup = spec.retrieve_blobs_and_proofs + spec.retrieve_blobs_and_proofs = retrieve_blobs_and_proofs + + class AtomicBoolean(): + value = False + is_called = AtomicBoolean() + + def wrap(flag: AtomicBoolean): + yield from func() + flag.value = True + + try: + yield from wrap(is_called) + finally: + spec.retrieve_blobs_and_proofs = retrieve_blobs_and_proofs_backup + assert is_called.value + + def get_anchor_root(spec, state): anchor_block_header = state.latest_block_header.copy() if anchor_block_header.state_root == spec.Bytes32(): @@ -15,7 +44,8 @@ def get_anchor_root(spec, state): def tick_and_add_block(spec, store, signed_block, test_steps, valid=True, - merge_block=False, block_not_found=False, is_optimistic=False): + merge_block=False, block_not_found=False, is_optimistic=False, + blob_data=None): pre_state = store.block_states[signed_block.message.parent_root] if merge_block: assert spec.is_merge_transition_block(pre_state, signed_block.message.body) @@ -30,6 +60,7 @@ def tick_and_add_block(spec, store, signed_block, test_steps, valid=True, valid=valid, block_not_found=block_not_found, is_optimistic=is_optimistic, + blob_data=blob_data, ) return post_state @@ -94,6 +125,13 @@ def get_attester_slashing_file_name(attester_slashing): return f"attester_slashing_{encode_hex(attester_slashing.hash_tree_root())}" +def get_blobs_file_name(blobs=None, blobs_root=None): + if blobs: + return f"blobs_{encode_hex(blobs.hash_tree_root())}" + else: + return f"blobs_{encode_hex(blobs_root)}" + + def on_tick_and_append_step(spec, store, time, test_steps): spec.on_tick(store, time) test_steps.append({'tick': int(time)}) @@ -119,35 +157,53 @@ def add_block(spec, test_steps, valid=True, block_not_found=False, - is_optimistic=False): + is_optimistic=False, + blob_data=None): """ Run on_block and on_attestation """ yield get_block_file_name(signed_block), signed_block - if not valid: - if is_optimistic: - run_on_block(spec, store, signed_block, valid=True) + # Check blob_data + if blob_data is not None: + assert len(blob_data.blobs) == len(blob_data.proofs) + blobs = spec.List[spec.Blob, spec.MAX_BLOBS_PER_BLOCK](blob_data.blobs) + blobs_root = blobs.hash_tree_root() + yield get_blobs_file_name(blobs_root=blobs_root), blobs + + is_blob_data_test = blob_data is not None + + def _append_step(is_blob_data_test, valid=True): + if is_blob_data_test: test_steps.append({ 'block': get_block_file_name(signed_block), - 'valid': False, + 'blobs': get_blobs_file_name(blobs_root=blobs_root), + 'proofs': [encode_hex(proof) for proof in blob_data.proofs], + 'valid': valid, }) + else: + test_steps.append({ + 'block': get_block_file_name(signed_block), + 'valid': valid, + }) + + if not valid: + if is_optimistic: + run_on_block(spec, store, signed_block, valid=True) + _append_step(is_blob_data_test, valid=False) else: try: run_on_block(spec, store, signed_block, valid=True) except (AssertionError, BlockNotFoundException) as e: if isinstance(e, BlockNotFoundException) and not block_not_found: assert False - test_steps.append({ - 'block': get_block_file_name(signed_block), - 'valid': False, - }) + _append_step(is_blob_data_test, valid=False) return else: assert False else: run_on_block(spec, store, signed_block, valid=True) - test_steps.append({'block': get_block_file_name(signed_block)}) + _append_step(is_blob_data_test) # An on_block step implies receiving block's attestations for attestation in signed_block.message.body.attestations: diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_get_head.py b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_get_head.py index 30f94b854c..886fcbd209 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_get_head.py +++ b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_get_head.py @@ -34,9 +34,6 @@ ) -rng = random.Random(1001) - - @with_altair_and_later @spec_state_test def test_genesis(spec, state): @@ -271,6 +268,7 @@ def test_proposer_boost_correct_head(spec, state): next_slots(spec, state_2, 2) block_2 = build_empty_block_for_next_slot(spec, state_2) signed_block_2 = state_transition_and_sign_block(spec, state_2.copy(), block_2) + rng = random.Random(1001) while spec.hash_tree_root(block_1) >= spec.hash_tree_root(block_2): block_2.body.graffiti = spec.Bytes32(hex(rng.getrandbits(8 * 32))[2:].zfill(64)) signed_block_2 = state_transition_and_sign_block(spec, state_2.copy(), block_2) @@ -339,6 +337,7 @@ def test_discard_equivocations_on_attester_slashing(spec, state): next_slots(spec, state_2, 2) block_2 = build_empty_block_for_next_slot(spec, state_2) signed_block_2 = state_transition_and_sign_block(spec, state_2.copy(), block_2) + rng = random.Random(1001) while spec.hash_tree_root(block_1) >= spec.hash_tree_root(block_2): block_2.body.graffiti = spec.Bytes32(hex(rng.getrandbits(8 * 32))[2:].zfill(64)) signed_block_2 = state_transition_and_sign_block(spec, state_2.copy(), block_2) diff --git a/tests/formats/fork_choice/README.md b/tests/formats/fork_choice/README.md index 3b28837de7..bfc8a423d7 100644 --- a/tests/formats/fork_choice/README.md +++ b/tests/formats/fork_choice/README.md @@ -2,6 +2,30 @@ The aim of the fork choice tests is to provide test coverage of the various components of the fork choice. +## Table of contents + + + + +- [Test case format](#test-case-format) + - [`meta.yaml`](#metayaml) + - [`anchor_state.ssz_snappy`](#anchor_statessz_snappy) + - [`anchor_block.ssz_snappy`](#anchor_blockssz_snappy) + - [`steps.yaml`](#stepsyaml) + - [`on_tick` execution step](#on_tick-execution-step) + - [`on_attestation` execution step](#on_attestation-execution-step) + - [`on_block` execution step](#on_block-execution-step) + - [`on_merge_block` execution step](#on_merge_block-execution-step) + - [`on_attester_slashing` execution step](#on_attester_slashing-execution-step) + - [`on_payload_info` execution step](#on_payload_info-execution-step) + - [Checks step](#checks-step) + - [`attestation_<32-byte-root>.ssz_snappy`](#attestation_32-byte-rootssz_snappy) + - [`block_<32-byte-root>.ssz_snappy`](#block_32-byte-rootssz_snappy) +- [Condition](#condition) + + + + ## Test case format ### `meta.yaml` @@ -59,14 +83,20 @@ The parameter that is required for executing `on_block(store, block)`. ```yaml { - block: string -- the name of the `block_<32-byte-root>.ssz_snappy` file. - To execute `on_block(store, block)` with the given attestation. - valid: bool -- optional, default to `true`. - If it's `false`, this execution step is expected to be invalid. + block: string -- the name of the `block_<32-byte-root>.ssz_snappy` file. + To execute `on_block(store, block)` with the given attestation. + blobs: string -- optional, the name of the `blobs_<32-byte-root>.ssz_snappy` file. + The blobs file content is a `List[Blob, MAX_BLOBS_PER_BLOCK]` SSZ object. + proofs: array of byte48 hex string -- optional, the proofs of blob commitments. + valid: bool -- optional, default to `true`. + If it's `false`, this execution step is expected to be invalid. } ``` + The file is located in the same folder (see below). +`blobs` and `proofs` are new fields from Deneb EIP-4844. These are the expected values from `retrieve_blobs_and_proofs()` helper inside `is_data_available()` helper. + After this step, the `store` object may have been updated. #### `on_merge_block` execution step diff --git a/tests/generators/fork_choice/main.py b/tests/generators/fork_choice/main.py index b0c9a9bb9d..7ff028cd80 100644 --- a/tests/generators/fork_choice/main.py +++ b/tests/generators/fork_choice/main.py @@ -19,7 +19,13 @@ ]} bellatrix_mods = combine_mods(_new_bellatrix_mods, altair_mods) capella_mods = bellatrix_mods # No additional Capella specific fork choice tests - deneb_mods = capella_mods # No additional Deneb specific fork choice tests + + # Deneb adds `is_data_available` tests + _new_deneb_mods = {key: 'eth2spec.test.deneb.fork_choice.test_' + key for key in [ + 'on_block', + ]} + deneb_mods = combine_mods(_new_deneb_mods, capella_mods) + eip6110_mods = deneb_mods # No additional EIP6110 specific fork choice tests all_mods = { From d2d351f7c99a7ac8ab69e3f9ff706467bf7c9664 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Tue, 25 Jul 2023 23:30:31 +0800 Subject: [PATCH 03/46] Add `test_invalid_data_unavailable` --- .../test/deneb/fork_choice/test_on_block.py | 33 ++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/tests/core/pyspec/eth2spec/test/deneb/fork_choice/test_on_block.py b/tests/core/pyspec/eth2spec/test/deneb/fork_choice/test_on_block.py index 85610675c5..e1dc536c6e 100644 --- a/tests/core/pyspec/eth2spec/test/deneb/fork_choice/test_on_block.py +++ b/tests/core/pyspec/eth2spec/test/deneb/fork_choice/test_on_block.py @@ -89,7 +89,7 @@ def test_invalid_incorrect_proof(spec, state): assert store.time == current_time # On receiving a block of `GENESIS_SLOT + 1` slot - block, blobs, blob_kzg_proofs = get_block_with_blob(spec, state, rng=rng) + block, blobs, _ = get_block_with_blob(spec, state, rng=rng) signed_block = state_transition_and_sign_block(spec, state, block) # Insert incorrect proof blob_kzg_proofs = [b'\xc0' + b'\x00' * 47] @@ -103,3 +103,34 @@ def run_func_1(): assert spec.get_head(store) != signed_block.message.hash_tree_root() yield 'steps', test_steps + + +@with_deneb_and_later +@spec_state_test +def test_invalid_data_unavailable(spec, state): + rng = Random(1234) + + test_steps = [] + # Initialization + store, anchor_block = get_genesis_forkchoice_store_and_block(spec, state) + yield 'anchor_state', state + yield 'anchor_block', anchor_block + current_time = state.slot * spec.config.SECONDS_PER_SLOT + store.genesis_time + on_tick_and_append_step(spec, store, current_time, test_steps) + assert store.time == current_time + + # On receiving a block of `GENESIS_SLOT + 1` slot + block, _, _ = get_block_with_blob(spec, state, rng=rng) + signed_block = state_transition_and_sign_block(spec, state, block) + + # data unavailable + blob_data = BlobData([], []) + + def run_func_1(): + yield from tick_and_add_block(spec, store, signed_block, test_steps, blob_data=blob_data, valid=False) + + yield from with_blob_data(spec, blob_data, run_func_1) + + assert spec.get_head(store) != signed_block.message.hash_tree_root() + + yield 'steps', test_steps From e79caff2f7db930fa6e164cab29a1817f61a26aa Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Tue, 25 Jul 2023 23:32:55 +0800 Subject: [PATCH 04/46] Clean up `is_data_available`. Remove the stub `retrieve_blobs_and_proofs` responses. --- pysetup/spec_builders/deneb.py | 4 ++-- specs/deneb/fork-choice.md | 5 ----- tests/core/pyspec/eth2spec/test/helpers/fork_choice.py | 7 +++++++ 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/pysetup/spec_builders/deneb.py b/pysetup/spec_builders/deneb.py index b4e180c2ae..c32bee8305 100644 --- a/pysetup/spec_builders/deneb.py +++ b/pysetup/spec_builders/deneb.py @@ -21,9 +21,9 @@ def preparations(cls): @classmethod def sundry_functions(cls) -> str: return ''' -def retrieve_blobs_and_proofs(beacon_block_root: Root) -> PyUnion[Tuple[Blob, KZGProof], Tuple[str, str]]: +def retrieve_blobs_and_proofs(beacon_block_root: Root) -> Tuple[Sequence[Blob], Sequence[KZGProof]]: # pylint: disable=unused-argument - return ("TEST", "TEST")''' + return [], []''' @classmethod def execution_engine_cls(cls) -> str: diff --git a/specs/deneb/fork-choice.md b/specs/deneb/fork-choice.md index 23eef436c1..2805fd1468 100644 --- a/specs/deneb/fork-choice.md +++ b/specs/deneb/fork-choice.md @@ -55,11 +55,6 @@ def is_data_available(beacon_block_root: Root, blob_kzg_commitments: Sequence[KZ # `MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS` blobs, proofs = retrieve_blobs_and_proofs(beacon_block_root) - # For testing, `retrieve_blobs_and_proofs` returns ("TEST", "TEST"). - # TODO: Remove it once we have a way to inject `BlobSidecar` into tests. - if isinstance(blobs, str) or isinstance(proofs, str): - return True - return verify_blob_kzg_proof_batch(blobs, blob_kzg_commitments, proofs) ``` diff --git a/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py b/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py index d73a9a01b7..c3f496a49f 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py +++ b/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py @@ -10,11 +10,18 @@ class BlobData(NamedTuple): + """ + The return values of ``retrieve_blobs_and_proofs`` helper. + """ blobs: Sequence[Any] proofs: Sequence[bytes] def with_blob_data(spec, blob_data, func): + """ + This helper runs the given ``func`` with monkeypatched ``retrieve_blobs_and_proofs`` + that returns ``blob_data.blobs, blob_data.proofs``. + """ def retrieve_blobs_and_proofs(beacon_block_root): return blob_data.blobs, blob_data.proofs From 85b0ae854f6958c6c6815fdfd3d14cd052ee805a Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Thu, 27 Jul 2023 23:12:49 +0800 Subject: [PATCH 05/46] handle `len(blobs) == 0` case --- tests/core/pyspec/eth2spec/test/helpers/fork_choice.py | 10 ++++++---- tests/formats/fork_choice/README.md | 3 ++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py b/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py index c3f496a49f..a6babefd3d 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py +++ b/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py @@ -173,10 +173,12 @@ def add_block(spec, # Check blob_data if blob_data is not None: - assert len(blob_data.blobs) == len(blob_data.proofs) blobs = spec.List[spec.Blob, spec.MAX_BLOBS_PER_BLOCK](blob_data.blobs) - blobs_root = blobs.hash_tree_root() - yield get_blobs_file_name(blobs_root=blobs_root), blobs + if len(blobs) > 0: + blobs_root = blobs.hash_tree_root() + yield get_blobs_file_name(blobs_root=blobs_root), blobs + else: + blobs_root = None is_blob_data_test = blob_data is not None @@ -184,7 +186,7 @@ def _append_step(is_blob_data_test, valid=True): if is_blob_data_test: test_steps.append({ 'block': get_block_file_name(signed_block), - 'blobs': get_blobs_file_name(blobs_root=blobs_root), + 'blobs': None if blobs_root is None else get_blobs_file_name(blobs_root=blobs_root), 'proofs': [encode_hex(proof) for proof in blob_data.proofs], 'valid': valid, }) diff --git a/tests/formats/fork_choice/README.md b/tests/formats/fork_choice/README.md index bfc8a423d7..4347e95f2c 100644 --- a/tests/formats/fork_choice/README.md +++ b/tests/formats/fork_choice/README.md @@ -85,8 +85,9 @@ The parameter that is required for executing `on_block(store, block)`. { block: string -- the name of the `block_<32-byte-root>.ssz_snappy` file. To execute `on_block(store, block)` with the given attestation. - blobs: string -- optional, the name of the `blobs_<32-byte-root>.ssz_snappy` file. + blobs: string or `null` -- optional, the name of the `blobs_<32-byte-root>.ssz_snappy` file. The blobs file content is a `List[Blob, MAX_BLOBS_PER_BLOCK]` SSZ object. + If it's `null`, `blobs` is an empty list. proofs: array of byte48 hex string -- optional, the proofs of blob commitments. valid: bool -- optional, default to `true`. If it's `false`, this execution step is expected to be invalid. From 6e4d4896ed9a27ae311f2e158783df202232ca31 Mon Sep 17 00:00:00 2001 From: WenceslasSANCHEZ Date: Thu, 27 Jul 2023 21:17:45 +0200 Subject: [PATCH 06/46] =?UTF-8?q?=F0=9F=90=9B=20set=20chaos=20to=20`True`?= =?UTF-8?q?=20to=20enable=20uint=20variation=20for=20non=20random=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/generators/ssz_generic/ssz_uints.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/generators/ssz_generic/ssz_uints.py b/tests/generators/ssz_generic/ssz_uints.py index 896443f4cc..f52b33f522 100644 --- a/tests/generators/ssz_generic/ssz_uints.py +++ b/tests/generators/ssz_generic/ssz_uints.py @@ -9,7 +9,7 @@ def uint_case_fn(rng: Random, mode: RandomizationMode, typ: Type[BasicView]): return get_random_ssz_object(rng, typ, max_bytes_length=typ.type_byte_length(), max_list_length=1, - mode=mode, chaos=False) + mode=mode, chaos=True) UINT_TYPES = [uint8, uint16, uint32, uint64, uint128, uint256] From 32056b2d44de3a4a3f41858f800bbabb01737252 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Mon, 31 Jul 2023 19:23:17 +0800 Subject: [PATCH 07/46] PR feedback from @djrtwo --- .../test/deneb/fork_choice/test_on_block.py | 84 ++++++++++++++----- .../eth2spec/test/helpers/fork_choice.py | 7 ++ 2 files changed, 72 insertions(+), 19 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/deneb/fork_choice/test_on_block.py b/tests/core/pyspec/eth2spec/test/deneb/fork_choice/test_on_block.py index e1dc536c6e..12451f4ca3 100644 --- a/tests/core/pyspec/eth2spec/test/deneb/fork_choice/test_on_block.py +++ b/tests/core/pyspec/eth2spec/test/deneb/fork_choice/test_on_block.py @@ -8,18 +8,20 @@ from eth2spec.test.helpers.block import ( build_empty_block_for_next_slot, ) +from eth2spec.test.helpers.execution_payload import ( + compute_el_block_hash, +) from eth2spec.test.helpers.fork_choice import ( BlobData, get_genesis_forkchoice_store_and_block, on_tick_and_append_step, - tick_and_add_block, - with_blob_data, + tick_and_add_block_with_data, ) from eth2spec.test.helpers.state import ( state_transition_and_sign_block, ) from eth2spec.test.helpers.sharding import ( - get_sample_opaque_tx + get_sample_opaque_tx, ) @@ -27,7 +29,7 @@ def get_block_with_blob(spec, state, rng=None): block = build_empty_block_for_next_slot(spec, state) opaque_tx, blobs, blob_kzg_commitments, blob_kzg_proofs = get_sample_opaque_tx(spec, blob_count=1, rng=rng) block.body.execution_payload.transactions = [opaque_tx] - # block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) + block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) block.body.blob_kzg_commitments = blob_kzg_commitments return block, blobs, blob_kzg_proofs @@ -51,10 +53,7 @@ def test_simple_blob_data(spec, state): signed_block = state_transition_and_sign_block(spec, state, block) blob_data = BlobData(blobs, blob_kzg_proofs) - def run_func_1(): - yield from tick_and_add_block(spec, store, signed_block, test_steps, blob_data=blob_data) - - yield from with_blob_data(spec, blob_data, run_func_1) + yield from tick_and_add_block_with_data(spec, store, signed_block, test_steps, blob_data) assert spec.get_head(store) == signed_block.message.hash_tree_root() @@ -64,10 +63,7 @@ def run_func_1(): signed_block = state_transition_and_sign_block(spec, state, block) blob_data = BlobData(blobs, blob_kzg_proofs) - def run_func_2(): - yield from tick_and_add_block(spec, store, signed_block, test_steps, blob_data=blob_data) - - yield from with_blob_data(spec, blob_data, run_func_2) + yield from tick_and_add_block_with_data(spec, store, signed_block, test_steps, blob_data) assert spec.get_head(store) == signed_block.message.hash_tree_root() @@ -95,10 +91,7 @@ def test_invalid_incorrect_proof(spec, state): blob_kzg_proofs = [b'\xc0' + b'\x00' * 47] blob_data = BlobData(blobs, blob_kzg_proofs) - def run_func_1(): - yield from tick_and_add_block(spec, store, signed_block, test_steps, blob_data=blob_data, valid=False) - - yield from with_blob_data(spec, blob_data, run_func_1) + yield from tick_and_add_block_with_data(spec, store, signed_block, test_steps, blob_data, valid=False) assert spec.get_head(store) != signed_block.message.hash_tree_root() @@ -126,10 +119,63 @@ def test_invalid_data_unavailable(spec, state): # data unavailable blob_data = BlobData([], []) - def run_func_1(): - yield from tick_and_add_block(spec, store, signed_block, test_steps, blob_data=blob_data, valid=False) + yield from tick_and_add_block_with_data(spec, store, signed_block, test_steps, blob_data, valid=False) + + assert spec.get_head(store) != signed_block.message.hash_tree_root() + + yield 'steps', test_steps + + +@with_deneb_and_later +@spec_state_test +def test_invalid_wrong_proofs_length(spec, state): + rng = Random(1234) + + test_steps = [] + # Initialization + store, anchor_block = get_genesis_forkchoice_store_and_block(spec, state) + yield 'anchor_state', state + yield 'anchor_block', anchor_block + current_time = state.slot * spec.config.SECONDS_PER_SLOT + store.genesis_time + on_tick_and_append_step(spec, store, current_time, test_steps) + assert store.time == current_time + + # On receiving a block of `GENESIS_SLOT + 1` slot + block, blobs, _ = get_block_with_blob(spec, state, rng=rng) + signed_block = state_transition_and_sign_block(spec, state, block) + + # unavailable proofs + blob_data = BlobData(blobs, []) + + yield from tick_and_add_block_with_data(spec, store, signed_block, test_steps, blob_data, valid=False) + + assert spec.get_head(store) != signed_block.message.hash_tree_root() + + yield 'steps', test_steps + + +@with_deneb_and_later +@spec_state_test +def test_invalid_wrong_blobs_length(spec, state): + rng = Random(1234) + + test_steps = [] + # Initialization + store, anchor_block = get_genesis_forkchoice_store_and_block(spec, state) + yield 'anchor_state', state + yield 'anchor_block', anchor_block + current_time = state.slot * spec.config.SECONDS_PER_SLOT + store.genesis_time + on_tick_and_append_step(spec, store, current_time, test_steps) + assert store.time == current_time + + # On receiving a block of `GENESIS_SLOT + 1` slot + block, _, blob_kzg_proofs = get_block_with_blob(spec, state, rng=rng) + signed_block = state_transition_and_sign_block(spec, state, block) + + # unavailable blobs + blob_data = BlobData([], blob_kzg_proofs) - yield from with_blob_data(spec, blob_data, run_func_1) + yield from tick_and_add_block_with_data(spec, store, signed_block, test_steps, blob_data, valid=False) assert spec.get_head(store) != signed_block.message.hash_tree_root() diff --git a/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py b/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py index a6babefd3d..ba4f294bc0 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py +++ b/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py @@ -73,6 +73,13 @@ def tick_and_add_block(spec, store, signed_block, test_steps, valid=True, return post_state +def tick_and_add_block_with_data(spec, store, signed_block, test_steps, blob_data, valid=True): + def run_func(): + yield from tick_and_add_block(spec, store, signed_block, test_steps, blob_data=blob_data, valid=valid) + + yield from with_blob_data(spec, blob_data, run_func) + + def add_attestation(spec, store, attestation, test_steps, is_from_block=False): spec.on_attestation(store, attestation, is_from_block=is_from_block) yield get_attestation_file_name(attestation), attestation From 39134d594ae0b8f345ec51b95b6ebec713321915 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Mon, 31 Jul 2023 19:47:53 +0800 Subject: [PATCH 08/46] Change it back to allow empty `blobs` list file --- tests/core/pyspec/eth2spec/test/helpers/fork_choice.py | 9 +++------ tests/formats/fork_choice/README.md | 5 ++--- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py b/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py index ba4f294bc0..e0e3547222 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py +++ b/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py @@ -181,11 +181,8 @@ def add_block(spec, # Check blob_data if blob_data is not None: blobs = spec.List[spec.Blob, spec.MAX_BLOBS_PER_BLOCK](blob_data.blobs) - if len(blobs) > 0: - blobs_root = blobs.hash_tree_root() - yield get_blobs_file_name(blobs_root=blobs_root), blobs - else: - blobs_root = None + blobs_root = blobs.hash_tree_root() + yield get_blobs_file_name(blobs_root=blobs_root), blobs is_blob_data_test = blob_data is not None @@ -193,7 +190,7 @@ def _append_step(is_blob_data_test, valid=True): if is_blob_data_test: test_steps.append({ 'block': get_block_file_name(signed_block), - 'blobs': None if blobs_root is None else get_blobs_file_name(blobs_root=blobs_root), + 'blobs': get_blobs_file_name(blobs_root=blobs_root), 'proofs': [encode_hex(proof) for proof in blob_data.proofs], 'valid': valid, }) diff --git a/tests/formats/fork_choice/README.md b/tests/formats/fork_choice/README.md index 4347e95f2c..d23de865b3 100644 --- a/tests/formats/fork_choice/README.md +++ b/tests/formats/fork_choice/README.md @@ -85,9 +85,8 @@ The parameter that is required for executing `on_block(store, block)`. { block: string -- the name of the `block_<32-byte-root>.ssz_snappy` file. To execute `on_block(store, block)` with the given attestation. - blobs: string or `null` -- optional, the name of the `blobs_<32-byte-root>.ssz_snappy` file. + blobs: string -- optional, the name of the `blobs_<32-byte-root>.ssz_snappy` file. The blobs file content is a `List[Blob, MAX_BLOBS_PER_BLOCK]` SSZ object. - If it's `null`, `blobs` is an empty list. proofs: array of byte48 hex string -- optional, the proofs of blob commitments. valid: bool -- optional, default to `true`. If it's `false`, this execution step is expected to be invalid. @@ -96,7 +95,7 @@ The parameter that is required for executing `on_block(store, block)`. The file is located in the same folder (see below). -`blobs` and `proofs` are new fields from Deneb EIP-4844. These are the expected values from `retrieve_blobs_and_proofs()` helper inside `is_data_available()` helper. +`blobs` and `proofs` are new fields from Deneb EIP-4844. These fields indicate the expected values from `retrieve_blobs_and_proofs()` helper inside `is_data_available()` helper. If these two fields are not provided, `retrieve_blobs_and_proofs()` returns empty lists. After this step, the `store` object may have been updated. From b412bdb34eb94844215acfb08164288eb05288c8 Mon Sep 17 00:00:00 2001 From: WenceslasSANCHEZ Date: Tue, 1 Aug 2023 21:44:35 +0200 Subject: [PATCH 09/46] =?UTF-8?q?=E2=9C=A8=20allow=20`container=5Fcase=5Ff?= =?UTF-8?q?n`=20to=20change=20chaos=20mode?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/generators/ssz_generic/ssz_container.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/generators/ssz_generic/ssz_container.py b/tests/generators/ssz_generic/ssz_container.py index 1b30d687ac..03877ca9fd 100644 --- a/tests/generators/ssz_generic/ssz_container.py +++ b/tests/generators/ssz_generic/ssz_container.py @@ -46,11 +46,11 @@ class BitsStruct(Container): E: Bitvector[8] -def container_case_fn(rng: Random, mode: RandomizationMode, typ: Type[View]): +def container_case_fn(rng: Random, mode: RandomizationMode, typ: Type[View], chaos: bool=False): return get_random_ssz_object(rng, typ, max_bytes_length=2000, max_list_length=2000, - mode=mode, chaos=False) + mode=mode, chaos=chaos) PRESET_CONTAINERS: Dict[str, Tuple[Type[View], Sequence[int]]] = { @@ -79,7 +79,7 @@ def valid_cases(): valid_test_case(lambda: container_case_fn(rng, mode, typ)) for variation in range(3): yield f'{name}_{mode.to_name()}_chaos_{variation}', \ - valid_test_case(lambda: container_case_fn(rng, mode, typ)) + valid_test_case(lambda: container_case_fn(rng, mode, typ, chaos=True)) def mod_offset(b: bytes, offset_index: int, change: Callable[[int], int]): From bd34c6560c9f43f5fbdf130ad0a4de44258e2a17 Mon Sep 17 00:00:00 2001 From: WenceslasSANCHEZ Date: Tue, 1 Aug 2023 21:45:30 +0200 Subject: [PATCH 10/46] =?UTF-8?q?=F0=9F=90=9B=20remove=20redundant=20modes?= =?UTF-8?q?=20for=20container=20without=20offsets?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/generators/ssz_generic/ssz_container.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/tests/generators/ssz_generic/ssz_container.py b/tests/generators/ssz_generic/ssz_container.py index 03877ca9fd..8b3e1dad65 100644 --- a/tests/generators/ssz_generic/ssz_container.py +++ b/tests/generators/ssz_generic/ssz_container.py @@ -68,12 +68,16 @@ def valid_cases(): for (name, (typ, offsets)) in PRESET_CONTAINERS.items(): for mode in [RandomizationMode.mode_zero, RandomizationMode.mode_max]: yield f'{name}_{mode.to_name()}', valid_test_case(lambda: container_case_fn(rng, mode, typ)) - random_modes = [RandomizationMode.mode_random, RandomizationMode.mode_zero, RandomizationMode.mode_max] + modes = [RandomizationMode.mode_random] if len(offsets) != 0: - random_modes.extend([RandomizationMode.mode_nil_count, - RandomizationMode.mode_one_count, - RandomizationMode.mode_max_count]) - for mode in random_modes: + # Notes: ``RandomizationMode.mode_zero`` and ``RandomizationMode.mode_max`` are + # pseudo-random modes here because the length of List and Bitlist are randoms. + modes.extend([RandomizationMode.mode_nil_count, + RandomizationMode.mode_one_count, + RandomizationMode.mode_max_count, + RandomizationMode.mode_zero, + RandomizationMode.mode_max]) + for mode in modes: for variation in range(10): yield f'{name}_{mode.to_name()}_{variation}', \ valid_test_case(lambda: container_case_fn(rng, mode, typ)) From 872e404bce9759262cdebd496e07b0bc109d53ee Mon Sep 17 00:00:00 2001 From: WenceslasSANCHEZ Date: Tue, 1 Aug 2023 21:46:16 +0200 Subject: [PATCH 11/46] =?UTF-8?q?=E2=8F=AA=EF=B8=8F=20reset=20``chaos``=20?= =?UTF-8?q?to=20False?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/generators/ssz_generic/ssz_uints.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/generators/ssz_generic/ssz_uints.py b/tests/generators/ssz_generic/ssz_uints.py index f52b33f522..896443f4cc 100644 --- a/tests/generators/ssz_generic/ssz_uints.py +++ b/tests/generators/ssz_generic/ssz_uints.py @@ -9,7 +9,7 @@ def uint_case_fn(rng: Random, mode: RandomizationMode, typ: Type[BasicView]): return get_random_ssz_object(rng, typ, max_bytes_length=typ.type_byte_length(), max_list_length=1, - mode=mode, chaos=True) + mode=mode, chaos=False) UINT_TYPES = [uint8, uint16, uint32, uint64, uint128, uint256] From 01dfc77202e41e04b751e9e3eaef993c4cf14836 Mon Sep 17 00:00:00 2001 From: WenceslasSANCHEZ Date: Tue, 1 Aug 2023 21:47:08 +0200 Subject: [PATCH 12/46] =?UTF-8?q?=F0=9F=90=9B=20remove=20non-random=20mode?= =?UTF-8?q?=20from=20variation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/generators/ssz_generic/ssz_uints.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/generators/ssz_generic/ssz_uints.py b/tests/generators/ssz_generic/ssz_uints.py index 896443f4cc..ea2d2f855b 100644 --- a/tests/generators/ssz_generic/ssz_uints.py +++ b/tests/generators/ssz_generic/ssz_uints.py @@ -18,13 +18,16 @@ def uint_case_fn(rng: Random, mode: RandomizationMode, typ: Type[BasicView]): def valid_cases(): rng = Random(1234) for uint_type in UINT_TYPES: + mode = RandomizationMode.mode_random byte_len = uint_type.type_byte_length() yield f'uint_{byte_len * 8}_last_byte_empty', \ valid_test_case(lambda: uint_type((2 ** ((byte_len - 1) * 8)) - 1)) for variation in range(5): - for mode in [RandomizationMode.mode_random, RandomizationMode.mode_zero, RandomizationMode.mode_max]: - yield f'uint_{byte_len * 8}_{mode.to_name()}_{variation}', \ - valid_test_case(lambda: uint_case_fn(rng, mode, uint_type)) + yield f'uint_{byte_len * 8}_{mode.to_name()}_{variation}',\ + valid_test_case(lambda: uint_case_fn(rng, mode, uint_type)) + for mode in [RandomizationMode.mode_zero, RandomizationMode.mode_max]: + yield f'uint_{byte_len * 8}_{mode.to_name()}',\ + valid_test_case(lambda: uint_case_fn(rng, mode, uint_type)) def invalid_cases(): From 6231dc2e4e2cf3793c951aad8bb16ddbc1fe49a6 Mon Sep 17 00:00:00 2001 From: WenceslasSANCHEZ Date: Tue, 1 Aug 2023 22:09:35 +0200 Subject: [PATCH 13/46] =?UTF-8?q?=F0=9F=90=9B=20split=20variation=20with?= =?UTF-8?q?=20and=20without=20chaos?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/generators/ssz_generic/ssz_container.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/tests/generators/ssz_generic/ssz_container.py b/tests/generators/ssz_generic/ssz_container.py index 8b3e1dad65..30cbd127cb 100644 --- a/tests/generators/ssz_generic/ssz_container.py +++ b/tests/generators/ssz_generic/ssz_container.py @@ -68,10 +68,24 @@ def valid_cases(): for (name, (typ, offsets)) in PRESET_CONTAINERS.items(): for mode in [RandomizationMode.mode_zero, RandomizationMode.mode_max]: yield f'{name}_{mode.to_name()}', valid_test_case(lambda: container_case_fn(rng, mode, typ)) + + modes = [RandomizationMode.mode_random, RandomizationMode.mode_zero, RandomizationMode.mode_max] + if len(offsets) != 0: + modes.extend([RandomizationMode.mode_nil_count, + RandomizationMode.mode_one_count, + RandomizationMode.mode_max_count]) + for mode in modes: + for variation in range(3): + yield f'{name}_{mode.to_name()}_chaos_{variation}', \ + valid_test_case(lambda: container_case_fn(rng, mode, typ, chaos=True)) + # Notes: Below is the second wave of iteration, and only the random mode is selected + # for container without offset since ``RandomizationMode.mode_zero`` and ``RandomizationMode.mode_max`` + # are deterministic. modes = [RandomizationMode.mode_random] if len(offsets) != 0: # Notes: ``RandomizationMode.mode_zero`` and ``RandomizationMode.mode_max`` are - # pseudo-random modes here because the length of List and Bitlist are randoms. + # pseudo-random modes for containers that contains List of Bitlist + # (because the length of List and Bitlist are randoms). modes.extend([RandomizationMode.mode_nil_count, RandomizationMode.mode_one_count, RandomizationMode.mode_max_count, @@ -81,9 +95,6 @@ def valid_cases(): for variation in range(10): yield f'{name}_{mode.to_name()}_{variation}', \ valid_test_case(lambda: container_case_fn(rng, mode, typ)) - for variation in range(3): - yield f'{name}_{mode.to_name()}_chaos_{variation}', \ - valid_test_case(lambda: container_case_fn(rng, mode, typ, chaos=True)) def mod_offset(b: bytes, offset_index: int, change: Callable[[int], int]): From da2121060e36afe774becab9ff6e52f076b5760d Mon Sep 17 00:00:00 2001 From: WenceslasSANCHEZ Date: Tue, 1 Aug 2023 22:19:48 +0200 Subject: [PATCH 14/46] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor=20+=20linte?= =?UTF-8?q?r=20comments?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/generators/ssz_generic/ssz_container.py | 11 +++-------- tests/generators/ssz_generic/ssz_uints.py | 4 ++-- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/tests/generators/ssz_generic/ssz_container.py b/tests/generators/ssz_generic/ssz_container.py index 30cbd127cb..84c7d8df54 100644 --- a/tests/generators/ssz_generic/ssz_container.py +++ b/tests/generators/ssz_generic/ssz_container.py @@ -71,9 +71,8 @@ def valid_cases(): modes = [RandomizationMode.mode_random, RandomizationMode.mode_zero, RandomizationMode.mode_max] if len(offsets) != 0: - modes.extend([RandomizationMode.mode_nil_count, - RandomizationMode.mode_one_count, - RandomizationMode.mode_max_count]) + modes = list(RandomizationMode) + for mode in modes: for variation in range(3): yield f'{name}_{mode.to_name()}_chaos_{variation}', \ @@ -86,11 +85,7 @@ def valid_cases(): # Notes: ``RandomizationMode.mode_zero`` and ``RandomizationMode.mode_max`` are # pseudo-random modes for containers that contains List of Bitlist # (because the length of List and Bitlist are randoms). - modes.extend([RandomizationMode.mode_nil_count, - RandomizationMode.mode_one_count, - RandomizationMode.mode_max_count, - RandomizationMode.mode_zero, - RandomizationMode.mode_max]) + modes = list(RandomizationMode) for mode in modes: for variation in range(10): yield f'{name}_{mode.to_name()}_{variation}', \ diff --git a/tests/generators/ssz_generic/ssz_uints.py b/tests/generators/ssz_generic/ssz_uints.py index ea2d2f855b..abf7fc75b2 100644 --- a/tests/generators/ssz_generic/ssz_uints.py +++ b/tests/generators/ssz_generic/ssz_uints.py @@ -23,10 +23,10 @@ def valid_cases(): yield f'uint_{byte_len * 8}_last_byte_empty', \ valid_test_case(lambda: uint_type((2 ** ((byte_len - 1) * 8)) - 1)) for variation in range(5): - yield f'uint_{byte_len * 8}_{mode.to_name()}_{variation}',\ + yield f'uint_{byte_len * 8}_{mode.to_name()}_{variation}', \ valid_test_case(lambda: uint_case_fn(rng, mode, uint_type)) for mode in [RandomizationMode.mode_zero, RandomizationMode.mode_max]: - yield f'uint_{byte_len * 8}_{mode.to_name()}',\ + yield f'uint_{byte_len * 8}_{mode.to_name()}', \ valid_test_case(lambda: uint_case_fn(rng, mode, uint_type)) From 7ba238a1e85a19b2409e52bba938cc3c9bed2523 Mon Sep 17 00:00:00 2001 From: Potuz Date: Wed, 2 Aug 2023 10:40:10 -0300 Subject: [PATCH 15/46] fix test format descrition --- tests/formats/kzg/verify_blob_kzg_proof_batch.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/formats/kzg/verify_blob_kzg_proof_batch.md b/tests/formats/kzg/verify_blob_kzg_proof_batch.md index 3bcc74d6bb..46d7f3345b 100644 --- a/tests/formats/kzg/verify_blob_kzg_proof_batch.md +++ b/tests/formats/kzg/verify_blob_kzg_proof_batch.md @@ -8,9 +8,9 @@ The test data is declared in a `data.yaml` file: ```yaml input: - blob: List[Blob] -- the data blob - commitment: List[KZGCommitment] -- the KZG commitment to the data blob - proof: List[KZGProof] -- The KZG proof + blobs: List[Blob] -- the data blob + commitments: List[KZGCommitment] -- the KZG commitment to the data blob + proofs: List[KZGProof] -- The KZG proof output: bool -- true (all proofs are valid) or false (some proofs incorrect) ``` From b30990330f7117eaa1c71fff8d0f48669712a817 Mon Sep 17 00:00:00 2001 From: Potuz Date: Wed, 2 Aug 2023 14:16:31 -0300 Subject: [PATCH 16/46] Danny's fix --- tests/formats/kzg/verify_blob_kzg_proof_batch.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/formats/kzg/verify_blob_kzg_proof_batch.md b/tests/formats/kzg/verify_blob_kzg_proof_batch.md index 46d7f3345b..82e668497d 100644 --- a/tests/formats/kzg/verify_blob_kzg_proof_batch.md +++ b/tests/formats/kzg/verify_blob_kzg_proof_batch.md @@ -1,6 +1,6 @@ # Test format: Verify blob KZG proof batch -Use the blob KZG proofs to verify that the KZG commitments for given `blob`s are correct +Use the blob KZG proofs to verify that the KZG commitments for given `blobs` are correct ## Test case format @@ -14,7 +14,7 @@ input: output: bool -- true (all proofs are valid) or false (some proofs incorrect) ``` -- `blob`s here are encoded as a string: hexadecimal encoding of `4096 * 32 = 131072` bytes, prefixed with `0x`. +- `blobs` here are encoded as a string: hexadecimal encoding of `4096 * 32 = 131072` bytes, prefixed with `0x`. All byte(s) fields are encoded as strings, hexadecimal encoding, prefixed with `0x`. From 36d9ea6cd19963ddf0f3628b438e85dc6acddf64 Mon Sep 17 00:00:00 2001 From: Dmitrii Shmatko Date: Thu, 3 Aug 2023 19:12:07 +0300 Subject: [PATCH 17/46] Fix dimension of nodeId and remove outdated comment (#3445) * Fix dimension of nodeId and remove incorrect comment * Remove debugging --- .../test/phase0/unittests/validator/test_validator_unittest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/core/pyspec/eth2spec/test/phase0/unittests/validator/test_validator_unittest.py b/tests/core/pyspec/eth2spec/test/phase0/unittests/validator/test_validator_unittest.py index 918ab96e2e..f5f417bbaf 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/unittests/validator/test_validator_unittest.py +++ b/tests/core/pyspec/eth2spec/test/phase0/unittests/validator/test_validator_unittest.py @@ -485,7 +485,7 @@ def test_get_aggregate_and_proof_signature(spec, state): def run_compute_subscribed_subnets_arguments(spec, rng=random.Random(1111)): - node_id = rng.randint(0, 2**40 - 1) # try VALIDATOR_REGISTRY_LIMIT + node_id = rng.randint(0, 2**256 - 1) epoch = rng.randint(0, 2**64 - 1) subnets = spec.compute_subscribed_subnets(node_id, epoch) assert len(subnets) == spec.config.SUBNETS_PER_NODE From 5612e7a9ee0abb7410932d06d6e6c41ff64c4f83 Mon Sep 17 00:00:00 2001 From: WenceslasSANCHEZ Date: Thu, 3 Aug 2023 18:19:33 +0200 Subject: [PATCH 18/46] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/generators/ssz_generic/ssz_container.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/tests/generators/ssz_generic/ssz_container.py b/tests/generators/ssz_generic/ssz_container.py index 84c7d8df54..e3d0ed3c61 100644 --- a/tests/generators/ssz_generic/ssz_container.py +++ b/tests/generators/ssz_generic/ssz_container.py @@ -69,8 +69,9 @@ def valid_cases(): for mode in [RandomizationMode.mode_zero, RandomizationMode.mode_max]: yield f'{name}_{mode.to_name()}', valid_test_case(lambda: container_case_fn(rng, mode, typ)) - modes = [RandomizationMode.mode_random, RandomizationMode.mode_zero, RandomizationMode.mode_max] - if len(offsets) != 0: + if len(offsets) == 0: + modes = [RandomizationMode.mode_random, RandomizationMode.mode_zero, RandomizationMode.mode_max] + else: modes = list(RandomizationMode) for mode in modes: @@ -80,12 +81,7 @@ def valid_cases(): # Notes: Below is the second wave of iteration, and only the random mode is selected # for container without offset since ``RandomizationMode.mode_zero`` and ``RandomizationMode.mode_max`` # are deterministic. - modes = [RandomizationMode.mode_random] - if len(offsets) != 0: - # Notes: ``RandomizationMode.mode_zero`` and ``RandomizationMode.mode_max`` are - # pseudo-random modes for containers that contains List of Bitlist - # (because the length of List and Bitlist are randoms). - modes = list(RandomizationMode) + modes = [RandomizationMode.mode_random] if len(offsets) != 0 else list(RandomizationMode) for mode in modes: for variation in range(10): yield f'{name}_{mode.to_name()}_{variation}', \ From 522ab42064db98f2c3e507e8bdaaa51de8c3d146 Mon Sep 17 00:00:00 2001 From: Wenceslas Sanchez <85337624+wenceslas-sanchez@users.noreply.github.com> Date: Thu, 3 Aug 2023 21:14:45 +0200 Subject: [PATCH 19/46] Update tests/generators/ssz_generic/ssz_container.py Co-authored-by: Hsiao-Wei Wang --- tests/generators/ssz_generic/ssz_container.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/generators/ssz_generic/ssz_container.py b/tests/generators/ssz_generic/ssz_container.py index e3d0ed3c61..2c1d37da88 100644 --- a/tests/generators/ssz_generic/ssz_container.py +++ b/tests/generators/ssz_generic/ssz_container.py @@ -81,7 +81,7 @@ def valid_cases(): # Notes: Below is the second wave of iteration, and only the random mode is selected # for container without offset since ``RandomizationMode.mode_zero`` and ``RandomizationMode.mode_max`` # are deterministic. - modes = [RandomizationMode.mode_random] if len(offsets) != 0 else list(RandomizationMode) + modes = [RandomizationMode.mode_random] if len(offsets) == 0 else list(RandomizationMode) for mode in modes: for variation in range(10): yield f'{name}_{mode.to_name()}_{variation}', \ From 1904b47e3e4e74d460eaa355f9d9b9834143f728 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Fri, 4 Aug 2023 21:41:57 +0800 Subject: [PATCH 20/46] Add `test_proposer_boost_is_first_block` test case --- .../test/phase0/fork_choice/test_on_block.py | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_on_block.py b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_on_block.py index 840413a364..cd41350496 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_on_block.py +++ b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_on_block.py @@ -539,6 +539,56 @@ def test_proposer_boost_root_same_slot_untimely_block(spec, state): yield 'steps', test_steps +@with_altair_and_later +@spec_state_test +def test_proposer_boost_is_first_block(spec, state): + test_steps = [] + genesis_state = state.copy() + + # Initialization + store, anchor_block = get_genesis_forkchoice_store_and_block(spec, state) + yield 'anchor_state', state + yield 'anchor_block', anchor_block + + # Build block that serves as head ONLY on timely arrival, and ONLY in that slot + state = genesis_state.copy() + next_slots(spec, state, 3) + pre_state = state.copy() + block_a = build_empty_block_for_next_slot(spec, state) + signed_block_a = state_transition_and_sign_block(spec, state, block_a) + + # Process block on timely arrival just before end of boost interval + time = (store.genesis_time + block_a.slot * spec.config.SECONDS_PER_SLOT + + spec.config.SECONDS_PER_SLOT // spec.INTERVALS_PER_SLOT - 1) + on_tick_and_append_step(spec, store, time, test_steps) + yield from add_block(spec, store, signed_block_a, test_steps) + # `proposer_boost_root` is now `block_a` + assert store.proposer_boost_root == spec.hash_tree_root(block_a) + assert spec.get_weight(store, spec.hash_tree_root(block_a)) > 0 + test_steps.append({ + 'checks': { + 'proposer_boost_root': encode_hex(store.proposer_boost_root), + } + }) + + # make a different block at the same slot + state = pre_state.copy() + block_b = block_a.copy() + block_b.body.graffiti = b'\x34' * 32 + signed_block_b = state_transition_and_sign_block(spec, state, block_b) + yield from add_block(spec, store, signed_block_b, test_steps) + # `proposer_boost_root` is still `block_a` + assert store.proposer_boost_root == spec.hash_tree_root(block_a) + assert spec.get_weight(store, spec.hash_tree_root(block_b)) == 0 + test_steps.append({ + 'checks': { + 'proposer_boost_root': encode_hex(store.proposer_boost_root), + } + }) + + yield 'steps', test_steps + + @with_altair_and_later @spec_state_test @with_presets([MINIMAL], reason="too slow") From fa1015ca03a681027beb9ecceb754e7e9d16709a Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Fri, 4 Aug 2023 21:58:01 +0800 Subject: [PATCH 21/46] Update Capella FC too --- specs/capella/fork-choice.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/specs/capella/fork-choice.md b/specs/capella/fork-choice.md index 87fec02f8c..a830080c11 100644 --- a/specs/capella/fork-choice.md +++ b/specs/capella/fork-choice.md @@ -106,7 +106,8 @@ def on_block(store: Store, signed_block: SignedBeaconBlock) -> None: # Add proposer score boost if the block is timely time_into_slot = (store.time - store.genesis_time) % SECONDS_PER_SLOT is_before_attesting_interval = time_into_slot < SECONDS_PER_SLOT // INTERVALS_PER_SLOT - if get_current_slot(store) == block.slot and is_before_attesting_interval: + is_first_block = store.proposer_boost_root == Root() + if get_current_slot(store) == block.slot and is_before_attesting_interval and is_first_block: store.proposer_boost_root = hash_tree_root(block) # Update checkpoints in store if necessary From 875fabcbd08a6b5bef1d2157e752d272858ec034 Mon Sep 17 00:00:00 2001 From: WenceslasSANCHEZ Date: Sun, 6 Aug 2023 14:13:11 +0200 Subject: [PATCH 22/46] =?UTF-8?q?=F0=9F=90=9B=20remove=20lambda=20definiti?= =?UTF-8?q?on=20from=20`generate=5Ffrom=5Ftests`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/pyspec/eth2spec/gen_helpers/gen_from_tests/gen.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/core/pyspec/eth2spec/gen_helpers/gen_from_tests/gen.py b/tests/core/pyspec/eth2spec/gen_helpers/gen_from_tests/gen.py index b951a6a85c..17ffe0b468 100644 --- a/tests/core/pyspec/eth2spec/gen_helpers/gen_from_tests/gen.py +++ b/tests/core/pyspec/eth2spec/gen_helpers/gen_from_tests/gen.py @@ -10,6 +10,10 @@ from eth2spec.gen_helpers.gen_base.gen_typing import TestCase, TestProvider +def generate_case_fn(tfn, generator_mode, phase, preset, bls_active): + return lambda: tfn(generator_mode=generator_mode, phase=phase, preset=preset, bls_active=bls_active) + + def generate_from_tests(runner_name: str, handler_name: str, src: Any, fork_name: SpecForkName, preset_name: PresetBaseName, bls_active: bool = True, @@ -52,7 +56,7 @@ def generate_from_tests(runner_name: str, handler_name: str, src: Any, suite_name=getattr(tfn, 'suite_name', 'pyspec_tests'), case_name=case_name, # TODO: with_all_phases and other per-phase tooling, should be replaced with per-fork equivalent. - case_fn=lambda: tfn(generator_mode=True, phase=phase, preset=preset_name, bls_active=bls_active) + case_fn=generate_case_fn(tfn, generator_mode=True, phase=phase, preset=preset_name, bls_active=bls_active) ) From 6f57e2bd26b1a5c3491a53d6212d242987c7cae0 Mon Sep 17 00:00:00 2001 From: zhiqiangxu <652732310@qq.com> Date: Wed, 9 Aug 2023 11:13:39 +0800 Subject: [PATCH 23/46] "can slashable" => "can be slashable" --- specs/phase0/validator.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/phase0/validator.md b/specs/phase0/validator.md index 602df09736..86b230654c 100644 --- a/specs/phase0/validator.md +++ b/specs/phase0/validator.md @@ -606,7 +606,7 @@ def get_aggregate_and_proof_signature(state: BeaconState, "Slashing" is the burning of some amount of validator funds and immediate ejection from the active validator set. In Phase 0, there are two ways in which funds can be slashed: [proposer slashing](#proposer-slashing) and [attester slashing](#attester-slashing). Although being slashed has serious repercussions, it is simple enough to avoid being slashed all together by remaining _consistent_ with respect to the messages a validator has previously signed. -*Note*: Signed data must be within a sequential `Fork` context to conflict. Messages cannot be slashed across diverging forks. If the previous fork version is 1 and the chain splits into fork 2 and 102, messages from 1 can slashable against messages in forks 1, 2, and 102. Messages in 2 cannot be slashable against messages in 102, and vice versa. +*Note*: Signed data must be within a sequential `Fork` context to conflict. Messages cannot be slashed across diverging forks. If the previous fork version is 1 and the chain splits into fork 2 and 102, messages from 1 can be slashable against messages in forks 1, 2, and 102. Messages in 2 cannot be slashable against messages in 102, and vice versa. ### Proposer slashing From 0b751fc25987f16fe8e192eba0368554efc48c48 Mon Sep 17 00:00:00 2001 From: Lion - dapplion <35266934+dapplion@users.noreply.github.com> Date: Sun, 20 Aug 2023 13:42:12 +0200 Subject: [PATCH 24/46] Whisk move non-preset values to config (#3482) --- specs/_features/whisk/beacon-chain.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/specs/_features/whisk/beacon-chain.md b/specs/_features/whisk/beacon-chain.md index c955585903..41aa3bb09f 100644 --- a/specs/_features/whisk/beacon-chain.md +++ b/specs/_features/whisk/beacon-chain.md @@ -12,6 +12,7 @@ - [Constants](#constants) - [Domain types](#domain-types) - [Preset](#preset) +- [Configuration](#configuration) - [Cryptography](#cryptography) - [BLS](#bls) - [Curdleproofs and opening proofs](#curdleproofs-and-opening-proofs) @@ -52,12 +53,17 @@ This document details the beacon chain additions and changes of to support the W | `CURDLEPROOFS_N_BLINDERS` | `uint64(4)` | number of blinders for curdleproofs | | `WHISK_CANDIDATE_TRACKERS_COUNT` | `uint64(2**14)` (= 16,384) | number of candidate trackers | | `WHISK_PROPOSER_TRACKERS_COUNT` | `uint64(2**13)` (= 8,192) | number of proposer trackers | -| `WHISK_EPOCHS_PER_SHUFFLING_PHASE` | `Epoch(2**8)` (= 256) | epochs per shuffling phase | | `WHISK_VALIDATORS_PER_SHUFFLE` | `uint64(2**7 - 4)` (= 124) | number of validators shuffled per shuffle step | -| `WHISK_PROPOSER_SELECTION_GAP` | `Epoch(2)` | gap between proposer selection and the block proposal phase | | `WHISK_MAX_SHUFFLE_PROOF_SIZE` | `uint64(2**15)` | max size of a shuffle proof | | `WHISK_MAX_OPENING_PROOF_SIZE` | `uint64(2**10)` | max size of a opening proof | +## Configuration + +| Name | Value | Description | +| ---------------------------------- | -------------------------- | ----------------------------------------------------------- | +| `WHISK_EPOCHS_PER_SHUFFLING_PHASE` | `Epoch(2**8)` (= 256) | epochs per shuffling phase | +| `WHISK_PROPOSER_SELECTION_GAP` | `Epoch(2)` | gap between proposer selection and the block proposal phase | + ## Cryptography ### BLS From eef61448a9aba2b61e77364bb920e028dd5963c1 Mon Sep 17 00:00:00 2001 From: Lion - dapplion <35266934+dapplion@users.noreply.github.com> Date: Sun, 20 Aug 2023 13:46:22 +0200 Subject: [PATCH 25/46] Whisk: don't mutate candidates during cooldown (#3483) --- specs/_features/whisk/beacon-chain.md | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/specs/_features/whisk/beacon-chain.md b/specs/_features/whisk/beacon-chain.md index 41aa3bb09f..c719dabe1b 100644 --- a/specs/_features/whisk/beacon-chain.md +++ b/specs/_features/whisk/beacon-chain.md @@ -324,30 +324,25 @@ def get_shuffle_indices(randao_reveal: BLSSignature) -> Sequence[uint64]: ```python def process_shuffled_trackers(state: BeaconState, body: BeaconBlockBody) -> None: - # Check the shuffle proof - shuffle_indices = get_shuffle_indices(body.randao_reveal) - pre_shuffle_trackers = [state.whisk_candidate_trackers[i] for i in shuffle_indices] - shuffle_epoch = get_current_epoch(state) % WHISK_EPOCHS_PER_SHUFFLING_PHASE if shuffle_epoch + WHISK_PROPOSER_SELECTION_GAP + 1 >= WHISK_EPOCHS_PER_SHUFFLING_PHASE: # Require trackers set to zero during cooldown assert body.whisk_post_shuffle_trackers == Vector[WhiskTracker, WHISK_VALIDATORS_PER_SHUFFLE]() assert body.whisk_shuffle_proof_M_commitment == BLSG1Point() assert body.whisk_shuffle_proof == WhiskShuffleProof() - post_shuffle_trackers = pre_shuffle_trackers else: # Require shuffled trackers during shuffle + shuffle_indices = get_shuffle_indices(body.randao_reveal) + pre_shuffle_trackers = [state.whisk_candidate_trackers[i] for i in shuffle_indices] assert IsValidWhiskShuffleProof( pre_shuffle_trackers, body.whisk_post_shuffle_trackers, body.whisk_shuffle_proof_M_commitment, body.whisk_shuffle_proof, ) - post_shuffle_trackers = body.whisk_post_shuffle_trackers - - # Shuffle candidate trackers - for i, shuffle_index in enumerate(shuffle_indices): - state.whisk_candidate_trackers[shuffle_index] = post_shuffle_trackers[i] + # Shuffle candidate trackers + for i, shuffle_index in enumerate(shuffle_indices): + state.whisk_candidate_trackers[shuffle_index] = body.whisk_post_shuffle_trackers[i] ``` ```python From fc03e8f714b0480fdec9acd21e7675ecc830cea1 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Sun, 20 Aug 2023 12:27:19 -0600 Subject: [PATCH 26/46] Rename BLS/KZG suite name --- tests/generators/bls/main.py | 2 +- tests/generators/kzg_4844/main.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/generators/bls/main.py b/tests/generators/bls/main.py index 8c187c68c1..35bd2b6a95 100644 --- a/tests/generators/bls/main.py +++ b/tests/generators/bls/main.py @@ -519,7 +519,7 @@ def cases_fn() -> Iterable[gen_typing.TestCase]: preset_name='general', runner_name='bls', handler_name=handler_name, - suite_name='small', + suite_name='bls', case_name=case_name, case_fn=lambda: [('data', 'data', case_content)] ) diff --git a/tests/generators/kzg_4844/main.py b/tests/generators/kzg_4844/main.py index 9297e524ac..1c5aefc61f 100644 --- a/tests/generators/kzg_4844/main.py +++ b/tests/generators/kzg_4844/main.py @@ -527,7 +527,7 @@ def cases_fn() -> Iterable[gen_typing.TestCase]: preset_name='general', runner_name='kzg', handler_name=handler_name, - suite_name='small', + suite_name='kzg-mainnet', case_name=case_name, case_fn=lambda: [('data', 'data', case_content)] ) From 6a944a60f31742b774f4956b4f2858605b936cf0 Mon Sep 17 00:00:00 2001 From: dapplion <35266934+dapplion@users.noreply.github.com> Date: Mon, 21 Aug 2023 11:58:08 +0200 Subject: [PATCH 27/46] Update preset and config files --- configs/mainnet.yaml | 6 ++++++ configs/minimal.yaml | 4 ++++ presets/mainnet/whisk.yaml | 4 ---- presets/minimal/whisk.yaml | 4 ---- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/configs/mainnet.yaml b/configs/mainnet.yaml index ed018aab1b..bcd18e5cdc 100644 --- a/configs/mainnet.yaml +++ b/configs/mainnet.yaml @@ -140,3 +140,9 @@ MAX_REQUEST_BLOB_SIDECARS: 768 MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS: 4096 # `6` BLOB_SIDECAR_SUBNET_COUNT: 6 + +# Whisk +# `Epoch(2**8)` +WHISK_EPOCHS_PER_SHUFFLING_PHASE: 256 +# `Epoch(2)` +WHISK_PROPOSER_SELECTION_GAP: 2 diff --git a/configs/minimal.yaml b/configs/minimal.yaml index 238598b0e0..d23ca7adb2 100644 --- a/configs/minimal.yaml +++ b/configs/minimal.yaml @@ -141,3 +141,7 @@ MAX_REQUEST_BLOB_SIDECARS: 768 MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS: 4096 # `6` BLOB_SIDECAR_SUBNET_COUNT: 6 + +# Whisk +WHISK_EPOCHS_PER_SHUFFLING_PHASE: 4 +WHISK_PROPOSER_SELECTION_GAP: 1 diff --git a/presets/mainnet/whisk.yaml b/presets/mainnet/whisk.yaml index 3086ff29de..f39b15bd33 100644 --- a/presets/mainnet/whisk.yaml +++ b/presets/mainnet/whisk.yaml @@ -8,12 +8,8 @@ CURDLEPROOFS_N_BLINDERS: 4 WHISK_CANDIDATE_TRACKERS_COUNT: 16384 # `uint64(2**13)` must be < WHISK_CANDIDATE_TRACKERS_COUNT WHISK_PROPOSER_TRACKERS_COUNT: 8192 -# `Epoch(2**8)` -WHISK_EPOCHS_PER_SHUFFLING_PHASE: 256 # `uint64(2**7 - CURDLEPROOFS_N_BLINDERS)` WHISK_VALIDATORS_PER_SHUFFLE: 124 -# `Epoch(2)` -WHISK_PROPOSER_SELECTION_GAP: 2 # `uint64(2**15)` TODO: will be replaced by a fix format once there's a serialized format WHISK_MAX_SHUFFLE_PROOF_SIZE: 32768 # `uint64(2**10)` TODO: will be replaced by a fix format once there's a serialized format diff --git a/presets/minimal/whisk.yaml b/presets/minimal/whisk.yaml index 1a726f79c2..0cb4b3a60c 100644 --- a/presets/minimal/whisk.yaml +++ b/presets/minimal/whisk.yaml @@ -9,11 +9,7 @@ WHISK_CANDIDATE_TRACKERS_COUNT: 32 # [customized] WHISK_PROPOSER_TRACKERS_COUNT: 16 # [customized] -WHISK_EPOCHS_PER_SHUFFLING_PHASE: 4 -# [customized] WHISK_VALIDATORS_PER_SHUFFLE: 4 -# [customized] -WHISK_PROPOSER_SELECTION_GAP: 1 # `uint64(2**15)` TODO: will be replaced by a fix format once there's a serialized format WHISK_MAX_SHUFFLE_PROOF_SIZE: 32768 # `uint64(2**10)` TODO: will be replaced by a fix format once there's a serialized format From 59680c04701c159f45fcc9f19a9bf9fb183dacf7 Mon Sep 17 00:00:00 2001 From: terence tsao Date: Tue, 5 Sep 2023 08:59:14 -0700 Subject: [PATCH 28/46] remove: old warnings --- specs/altair/fork.md | 2 -- specs/altair/p2p-interface.md | 6 ------ specs/altair/validator.md | 5 ----- specs/bellatrix/fork.md | 2 -- specs/bellatrix/p2p-interface.md | 6 ------ specs/capella/fork.md | 2 -- 6 files changed, 23 deletions(-) diff --git a/specs/altair/fork.md b/specs/altair/fork.md index bf8499a219..60b048abda 100644 --- a/specs/altair/fork.md +++ b/specs/altair/fork.md @@ -22,8 +22,6 @@ This document describes the process of the first upgrade of the beacon chain: th ## Configuration -Warning: this configuration is not definitive. - | Name | Value | | - | - | | `ALTAIR_FORK_VERSION` | `Version('0x01000000')` | diff --git a/specs/altair/p2p-interface.md b/specs/altair/p2p-interface.md index 0f278b08c5..fac540fb99 100644 --- a/specs/altair/p2p-interface.md +++ b/specs/altair/p2p-interface.md @@ -13,7 +13,6 @@ Altair adds new messages, topics and data to the Req-Resp, Gossip and Discovery -- [Warning](#warning) - [Modifications in Altair](#modifications-in-altair) - [MetaData](#metadata) - [The gossip domain: gossipsub](#the-gossip-domain-gossipsub) @@ -38,11 +37,6 @@ Altair adds new messages, topics and data to the Req-Resp, Gossip and Discovery -## Warning - -This document is currently illustrative for early Altair testnets and some parts are subject to change. -Refer to the note in the [validator guide](./validator.md) for further details. - ## Modifications in Altair ### MetaData diff --git a/specs/altair/validator.md b/specs/altair/validator.md index 013a516490..3602377acd 100644 --- a/specs/altair/validator.md +++ b/specs/altair/validator.md @@ -10,7 +10,6 @@ This is an accompanying document to [Altair -- The Beacon Chain](./beacon-chain. - [Introduction](#introduction) - [Prerequisites](#prerequisites) -- [Warning](#warning) - [Constants](#constants) - [Misc](#misc) - [Containers](#containers) @@ -63,10 +62,6 @@ Block proposers incorporate the (aggregated) sync committee signatures into each All terminology, constants, functions, and protocol mechanics defined in the [Altair -- The Beacon Chain](./beacon-chain.md) doc are requisite for this document and used throughout. Please see this document before continuing and use as a reference throughout. -## Warning - -This document is currently illustrative for early Altair testnets and some parts are subject to change, especially pending implementation and profiling of Altair testnets. - ## Constants ### Misc diff --git a/specs/bellatrix/fork.md b/specs/bellatrix/fork.md index a114e5a5fa..569dccdc66 100644 --- a/specs/bellatrix/fork.md +++ b/specs/bellatrix/fork.md @@ -22,8 +22,6 @@ This document describes the process of Bellatrix upgrade. ## Configuration -Warning: this configuration is not definitive. - | Name | Value | | - | - | | `BELLATRIX_FORK_VERSION` | `Version('0x02000000')` | diff --git a/specs/bellatrix/p2p-interface.md b/specs/bellatrix/p2p-interface.md index 7d80d40a83..032bc9ebec 100644 --- a/specs/bellatrix/p2p-interface.md +++ b/specs/bellatrix/p2p-interface.md @@ -12,7 +12,6 @@ Readers should understand the Phase 0 and Altair documents and use them as a bas - - [Warning](#warning) - [Modifications in Bellatrix](#modifications-in-bellatrix) - [The gossip domain: gossipsub](#the-gossip-domain-gossipsub) - [Topics and messages](#topics-and-messages) @@ -33,11 +32,6 @@ Readers should understand the Phase 0 and Altair documents and use them as a bas -## Warning - -This document is currently illustrative for early Bellatrix testnets and some parts are subject to change. -Refer to the note in the [validator guide](./validator.md) for further details. - ## Modifications in Bellatrix ### The gossip domain: gossipsub diff --git a/specs/capella/fork.md b/specs/capella/fork.md index 95bdf79aee..73d4ba2b71 100644 --- a/specs/capella/fork.md +++ b/specs/capella/fork.md @@ -22,8 +22,6 @@ This document describes the process of the Capella upgrade. ## Configuration -Warning: this configuration is not definitive. - | Name | Value | | - | - | | `CAPELLA_FORK_VERSION` | `Version('0x03000000')` | From 3eaa184fc776add977e55f15c95d8fd1a7524d88 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Wed, 6 Sep 2023 11:36:35 +0800 Subject: [PATCH 29/46] Pin `curdleproofs==0.1.1` --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index efa57ac2b1..b46423bdc1 100644 --- a/setup.py +++ b/setup.py @@ -519,6 +519,6 @@ def run(self): "lru-dict==1.2.0", MARKO_VERSION, "py_arkworks_bls12381==0.3.4", - "curdleproofs @ git+https://github.com/nalinbhardwaj/curdleproofs.pie@805d06785b6ff35fde7148762277dd1ae678beeb#egg=curdleproofs&subdirectory=curdleproofs", + "curdleproofs==0.1.1", ] ) From e6f7c99b77a6918eff0b21c7fc534a802adc6352 Mon Sep 17 00:00:00 2001 From: dapplion <35266934+dapplion@users.noreply.github.com> Date: Tue, 12 Sep 2023 10:33:36 +0200 Subject: [PATCH 30/46] Add limit inbound churn --- specs/_features/limit_churn/beacon_chain.md | 88 +++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 specs/_features/limit_churn/beacon_chain.md diff --git a/specs/_features/limit_churn/beacon_chain.md b/specs/_features/limit_churn/beacon_chain.md new file mode 100644 index 0000000000..7a7a98bf05 --- /dev/null +++ b/specs/_features/limit_churn/beacon_chain.md @@ -0,0 +1,88 @@ +Limit churn -- The Beacon Chain + +## Table of contents + + + + + +- [Introduction](#introduction) +- [Configuration](#configuration) + - [Validator cycle](#validator-cycle) +- [Helper functions](#helper-functions) + - [Beacon state accessors](#beacon-state-accessors) + - [New `get_validator_inbound_churn_limit`](#new-get_validator_inbound_churn_limit) +- [Beacon chain state transition function](#beacon-chain-state-transition-function) + - [Epoch processing](#epoch-processing) + - [Registry updates](#registry-updates) + + + + +## Introduction + +This is the beacon chain specification to limit the max inbound churn value, motivated to limit the validator active set growth rate. + +*Note:* This specification is built upon [Capella](../../capella/beacon_chain.md) and is under active development. + +## Configuration + +### Validator cycle + +| Name | Value | +| - | - | +| `MAX_PER_EPOCH_INBOUND_CHURN_LIMIT` | `uint64(12)` (= 12) | + +## Helper functions + +### Beacon state accessors + +#### New `get_validator_inbound_churn_limit` + +```python +def get_validator_inbound_churn_limit(state: BeaconState) -> uint64: + """ + Return the validator inbound churn limit for the current epoch. + """ + active_validator_indices = get_active_validator_indices(state, get_current_epoch(state)) + return min( + MAX_PER_EPOCH_INBOUND_CHURN_LIMIT, + max( + MIN_PER_EPOCH_CHURN_LIMIT, + uint64(len(active_validator_indices)) // CHURN_LIMIT_QUOTIENT + ) + ) +``` + +## Beacon chain state transition function + +### Epoch processing + +#### Registry updates + +```python +def process_registry_updates(state: BeaconState) -> None: + # Process activation eligibility and ejections + for index, validator in enumerate(state.validators): + if is_eligible_for_activation_queue(validator): + validator.activation_eligibility_epoch = get_current_epoch(state) + 1 + + if ( + is_active_validator(validator, get_current_epoch(state)) + and validator.effective_balance <= EJECTION_BALANCE + ): + initiate_validator_exit(state, ValidatorIndex(index)) + + # Queue validators eligible for activation and not yet dequeued for activation + activation_queue = sorted([ + index for index, validator in enumerate(state.validators) + if is_eligible_for_activation(state, validator) + # Order by the sequence of activation_eligibility_epoch setting and then index + ], key=lambda index: (state.validators[index].activation_eligibility_epoch, index)) + # Dequeued validators for activation up to churn limit + # [Modified in limit churn] + for index in activation_queue[:get_validator_inbound_churn_limit(state)]: + validator = state.validators[index] + validator.activation_epoch = compute_activation_exit_epoch(get_current_epoch(state)) +``` + From fd37ffcb61325753edbd8ff56a4cd2e45b5f244f Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Tue, 12 Sep 2023 21:35:46 +0800 Subject: [PATCH 31/46] Add _features/eip7668 and make linter happy --- .gitignore | 1 + pysetup/constants.py | 1 + pysetup/md_doc_paths.py | 2 + pysetup/spec_builders/__init__.py | 3 +- pysetup/spec_builders/eip7668.py | 12 ++ .../{limit_churn => eip7668}/beacon_chain.md | 10 +- specs/_features/eip7668/fork.md | 139 ++++++++++++++++++ 7 files changed, 162 insertions(+), 6 deletions(-) create mode 100644 pysetup/spec_builders/eip7668.py rename specs/_features/{limit_churn => eip7668}/beacon_chain.md (94%) create mode 100644 specs/_features/eip7668/fork.md diff --git a/.gitignore b/.gitignore index cdfddfb0c3..7eacf101cf 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,7 @@ tests/core/pyspec/eth2spec/capella/ tests/core/pyspec/eth2spec/deneb/ tests/core/pyspec/eth2spec/eip6110/ tests/core/pyspec/eth2spec/eip7002/ +tests/core/pyspec/eth2spec/eip7668/ tests/core/pyspec/eth2spec/whisk/ # coverage reports diff --git a/pysetup/constants.py b/pysetup/constants.py index 8d53455634..f6f8ed8e43 100644 --- a/pysetup/constants.py +++ b/pysetup/constants.py @@ -6,6 +6,7 @@ DENEB = 'deneb' EIP6110 = 'eip6110' EIP7002 = 'eip7002' +EIP7668 = 'eip7668' WHISK = 'whisk' diff --git a/pysetup/md_doc_paths.py b/pysetup/md_doc_paths.py index 781ae41db3..8031b09361 100644 --- a/pysetup/md_doc_paths.py +++ b/pysetup/md_doc_paths.py @@ -9,6 +9,7 @@ EIP6110, WHISK, EIP7002, + EIP7668, ) @@ -21,6 +22,7 @@ EIP6110: DENEB, WHISK: CAPELLA, EIP7002: CAPELLA, + EIP7668: CAPELLA, } ALL_FORKS = list(PREVIOUS_FORK_OF.keys()) diff --git a/pysetup/spec_builders/__init__.py b/pysetup/spec_builders/__init__.py index 794ae50d29..6f0f1d2032 100644 --- a/pysetup/spec_builders/__init__.py +++ b/pysetup/spec_builders/__init__.py @@ -5,6 +5,7 @@ from .deneb import DenebSpecBuilder from .eip6110 import EIP6110SpecBuilder from .eip7002 import EIP7002SpecBuilder +from .eip7668 import EIP7668SpecBuilder from .whisk import WhiskSpecBuilder @@ -12,6 +13,6 @@ builder.fork: builder for builder in ( Phase0SpecBuilder, AltairSpecBuilder, BellatrixSpecBuilder, CapellaSpecBuilder, DenebSpecBuilder, - EIP6110SpecBuilder, EIP7002SpecBuilder, WhiskSpecBuilder, + EIP6110SpecBuilder, EIP7002SpecBuilder, EIP7668SpecBuilder, WhiskSpecBuilder, ) } diff --git a/pysetup/spec_builders/eip7668.py b/pysetup/spec_builders/eip7668.py new file mode 100644 index 0000000000..55332dd9ee --- /dev/null +++ b/pysetup/spec_builders/eip7668.py @@ -0,0 +1,12 @@ +from .base import BaseSpecBuilder +from ..constants import EIP7668 + + +class EIP7668SpecBuilder(BaseSpecBuilder): + fork: str = EIP7668 + + @classmethod + def imports(cls, preset_name: str): + return super().imports(preset_name) + f''' +from eth2spec.capella import {preset_name} as capella +''' diff --git a/specs/_features/limit_churn/beacon_chain.md b/specs/_features/eip7668/beacon_chain.md similarity index 94% rename from specs/_features/limit_churn/beacon_chain.md rename to specs/_features/eip7668/beacon_chain.md index 7a7a98bf05..aeba214ebf 100644 --- a/specs/_features/limit_churn/beacon_chain.md +++ b/specs/_features/eip7668/beacon_chain.md @@ -46,11 +46,11 @@ def get_validator_inbound_churn_limit(state: BeaconState) -> uint64: """ active_validator_indices = get_active_validator_indices(state, get_current_epoch(state)) return min( - MAX_PER_EPOCH_INBOUND_CHURN_LIMIT, - max( - MIN_PER_EPOCH_CHURN_LIMIT, - uint64(len(active_validator_indices)) // CHURN_LIMIT_QUOTIENT - ) + MAX_PER_EPOCH_INBOUND_CHURN_LIMIT, + max( + MIN_PER_EPOCH_CHURN_LIMIT, + uint64(len(active_validator_indices)) // CHURN_LIMIT_QUOTIENT, + ), ) ``` diff --git a/specs/_features/eip7668/fork.md b/specs/_features/eip7668/fork.md new file mode 100644 index 0000000000..93d2a7d521 --- /dev/null +++ b/specs/_features/eip7668/fork.md @@ -0,0 +1,139 @@ +# EIP-7668 -- Fork Logic + +**Notice**: This document is a work-in-progress for researchers and implementers. + +## Table of contents + + + + +- [Introduction](#introduction) +- [Configuration](#configuration) +- [Helper functions](#helper-functions) + - [Misc](#misc) + - [Modified `compute_fork_version`](#modified-compute_fork_version) +- [Fork to EIP-7668](#fork-to-eip-7668) + - [Fork trigger](#fork-trigger) + - [Upgrading the state](#upgrading-the-state) + + + +## Introduction + +This document describes the process of EIP-7668 upgrade. + +## Configuration + +Warning: this configuration is not definitive. + +| Name | Value | +| - | - | +| `EIP7668_FORK_VERSION` | `Version('0x05000000')` | +| `EIP7668_FORK_EPOCH` | `Epoch(18446744073709551615)` **TBD** | + +## Helper functions + +### Misc + +#### Modified `compute_fork_version` + +```python +def compute_fork_version(epoch: Epoch) -> Version: + """ + Return the fork version at the given ``epoch``. + """ + if epoch >= EIP7668_FORK_EPOCH: + return EIP7668_FORK_VERSION + if epoch >= CAPELLA_FORK_EPOCH: + return CAPELLA_FORK_VERSION + if epoch >= BELLATRIX_FORK_EPOCH: + return BELLATRIX_FORK_VERSION + if epoch >= ALTAIR_FORK_EPOCH: + return ALTAIR_FORK_VERSION + return GENESIS_FORK_VERSION +``` + +## Fork to EIP-7668 + +### Fork trigger + +TBD. This fork is defined for testing purposes, the EIP may be combined with other consensus-layer upgrade. +For now, we assume the condition will be triggered at epoch `EIP7668_FORK_EPOCH`. + +Note that for the pure EIP-7668 networks, we don't apply `upgrade_to_eip7668` since it starts with EIP-7668 version logic. + +### Upgrading the state + +If `state.slot % SLOTS_PER_EPOCH == 0` and `compute_epoch_at_slot(state.slot) == EIP7668_FORK_EPOCH`, +an irregular state change is made to upgrade to EIP-7668. + +```python +def upgrade_to_eip7668(pre: capella.BeaconState) -> BeaconState: + epoch = capella.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=pre.latest_execution_payload_header.withdrawals_root, + ) + post = BeaconState( + # Versioning + genesis_time=pre.genesis_time, + genesis_validators_root=pre.genesis_validators_root, + slot=pre.slot, + fork=Fork( + previous_version=pre.fork.current_version, + current_version=EIP7668_FORK_VERSION, # [Modified in EIP-7668] + epoch=epoch, + ), + # History + latest_block_header=pre.latest_block_header, + block_roots=pre.block_roots, + state_roots=pre.state_roots, + historical_roots=pre.historical_roots, + # Eth1 + eth1_data=pre.eth1_data, + eth1_data_votes=pre.eth1_data_votes, + eth1_deposit_index=pre.eth1_deposit_index, + # Registry + validators=pre.validators, + balances=pre.balances, + # Randomness + randao_mixes=pre.randao_mixes, + # Slashings + slashings=pre.slashings, + # Participation + previous_epoch_participation=pre.previous_epoch_participation, + current_epoch_participation=pre.current_epoch_participation, + # Finality + justification_bits=pre.justification_bits, + previous_justified_checkpoint=pre.previous_justified_checkpoint, + current_justified_checkpoint=pre.current_justified_checkpoint, + finalized_checkpoint=pre.finalized_checkpoint, + # Inactivity + inactivity_scores=pre.inactivity_scores, + # Sync + current_sync_committee=pre.current_sync_committee, + next_sync_committee=pre.next_sync_committee, + # Execution-layer + latest_execution_payload_header=latest_execution_payload_header, + # Withdrawals + next_withdrawal_index=pre.next_withdrawal_index, + next_withdrawal_validator_index=pre.next_withdrawal_validator_index, + # Deep history valid from Capella onwards + historical_summaries=pre.historical_summaries, + ) + + return post +``` From cc3ced59653c39fb05a46ff33735144623ccdb1e Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Tue, 12 Sep 2023 21:48:35 +0800 Subject: [PATCH 32/46] Enable eip7668 pytest --- configs/mainnet.yaml | 6 ++++++ configs/minimal.yaml | 6 ++++++ tests/core/pyspec/eth2spec/test/context.py | 5 ++++- tests/core/pyspec/eth2spec/test/helpers/constants.py | 2 ++ tests/core/pyspec/eth2spec/test/helpers/forks.py | 4 +++- tests/core/pyspec/eth2spec/test/helpers/genesis.py | 5 ++++- 6 files changed, 25 insertions(+), 3 deletions(-) diff --git a/configs/mainnet.yaml b/configs/mainnet.yaml index bcd18e5cdc..87757d12ea 100644 --- a/configs/mainnet.yaml +++ b/configs/mainnet.yaml @@ -56,6 +56,9 @@ EIP6110_FORK_EPOCH: 18446744073709551615 # EIP7002 EIP7002_FORK_VERSION: 0x05000000 # temporary stub EIP7002_FORK_EPOCH: 18446744073709551615 +# EIP7668 +EIP7668_FORK_VERSION: 0x05000000 # temporary stub +EIP7668_FORK_EPOCH: 18446744073709551615 # WHISK WHISK_FORK_VERSION: 0x06000000 # temporary stub WHISK_FORK_EPOCH: 18446744073709551615 @@ -146,3 +149,6 @@ BLOB_SIDECAR_SUBNET_COUNT: 6 WHISK_EPOCHS_PER_SHUFFLING_PHASE: 256 # `Epoch(2)` WHISK_PROPOSER_SELECTION_GAP: 2 + +# EIP7668 +MAX_PER_EPOCH_INBOUND_CHURN_LIMIT: 12 diff --git a/configs/minimal.yaml b/configs/minimal.yaml index d23ca7adb2..642603bdda 100644 --- a/configs/minimal.yaml +++ b/configs/minimal.yaml @@ -55,6 +55,9 @@ EIP6110_FORK_EPOCH: 18446744073709551615 # EIP7002 EIP7002_FORK_VERSION: 0x05000001 EIP7002_FORK_EPOCH: 18446744073709551615 +# EIP7668 +EIP7668_FORK_VERSION: 0x05000001 # temporary stub +EIP7668_FORK_EPOCH: 18446744073709551615 # WHISK WHISK_FORK_VERSION: 0x06000001 WHISK_FORK_EPOCH: 18446744073709551615 @@ -145,3 +148,6 @@ BLOB_SIDECAR_SUBNET_COUNT: 6 # Whisk WHISK_EPOCHS_PER_SHUFFLING_PHASE: 4 WHISK_PROPOSER_SELECTION_GAP: 1 + +# EIP7668 +MAX_PER_EPOCH_INBOUND_CHURN_LIMIT: 12 diff --git a/tests/core/pyspec/eth2spec/test/context.py b/tests/core/pyspec/eth2spec/test/context.py index 0c9d4a1ec5..1da2947214 100644 --- a/tests/core/pyspec/eth2spec/test/context.py +++ b/tests/core/pyspec/eth2spec/test/context.py @@ -10,12 +10,13 @@ from eth2spec.deneb import mainnet as spec_deneb_mainnet, minimal as spec_deneb_minimal from eth2spec.eip6110 import mainnet as spec_eip6110_mainnet, minimal as spec_eip6110_minimal from eth2spec.eip7002 import mainnet as spec_eip7002_mainnet, minimal as spec_eip7002_minimal +from eth2spec.eip7668 import mainnet as spec_eip7668_mainnet, minimal as spec_eip7668_minimal from eth2spec.utils import bls from .exceptions import SkippedTest from .helpers.constants import ( PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB, - EIP6110, EIP7002, + EIP6110, EIP7002, EIP7668, MINIMAL, MAINNET, ALL_PHASES, ALL_FORK_UPGRADES, @@ -85,6 +86,7 @@ class ForkMeta: DENEB: spec_deneb_minimal, EIP6110: spec_eip6110_minimal, EIP7002: spec_eip7002_minimal, + EIP7668: spec_eip7668_minimal, }, MAINNET: { PHASE0: spec_phase0_mainnet, @@ -94,6 +96,7 @@ class ForkMeta: DENEB: spec_deneb_mainnet, EIP6110: spec_eip6110_mainnet, EIP7002: spec_eip7002_mainnet, + EIP7668: spec_eip7668_mainnet, }, } diff --git a/tests/core/pyspec/eth2spec/test/helpers/constants.py b/tests/core/pyspec/eth2spec/test/helpers/constants.py index 82e4f9d0a5..a90edb2ec7 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/constants.py +++ b/tests/core/pyspec/eth2spec/test/helpers/constants.py @@ -18,6 +18,7 @@ DAS = SpecForkName('das') EIP6110 = SpecForkName('eip6110') EIP7002 = SpecForkName('eip7002') +EIP7668 = SpecForkName('eip7668') # # SpecFork settings @@ -34,6 +35,7 @@ # Experimental patches EIP6110, EIP7002, + EIP7668, ) # The forks that have light client specs LIGHT_CLIENT_TESTING_FORKS = (*[item for item in MAINNET_FORKS if item != PHASE0], DENEB) diff --git a/tests/core/pyspec/eth2spec/test/helpers/forks.py b/tests/core/pyspec/eth2spec/test/helpers/forks.py index 492af47fe3..2e247dfc3a 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/forks.py +++ b/tests/core/pyspec/eth2spec/test/helpers/forks.py @@ -1,10 +1,12 @@ from .constants import ( PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB, - EIP6110, EIP7002, + EIP6110, EIP7002, EIP7668, ) def is_post_fork(a, b): + if a == EIP7668: + return b in [PHASE0, ALTAIR, BELLATRIX, CAPELLA, EIP7668] if a == EIP7002: return b in [PHASE0, ALTAIR, BELLATRIX, CAPELLA, EIP7002] if a == EIP6110: diff --git a/tests/core/pyspec/eth2spec/test/helpers/genesis.py b/tests/core/pyspec/eth2spec/test/helpers/genesis.py index e55bdef5ce..4f2f0721f2 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/genesis.py +++ b/tests/core/pyspec/eth2spec/test/helpers/genesis.py @@ -1,5 +1,5 @@ from eth2spec.test.helpers.constants import ( - ALTAIR, BELLATRIX, CAPELLA, DENEB, EIP6110, EIP7002, + ALTAIR, BELLATRIX, CAPELLA, DENEB, EIP6110, EIP7002, EIP7668, ) from eth2spec.test.helpers.execution_payload import ( compute_el_header_block_hash, @@ -93,6 +93,9 @@ def create_genesis_state(spec, validator_balances, activation_threshold): elif spec.fork == EIP7002: previous_version = spec.config.CAPELLA_FORK_VERSION current_version = spec.config.EIP7002_FORK_VERSION + elif spec.fork == EIP7668: + previous_version = spec.config.CAPELLA_FORK_VERSION + current_version = spec.config.EIP7668_FORK_VERSION state = spec.BeaconState( genesis_time=0, From 298a6304de2e46c70a26f1e9a34948dfa8a0b762 Mon Sep 17 00:00:00 2001 From: dapplion <35266934+dapplion@users.noreply.github.com> Date: Wed, 13 Sep 2023 10:57:52 +0200 Subject: [PATCH 33/46] review PR --- specs/_features/eip7668/beacon_chain.md | 27 ++++++++++--------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/specs/_features/eip7668/beacon_chain.md b/specs/_features/eip7668/beacon_chain.md index aeba214ebf..c14fca32e1 100644 --- a/specs/_features/eip7668/beacon_chain.md +++ b/specs/_features/eip7668/beacon_chain.md @@ -1,4 +1,4 @@ -Limit churn -- The Beacon Chain +EIP-7668 -- The Beacon Chain ## Table of contents @@ -11,7 +11,7 @@ Limit churn -- The Beacon Chain - [Validator cycle](#validator-cycle) - [Helper functions](#helper-functions) - [Beacon state accessors](#beacon-state-accessors) - - [New `get_validator_inbound_churn_limit`](#new-get_validator_inbound_churn_limit) + - [New `get_validator_activation_churn_limit`](#new-get_validator_activation_churn_limit) - [Beacon chain state transition function](#beacon-chain-state-transition-function) - [Epoch processing](#epoch-processing) - [Registry updates](#registry-updates) @@ -31,27 +31,20 @@ This is the beacon chain specification to limit the max inbound churn value, mot | Name | Value | | - | - | -| `MAX_PER_EPOCH_INBOUND_CHURN_LIMIT` | `uint64(12)` (= 12) | +| `MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT` | `uint64(12)` (= 12) | ## Helper functions ### Beacon state accessors -#### New `get_validator_inbound_churn_limit` +#### New `get_validator_activation_churn_limit` ```python -def get_validator_inbound_churn_limit(state: BeaconState) -> uint64: +def get_validator_activation_churn_limit(state: BeaconState) -> uint64: """ - Return the validator inbound churn limit for the current epoch. + Return the validator activation churn limit for the current epoch. """ - active_validator_indices = get_active_validator_indices(state, get_current_epoch(state)) - return min( - MAX_PER_EPOCH_INBOUND_CHURN_LIMIT, - max( - MIN_PER_EPOCH_CHURN_LIMIT, - uint64(len(active_validator_indices)) // CHURN_LIMIT_QUOTIENT, - ), - ) + return min(MAX_PER_EPOCH_INBOUND_CHURN_LIMIT, get_validator_churn_limit(state)) ``` ## Beacon chain state transition function @@ -60,6 +53,8 @@ def get_validator_inbound_churn_limit(state: BeaconState) -> uint64: #### Registry updates +Note: The function `process_registry_updates` is modified to utilize `get_validator_inbound_churn_limit()` the rate limit the activation queue for EIP-7668. + ```python def process_registry_updates(state: BeaconState) -> None: # Process activation eligibility and ejections @@ -80,8 +75,8 @@ def process_registry_updates(state: BeaconState) -> None: # Order by the sequence of activation_eligibility_epoch setting and then index ], key=lambda index: (state.validators[index].activation_eligibility_epoch, index)) # Dequeued validators for activation up to churn limit - # [Modified in limit churn] - for index in activation_queue[:get_validator_inbound_churn_limit(state)]: + # [Modified in EIP7668] + for index in activation_queue[:get_validator_activation_churn_limit(state)]: validator = state.validators[index] validator.activation_epoch = compute_activation_exit_epoch(get_current_epoch(state)) ``` From 417b95c3e6376d61c397b29e103f8f7e60bc1a62 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Wed, 13 Sep 2023 17:04:39 +0800 Subject: [PATCH 34/46] Add basic activation churn limit tests --- configs/mainnet.yaml | 2 +- configs/minimal.yaml | 7 +- specs/_features/eip7668/beacon_chain.md | 2 +- tests/core/pyspec/eth2spec/test/context.py | 27 +++++- .../pyspec/eth2spec/test/eip7668/__init__.py | 0 .../test/eip7668/epoch_processing/__init__.py | 0 .../test_process_registry_updates.py | 86 +++++++++++++++++++ .../pyspec/eth2spec/test/helpers/forks.py | 4 + .../test_process_registry_updates.py | 14 +-- 9 files changed, 129 insertions(+), 13 deletions(-) create mode 100644 tests/core/pyspec/eth2spec/test/eip7668/__init__.py create mode 100644 tests/core/pyspec/eth2spec/test/eip7668/epoch_processing/__init__.py create mode 100644 tests/core/pyspec/eth2spec/test/eip7668/epoch_processing/test_process_registry_updates.py diff --git a/configs/mainnet.yaml b/configs/mainnet.yaml index 87757d12ea..1b38591aae 100644 --- a/configs/mainnet.yaml +++ b/configs/mainnet.yaml @@ -151,4 +151,4 @@ WHISK_EPOCHS_PER_SHUFFLING_PHASE: 256 WHISK_PROPOSER_SELECTION_GAP: 2 # EIP7668 -MAX_PER_EPOCH_INBOUND_CHURN_LIMIT: 12 +MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT: 12 diff --git a/configs/minimal.yaml b/configs/minimal.yaml index 642603bdda..edfb0f7e84 100644 --- a/configs/minimal.yaml +++ b/configs/minimal.yaml @@ -85,8 +85,8 @@ INACTIVITY_SCORE_BIAS: 4 INACTIVITY_SCORE_RECOVERY_RATE: 16 # 2**4 * 10**9 (= 16,000,000,000) Gwei EJECTION_BALANCE: 16000000000 -# 2**2 (= 4) -MIN_PER_EPOCH_CHURN_LIMIT: 4 +# [customized] +MIN_PER_EPOCH_CHURN_LIMIT: 2 # [customized] scale queue churn at much lower validator counts for testing CHURN_LIMIT_QUOTIENT: 32 @@ -150,4 +150,5 @@ WHISK_EPOCHS_PER_SHUFFLING_PHASE: 4 WHISK_PROPOSER_SELECTION_GAP: 1 # EIP7668 -MAX_PER_EPOCH_INBOUND_CHURN_LIMIT: 12 +# [customized] +MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT: 4 diff --git a/specs/_features/eip7668/beacon_chain.md b/specs/_features/eip7668/beacon_chain.md index c14fca32e1..a8952b93af 100644 --- a/specs/_features/eip7668/beacon_chain.md +++ b/specs/_features/eip7668/beacon_chain.md @@ -44,7 +44,7 @@ def get_validator_activation_churn_limit(state: BeaconState) -> uint64: """ Return the validator activation churn limit for the current epoch. """ - return min(MAX_PER_EPOCH_INBOUND_CHURN_LIMIT, get_validator_churn_limit(state)) + return min(MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT, get_validator_churn_limit(state)) ``` ## Beacon chain state transition function diff --git a/tests/core/pyspec/eth2spec/test/context.py b/tests/core/pyspec/eth2spec/test/context.py index 1da2947214..f1a1a70349 100644 --- a/tests/core/pyspec/eth2spec/test/context.py +++ b/tests/core/pyspec/eth2spec/test/context.py @@ -165,14 +165,34 @@ def default_balances(spec: Spec): return [spec.MAX_EFFECTIVE_BALANCE] * num_validators -def scaled_churn_balances(spec: Spec): +def scaled_churn_balances_min_churn_limit(spec: Spec): """ Helper method to create enough validators to scale the churn limit. (This is *firmly* over the churn limit -- thus the +2 instead of just +1) See the second argument of ``max`` in ``get_validator_churn_limit``. - Usage: `@with_custom_state(balances_fn=scaled_churn_balances, ...)` + Usage: `@with_custom_state(balances_fn=scaled_churn_balances_min_churn_limit, ...)` """ - num_validators = spec.config.CHURN_LIMIT_QUOTIENT * (2 + spec.config.MIN_PER_EPOCH_CHURN_LIMIT) + num_validators = spec.config.CHURN_LIMIT_QUOTIENT * (spec.config.MIN_PER_EPOCH_CHURN_LIMIT + 2) + return [spec.MAX_EFFECTIVE_BALANCE] * num_validators + + +def scaled_churn_balances_equal_inbound_churn_limit(spec: Spec): + """ + Helper method to create enough validators to scale the churn limit. + (This is *firmly* over the churn limit -- thus the +2 instead of just +1) + Usage: `@with_custom_state(balances_fn=scaled_churn_balances_exceed_inbound_churn_limit, ...)` + """ + num_validators = spec.config.CHURN_LIMIT_QUOTIENT * (spec.config.MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT) + return [spec.MAX_EFFECTIVE_BALANCE] * num_validators + + +def scaled_churn_balances_exceed_inbound_churn_limit(spec: Spec): + """ + Helper method to create enough validators to scale the churn limit. + (This is *firmly* over the churn limit -- thus the +2 instead of just +1) + Usage: `@with_custom_state(balances_fn=scaled_churn_balances_exceed_inbound_churn_limit, ...)` + """ + num_validators = spec.config.CHURN_LIMIT_QUOTIENT * (spec.config.MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT + 2) return [spec.MAX_EFFECTIVE_BALANCE] * num_validators @@ -548,6 +568,7 @@ def wrapper(*args, spec: Spec, **kw): with_deneb_and_later = with_all_phases_from(DENEB) with_eip6110_and_later = with_all_phases_from(EIP6110) with_eip7002_and_later = with_all_phases_from(EIP7002) +with_eip7668_and_later = with_all_phases_from(EIP7668) class quoted_str(str): diff --git a/tests/core/pyspec/eth2spec/test/eip7668/__init__.py b/tests/core/pyspec/eth2spec/test/eip7668/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/core/pyspec/eth2spec/test/eip7668/epoch_processing/__init__.py b/tests/core/pyspec/eth2spec/test/eip7668/epoch_processing/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/core/pyspec/eth2spec/test/eip7668/epoch_processing/test_process_registry_updates.py b/tests/core/pyspec/eth2spec/test/eip7668/epoch_processing/test_process_registry_updates.py new file mode 100644 index 0000000000..fb0fa2ef01 --- /dev/null +++ b/tests/core/pyspec/eth2spec/test/eip7668/epoch_processing/test_process_registry_updates.py @@ -0,0 +1,86 @@ +from eth2spec.test.helpers.keys import pubkeys +from eth2spec.test.helpers.constants import MINIMAL +from eth2spec.test.context import ( + with_eip7668_and_later, + spec_test, + spec_state_test, + single_phase, + with_custom_state, + with_presets, + scaled_churn_balances_exceed_inbound_churn_limit, + scaled_churn_balances_equal_inbound_churn_limit, +) +from eth2spec.test.helpers.epoch_processing import run_epoch_processing_with + + +def run_process_registry_updates(spec, state): + yield from run_epoch_processing_with(spec, state, 'process_registry_updates') + + +def run_test_inbound_churn_limit(spec, state): + mock_activations = 1 + + for i in range(mock_activations): + index = len(state.validators) + i + validator = spec.Validator( + pubkey=pubkeys[index], + withdrawal_credentials=spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + b'\x00' * 11 + b'\x56' * 20, + activation_eligibility_epoch=0, + activation_epoch=spec.FAR_FUTURE_EPOCH, + exit_epoch=spec.FAR_FUTURE_EPOCH, + withdrawable_epoch=spec.FAR_FUTURE_EPOCH, + effective_balance=spec.MAX_EFFECTIVE_BALANCE, + ) + state.validators.append(validator) + state.balances.append(spec.MAX_EFFECTIVE_BALANCE) + state.previous_epoch_participation.append(spec.ParticipationFlags(0b0000_0000)) + state.current_epoch_participation.append(spec.ParticipationFlags(0b0000_0000)) + state.inactivity_scores.append(0) + state.validators[index].activation_epoch = spec.FAR_FUTURE_EPOCH + + churn_limit_0 = spec.get_validator_activation_churn_limit(state) + + yield from run_process_registry_updates(spec, state) + + # Half should churn in first run of registry update + for i in range(mock_activations): + if i < churn_limit_0: + assert state.validators[i].activation_epoch < spec.FAR_FUTURE_EPOCH + else: + assert state.validators[i].activation_epoch == spec.FAR_FUTURE_EPOCH + + +@with_eip7668_and_later +@with_presets([MINIMAL], + reason="mainnet config leads to larger validator set than limit of public/private keys pre-generated") +@spec_test +@with_custom_state(balances_fn=scaled_churn_balances_exceed_inbound_churn_limit, + threshold_fn=lambda spec: spec.config.EJECTION_BALANCE) +@single_phase +def test_inbound_churn_limit__greater_than_inbound_limit(spec, state): + assert spec.get_validator_activation_churn_limit(state) == spec.config.MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT + assert spec.get_validator_churn_limit(state) > spec.config.MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT + yield from run_test_inbound_churn_limit(spec, state) + + +@with_eip7668_and_later +@with_presets([MINIMAL], + reason="mainnet config leads to larger validator set than limit of public/private keys pre-generated") +@spec_test +@with_custom_state(balances_fn=scaled_churn_balances_equal_inbound_churn_limit, + threshold_fn=lambda spec: spec.config.EJECTION_BALANCE) +@single_phase +def test_inbound_churn_limit__equal_to_inbound_limit(spec, state): + assert spec.get_validator_activation_churn_limit(state) == spec.config.MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT + assert spec.get_validator_churn_limit(state) == spec.config.MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT + yield from run_test_inbound_churn_limit(spec, state) + + +@with_eip7668_and_later +@with_presets([MINIMAL], + reason="mainnet config leads to larger validator set than limit of public/private keys pre-generated") +@spec_state_test +def test_inbound_churn_limit__less_than_inbound_limit(spec, state): + assert spec.get_validator_activation_churn_limit(state) < spec.config.MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT + assert spec.get_validator_churn_limit(state) < spec.config.MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT + yield from run_test_inbound_churn_limit(spec, state) diff --git a/tests/core/pyspec/eth2spec/test/helpers/forks.py b/tests/core/pyspec/eth2spec/test/helpers/forks.py index 2e247dfc3a..91434c0caf 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/forks.py +++ b/tests/core/pyspec/eth2spec/test/helpers/forks.py @@ -46,3 +46,7 @@ def is_post_eip6110(spec): def is_post_eip7002(spec): return is_post_fork(spec.fork, EIP7002) + + +def is_post_eip7668(spec): + return is_post_fork(spec.fork, EIP7668) diff --git a/tests/core/pyspec/eth2spec/test/phase0/epoch_processing/test_process_registry_updates.py b/tests/core/pyspec/eth2spec/test/phase0/epoch_processing/test_process_registry_updates.py index b4c5f81a0d..b7a7be76ab 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/epoch_processing/test_process_registry_updates.py +++ b/tests/core/pyspec/eth2spec/test/phase0/epoch_processing/test_process_registry_updates.py @@ -5,7 +5,7 @@ spec_test, spec_state_test, with_all_phases, single_phase, with_custom_state, with_presets, - scaled_churn_balances, + scaled_churn_balances_min_churn_limit, ) from eth2spec.test.helpers.epoch_processing import run_epoch_processing_with @@ -164,7 +164,8 @@ def test_activation_queue_efficiency_min(spec, state): @with_presets([MINIMAL], reason="mainnet config leads to larger validator set than limit of public/private keys pre-generated") @spec_test -@with_custom_state(balances_fn=scaled_churn_balances, threshold_fn=lambda spec: spec.config.EJECTION_BALANCE) +@with_custom_state(balances_fn=scaled_churn_balances_min_churn_limit, + threshold_fn=lambda spec: spec.config.EJECTION_BALANCE) @single_phase def test_activation_queue_efficiency_scaled(spec, state): assert spec.get_validator_churn_limit(state) > spec.config.MIN_PER_EPOCH_CHURN_LIMIT @@ -227,7 +228,8 @@ def test_ejection_past_churn_limit_min(spec, state): @with_presets([MINIMAL], reason="mainnet config leads to larger validator set than limit of public/private keys pre-generated") @spec_test -@with_custom_state(balances_fn=scaled_churn_balances, threshold_fn=lambda spec: spec.config.EJECTION_BALANCE) +@with_custom_state(balances_fn=scaled_churn_balances_min_churn_limit, + threshold_fn=lambda spec: spec.config.EJECTION_BALANCE) @single_phase def test_ejection_past_churn_limit_scaled(spec, state): assert spec.get_validator_churn_limit(state) > spec.config.MIN_PER_EPOCH_CHURN_LIMIT @@ -324,7 +326,8 @@ def test_activation_queue_activation_and_ejection__exceed_churn_limit(spec, stat @with_presets([MINIMAL], reason="mainnet config leads to larger validator set than limit of public/private keys pre-generated") @spec_test -@with_custom_state(balances_fn=scaled_churn_balances, threshold_fn=lambda spec: spec.config.EJECTION_BALANCE) +@with_custom_state(balances_fn=scaled_churn_balances_min_churn_limit, + threshold_fn=lambda spec: spec.config.EJECTION_BALANCE) @single_phase def test_activation_queue_activation_and_ejection__scaled_churn_limit(spec, state): churn_limit = spec.get_validator_churn_limit(state) @@ -336,7 +339,8 @@ def test_activation_queue_activation_and_ejection__scaled_churn_limit(spec, stat @with_presets([MINIMAL], reason="mainnet config leads to larger validator set than limit of public/private keys pre-generated") @spec_test -@with_custom_state(balances_fn=scaled_churn_balances, threshold_fn=lambda spec: spec.config.EJECTION_BALANCE) +@with_custom_state(balances_fn=scaled_churn_balances_min_churn_limit, + threshold_fn=lambda spec: spec.config.EJECTION_BALANCE) @single_phase def test_activation_queue_activation_and_ejection__exceed_scaled_churn_limit(spec, state): churn_limit = spec.get_validator_churn_limit(state) From 8878a316c443d240735d84fd3eb13bb8ca1f762c Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Wed, 13 Sep 2023 19:05:01 +0800 Subject: [PATCH 35/46] Fix test_process_voluntary_exit.py --- .../phase0/block_processing/test_process_voluntary_exit.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/phase0/block_processing/test_process_voluntary_exit.py b/tests/core/pyspec/eth2spec/test/phase0/block_processing/test_process_voluntary_exit.py index 4a7286d523..97208dfcdd 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/block_processing/test_process_voluntary_exit.py +++ b/tests/core/pyspec/eth2spec/test/phase0/block_processing/test_process_voluntary_exit.py @@ -3,7 +3,8 @@ spec_state_test, always_bls, with_all_phases, with_presets, spec_test, single_phase, - with_custom_state, scaled_churn_balances, + with_custom_state, + scaled_churn_balances_min_churn_limit, ) from eth2spec.test.helpers.keys import pubkey_to_privkey from eth2spec.test.helpers.voluntary_exits import ( @@ -102,7 +103,8 @@ def test_success_exit_queue__min_churn(spec, state): @with_presets([MINIMAL], reason="mainnet config leads to larger validator set than limit of public/private keys pre-generated") @spec_test -@with_custom_state(balances_fn=scaled_churn_balances, threshold_fn=lambda spec: spec.config.EJECTION_BALANCE) +@with_custom_state(balances_fn=scaled_churn_balances_min_churn_limit, + threshold_fn=lambda spec: spec.config.EJECTION_BALANCE) @single_phase def test_success_exit_queue__scaled_churn(spec, state): churn_limit = spec.get_validator_churn_limit(state) From 28286e7e5fc5a006cc9f2ca9051887d8551cf7c9 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Thu, 14 Sep 2023 19:05:31 +0800 Subject: [PATCH 36/46] Fix tests --- .../test_process_registry_updates.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/eip7668/epoch_processing/test_process_registry_updates.py b/tests/core/pyspec/eth2spec/test/eip7668/epoch_processing/test_process_registry_updates.py index fb0fa2ef01..d68a659ef0 100644 --- a/tests/core/pyspec/eth2spec/test/eip7668/epoch_processing/test_process_registry_updates.py +++ b/tests/core/pyspec/eth2spec/test/eip7668/epoch_processing/test_process_registry_updates.py @@ -18,10 +18,12 @@ def run_process_registry_updates(spec, state): def run_test_inbound_churn_limit(spec, state): - mock_activations = 1 + mock_activations = spec.get_validator_activation_churn_limit(state) * 2 + + validator_count_0 = len(state.validators) for i in range(mock_activations): - index = len(state.validators) + i + index = validator_count_0 + i validator = spec.Validator( pubkey=pubkeys[index], withdrawal_credentials=spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + b'\x00' * 11 + b'\x56' * 20, @@ -44,10 +46,12 @@ def run_test_inbound_churn_limit(spec, state): # Half should churn in first run of registry update for i in range(mock_activations): - if i < churn_limit_0: - assert state.validators[i].activation_epoch < spec.FAR_FUTURE_EPOCH + index = validator_count_0 + i + if index < validator_count_0 + churn_limit_0: + # The eligible validators within the activation churn limit should have been activated + assert state.validators[index].activation_epoch < spec.FAR_FUTURE_EPOCH else: - assert state.validators[i].activation_epoch == spec.FAR_FUTURE_EPOCH + assert state.validators[index].activation_epoch == spec.FAR_FUTURE_EPOCH @with_eip7668_and_later From 19bf51dd93cf8f8a05457f72afa966068ef9aef7 Mon Sep 17 00:00:00 2001 From: dapplion <35266934+dapplion@users.noreply.github.com> Date: Thu, 14 Sep 2023 13:44:32 +0200 Subject: [PATCH 37/46] Rename eip7668 to eip7514 --- configs/mainnet.yaml | 8 +++--- configs/minimal.yaml | 8 +++--- pysetup/constants.py | 2 +- pysetup/md_doc_paths.py | 4 +-- pysetup/spec_builders/__init__.py | 4 +-- pysetup/spec_builders/{eip7668.py => eip7514} | 6 ++-- .../{eip7668 => eip7514}/beacon_chain.md | 6 ++-- specs/_features/{eip7668 => eip7514}/fork.md | 28 +++++++++---------- tests/core/pyspec/eth2spec/test/context.py | 10 +++---- .../test/{eip7668 => eip7514}/__init__.py | 0 .../epoch_processing/__init__.py | 0 .../test_process_registry_updates.py | 8 +++--- .../pyspec/eth2spec/test/helpers/constants.py | 4 +-- .../pyspec/eth2spec/test/helpers/forks.py | 10 +++---- .../pyspec/eth2spec/test/helpers/genesis.py | 6 ++-- 15 files changed, 52 insertions(+), 52 deletions(-) rename pysetup/spec_builders/{eip7668.py => eip7514} (67%) rename specs/_features/{eip7668 => eip7514}/beacon_chain.md (97%) rename specs/_features/{eip7668 => eip7514}/fork.md (86%) rename tests/core/pyspec/eth2spec/test/{eip7668 => eip7514}/__init__.py (100%) rename tests/core/pyspec/eth2spec/test/{eip7668 => eip7514}/epoch_processing/__init__.py (100%) rename tests/core/pyspec/eth2spec/test/{eip7668 => eip7514}/epoch_processing/test_process_registry_updates.py (97%) diff --git a/configs/mainnet.yaml b/configs/mainnet.yaml index 1b38591aae..75a34bd8aa 100644 --- a/configs/mainnet.yaml +++ b/configs/mainnet.yaml @@ -56,9 +56,9 @@ EIP6110_FORK_EPOCH: 18446744073709551615 # EIP7002 EIP7002_FORK_VERSION: 0x05000000 # temporary stub EIP7002_FORK_EPOCH: 18446744073709551615 -# EIP7668 -EIP7668_FORK_VERSION: 0x05000000 # temporary stub -EIP7668_FORK_EPOCH: 18446744073709551615 +# EIP7514 +EIP7514_FORK_VERSION: 0x05000000 # temporary stub +EIP7514_FORK_EPOCH: 18446744073709551615 # WHISK WHISK_FORK_VERSION: 0x06000000 # temporary stub WHISK_FORK_EPOCH: 18446744073709551615 @@ -150,5 +150,5 @@ WHISK_EPOCHS_PER_SHUFFLING_PHASE: 256 # `Epoch(2)` WHISK_PROPOSER_SELECTION_GAP: 2 -# EIP7668 +# EIP7514 MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT: 12 diff --git a/configs/minimal.yaml b/configs/minimal.yaml index edfb0f7e84..368e71c17c 100644 --- a/configs/minimal.yaml +++ b/configs/minimal.yaml @@ -55,9 +55,9 @@ EIP6110_FORK_EPOCH: 18446744073709551615 # EIP7002 EIP7002_FORK_VERSION: 0x05000001 EIP7002_FORK_EPOCH: 18446744073709551615 -# EIP7668 -EIP7668_FORK_VERSION: 0x05000001 # temporary stub -EIP7668_FORK_EPOCH: 18446744073709551615 +# EIP7514 +EIP7514_FORK_VERSION: 0x05000001 # temporary stub +EIP7514_FORK_EPOCH: 18446744073709551615 # WHISK WHISK_FORK_VERSION: 0x06000001 WHISK_FORK_EPOCH: 18446744073709551615 @@ -149,6 +149,6 @@ BLOB_SIDECAR_SUBNET_COUNT: 6 WHISK_EPOCHS_PER_SHUFFLING_PHASE: 4 WHISK_PROPOSER_SELECTION_GAP: 1 -# EIP7668 +# EIP7514 # [customized] MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT: 4 diff --git a/pysetup/constants.py b/pysetup/constants.py index f6f8ed8e43..765429e589 100644 --- a/pysetup/constants.py +++ b/pysetup/constants.py @@ -6,7 +6,7 @@ DENEB = 'deneb' EIP6110 = 'eip6110' EIP7002 = 'eip7002' -EIP7668 = 'eip7668' +EIP7514 = 'eip7514' WHISK = 'whisk' diff --git a/pysetup/md_doc_paths.py b/pysetup/md_doc_paths.py index 8031b09361..51dbfae253 100644 --- a/pysetup/md_doc_paths.py +++ b/pysetup/md_doc_paths.py @@ -9,7 +9,7 @@ EIP6110, WHISK, EIP7002, - EIP7668, + EIP7514, ) @@ -22,7 +22,7 @@ EIP6110: DENEB, WHISK: CAPELLA, EIP7002: CAPELLA, - EIP7668: CAPELLA, + EIP7514: CAPELLA, } ALL_FORKS = list(PREVIOUS_FORK_OF.keys()) diff --git a/pysetup/spec_builders/__init__.py b/pysetup/spec_builders/__init__.py index 6f0f1d2032..aa26b431fe 100644 --- a/pysetup/spec_builders/__init__.py +++ b/pysetup/spec_builders/__init__.py @@ -5,7 +5,7 @@ from .deneb import DenebSpecBuilder from .eip6110 import EIP6110SpecBuilder from .eip7002 import EIP7002SpecBuilder -from .eip7668 import EIP7668SpecBuilder +from .eip7514 import EIP7514SpecBuilder from .whisk import WhiskSpecBuilder @@ -13,6 +13,6 @@ builder.fork: builder for builder in ( Phase0SpecBuilder, AltairSpecBuilder, BellatrixSpecBuilder, CapellaSpecBuilder, DenebSpecBuilder, - EIP6110SpecBuilder, EIP7002SpecBuilder, EIP7668SpecBuilder, WhiskSpecBuilder, + EIP6110SpecBuilder, EIP7002SpecBuilder, EIP7514SpecBuilder, WhiskSpecBuilder, ) } diff --git a/pysetup/spec_builders/eip7668.py b/pysetup/spec_builders/eip7514 similarity index 67% rename from pysetup/spec_builders/eip7668.py rename to pysetup/spec_builders/eip7514 index 55332dd9ee..280cd11996 100644 --- a/pysetup/spec_builders/eip7668.py +++ b/pysetup/spec_builders/eip7514 @@ -1,9 +1,9 @@ from .base import BaseSpecBuilder -from ..constants import EIP7668 +from ..constants import EIP7514 -class EIP7668SpecBuilder(BaseSpecBuilder): - fork: str = EIP7668 +class EIP7514SpecBuilder(BaseSpecBuilder): + fork: str = EIP7514 @classmethod def imports(cls, preset_name: str): diff --git a/specs/_features/eip7668/beacon_chain.md b/specs/_features/eip7514/beacon_chain.md similarity index 97% rename from specs/_features/eip7668/beacon_chain.md rename to specs/_features/eip7514/beacon_chain.md index a8952b93af..321596debb 100644 --- a/specs/_features/eip7668/beacon_chain.md +++ b/specs/_features/eip7514/beacon_chain.md @@ -1,4 +1,4 @@ -EIP-7668 -- The Beacon Chain +EIP-7514 -- The Beacon Chain ## Table of contents @@ -53,7 +53,7 @@ def get_validator_activation_churn_limit(state: BeaconState) -> uint64: #### Registry updates -Note: The function `process_registry_updates` is modified to utilize `get_validator_inbound_churn_limit()` the rate limit the activation queue for EIP-7668. +Note: The function `process_registry_updates` is modified to utilize `get_validator_inbound_churn_limit()` the rate limit the activation queue for EIP-7514. ```python def process_registry_updates(state: BeaconState) -> None: @@ -75,7 +75,7 @@ def process_registry_updates(state: BeaconState) -> None: # Order by the sequence of activation_eligibility_epoch setting and then index ], key=lambda index: (state.validators[index].activation_eligibility_epoch, index)) # Dequeued validators for activation up to churn limit - # [Modified in EIP7668] + # [Modified in EIP7514] for index in activation_queue[:get_validator_activation_churn_limit(state)]: validator = state.validators[index] validator.activation_epoch = compute_activation_exit_epoch(get_current_epoch(state)) diff --git a/specs/_features/eip7668/fork.md b/specs/_features/eip7514/fork.md similarity index 86% rename from specs/_features/eip7668/fork.md rename to specs/_features/eip7514/fork.md index 93d2a7d521..05924874b2 100644 --- a/specs/_features/eip7668/fork.md +++ b/specs/_features/eip7514/fork.md @@ -1,4 +1,4 @@ -# EIP-7668 -- Fork Logic +# EIP-7514 -- Fork Logic **Notice**: This document is a work-in-progress for researchers and implementers. @@ -12,7 +12,7 @@ - [Helper functions](#helper-functions) - [Misc](#misc) - [Modified `compute_fork_version`](#modified-compute_fork_version) -- [Fork to EIP-7668](#fork-to-eip-7668) +- [Fork to EIP-7514](#fork-to-eip-7514) - [Fork trigger](#fork-trigger) - [Upgrading the state](#upgrading-the-state) @@ -20,7 +20,7 @@ ## Introduction -This document describes the process of EIP-7668 upgrade. +This document describes the process of EIP-7514 upgrade. ## Configuration @@ -28,8 +28,8 @@ Warning: this configuration is not definitive. | Name | Value | | - | - | -| `EIP7668_FORK_VERSION` | `Version('0x05000000')` | -| `EIP7668_FORK_EPOCH` | `Epoch(18446744073709551615)` **TBD** | +| `EIP7514_FORK_VERSION` | `Version('0x05000000')` | +| `EIP7514_FORK_EPOCH` | `Epoch(18446744073709551615)` **TBD** | ## Helper functions @@ -42,8 +42,8 @@ def compute_fork_version(epoch: Epoch) -> Version: """ Return the fork version at the given ``epoch``. """ - if epoch >= EIP7668_FORK_EPOCH: - return EIP7668_FORK_VERSION + if epoch >= EIP7514_FORK_EPOCH: + return EIP7514_FORK_VERSION if epoch >= CAPELLA_FORK_EPOCH: return CAPELLA_FORK_VERSION if epoch >= BELLATRIX_FORK_EPOCH: @@ -53,22 +53,22 @@ def compute_fork_version(epoch: Epoch) -> Version: return GENESIS_FORK_VERSION ``` -## Fork to EIP-7668 +## Fork to EIP-7514 ### Fork trigger TBD. This fork is defined for testing purposes, the EIP may be combined with other consensus-layer upgrade. -For now, we assume the condition will be triggered at epoch `EIP7668_FORK_EPOCH`. +For now, we assume the condition will be triggered at epoch `EIP7514_FORK_EPOCH`. -Note that for the pure EIP-7668 networks, we don't apply `upgrade_to_eip7668` since it starts with EIP-7668 version logic. +Note that for the pure EIP-7514 networks, we don't apply `upgrade_to_eip7514` since it starts with EIP-7514 version logic. ### Upgrading the state -If `state.slot % SLOTS_PER_EPOCH == 0` and `compute_epoch_at_slot(state.slot) == EIP7668_FORK_EPOCH`, -an irregular state change is made to upgrade to EIP-7668. +If `state.slot % SLOTS_PER_EPOCH == 0` and `compute_epoch_at_slot(state.slot) == EIP7514_FORK_EPOCH`, +an irregular state change is made to upgrade to EIP-7514. ```python -def upgrade_to_eip7668(pre: capella.BeaconState) -> BeaconState: +def upgrade_to_eip7514(pre: capella.BeaconState) -> BeaconState: epoch = capella.get_current_epoch(pre) latest_execution_payload_header = ExecutionPayloadHeader( parent_hash=pre.latest_execution_payload_header.parent_hash, @@ -94,7 +94,7 @@ def upgrade_to_eip7668(pre: capella.BeaconState) -> BeaconState: slot=pre.slot, fork=Fork( previous_version=pre.fork.current_version, - current_version=EIP7668_FORK_VERSION, # [Modified in EIP-7668] + current_version=EIP7514_FORK_VERSION, # [Modified in EIP-7514] epoch=epoch, ), # History diff --git a/tests/core/pyspec/eth2spec/test/context.py b/tests/core/pyspec/eth2spec/test/context.py index f1a1a70349..d646f08b95 100644 --- a/tests/core/pyspec/eth2spec/test/context.py +++ b/tests/core/pyspec/eth2spec/test/context.py @@ -10,13 +10,13 @@ from eth2spec.deneb import mainnet as spec_deneb_mainnet, minimal as spec_deneb_minimal from eth2spec.eip6110 import mainnet as spec_eip6110_mainnet, minimal as spec_eip6110_minimal from eth2spec.eip7002 import mainnet as spec_eip7002_mainnet, minimal as spec_eip7002_minimal -from eth2spec.eip7668 import mainnet as spec_eip7668_mainnet, minimal as spec_eip7668_minimal +from eth2spec.eip7514 import mainnet as spec_eip7514_mainnet, minimal as spec_eip7514_minimal from eth2spec.utils import bls from .exceptions import SkippedTest from .helpers.constants import ( PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB, - EIP6110, EIP7002, EIP7668, + EIP6110, EIP7002, EIP7514, MINIMAL, MAINNET, ALL_PHASES, ALL_FORK_UPGRADES, @@ -86,7 +86,7 @@ class ForkMeta: DENEB: spec_deneb_minimal, EIP6110: spec_eip6110_minimal, EIP7002: spec_eip7002_minimal, - EIP7668: spec_eip7668_minimal, + EIP7514: spec_eip7514_minimal, }, MAINNET: { PHASE0: spec_phase0_mainnet, @@ -96,7 +96,7 @@ class ForkMeta: DENEB: spec_deneb_mainnet, EIP6110: spec_eip6110_mainnet, EIP7002: spec_eip7002_mainnet, - EIP7668: spec_eip7668_mainnet, + EIP7514: spec_eip7514_mainnet, }, } @@ -568,7 +568,7 @@ def wrapper(*args, spec: Spec, **kw): with_deneb_and_later = with_all_phases_from(DENEB) with_eip6110_and_later = with_all_phases_from(EIP6110) with_eip7002_and_later = with_all_phases_from(EIP7002) -with_eip7668_and_later = with_all_phases_from(EIP7668) +with_eip7514_and_later = with_all_phases_from(EIP7514) class quoted_str(str): diff --git a/tests/core/pyspec/eth2spec/test/eip7668/__init__.py b/tests/core/pyspec/eth2spec/test/eip7514/__init__.py similarity index 100% rename from tests/core/pyspec/eth2spec/test/eip7668/__init__.py rename to tests/core/pyspec/eth2spec/test/eip7514/__init__.py diff --git a/tests/core/pyspec/eth2spec/test/eip7668/epoch_processing/__init__.py b/tests/core/pyspec/eth2spec/test/eip7514/epoch_processing/__init__.py similarity index 100% rename from tests/core/pyspec/eth2spec/test/eip7668/epoch_processing/__init__.py rename to tests/core/pyspec/eth2spec/test/eip7514/epoch_processing/__init__.py diff --git a/tests/core/pyspec/eth2spec/test/eip7668/epoch_processing/test_process_registry_updates.py b/tests/core/pyspec/eth2spec/test/eip7514/epoch_processing/test_process_registry_updates.py similarity index 97% rename from tests/core/pyspec/eth2spec/test/eip7668/epoch_processing/test_process_registry_updates.py rename to tests/core/pyspec/eth2spec/test/eip7514/epoch_processing/test_process_registry_updates.py index d68a659ef0..aa8e2c41b8 100644 --- a/tests/core/pyspec/eth2spec/test/eip7668/epoch_processing/test_process_registry_updates.py +++ b/tests/core/pyspec/eth2spec/test/eip7514/epoch_processing/test_process_registry_updates.py @@ -1,7 +1,7 @@ from eth2spec.test.helpers.keys import pubkeys from eth2spec.test.helpers.constants import MINIMAL from eth2spec.test.context import ( - with_eip7668_and_later, + with_eip7514_and_later, spec_test, spec_state_test, single_phase, @@ -54,7 +54,7 @@ def run_test_inbound_churn_limit(spec, state): assert state.validators[index].activation_epoch == spec.FAR_FUTURE_EPOCH -@with_eip7668_and_later +@with_eip7514_and_later @with_presets([MINIMAL], reason="mainnet config leads to larger validator set than limit of public/private keys pre-generated") @spec_test @@ -67,7 +67,7 @@ def test_inbound_churn_limit__greater_than_inbound_limit(spec, state): yield from run_test_inbound_churn_limit(spec, state) -@with_eip7668_and_later +@with_eip7514_and_later @with_presets([MINIMAL], reason="mainnet config leads to larger validator set than limit of public/private keys pre-generated") @spec_test @@ -80,7 +80,7 @@ def test_inbound_churn_limit__equal_to_inbound_limit(spec, state): yield from run_test_inbound_churn_limit(spec, state) -@with_eip7668_and_later +@with_eip7514_and_later @with_presets([MINIMAL], reason="mainnet config leads to larger validator set than limit of public/private keys pre-generated") @spec_state_test diff --git a/tests/core/pyspec/eth2spec/test/helpers/constants.py b/tests/core/pyspec/eth2spec/test/helpers/constants.py index a90edb2ec7..dff1ede20d 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/constants.py +++ b/tests/core/pyspec/eth2spec/test/helpers/constants.py @@ -18,7 +18,7 @@ DAS = SpecForkName('das') EIP6110 = SpecForkName('eip6110') EIP7002 = SpecForkName('eip7002') -EIP7668 = SpecForkName('eip7668') +EIP7514 = SpecForkName('eip7514') # # SpecFork settings @@ -35,7 +35,7 @@ # Experimental patches EIP6110, EIP7002, - EIP7668, + EIP7514, ) # The forks that have light client specs LIGHT_CLIENT_TESTING_FORKS = (*[item for item in MAINNET_FORKS if item != PHASE0], DENEB) diff --git a/tests/core/pyspec/eth2spec/test/helpers/forks.py b/tests/core/pyspec/eth2spec/test/helpers/forks.py index 91434c0caf..4322e220aa 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/forks.py +++ b/tests/core/pyspec/eth2spec/test/helpers/forks.py @@ -1,12 +1,12 @@ from .constants import ( PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB, - EIP6110, EIP7002, EIP7668, + EIP6110, EIP7002, EIP7514, ) def is_post_fork(a, b): - if a == EIP7668: - return b in [PHASE0, ALTAIR, BELLATRIX, CAPELLA, EIP7668] + if a == EIP7514: + return b in [PHASE0, ALTAIR, BELLATRIX, CAPELLA, EIP7514] if a == EIP7002: return b in [PHASE0, ALTAIR, BELLATRIX, CAPELLA, EIP7002] if a == EIP6110: @@ -48,5 +48,5 @@ def is_post_eip7002(spec): return is_post_fork(spec.fork, EIP7002) -def is_post_eip7668(spec): - return is_post_fork(spec.fork, EIP7668) +def is_post_eip7514(spec): + return is_post_fork(spec.fork, EIP7514) diff --git a/tests/core/pyspec/eth2spec/test/helpers/genesis.py b/tests/core/pyspec/eth2spec/test/helpers/genesis.py index 4f2f0721f2..8e01643bb2 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/genesis.py +++ b/tests/core/pyspec/eth2spec/test/helpers/genesis.py @@ -1,5 +1,5 @@ from eth2spec.test.helpers.constants import ( - ALTAIR, BELLATRIX, CAPELLA, DENEB, EIP6110, EIP7002, EIP7668, + ALTAIR, BELLATRIX, CAPELLA, DENEB, EIP6110, EIP7002, EIP7514, ) from eth2spec.test.helpers.execution_payload import ( compute_el_header_block_hash, @@ -93,9 +93,9 @@ def create_genesis_state(spec, validator_balances, activation_threshold): elif spec.fork == EIP7002: previous_version = spec.config.CAPELLA_FORK_VERSION current_version = spec.config.EIP7002_FORK_VERSION - elif spec.fork == EIP7668: + elif spec.fork == EIP7514: previous_version = spec.config.CAPELLA_FORK_VERSION - current_version = spec.config.EIP7668_FORK_VERSION + current_version = spec.config.EIP7514_FORK_VERSION state = spec.BeaconState( genesis_time=0, From a56c4d026ff4fd39cbd77b7b8545eae719613018 Mon Sep 17 00:00:00 2001 From: dapplion <35266934+dapplion@users.noreply.github.com> Date: Thu, 14 Sep 2023 13:47:11 +0200 Subject: [PATCH 38/46] add extension --- pysetup/spec_builders/{eip7514 => eip7514.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename pysetup/spec_builders/{eip7514 => eip7514.py} (100%) diff --git a/pysetup/spec_builders/eip7514 b/pysetup/spec_builders/eip7514.py similarity index 100% rename from pysetup/spec_builders/eip7514 rename to pysetup/spec_builders/eip7514.py From f165d39472b1c7f8a39247ffedd453608ac37982 Mon Sep 17 00:00:00 2001 From: Lion - dapplion <35266934+dapplion@users.noreply.github.com> Date: Thu, 14 Sep 2023 16:31:12 +0200 Subject: [PATCH 39/46] Update mainnet.yaml --- configs/mainnet.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configs/mainnet.yaml b/configs/mainnet.yaml index 75a34bd8aa..0bd46649e3 100644 --- a/configs/mainnet.yaml +++ b/configs/mainnet.yaml @@ -151,4 +151,4 @@ WHISK_EPOCHS_PER_SHUFFLING_PHASE: 256 WHISK_PROPOSER_SELECTION_GAP: 2 # EIP7514 -MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT: 12 +MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT: 8 From 0efd7785940ec5bbb015aec5e97d2c64d1f51d95 Mon Sep 17 00:00:00 2001 From: Lion - dapplion <35266934+dapplion@users.noreply.github.com> Date: Thu, 14 Sep 2023 16:31:42 +0200 Subject: [PATCH 40/46] Update beacon_chain.md --- specs/_features/eip7514/beacon_chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/_features/eip7514/beacon_chain.md b/specs/_features/eip7514/beacon_chain.md index 321596debb..993beee5db 100644 --- a/specs/_features/eip7514/beacon_chain.md +++ b/specs/_features/eip7514/beacon_chain.md @@ -31,7 +31,7 @@ This is the beacon chain specification to limit the max inbound churn value, mot | Name | Value | | - | - | -| `MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT` | `uint64(12)` (= 12) | +| `MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT` | `uint64(8)` (= 8) | ## Helper functions From 909388ba8d2e9f24f827f4edce08912aae7c11cd Mon Sep 17 00:00:00 2001 From: Max Inden Date: Mon, 4 Sep 2023 20:09:33 +0200 Subject: [PATCH 41/46] fix(sepcs/p2p): link to libp2p Yamux specification Previously the specification would link to the Hashicorp Yamux specification. https://github.com/hashicorp/yamux/blob/master/spec.md Since adoption by libp2p, there have been multiple refinements to the original specification. The improved specification can be found in the libp2p specification repository. https://github.com/libp2p/specs/blob/master/yamux/README.md --- specs/phase0/p2p-interface.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specs/phase0/p2p-interface.md b/specs/phase0/p2p-interface.md index bbb4c4d427..a374443b8c 100644 --- a/specs/phase0/p2p-interface.md +++ b/specs/phase0/p2p-interface.md @@ -158,11 +158,11 @@ This applies to transports that are natively incapable of multiplexing (e.g. TCP and is omitted for capable transports (e.g. QUIC). Two multiplexers are commonplace in libp2p implementations: -[mplex](https://github.com/libp2p/specs/tree/master/mplex) and [yamux](https://github.com/hashicorp/yamux/blob/master/spec.md). +[mplex](https://github.com/libp2p/specs/tree/master/mplex) and [yamux](https://github.com/libp2p/specs/blob/master/yamux/README.md). Their protocol IDs are, respectively: `/mplex/6.7.0` and `/yamux/1.0.0`. Clients MUST support [mplex](https://github.com/libp2p/specs/tree/master/mplex) -and MAY support [yamux](https://github.com/hashicorp/yamux/blob/master/spec.md). +and MAY support [yamux](https://github.com/libp2p/specs/blob/master/yamux/README.md). If both are supported by the client, yamux MUST take precedence during negotiation. See the [Rationale](#design-decision-rationale) section below for tradeoffs. From e5e50e3e407c062702be0bc395acf8e1a8030b6e Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Fri, 15 Sep 2023 10:25:47 +0800 Subject: [PATCH 42/46] Add EIP-7514 into Deneb --- .gitignore | 1 - configs/mainnet.yaml | 9 +- configs/minimal.yaml | 9 +- pysetup/constants.py | 1 - pysetup/md_doc_paths.py | 2 - pysetup/spec_builders/__init__.py | 3 +- pysetup/spec_builders/eip7514.py | 12 -- specs/_features/eip7514/beacon_chain.md | 83 ----------- specs/_features/eip7514/fork.md | 139 ------------------ specs/deneb/beacon-chain.md | 53 +++++++ tests/core/pyspec/eth2spec/test/context.py | 14 +- .../epoch_processing}/__init__.py | 0 .../test_process_registry_updates.py | 30 ++-- .../test/eip7514/epoch_processing/__init__.py | 0 .../pyspec/eth2spec/test/helpers/constants.py | 2 - .../pyspec/eth2spec/test/helpers/forks.py | 8 +- .../pyspec/eth2spec/test/helpers/genesis.py | 5 +- tests/generators/epoch_processing/main.py | 5 +- 18 files changed, 84 insertions(+), 292 deletions(-) delete mode 100644 pysetup/spec_builders/eip7514.py delete mode 100644 specs/_features/eip7514/beacon_chain.md delete mode 100644 specs/_features/eip7514/fork.md rename tests/core/pyspec/eth2spec/test/{eip7514 => deneb/epoch_processing}/__init__.py (100%) rename tests/core/pyspec/eth2spec/test/{eip7514 => deneb}/epoch_processing/test_process_registry_updates.py (80%) delete mode 100644 tests/core/pyspec/eth2spec/test/eip7514/epoch_processing/__init__.py diff --git a/.gitignore b/.gitignore index 7eacf101cf..cdfddfb0c3 100644 --- a/.gitignore +++ b/.gitignore @@ -23,7 +23,6 @@ tests/core/pyspec/eth2spec/capella/ tests/core/pyspec/eth2spec/deneb/ tests/core/pyspec/eth2spec/eip6110/ tests/core/pyspec/eth2spec/eip7002/ -tests/core/pyspec/eth2spec/eip7668/ tests/core/pyspec/eth2spec/whisk/ # coverage reports diff --git a/configs/mainnet.yaml b/configs/mainnet.yaml index 0bd46649e3..1b1a0ffa6d 100644 --- a/configs/mainnet.yaml +++ b/configs/mainnet.yaml @@ -56,9 +56,6 @@ EIP6110_FORK_EPOCH: 18446744073709551615 # EIP7002 EIP7002_FORK_VERSION: 0x05000000 # temporary stub EIP7002_FORK_EPOCH: 18446744073709551615 -# EIP7514 -EIP7514_FORK_VERSION: 0x05000000 # temporary stub -EIP7514_FORK_EPOCH: 18446744073709551615 # WHISK WHISK_FORK_VERSION: 0x06000000 # temporary stub WHISK_FORK_EPOCH: 18446744073709551615 @@ -90,7 +87,8 @@ EJECTION_BALANCE: 16000000000 MIN_PER_EPOCH_CHURN_LIMIT: 4 # 2**16 (= 65,536) CHURN_LIMIT_QUOTIENT: 65536 - +# [New in Deneb:EIP7514] 2**3 (=8) +MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT: 8 # Fork choice # --------------------------------------------------------------- @@ -149,6 +147,3 @@ BLOB_SIDECAR_SUBNET_COUNT: 6 WHISK_EPOCHS_PER_SHUFFLING_PHASE: 256 # `Epoch(2)` WHISK_PROPOSER_SELECTION_GAP: 2 - -# EIP7514 -MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT: 8 diff --git a/configs/minimal.yaml b/configs/minimal.yaml index 368e71c17c..da3c015766 100644 --- a/configs/minimal.yaml +++ b/configs/minimal.yaml @@ -55,9 +55,6 @@ EIP6110_FORK_EPOCH: 18446744073709551615 # EIP7002 EIP7002_FORK_VERSION: 0x05000001 EIP7002_FORK_EPOCH: 18446744073709551615 -# EIP7514 -EIP7514_FORK_VERSION: 0x05000001 # temporary stub -EIP7514_FORK_EPOCH: 18446744073709551615 # WHISK WHISK_FORK_VERSION: 0x06000001 WHISK_FORK_EPOCH: 18446744073709551615 @@ -89,6 +86,8 @@ EJECTION_BALANCE: 16000000000 MIN_PER_EPOCH_CHURN_LIMIT: 2 # [customized] scale queue churn at much lower validator counts for testing CHURN_LIMIT_QUOTIENT: 32 +# [New in Deneb:EIP7514] [customized] +MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT: 4 # Fork choice @@ -148,7 +147,3 @@ BLOB_SIDECAR_SUBNET_COUNT: 6 # Whisk WHISK_EPOCHS_PER_SHUFFLING_PHASE: 4 WHISK_PROPOSER_SELECTION_GAP: 1 - -# EIP7514 -# [customized] -MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT: 4 diff --git a/pysetup/constants.py b/pysetup/constants.py index 765429e589..8d53455634 100644 --- a/pysetup/constants.py +++ b/pysetup/constants.py @@ -6,7 +6,6 @@ DENEB = 'deneb' EIP6110 = 'eip6110' EIP7002 = 'eip7002' -EIP7514 = 'eip7514' WHISK = 'whisk' diff --git a/pysetup/md_doc_paths.py b/pysetup/md_doc_paths.py index 51dbfae253..781ae41db3 100644 --- a/pysetup/md_doc_paths.py +++ b/pysetup/md_doc_paths.py @@ -9,7 +9,6 @@ EIP6110, WHISK, EIP7002, - EIP7514, ) @@ -22,7 +21,6 @@ EIP6110: DENEB, WHISK: CAPELLA, EIP7002: CAPELLA, - EIP7514: CAPELLA, } ALL_FORKS = list(PREVIOUS_FORK_OF.keys()) diff --git a/pysetup/spec_builders/__init__.py b/pysetup/spec_builders/__init__.py index aa26b431fe..794ae50d29 100644 --- a/pysetup/spec_builders/__init__.py +++ b/pysetup/spec_builders/__init__.py @@ -5,7 +5,6 @@ from .deneb import DenebSpecBuilder from .eip6110 import EIP6110SpecBuilder from .eip7002 import EIP7002SpecBuilder -from .eip7514 import EIP7514SpecBuilder from .whisk import WhiskSpecBuilder @@ -13,6 +12,6 @@ builder.fork: builder for builder in ( Phase0SpecBuilder, AltairSpecBuilder, BellatrixSpecBuilder, CapellaSpecBuilder, DenebSpecBuilder, - EIP6110SpecBuilder, EIP7002SpecBuilder, EIP7514SpecBuilder, WhiskSpecBuilder, + EIP6110SpecBuilder, EIP7002SpecBuilder, WhiskSpecBuilder, ) } diff --git a/pysetup/spec_builders/eip7514.py b/pysetup/spec_builders/eip7514.py deleted file mode 100644 index 280cd11996..0000000000 --- a/pysetup/spec_builders/eip7514.py +++ /dev/null @@ -1,12 +0,0 @@ -from .base import BaseSpecBuilder -from ..constants import EIP7514 - - -class EIP7514SpecBuilder(BaseSpecBuilder): - fork: str = EIP7514 - - @classmethod - def imports(cls, preset_name: str): - return super().imports(preset_name) + f''' -from eth2spec.capella import {preset_name} as capella -''' diff --git a/specs/_features/eip7514/beacon_chain.md b/specs/_features/eip7514/beacon_chain.md deleted file mode 100644 index 993beee5db..0000000000 --- a/specs/_features/eip7514/beacon_chain.md +++ /dev/null @@ -1,83 +0,0 @@ -EIP-7514 -- The Beacon Chain - -## Table of contents - - - - - -- [Introduction](#introduction) -- [Configuration](#configuration) - - [Validator cycle](#validator-cycle) -- [Helper functions](#helper-functions) - - [Beacon state accessors](#beacon-state-accessors) - - [New `get_validator_activation_churn_limit`](#new-get_validator_activation_churn_limit) -- [Beacon chain state transition function](#beacon-chain-state-transition-function) - - [Epoch processing](#epoch-processing) - - [Registry updates](#registry-updates) - - - - -## Introduction - -This is the beacon chain specification to limit the max inbound churn value, motivated to limit the validator active set growth rate. - -*Note:* This specification is built upon [Capella](../../capella/beacon_chain.md) and is under active development. - -## Configuration - -### Validator cycle - -| Name | Value | -| - | - | -| `MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT` | `uint64(8)` (= 8) | - -## Helper functions - -### Beacon state accessors - -#### New `get_validator_activation_churn_limit` - -```python -def get_validator_activation_churn_limit(state: BeaconState) -> uint64: - """ - Return the validator activation churn limit for the current epoch. - """ - return min(MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT, get_validator_churn_limit(state)) -``` - -## Beacon chain state transition function - -### Epoch processing - -#### Registry updates - -Note: The function `process_registry_updates` is modified to utilize `get_validator_inbound_churn_limit()` the rate limit the activation queue for EIP-7514. - -```python -def process_registry_updates(state: BeaconState) -> None: - # Process activation eligibility and ejections - for index, validator in enumerate(state.validators): - if is_eligible_for_activation_queue(validator): - validator.activation_eligibility_epoch = get_current_epoch(state) + 1 - - if ( - is_active_validator(validator, get_current_epoch(state)) - and validator.effective_balance <= EJECTION_BALANCE - ): - initiate_validator_exit(state, ValidatorIndex(index)) - - # Queue validators eligible for activation and not yet dequeued for activation - activation_queue = sorted([ - index for index, validator in enumerate(state.validators) - if is_eligible_for_activation(state, validator) - # Order by the sequence of activation_eligibility_epoch setting and then index - ], key=lambda index: (state.validators[index].activation_eligibility_epoch, index)) - # Dequeued validators for activation up to churn limit - # [Modified in EIP7514] - for index in activation_queue[:get_validator_activation_churn_limit(state)]: - validator = state.validators[index] - validator.activation_epoch = compute_activation_exit_epoch(get_current_epoch(state)) -``` - diff --git a/specs/_features/eip7514/fork.md b/specs/_features/eip7514/fork.md deleted file mode 100644 index 05924874b2..0000000000 --- a/specs/_features/eip7514/fork.md +++ /dev/null @@ -1,139 +0,0 @@ -# EIP-7514 -- Fork Logic - -**Notice**: This document is a work-in-progress for researchers and implementers. - -## Table of contents - - - - -- [Introduction](#introduction) -- [Configuration](#configuration) -- [Helper functions](#helper-functions) - - [Misc](#misc) - - [Modified `compute_fork_version`](#modified-compute_fork_version) -- [Fork to EIP-7514](#fork-to-eip-7514) - - [Fork trigger](#fork-trigger) - - [Upgrading the state](#upgrading-the-state) - - - -## Introduction - -This document describes the process of EIP-7514 upgrade. - -## Configuration - -Warning: this configuration is not definitive. - -| Name | Value | -| - | - | -| `EIP7514_FORK_VERSION` | `Version('0x05000000')` | -| `EIP7514_FORK_EPOCH` | `Epoch(18446744073709551615)` **TBD** | - -## Helper functions - -### Misc - -#### Modified `compute_fork_version` - -```python -def compute_fork_version(epoch: Epoch) -> Version: - """ - Return the fork version at the given ``epoch``. - """ - if epoch >= EIP7514_FORK_EPOCH: - return EIP7514_FORK_VERSION - if epoch >= CAPELLA_FORK_EPOCH: - return CAPELLA_FORK_VERSION - if epoch >= BELLATRIX_FORK_EPOCH: - return BELLATRIX_FORK_VERSION - if epoch >= ALTAIR_FORK_EPOCH: - return ALTAIR_FORK_VERSION - return GENESIS_FORK_VERSION -``` - -## Fork to EIP-7514 - -### Fork trigger - -TBD. This fork is defined for testing purposes, the EIP may be combined with other consensus-layer upgrade. -For now, we assume the condition will be triggered at epoch `EIP7514_FORK_EPOCH`. - -Note that for the pure EIP-7514 networks, we don't apply `upgrade_to_eip7514` since it starts with EIP-7514 version logic. - -### Upgrading the state - -If `state.slot % SLOTS_PER_EPOCH == 0` and `compute_epoch_at_slot(state.slot) == EIP7514_FORK_EPOCH`, -an irregular state change is made to upgrade to EIP-7514. - -```python -def upgrade_to_eip7514(pre: capella.BeaconState) -> BeaconState: - epoch = capella.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=pre.latest_execution_payload_header.withdrawals_root, - ) - post = BeaconState( - # Versioning - genesis_time=pre.genesis_time, - genesis_validators_root=pre.genesis_validators_root, - slot=pre.slot, - fork=Fork( - previous_version=pre.fork.current_version, - current_version=EIP7514_FORK_VERSION, # [Modified in EIP-7514] - epoch=epoch, - ), - # History - latest_block_header=pre.latest_block_header, - block_roots=pre.block_roots, - state_roots=pre.state_roots, - historical_roots=pre.historical_roots, - # Eth1 - eth1_data=pre.eth1_data, - eth1_data_votes=pre.eth1_data_votes, - eth1_deposit_index=pre.eth1_deposit_index, - # Registry - validators=pre.validators, - balances=pre.balances, - # Randomness - randao_mixes=pre.randao_mixes, - # Slashings - slashings=pre.slashings, - # Participation - previous_epoch_participation=pre.previous_epoch_participation, - current_epoch_participation=pre.current_epoch_participation, - # Finality - justification_bits=pre.justification_bits, - previous_justified_checkpoint=pre.previous_justified_checkpoint, - current_justified_checkpoint=pre.current_justified_checkpoint, - finalized_checkpoint=pre.finalized_checkpoint, - # Inactivity - inactivity_scores=pre.inactivity_scores, - # Sync - current_sync_committee=pre.current_sync_committee, - next_sync_committee=pre.next_sync_committee, - # Execution-layer - latest_execution_payload_header=latest_execution_payload_header, - # Withdrawals - next_withdrawal_index=pre.next_withdrawal_index, - next_withdrawal_validator_index=pre.next_withdrawal_validator_index, - # Deep history valid from Capella onwards - historical_summaries=pre.historical_summaries, - ) - - return post -``` diff --git a/specs/deneb/beacon-chain.md b/specs/deneb/beacon-chain.md index 2328b20e03..f1a8e18e0d 100644 --- a/specs/deneb/beacon-chain.md +++ b/specs/deneb/beacon-chain.md @@ -16,6 +16,7 @@ - [Preset](#preset) - [Execution](#execution) - [Configuration](#configuration) + - [Validator cycle](#validator-cycle) - [Containers](#containers) - [Extended containers](#extended-containers) - [`BeaconBlockBody`](#beaconblockbody) @@ -26,6 +27,7 @@ - [`kzg_commitment_to_versioned_hash`](#kzg_commitment_to_versioned_hash) - [Beacon state accessors](#beacon-state-accessors) - [Modified `get_attestation_participation_flag_indices`](#modified-get_attestation_participation_flag_indices) + - [New `get_validator_activation_churn_limit`](#new-get_validator_activation_churn_limit) - [Beacon chain state transition function](#beacon-chain-state-transition-function) - [Execution engine](#execution-engine) - [Request data](#request-data) @@ -40,6 +42,8 @@ - [Execution payload](#execution-payload) - [Modified `process_execution_payload`](#modified-process_execution_payload) - [Modified `process_voluntary_exit`](#modified-process_voluntary_exit) + - [Epoch processing](#epoch-processing) + - [Registry updates](#registry-updates) - [Testing](#testing) @@ -52,6 +56,7 @@ Deneb is a consensus-layer upgrade containing a number of features. Including: * [EIP-4844](https://eips.ethereum.org/EIPS/eip-4844): Shard Blob Transactions scale data-availability of Ethereum in a simple, forwards-compatible manner * [EIP-7044](https://github.com/ethereum/EIPs/pull/7044): Perpetually Valid Signed Voluntary Exits * [EIP-7045](https://eips.ethereum.org/EIPS/eip-7045): Increase Max Attestation Inclusion Slot +* [EIP-7514](https://eips.ethereum.org/EIPS/eip-7514): Add Max Epoch Churn Limit ## Custom types @@ -89,6 +94,12 @@ and are limited by `MAX_BLOB_GAS_PER_BLOCK // GAS_PER_BLOB`. However the CL limi ## Configuration +### Validator cycle + +| Name | Value | +| - | - | +| `MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT` | `uint64(2**3)` (= 8) | + ## Containers ### Extended containers @@ -211,6 +222,16 @@ def get_attestation_participation_flag_indices(state: BeaconState, return participation_flag_indices ``` +#### New `get_validator_activation_churn_limit` + +```python +def get_validator_activation_churn_limit(state: BeaconState) -> uint64: + """ + Return the validator activation churn limit for the current epoch. + """ + return min(MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT, get_validator_churn_limit(state)) +``` + ## Beacon chain state transition function ### Execution engine @@ -415,6 +436,38 @@ def process_voluntary_exit(state: BeaconState, signed_voluntary_exit: SignedVolu initiate_validator_exit(state, voluntary_exit.validator_index) ``` +### Epoch processing + +#### Registry updates + +*Note*: The function `process_registry_updates` is modified to utilize `get_validator_activation_churn_limit()` the rate limit the activation queue for EIP-7514. + +```python +def process_registry_updates(state: BeaconState) -> None: + # Process activation eligibility and ejections + for index, validator in enumerate(state.validators): + if is_eligible_for_activation_queue(validator): + validator.activation_eligibility_epoch = get_current_epoch(state) + 1 + + if ( + is_active_validator(validator, get_current_epoch(state)) + and validator.effective_balance <= EJECTION_BALANCE + ): + initiate_validator_exit(state, ValidatorIndex(index)) + + # Queue validators eligible for activation and not yet dequeued for activation + activation_queue = sorted([ + index for index, validator in enumerate(state.validators) + if is_eligible_for_activation(state, validator) + # Order by the sequence of activation_eligibility_epoch setting and then index + ], key=lambda index: (state.validators[index].activation_eligibility_epoch, index)) + # Dequeued validators for activation up to churn limit + # [Modified in Deneb:EIP7514] + for index in activation_queue[:get_validator_activation_churn_limit(state)]: + validator = state.validators[index] + validator.activation_epoch = compute_activation_exit_epoch(get_current_epoch(state)) +``` + ## Testing *Note*: The function `initialize_beacon_state_from_eth1` is modified for pure Deneb testing only. diff --git a/tests/core/pyspec/eth2spec/test/context.py b/tests/core/pyspec/eth2spec/test/context.py index d646f08b95..7289fdf0fa 100644 --- a/tests/core/pyspec/eth2spec/test/context.py +++ b/tests/core/pyspec/eth2spec/test/context.py @@ -10,13 +10,12 @@ from eth2spec.deneb import mainnet as spec_deneb_mainnet, minimal as spec_deneb_minimal from eth2spec.eip6110 import mainnet as spec_eip6110_mainnet, minimal as spec_eip6110_minimal from eth2spec.eip7002 import mainnet as spec_eip7002_mainnet, minimal as spec_eip7002_minimal -from eth2spec.eip7514 import mainnet as spec_eip7514_mainnet, minimal as spec_eip7514_minimal from eth2spec.utils import bls from .exceptions import SkippedTest from .helpers.constants import ( PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB, - EIP6110, EIP7002, EIP7514, + EIP6110, EIP7002, MINIMAL, MAINNET, ALL_PHASES, ALL_FORK_UPGRADES, @@ -86,7 +85,6 @@ class ForkMeta: DENEB: spec_deneb_minimal, EIP6110: spec_eip6110_minimal, EIP7002: spec_eip7002_minimal, - EIP7514: spec_eip7514_minimal, }, MAINNET: { PHASE0: spec_phase0_mainnet, @@ -96,7 +94,6 @@ class ForkMeta: DENEB: spec_deneb_mainnet, EIP6110: spec_eip6110_mainnet, EIP7002: spec_eip7002_mainnet, - EIP7514: spec_eip7514_mainnet, }, } @@ -176,21 +173,21 @@ def scaled_churn_balances_min_churn_limit(spec: Spec): return [spec.MAX_EFFECTIVE_BALANCE] * num_validators -def scaled_churn_balances_equal_inbound_churn_limit(spec: Spec): +def scaled_churn_balances_equal_activation_churn_limit(spec: Spec): """ Helper method to create enough validators to scale the churn limit. (This is *firmly* over the churn limit -- thus the +2 instead of just +1) - Usage: `@with_custom_state(balances_fn=scaled_churn_balances_exceed_inbound_churn_limit, ...)` + Usage: `@with_custom_state(balances_fn=scaled_churn_balances_exceed_activation_churn_limit, ...)` """ num_validators = spec.config.CHURN_LIMIT_QUOTIENT * (spec.config.MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT) return [spec.MAX_EFFECTIVE_BALANCE] * num_validators -def scaled_churn_balances_exceed_inbound_churn_limit(spec: Spec): +def scaled_churn_balances_exceed_activation_churn_limit(spec: Spec): """ Helper method to create enough validators to scale the churn limit. (This is *firmly* over the churn limit -- thus the +2 instead of just +1) - Usage: `@with_custom_state(balances_fn=scaled_churn_balances_exceed_inbound_churn_limit, ...)` + Usage: `@with_custom_state(balances_fn=scaled_churn_balances_exceed_activation_churn_limit, ...)` """ num_validators = spec.config.CHURN_LIMIT_QUOTIENT * (spec.config.MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT + 2) return [spec.MAX_EFFECTIVE_BALANCE] * num_validators @@ -568,7 +565,6 @@ def wrapper(*args, spec: Spec, **kw): with_deneb_and_later = with_all_phases_from(DENEB) with_eip6110_and_later = with_all_phases_from(EIP6110) with_eip7002_and_later = with_all_phases_from(EIP7002) -with_eip7514_and_later = with_all_phases_from(EIP7514) class quoted_str(str): diff --git a/tests/core/pyspec/eth2spec/test/eip7514/__init__.py b/tests/core/pyspec/eth2spec/test/deneb/epoch_processing/__init__.py similarity index 100% rename from tests/core/pyspec/eth2spec/test/eip7514/__init__.py rename to tests/core/pyspec/eth2spec/test/deneb/epoch_processing/__init__.py diff --git a/tests/core/pyspec/eth2spec/test/eip7514/epoch_processing/test_process_registry_updates.py b/tests/core/pyspec/eth2spec/test/deneb/epoch_processing/test_process_registry_updates.py similarity index 80% rename from tests/core/pyspec/eth2spec/test/eip7514/epoch_processing/test_process_registry_updates.py rename to tests/core/pyspec/eth2spec/test/deneb/epoch_processing/test_process_registry_updates.py index aa8e2c41b8..d0b7638c6d 100644 --- a/tests/core/pyspec/eth2spec/test/eip7514/epoch_processing/test_process_registry_updates.py +++ b/tests/core/pyspec/eth2spec/test/deneb/epoch_processing/test_process_registry_updates.py @@ -1,14 +1,14 @@ from eth2spec.test.helpers.keys import pubkeys from eth2spec.test.helpers.constants import MINIMAL from eth2spec.test.context import ( - with_eip7514_and_later, + with_deneb_and_later, spec_test, spec_state_test, single_phase, with_custom_state, with_presets, - scaled_churn_balances_exceed_inbound_churn_limit, - scaled_churn_balances_equal_inbound_churn_limit, + scaled_churn_balances_exceed_activation_churn_limit, + scaled_churn_balances_equal_activation_churn_limit, ) from eth2spec.test.helpers.epoch_processing import run_epoch_processing_with @@ -17,7 +17,7 @@ def run_process_registry_updates(spec, state): yield from run_epoch_processing_with(spec, state, 'process_registry_updates') -def run_test_inbound_churn_limit(spec, state): +def run_test_activation_churn_limit(spec, state): mock_activations = spec.get_validator_activation_churn_limit(state) * 2 validator_count_0 = len(state.validators) @@ -54,37 +54,37 @@ def run_test_inbound_churn_limit(spec, state): assert state.validators[index].activation_epoch == spec.FAR_FUTURE_EPOCH -@with_eip7514_and_later +@with_deneb_and_later @with_presets([MINIMAL], reason="mainnet config leads to larger validator set than limit of public/private keys pre-generated") @spec_test -@with_custom_state(balances_fn=scaled_churn_balances_exceed_inbound_churn_limit, +@with_custom_state(balances_fn=scaled_churn_balances_exceed_activation_churn_limit, threshold_fn=lambda spec: spec.config.EJECTION_BALANCE) @single_phase -def test_inbound_churn_limit__greater_than_inbound_limit(spec, state): +def test_activation_churn_limit__greater_than_inbound_limit(spec, state): assert spec.get_validator_activation_churn_limit(state) == spec.config.MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT assert spec.get_validator_churn_limit(state) > spec.config.MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT - yield from run_test_inbound_churn_limit(spec, state) + yield from run_test_activation_churn_limit(spec, state) -@with_eip7514_and_later +@with_deneb_and_later @with_presets([MINIMAL], reason="mainnet config leads to larger validator set than limit of public/private keys pre-generated") @spec_test -@with_custom_state(balances_fn=scaled_churn_balances_equal_inbound_churn_limit, +@with_custom_state(balances_fn=scaled_churn_balances_equal_activation_churn_limit, threshold_fn=lambda spec: spec.config.EJECTION_BALANCE) @single_phase -def test_inbound_churn_limit__equal_to_inbound_limit(spec, state): +def test_activation_churn_limit__equal_to_inbound_limit(spec, state): assert spec.get_validator_activation_churn_limit(state) == spec.config.MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT assert spec.get_validator_churn_limit(state) == spec.config.MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT - yield from run_test_inbound_churn_limit(spec, state) + yield from run_test_activation_churn_limit(spec, state) -@with_eip7514_and_later +@with_deneb_and_later @with_presets([MINIMAL], reason="mainnet config leads to larger validator set than limit of public/private keys pre-generated") @spec_state_test -def test_inbound_churn_limit__less_than_inbound_limit(spec, state): +def test_activation_churn_limit__less_than_inbound_limit(spec, state): assert spec.get_validator_activation_churn_limit(state) < spec.config.MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT assert spec.get_validator_churn_limit(state) < spec.config.MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT - yield from run_test_inbound_churn_limit(spec, state) + yield from run_test_activation_churn_limit(spec, state) diff --git a/tests/core/pyspec/eth2spec/test/eip7514/epoch_processing/__init__.py b/tests/core/pyspec/eth2spec/test/eip7514/epoch_processing/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tests/core/pyspec/eth2spec/test/helpers/constants.py b/tests/core/pyspec/eth2spec/test/helpers/constants.py index dff1ede20d..82e4f9d0a5 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/constants.py +++ b/tests/core/pyspec/eth2spec/test/helpers/constants.py @@ -18,7 +18,6 @@ DAS = SpecForkName('das') EIP6110 = SpecForkName('eip6110') EIP7002 = SpecForkName('eip7002') -EIP7514 = SpecForkName('eip7514') # # SpecFork settings @@ -35,7 +34,6 @@ # Experimental patches EIP6110, EIP7002, - EIP7514, ) # The forks that have light client specs LIGHT_CLIENT_TESTING_FORKS = (*[item for item in MAINNET_FORKS if item != PHASE0], DENEB) diff --git a/tests/core/pyspec/eth2spec/test/helpers/forks.py b/tests/core/pyspec/eth2spec/test/helpers/forks.py index 4322e220aa..492af47fe3 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/forks.py +++ b/tests/core/pyspec/eth2spec/test/helpers/forks.py @@ -1,12 +1,10 @@ from .constants import ( PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB, - EIP6110, EIP7002, EIP7514, + EIP6110, EIP7002, ) def is_post_fork(a, b): - if a == EIP7514: - return b in [PHASE0, ALTAIR, BELLATRIX, CAPELLA, EIP7514] if a == EIP7002: return b in [PHASE0, ALTAIR, BELLATRIX, CAPELLA, EIP7002] if a == EIP6110: @@ -46,7 +44,3 @@ def is_post_eip6110(spec): def is_post_eip7002(spec): return is_post_fork(spec.fork, EIP7002) - - -def is_post_eip7514(spec): - return is_post_fork(spec.fork, EIP7514) diff --git a/tests/core/pyspec/eth2spec/test/helpers/genesis.py b/tests/core/pyspec/eth2spec/test/helpers/genesis.py index 8e01643bb2..e55bdef5ce 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/genesis.py +++ b/tests/core/pyspec/eth2spec/test/helpers/genesis.py @@ -1,5 +1,5 @@ from eth2spec.test.helpers.constants import ( - ALTAIR, BELLATRIX, CAPELLA, DENEB, EIP6110, EIP7002, EIP7514, + ALTAIR, BELLATRIX, CAPELLA, DENEB, EIP6110, EIP7002, ) from eth2spec.test.helpers.execution_payload import ( compute_el_header_block_hash, @@ -93,9 +93,6 @@ def create_genesis_state(spec, validator_balances, activation_threshold): elif spec.fork == EIP7002: previous_version = spec.config.CAPELLA_FORK_VERSION current_version = spec.config.EIP7002_FORK_VERSION - elif spec.fork == EIP7514: - previous_version = spec.config.CAPELLA_FORK_VERSION - current_version = spec.config.EIP7514_FORK_VERSION state = spec.BeaconState( genesis_time=0, diff --git a/tests/generators/epoch_processing/main.py b/tests/generators/epoch_processing/main.py index 645c84cb6b..63c2a548fd 100644 --- a/tests/generators/epoch_processing/main.py +++ b/tests/generators/epoch_processing/main.py @@ -32,7 +32,10 @@ ]} capella_mods = combine_mods(_new_capella_mods, bellatrix_mods) - deneb_mods = capella_mods + _new_deneb_mods = {key: 'eth2spec.test.deneb.epoch_processing.test_process_' + key for key in [ + 'registry_updates', + ]} + deneb_mods = combine_mods(_new_deneb_mods, capella_mods) eip6110_mods = deneb_mods From 468ae9aae434bb6b052c776a557fe701cacd3006 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Fri, 15 Sep 2023 11:04:08 +0800 Subject: [PATCH 43/46] Update the link of `EIP-7044` --- specs/deneb/beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/deneb/beacon-chain.md b/specs/deneb/beacon-chain.md index 2328b20e03..50c2248d19 100644 --- a/specs/deneb/beacon-chain.md +++ b/specs/deneb/beacon-chain.md @@ -50,7 +50,7 @@ Deneb is a consensus-layer upgrade containing a number of features. Including: * [EIP-4788](https://eips.ethereum.org/EIPS/eip-4788): Beacon block root in the EVM * [EIP-4844](https://eips.ethereum.org/EIPS/eip-4844): Shard Blob Transactions scale data-availability of Ethereum in a simple, forwards-compatible manner -* [EIP-7044](https://github.com/ethereum/EIPs/pull/7044): Perpetually Valid Signed Voluntary Exits +* [EIP-7044](https://eips.ethereum.org/EIPS/eip-7044): Perpetually Valid Signed Voluntary Exits * [EIP-7045](https://eips.ethereum.org/EIPS/eip-7045): Increase Max Attestation Inclusion Slot ## Custom types From 26d3fa3efd42ba7968edcfd9899908b8eda24ddc Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Fri, 15 Sep 2023 22:02:34 +0800 Subject: [PATCH 44/46] Apply suggestions from code review Co-authored-by: danny --- configs/mainnet.yaml | 2 +- configs/minimal.yaml | 2 +- specs/deneb/beacon-chain.md | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/configs/mainnet.yaml b/configs/mainnet.yaml index 1b1a0ffa6d..252f82dbe1 100644 --- a/configs/mainnet.yaml +++ b/configs/mainnet.yaml @@ -87,7 +87,7 @@ EJECTION_BALANCE: 16000000000 MIN_PER_EPOCH_CHURN_LIMIT: 4 # 2**16 (= 65,536) CHURN_LIMIT_QUOTIENT: 65536 -# [New in Deneb:EIP7514] 2**3 (=8) +# [New in Deneb:EIP7514] 2**3 (= 8) MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT: 8 # Fork choice diff --git a/configs/minimal.yaml b/configs/minimal.yaml index da3c015766..a3b1a8d5ad 100644 --- a/configs/minimal.yaml +++ b/configs/minimal.yaml @@ -82,7 +82,7 @@ INACTIVITY_SCORE_BIAS: 4 INACTIVITY_SCORE_RECOVERY_RATE: 16 # 2**4 * 10**9 (= 16,000,000,000) Gwei EJECTION_BALANCE: 16000000000 -# [customized] +# [customized] more easily demonstrate the difference between this value and the activation churn limit MIN_PER_EPOCH_CHURN_LIMIT: 2 # [customized] scale queue churn at much lower validator counts for testing CHURN_LIMIT_QUOTIENT: 32 diff --git a/specs/deneb/beacon-chain.md b/specs/deneb/beacon-chain.md index f1a8e18e0d..5902f4091a 100644 --- a/specs/deneb/beacon-chain.md +++ b/specs/deneb/beacon-chain.md @@ -440,7 +440,7 @@ def process_voluntary_exit(state: BeaconState, signed_voluntary_exit: SignedVolu #### Registry updates -*Note*: The function `process_registry_updates` is modified to utilize `get_validator_activation_churn_limit()` the rate limit the activation queue for EIP-7514. +*Note*: The function `process_registry_updates` is modified to utilize `get_validator_activation_churn_limit()` to rate limit the activation queue for EIP-7514. ```python def process_registry_updates(state: BeaconState) -> None: @@ -461,7 +461,7 @@ def process_registry_updates(state: BeaconState) -> None: if is_eligible_for_activation(state, validator) # Order by the sequence of activation_eligibility_epoch setting and then index ], key=lambda index: (state.validators[index].activation_eligibility_epoch, index)) - # Dequeued validators for activation up to churn limit + # Dequeued validators for activation up to activation churn limit # [Modified in Deneb:EIP7514] for index in activation_queue[:get_validator_activation_churn_limit(state)]: validator = state.validators[index] From e8041749a2252121f08c13d8b2ae5c52b57e6bb2 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Fri, 15 Sep 2023 22:05:26 +0800 Subject: [PATCH 45/46] Apply PR feedback. Rename `inbound_limit` to `activation_limit` --- .../deneb/epoch_processing/test_process_registry_updates.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/deneb/epoch_processing/test_process_registry_updates.py b/tests/core/pyspec/eth2spec/test/deneb/epoch_processing/test_process_registry_updates.py index d0b7638c6d..4cbcc1ed5c 100644 --- a/tests/core/pyspec/eth2spec/test/deneb/epoch_processing/test_process_registry_updates.py +++ b/tests/core/pyspec/eth2spec/test/deneb/epoch_processing/test_process_registry_updates.py @@ -61,7 +61,7 @@ def run_test_activation_churn_limit(spec, state): @with_custom_state(balances_fn=scaled_churn_balances_exceed_activation_churn_limit, threshold_fn=lambda spec: spec.config.EJECTION_BALANCE) @single_phase -def test_activation_churn_limit__greater_than_inbound_limit(spec, state): +def test_activation_churn_limit__greater_than_activation_limit(spec, state): assert spec.get_validator_activation_churn_limit(state) == spec.config.MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT assert spec.get_validator_churn_limit(state) > spec.config.MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT yield from run_test_activation_churn_limit(spec, state) @@ -74,7 +74,7 @@ def test_activation_churn_limit__greater_than_inbound_limit(spec, state): @with_custom_state(balances_fn=scaled_churn_balances_equal_activation_churn_limit, threshold_fn=lambda spec: spec.config.EJECTION_BALANCE) @single_phase -def test_activation_churn_limit__equal_to_inbound_limit(spec, state): +def test_activation_churn_limit__equal_to_activation_limit(spec, state): assert spec.get_validator_activation_churn_limit(state) == spec.config.MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT assert spec.get_validator_churn_limit(state) == spec.config.MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT yield from run_test_activation_churn_limit(spec, state) @@ -84,7 +84,7 @@ def test_activation_churn_limit__equal_to_inbound_limit(spec, state): @with_presets([MINIMAL], reason="mainnet config leads to larger validator set than limit of public/private keys pre-generated") @spec_state_test -def test_activation_churn_limit__less_than_inbound_limit(spec, state): +def test_activation_churn_limit__less_than_activation_limit(spec, state): assert spec.get_validator_activation_churn_limit(state) < spec.config.MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT assert spec.get_validator_churn_limit(state) < spec.config.MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT yield from run_test_activation_churn_limit(spec, state) From 206c328aa3e6603dd53ebeaf95de3578ff2b8995 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Mon, 18 Sep 2023 10:07:29 +0800 Subject: [PATCH 46/46] bump version.txt to 1.4.0-beta.2 --- 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 1b4859bc51..5a5d178b3a 100644 --- a/tests/core/pyspec/eth2spec/VERSION.txt +++ b/tests/core/pyspec/eth2spec/VERSION.txt @@ -1 +1 @@ -1.4.0-beta.1 +1.4.0-beta.2