From 1c8d57eeb721b6f5d5ba35dea2c31cd0f3c7cf8b Mon Sep 17 00:00:00 2001 From: Jacek Sieka Date: Thu, 27 Oct 2022 10:33:38 +0200 Subject: [PATCH 01/32] Historical batches This PR, a continuation of replaces `historical_roots` with `historical_block_roots`. By keeping an accumulator of historical block roots in the state, it becomes possible to validate the entire block history that led up to that particular state without executing the transitions, and without checking them one by one in backwards order using a parent chain. This is interesting for archival purposes as well as when implementing sync protocols that can verify chunks of blocks quickly, meaning they can be downloaded in any order. It's also useful as it provides a canonical hash by which such chunks of blocks can be named, with a direct reference in the state. In this PR, `historical_roots` is frozen at its current value and `historical_batches` are computed from the merge epoch onwards. After this PR, `block_batch_root` in the state can be used to verify an era of blocks against the state with a simple root check. The `historical_roots` values on the other hand can be used to verify that a constant distributed with clients is valid for a particular state, and therefore extends the block validation all the way back to genesis without backfilling `block_batch_root` and without introducing any new security assumptions in the client. As far as naming goes, it's convenient to talk about an "era" being 8192 slots ~= 1.14 days. The 8192 number comes from the SLOTS_PER_HISTORICAL_ROOT constant. With multiple easily verifable blocks in a file, it becomes trivial to offload block history to out-of-protocol transfer methods (bittorrent / ftp / whatever) - including execution payloads, paving the way for a future in which clients purge block history in p2p. This PR can be applied along with the merge which simplifies payload distribution from the get-go. Both execution and consensus clients benefit because from the merge onwards, they both need to be able to supply ranges of blocks in the sync protocol from what effectively is "cold storage". Another possibility is to include it in a future cleanup PR - this complicates the "cold storage" mode above by not covering exection payloads from start. --- specs/capella/beacon-chain.md | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/specs/capella/beacon-chain.md b/specs/capella/beacon-chain.md index 3b6dc4453..d80b70610 100644 --- a/specs/capella/beacon-chain.md +++ b/specs/capella/beacon-chain.md @@ -21,6 +21,7 @@ - [`Withdrawal`](#withdrawal) - [`BLSToExecutionChange`](#blstoexecutionchange) - [`SignedBLSToExecutionChange`](#signedblstoexecutionchange) + - [`HistoricalBatchSummary`](#historicalbatchsummary) - [Extended Containers](#extended-containers) - [`ExecutionPayload`](#executionpayload) - [`ExecutionPayloadHeader`](#executionpayloadheader) @@ -37,6 +38,7 @@ - [Epoch processing](#epoch-processing) - [Full withdrawals](#full-withdrawals) - [Partial withdrawals](#partial-withdrawals) + - [Historical batches updates](#historical-batches-updates) - [Block processing](#block-processing) - [New `process_withdrawals`](#new-process_withdrawals) - [Modified `process_execution_payload`](#modified-process_execution_payload) @@ -132,6 +134,18 @@ class SignedBLSToExecutionChange(Container): signature: BLSSignature ``` +#### `HistoricalBatchSummary` + +```python +class HistoricalBatchSummary(Container): + """ + `HistoricalBatchSummary` matches the components of the phase0 HistoricalBatch + making the two hash_tree_root-compatible. + """ + block_batch_root: Root + state_batch_root: Root +``` + ### Extended Containers #### `ExecutionPayload` @@ -213,7 +227,8 @@ class BeaconState(Container): latest_block_header: BeaconBlockHeader block_roots: Vector[Root, SLOTS_PER_HISTORICAL_ROOT] state_roots: Vector[Root, SLOTS_PER_HISTORICAL_ROOT] - historical_roots: List[Root, HISTORICAL_ROOTS_LIMIT] + historical_roots: List[Root, HISTORICAL_ROOTS_LIMIT] # Frozen in Merge, replaced by historical_batches + historical_batches: List[HistoricalBatchSummary, HISTORICAL_ROOTS_LIMIT] # Valid from Merge onwards # Eth1 eth1_data: Eth1Data eth1_data_votes: List[Eth1Data, EPOCHS_PER_ETH1_VOTING_PERIOD * SLOTS_PER_EPOCH] @@ -320,7 +335,7 @@ def process_epoch(state: BeaconState) -> None: process_effective_balance_updates(state) process_slashings_reset(state) process_randao_mixes_reset(state) - process_historical_roots_update(state) + process_historical_batches_update(state) process_participation_flag_updates(state) process_sync_committee_updates(state) process_full_withdrawals(state) # [New in Capella] @@ -367,6 +382,21 @@ def process_partial_withdrawals(state: BeaconState) -> None: state.next_partial_withdrawal_validator_index = validator_index ``` +#### Historical batches updates + +*Note*: The function `process_historical_batches_update` replaces `process_historical_roots_update` in phase0. + +```python +def process_historical_batches_update(state: BeaconState) -> None: + # Set historical block root accumulator + next_epoch = Epoch(get_current_epoch(state) + 1) + if next_epoch % (SLOTS_PER_HISTORICAL_ROOT // SLOTS_PER_EPOCH) == 0: + historical_batch = HistoricalBatchSummary( + block_batch_root=hash_tree_root(state.block_roots), + state_batch_root=hash_tree_root(state.state_roots)) + state.historical_batches.append(historical_batch) +``` + ### Block processing ```python From 4d1b487b21082cea70c1a0664c1ad0304300f6cc Mon Sep 17 00:00:00 2001 From: Jacek Sieka Date: Thu, 27 Oct 2022 10:38:09 +0200 Subject: [PATCH 02/32] move field last avoids changing "header" fields in state --- specs/capella/beacon-chain.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/specs/capella/beacon-chain.md b/specs/capella/beacon-chain.md index d80b70610..433ccba57 100644 --- a/specs/capella/beacon-chain.md +++ b/specs/capella/beacon-chain.md @@ -228,7 +228,6 @@ class BeaconState(Container): block_roots: Vector[Root, SLOTS_PER_HISTORICAL_ROOT] state_roots: Vector[Root, SLOTS_PER_HISTORICAL_ROOT] historical_roots: List[Root, HISTORICAL_ROOTS_LIMIT] # Frozen in Merge, replaced by historical_batches - historical_batches: List[HistoricalBatchSummary, HISTORICAL_ROOTS_LIMIT] # Valid from Merge onwards # Eth1 eth1_data: Eth1Data eth1_data_votes: List[Eth1Data, EPOCHS_PER_ETH1_VOTING_PERIOD * SLOTS_PER_EPOCH] @@ -259,6 +258,8 @@ class BeaconState(Container): withdrawal_queue: List[Withdrawal, WITHDRAWAL_QUEUE_LIMIT] # [New in Capella] next_withdrawal_index: WithdrawalIndex # [New in Capella] next_partial_withdrawal_validator_index: ValidatorIndex # [New in Capella] + # Deep history + historical_batches: List[HistoricalBatchSummary, HISTORICAL_ROOTS_LIMIT] # Valid from Merge onwards ``` ## Helpers From 8bf801ecc64aaa3e1b5ba24b80855bb5e7fa1112 Mon Sep 17 00:00:00 2001 From: Etan Kissling Date: Tue, 6 Dec 2022 17:44:41 +0100 Subject: [PATCH 03/32] Add `block_to_light_client_header` helper Introduce `block_to_light_client_header` helper function to enable future forks to override it with additional info (e.g., execution), without having to change the general light client logic. Likewise, update existing light client data creation flow to use `block_to_light_client_header` and default-initialize empty fields. Furthermore, generalize `create_update` helper to streamline test code using `block_to_light_client_header`. Note: In Altair spec, LC header is the same as `BeaconBlockHeader`. however; future forks will extend it with more information. --- specs/altair/light-client/full-node.md | 64 ++++---- .../light_client/test_update_ranking.py | 154 ++++++++---------- .../light_client/test_sync_protocol.py | 135 +++++++-------- .../eth2spec/test/helpers/light_client.py | 52 +++--- 4 files changed, 181 insertions(+), 224 deletions(-) diff --git a/specs/altair/light-client/full-node.md b/specs/altair/light-client/full-node.md index 53ba4dc82..2dd2ded6c 100644 --- a/specs/altair/light-client/full-node.md +++ b/specs/altair/light-client/full-node.md @@ -11,6 +11,7 @@ - [Introduction](#introduction) - [Helper functions](#helper-functions) - [`compute_merkle_proof_for_state`](#compute_merkle_proof_for_state) + - [`block_to_light_client_header`](#block_to_light_client_header) - [Deriving light client data](#deriving-light-client-data) - [`create_light_client_bootstrap`](#create_light_client_bootstrap) - [`create_light_client_update`](#create_light_client_update) @@ -34,6 +35,19 @@ def compute_merkle_proof_for_state(state: BeaconState, ... ``` +### `block_to_light_client_header` + +```python +def block_to_light_client_header(block: SignedBeaconBlock) -> BeaconBlockHeader: + return BeaconBlockHeader( + slot=block.message.slot, + proposer_index=block.message.proposer_index, + parent_root=block.message.parent_root, + state_root=block.message.state_root, + body_root=hash_tree_root(block.message.body), + ) +``` + ## Deriving light client data Full nodes are expected to derive light client data from historic blocks and states and provide it to other clients. @@ -55,13 +69,7 @@ def create_light_client_bootstrap(state: BeaconState, assert hash_tree_root(header) == hash_tree_root(block.message) return LightClientBootstrap( - header=BeaconBlockHeader( - slot=state.latest_block_header.slot, - proposer_index=state.latest_block_header.proposer_index, - parent_root=state.latest_block_header.parent_root, - state_root=hash_tree_root(state), - body_root=state.latest_block_header.body_root, - ), + header=block_to_light_client_header(block), current_sync_committee=state.current_sync_committee, current_sync_committee_branch=compute_merkle_proof_for_state(state, CURRENT_SYNC_COMMITTEE_INDEX), ) @@ -103,42 +111,30 @@ def create_light_client_update(state: BeaconState, assert hash_tree_root(attested_header) == hash_tree_root(attested_block.message) == block.message.parent_root update_attested_period = compute_sync_committee_period_at_slot(attested_block.message.slot) + update = LightClientUpdate() + + update.attested_header = block_to_light_client_header(attested_block) + # `next_sync_committee` is only useful if the message is signed by the current sync committee if update_attested_period == update_signature_period: - next_sync_committee = attested_state.next_sync_committee - next_sync_committee_branch = compute_merkle_proof_for_state(attested_state, NEXT_SYNC_COMMITTEE_INDEX) - else: - next_sync_committee = SyncCommittee() - next_sync_committee_branch = [Bytes32() for _ in range(floorlog2(NEXT_SYNC_COMMITTEE_INDEX))] + update.next_sync_committee = attested_state.next_sync_committee + update.next_sync_committee_branch = \ + compute_merkle_proof_for_state(attested_state, NEXT_SYNC_COMMITTEE_INDEX) # Indicate finality whenever possible if finalized_block is not None: if finalized_block.message.slot != GENESIS_SLOT: - finalized_header = BeaconBlockHeader( - slot=finalized_block.message.slot, - proposer_index=finalized_block.message.proposer_index, - parent_root=finalized_block.message.parent_root, - state_root=finalized_block.message.state_root, - body_root=hash_tree_root(finalized_block.message.body), - ) - assert hash_tree_root(finalized_header) == attested_state.finalized_checkpoint.root + update.finalized_header = block_to_light_client_header(finalized_block) + assert hash_tree_root(update.finalized_header) == attested_state.finalized_checkpoint.root else: assert attested_state.finalized_checkpoint.root == Bytes32() - finalized_header = BeaconBlockHeader() - finality_branch = compute_merkle_proof_for_state(attested_state, FINALIZED_ROOT_INDEX) - else: - finalized_header = BeaconBlockHeader() - finality_branch = [Bytes32() for _ in range(floorlog2(FINALIZED_ROOT_INDEX))] + update.finality_branch = \ + compute_merkle_proof_for_state(attested_state, FINALIZED_ROOT_INDEX) - return LightClientUpdate( - attested_header=attested_header, - next_sync_committee=next_sync_committee, - next_sync_committee_branch=next_sync_committee_branch, - finalized_header=finalized_header, - finality_branch=finality_branch, - sync_aggregate=block.message.body.sync_aggregate, - signature_slot=block.message.slot, - ) + update.sync_aggregate = block.message.body.sync_aggregate + update.signature_slot = block.message.slot + + return update ``` Full nodes SHOULD provide the best derivable `LightClientUpdate` (according to `is_better_update`) for each sync committee period covering any epochs in range `[max(ALTAIR_FORK_EPOCH, current_epoch - MIN_EPOCHS_FOR_BLOCK_REQUESTS), current_epoch]` where `current_epoch` is defined by the current wall-clock time. Full nodes MAY also provide `LightClientUpdate` for other sync committee periods. diff --git a/tests/core/pyspec/eth2spec/test/altair/light_client/test_update_ranking.py b/tests/core/pyspec/eth2spec/test/altair/light_client/test_update_ranking.py index 23ad79584..bde70a940 100644 --- a/tests/core/pyspec/eth2spec/test/altair/light_client/test_update_ranking.py +++ b/tests/core/pyspec/eth2spec/test/altair/light_client/test_update_ranking.py @@ -9,45 +9,23 @@ from eth2spec.test.helpers.attestations import ( ) from eth2spec.test.helpers.constants import MINIMAL from eth2spec.test.helpers.light_client import ( - get_sync_aggregate, - signed_block_to_header, + create_update, ) from eth2spec.test.helpers.state import ( next_slots, ) -from math import floor -def create_update(spec, test, with_next, with_finality, participation_rate): +def create_test_update(spec, test, with_next, with_finality, participation_rate): attested_state, attested_block, finalized_block = test - num_participants = floor(spec.SYNC_COMMITTEE_SIZE * participation_rate) - - attested_header = signed_block_to_header(spec, attested_block) - - if with_next: - next_sync_committee = attested_state.next_sync_committee - next_sync_committee_branch = spec.compute_merkle_proof_for_state(attested_state, spec.NEXT_SYNC_COMMITTEE_INDEX) - else: - next_sync_committee = spec.SyncCommittee() - next_sync_committee_branch = [spec.Bytes32() for _ in range(spec.floorlog2(spec.NEXT_SYNC_COMMITTEE_INDEX))] - - if with_finality: - finalized_header = signed_block_to_header(spec, finalized_block) - finality_branch = spec.compute_merkle_proof_for_state(attested_state, spec.FINALIZED_ROOT_INDEX) - else: - finalized_header = spec.BeaconBlockHeader() - finality_branch = [spec.Bytes32() for _ in range(spec.floorlog2(spec.FINALIZED_ROOT_INDEX))] - - sync_aggregate, signature_slot = get_sync_aggregate(spec, attested_state, num_participants) - - return spec.LightClientUpdate( - attested_header=attested_header, - next_sync_committee=next_sync_committee, - next_sync_committee_branch=next_sync_committee_branch, - finalized_header=finalized_header, - finality_branch=finality_branch, - sync_aggregate=sync_aggregate, - signature_slot=signature_slot, + return create_update( + spec, + attested_state, + attested_block, + finalized_block, + with_next, + with_finality, + participation_rate, ) @@ -84,76 +62,76 @@ def test_update_ranking(spec, state): # Create updates (in descending order of quality) updates = [ # Updates with sync committee finality - create_update(spec, fin, with_next=1, with_finality=1, participation_rate=1.0), - create_update(spec, lat, with_next=1, with_finality=1, participation_rate=1.0), - create_update(spec, fin, with_next=1, with_finality=1, participation_rate=0.8), - create_update(spec, lat, with_next=1, with_finality=1, participation_rate=0.8), + create_test_update(spec, fin, with_next=1, with_finality=1, participation_rate=1.0), + create_test_update(spec, lat, with_next=1, with_finality=1, participation_rate=1.0), + create_test_update(spec, fin, with_next=1, with_finality=1, participation_rate=0.8), + create_test_update(spec, lat, with_next=1, with_finality=1, participation_rate=0.8), # Updates without sync committee finality - create_update(spec, att, with_next=1, with_finality=1, participation_rate=1.0), - create_update(spec, att, with_next=1, with_finality=1, participation_rate=0.8), + create_test_update(spec, att, with_next=1, with_finality=1, participation_rate=1.0), + create_test_update(spec, att, with_next=1, with_finality=1, participation_rate=0.8), # Updates without indication of any finality - create_update(spec, att, with_next=1, with_finality=0, participation_rate=1.0), - create_update(spec, fin, with_next=1, with_finality=0, participation_rate=1.0), - create_update(spec, lat, with_next=1, with_finality=0, participation_rate=1.0), - create_update(spec, att, with_next=1, with_finality=0, participation_rate=0.8), - create_update(spec, fin, with_next=1, with_finality=0, participation_rate=0.8), - create_update(spec, lat, with_next=1, with_finality=0, participation_rate=0.8), + create_test_update(spec, att, with_next=1, with_finality=0, participation_rate=1.0), + create_test_update(spec, fin, with_next=1, with_finality=0, participation_rate=1.0), + create_test_update(spec, lat, with_next=1, with_finality=0, participation_rate=1.0), + create_test_update(spec, att, with_next=1, with_finality=0, participation_rate=0.8), + create_test_update(spec, fin, with_next=1, with_finality=0, participation_rate=0.8), + create_test_update(spec, lat, with_next=1, with_finality=0, participation_rate=0.8), # Updates with sync committee finality but no `next_sync_committee` - create_update(spec, sig, with_next=0, with_finality=1, participation_rate=1.0), - create_update(spec, fin, with_next=0, with_finality=1, participation_rate=1.0), - create_update(spec, lat, with_next=0, with_finality=1, participation_rate=1.0), - create_update(spec, sig, with_next=0, with_finality=1, participation_rate=0.8), - create_update(spec, fin, with_next=0, with_finality=1, participation_rate=0.8), - create_update(spec, lat, with_next=0, with_finality=1, participation_rate=0.8), + create_test_update(spec, sig, with_next=0, with_finality=1, participation_rate=1.0), + create_test_update(spec, fin, with_next=0, with_finality=1, participation_rate=1.0), + create_test_update(spec, lat, with_next=0, with_finality=1, participation_rate=1.0), + create_test_update(spec, sig, with_next=0, with_finality=1, participation_rate=0.8), + create_test_update(spec, fin, with_next=0, with_finality=1, participation_rate=0.8), + create_test_update(spec, lat, with_next=0, with_finality=1, participation_rate=0.8), # Updates without sync committee finality and also no `next_sync_committee` - create_update(spec, att, with_next=0, with_finality=1, participation_rate=1.0), - create_update(spec, att, with_next=0, with_finality=1, participation_rate=0.8), + create_test_update(spec, att, with_next=0, with_finality=1, participation_rate=1.0), + create_test_update(spec, att, with_next=0, with_finality=1, participation_rate=0.8), # Updates without indication of any finality nor `next_sync_committee` - create_update(spec, sig, with_next=0, with_finality=0, participation_rate=1.0), - create_update(spec, att, with_next=0, with_finality=0, participation_rate=1.0), - create_update(spec, fin, with_next=0, with_finality=0, participation_rate=1.0), - create_update(spec, lat, with_next=0, with_finality=0, participation_rate=1.0), - create_update(spec, sig, with_next=0, with_finality=0, participation_rate=0.8), - create_update(spec, att, with_next=0, with_finality=0, participation_rate=0.8), - create_update(spec, fin, with_next=0, with_finality=0, participation_rate=0.8), - create_update(spec, lat, with_next=0, with_finality=0, participation_rate=0.8), + create_test_update(spec, sig, with_next=0, with_finality=0, participation_rate=1.0), + create_test_update(spec, att, with_next=0, with_finality=0, participation_rate=1.0), + create_test_update(spec, fin, with_next=0, with_finality=0, participation_rate=1.0), + create_test_update(spec, lat, with_next=0, with_finality=0, participation_rate=1.0), + create_test_update(spec, sig, with_next=0, with_finality=0, participation_rate=0.8), + create_test_update(spec, att, with_next=0, with_finality=0, participation_rate=0.8), + create_test_update(spec, fin, with_next=0, with_finality=0, participation_rate=0.8), + create_test_update(spec, lat, with_next=0, with_finality=0, participation_rate=0.8), # Updates with low sync committee participation - create_update(spec, fin, with_next=1, with_finality=1, participation_rate=0.4), - create_update(spec, lat, with_next=1, with_finality=1, participation_rate=0.4), - create_update(spec, att, with_next=1, with_finality=1, participation_rate=0.4), - create_update(spec, att, with_next=1, with_finality=0, participation_rate=0.4), - create_update(spec, fin, with_next=1, with_finality=0, participation_rate=0.4), - create_update(spec, lat, with_next=1, with_finality=0, participation_rate=0.4), - create_update(spec, sig, with_next=0, with_finality=1, participation_rate=0.4), - create_update(spec, fin, with_next=0, with_finality=1, participation_rate=0.4), - create_update(spec, lat, with_next=0, with_finality=1, participation_rate=0.4), - create_update(spec, att, with_next=0, with_finality=1, participation_rate=0.4), - create_update(spec, sig, with_next=0, with_finality=0, participation_rate=0.4), - create_update(spec, att, with_next=0, with_finality=0, participation_rate=0.4), - create_update(spec, fin, with_next=0, with_finality=0, participation_rate=0.4), - create_update(spec, lat, with_next=0, with_finality=0, participation_rate=0.4), + create_test_update(spec, fin, with_next=1, with_finality=1, participation_rate=0.4), + create_test_update(spec, lat, with_next=1, with_finality=1, participation_rate=0.4), + create_test_update(spec, att, with_next=1, with_finality=1, participation_rate=0.4), + create_test_update(spec, att, with_next=1, with_finality=0, participation_rate=0.4), + create_test_update(spec, fin, with_next=1, with_finality=0, participation_rate=0.4), + create_test_update(spec, lat, with_next=1, with_finality=0, participation_rate=0.4), + create_test_update(spec, sig, with_next=0, with_finality=1, participation_rate=0.4), + create_test_update(spec, fin, with_next=0, with_finality=1, participation_rate=0.4), + create_test_update(spec, lat, with_next=0, with_finality=1, participation_rate=0.4), + create_test_update(spec, att, with_next=0, with_finality=1, participation_rate=0.4), + create_test_update(spec, sig, with_next=0, with_finality=0, participation_rate=0.4), + create_test_update(spec, att, with_next=0, with_finality=0, participation_rate=0.4), + create_test_update(spec, fin, with_next=0, with_finality=0, participation_rate=0.4), + create_test_update(spec, lat, with_next=0, with_finality=0, participation_rate=0.4), # Updates with very low sync committee participation - create_update(spec, fin, with_next=1, with_finality=1, participation_rate=0.2), - create_update(spec, lat, with_next=1, with_finality=1, participation_rate=0.2), - create_update(spec, att, with_next=1, with_finality=1, participation_rate=0.2), - create_update(spec, att, with_next=1, with_finality=0, participation_rate=0.2), - create_update(spec, fin, with_next=1, with_finality=0, participation_rate=0.2), - create_update(spec, lat, with_next=1, with_finality=0, participation_rate=0.2), - create_update(spec, sig, with_next=0, with_finality=1, participation_rate=0.2), - create_update(spec, fin, with_next=0, with_finality=1, participation_rate=0.2), - create_update(spec, lat, with_next=0, with_finality=1, participation_rate=0.2), - create_update(spec, att, with_next=0, with_finality=1, participation_rate=0.2), - create_update(spec, sig, with_next=0, with_finality=0, participation_rate=0.2), - create_update(spec, att, with_next=0, with_finality=0, participation_rate=0.2), - create_update(spec, fin, with_next=0, with_finality=0, participation_rate=0.2), - create_update(spec, lat, with_next=0, with_finality=0, participation_rate=0.2), + create_test_update(spec, fin, with_next=1, with_finality=1, participation_rate=0.2), + create_test_update(spec, lat, with_next=1, with_finality=1, participation_rate=0.2), + create_test_update(spec, att, with_next=1, with_finality=1, participation_rate=0.2), + create_test_update(spec, att, with_next=1, with_finality=0, participation_rate=0.2), + create_test_update(spec, fin, with_next=1, with_finality=0, participation_rate=0.2), + create_test_update(spec, lat, with_next=1, with_finality=0, participation_rate=0.2), + create_test_update(spec, sig, with_next=0, with_finality=1, participation_rate=0.2), + create_test_update(spec, fin, with_next=0, with_finality=1, participation_rate=0.2), + create_test_update(spec, lat, with_next=0, with_finality=1, participation_rate=0.2), + create_test_update(spec, att, with_next=0, with_finality=1, participation_rate=0.2), + create_test_update(spec, sig, with_next=0, with_finality=0, participation_rate=0.2), + create_test_update(spec, att, with_next=0, with_finality=0, participation_rate=0.2), + create_test_update(spec, fin, with_next=0, with_finality=0, participation_rate=0.2), + create_test_update(spec, lat, with_next=0, with_finality=0, participation_rate=0.2), ] yield "updates", updates diff --git a/tests/core/pyspec/eth2spec/test/altair/unittests/light_client/test_sync_protocol.py b/tests/core/pyspec/eth2spec/test/altair/unittests/light_client/test_sync_protocol.py index bf09cc30e..a72f1980b 100644 --- a/tests/core/pyspec/eth2spec/test/altair/unittests/light_client/test_sync_protocol.py +++ b/tests/core/pyspec/eth2spec/test/altair/unittests/light_client/test_sync_protocol.py @@ -11,43 +11,44 @@ from eth2spec.test.helpers.attestations import ( ) from eth2spec.test.helpers.constants import MINIMAL from eth2spec.test.helpers.light_client import ( - get_sync_aggregate, - initialize_light_client_store, - signed_block_to_header, + create_update, ) from eth2spec.test.helpers.state import ( next_slots, ) +def setup_test(spec, state): + trusted_block = spec.SignedBeaconBlock() + trusted_block.message.state_root = state.hash_tree_root() + trusted_block_root = trusted_block.message.hash_tree_root() + bootstrap = spec.create_light_client_bootstrap(state, trusted_block) + store = spec.initialize_light_client_store(trusted_block_root, bootstrap) + store.next_sync_committee = state.next_sync_committee + + return (trusted_block, store) + + @with_altair_and_later @spec_state_test_with_matching_config def test_process_light_client_update_not_timeout(spec, state): - store = initialize_light_client_store(spec, state) + genesis_block, store = setup_test(spec, state) # Block at slot 1 doesn't increase sync committee period, so it won't force update store.finalized_header attested_block = state_transition_with_full_block(spec, state, False, False) - attested_header = signed_block_to_header(spec, attested_block) - - # Sync committee signing the attested_header - sync_aggregate, signature_slot = get_sync_aggregate(spec, state) - next_sync_committee = spec.SyncCommittee() - next_sync_committee_branch = [spec.Bytes32() for _ in range(spec.floorlog2(spec.NEXT_SYNC_COMMITTEE_INDEX))] + signature_slot = state.slot + 1 # Ensure that finality checkpoint is genesis assert state.finalized_checkpoint.epoch == 0 - # Finality is unchanged - finalized_header = spec.BeaconBlockHeader() - finality_branch = [spec.Bytes32() for _ in range(spec.floorlog2(spec.FINALIZED_ROOT_INDEX))] - update = spec.LightClientUpdate( - attested_header=attested_header, - next_sync_committee=next_sync_committee, - next_sync_committee_branch=next_sync_committee_branch, - finalized_header=finalized_header, - finality_branch=finality_branch, - sync_aggregate=sync_aggregate, - signature_slot=signature_slot, + update = create_update( + spec, + attested_state=state, + attested_block=attested_block, + finalized_block=genesis_block, + with_next=False, + with_finality=False, + participation_rate=1.0, ) pre_store = deepcopy(store) @@ -64,7 +65,7 @@ def test_process_light_client_update_not_timeout(spec, state): @spec_state_test_with_matching_config @with_presets([MINIMAL], reason="too slow") def test_process_light_client_update_at_period_boundary(spec, state): - store = initialize_light_client_store(spec, state) + genesis_block, store = setup_test(spec, state) # Forward to slot before next sync committee period so that next block is final one in period next_slots(spec, state, spec.UPDATE_TIMEOUT - 2) @@ -73,25 +74,16 @@ def test_process_light_client_update_at_period_boundary(spec, state): assert store_period == update_period attested_block = state_transition_with_full_block(spec, state, False, False) - attested_header = signed_block_to_header(spec, attested_block) + signature_slot = state.slot + 1 - # Sync committee signing the attested_header - sync_aggregate, signature_slot = get_sync_aggregate(spec, state) - next_sync_committee = spec.SyncCommittee() - next_sync_committee_branch = [spec.Bytes32() for _ in range(spec.floorlog2(spec.NEXT_SYNC_COMMITTEE_INDEX))] - - # Finality is unchanged - finalized_header = spec.BeaconBlockHeader() - finality_branch = [spec.Bytes32() for _ in range(spec.floorlog2(spec.FINALIZED_ROOT_INDEX))] - - update = spec.LightClientUpdate( - attested_header=attested_header, - next_sync_committee=next_sync_committee, - next_sync_committee_branch=next_sync_committee_branch, - finalized_header=finalized_header, - finality_branch=finality_branch, - sync_aggregate=sync_aggregate, - signature_slot=signature_slot, + update = create_update( + spec, + attested_state=state, + attested_block=attested_block, + finalized_block=genesis_block, + with_next=False, + with_finality=False, + participation_rate=1.0, ) pre_store = deepcopy(store) @@ -108,7 +100,7 @@ def test_process_light_client_update_at_period_boundary(spec, state): @spec_state_test_with_matching_config @with_presets([MINIMAL], reason="too slow") def test_process_light_client_update_timeout(spec, state): - store = initialize_light_client_store(spec, state) + genesis_block, store = setup_test(spec, state) # Forward to next sync committee period next_slots(spec, state, spec.UPDATE_TIMEOUT) @@ -117,26 +109,16 @@ def test_process_light_client_update_timeout(spec, state): assert store_period + 1 == update_period attested_block = state_transition_with_full_block(spec, state, False, False) - attested_header = signed_block_to_header(spec, attested_block) + signature_slot = state.slot + 1 - # Sync committee signing the attested_header - sync_aggregate, signature_slot = get_sync_aggregate(spec, state) - - # Sync committee is updated - next_sync_committee = state.next_sync_committee - next_sync_committee_branch = spec.compute_merkle_proof_for_state(state, spec.NEXT_SYNC_COMMITTEE_INDEX) - # Finality is unchanged - finalized_header = spec.BeaconBlockHeader() - finality_branch = [spec.Bytes32() for _ in range(spec.floorlog2(spec.FINALIZED_ROOT_INDEX))] - - update = spec.LightClientUpdate( - attested_header=attested_header, - next_sync_committee=next_sync_committee, - next_sync_committee_branch=next_sync_committee_branch, - finalized_header=finalized_header, - finality_branch=finality_branch, - sync_aggregate=sync_aggregate, - signature_slot=signature_slot, + update = create_update( + spec, + attested_state=state, + attested_block=attested_block, + finalized_block=genesis_block, + with_next=True, + with_finality=False, + participation_rate=1.0, ) pre_store = deepcopy(store) @@ -153,7 +135,7 @@ def test_process_light_client_update_timeout(spec, state): @spec_state_test_with_matching_config @with_presets([MINIMAL], reason="too slow") def test_process_light_client_update_finality_updated(spec, state): - store = initialize_light_client_store(spec, state) + _, store = setup_test(spec, state) # Change finality blocks = [] @@ -169,28 +151,21 @@ def test_process_light_client_update_finality_updated(spec, state): assert store_period == update_period attested_block = blocks[-1] - attested_header = signed_block_to_header(spec, attested_block) + signature_slot = state.slot + 1 - # Sync committee signing the attested_header - sync_aggregate, signature_slot = get_sync_aggregate(spec, state) - - # Updated sync_committee and finality - next_sync_committee = spec.SyncCommittee() - next_sync_committee_branch = [spec.Bytes32() for _ in range(spec.floorlog2(spec.NEXT_SYNC_COMMITTEE_INDEX))] + # Updated finality finalized_block = blocks[spec.SLOTS_PER_EPOCH - 1] - finalized_header = signed_block_to_header(spec, finalized_block) - assert finalized_header.slot == spec.compute_start_slot_at_epoch(state.finalized_checkpoint.epoch) - assert finalized_header.hash_tree_root() == state.finalized_checkpoint.root - finality_branch = spec.compute_merkle_proof_for_state(state, spec.FINALIZED_ROOT_INDEX) + assert finalized_block.message.slot == spec.compute_start_slot_at_epoch(state.finalized_checkpoint.epoch) + assert finalized_block.message.hash_tree_root() == state.finalized_checkpoint.root - update = spec.LightClientUpdate( - attested_header=attested_header, - next_sync_committee=next_sync_committee, - next_sync_committee_branch=next_sync_committee_branch, - finalized_header=finalized_header, - finality_branch=finality_branch, - sync_aggregate=sync_aggregate, - signature_slot=signature_slot, + update = create_update( + spec, + attested_state=state, + attested_block=attested_block, + finalized_block=finalized_block, + with_next=False, + with_finality=True, + participation_rate=1.0, ) spec.process_light_client_update(store, update, signature_slot, state.genesis_validators_root) diff --git a/tests/core/pyspec/eth2spec/test/helpers/light_client.py b/tests/core/pyspec/eth2spec/test/helpers/light_client.py index 8d632b3a1..39175a352 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/light_client.py +++ b/tests/core/pyspec/eth2spec/test/helpers/light_client.py @@ -5,28 +5,7 @@ from eth2spec.test.helpers.sync_committee import ( compute_aggregate_sync_committee_signature, compute_committee_indices, ) - - -def signed_block_to_header(spec, block): - return spec.BeaconBlockHeader( - slot=block.message.slot, - proposer_index=block.message.proposer_index, - parent_root=block.message.parent_root, - state_root=block.message.state_root, - body_root=block.message.body.hash_tree_root(), - ) - - -def initialize_light_client_store(spec, state): - return spec.LightClientStore( - finalized_header=spec.BeaconBlockHeader(), - current_sync_committee=state.current_sync_committee, - next_sync_committee=state.next_sync_committee, - best_valid_update=None, - optimistic_header=spec.BeaconBlockHeader(), - previous_max_active_participants=0, - current_max_active_participants=0, - ) +from math import floor def get_sync_aggregate(spec, state, num_participants=None, signature_slot=None): @@ -60,3 +39,32 @@ def get_sync_aggregate(spec, state, num_participants=None, signature_slot=None): sync_committee_signature=sync_committee_signature, ) return sync_aggregate, signature_slot + + +def create_update(spec, + attested_state, + attested_block, + finalized_block, + with_next, + with_finality, + participation_rate): + num_participants = floor(spec.SYNC_COMMITTEE_SIZE * participation_rate) + + update = spec.LightClientUpdate() + + update.attested_header = spec.block_to_light_client_header(attested_block) + + if with_next: + update.next_sync_committee = attested_state.next_sync_committee + update.next_sync_committee_branch = \ + spec.compute_merkle_proof_for_state(attested_state, spec.NEXT_SYNC_COMMITTEE_INDEX) + + if with_finality: + update.finalized_header = spec.block_to_light_client_header(finalized_block) + update.finality_branch = \ + spec.compute_merkle_proof_for_state(attested_state, spec.FINALIZED_ROOT_INDEX) + + update.sync_aggregate, update.signature_slot = \ + get_sync_aggregate(spec, attested_state, num_participants) + + return update From d4b8ab1173bbc8cbde07143fdc1c264b3a3a7883 Mon Sep 17 00:00:00 2001 From: dapplion <35266934+dapplion@users.noreply.github.com> Date: Tue, 13 Dec 2022 09:29:28 +0700 Subject: [PATCH 04/32] Clarify BeaconBlockAndBlobsSidecarByRoot no blob available --- specs/eip4844/p2p-interface.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/specs/eip4844/p2p-interface.md b/specs/eip4844/p2p-interface.md index f031b3723..13ada494e 100644 --- a/specs/eip4844/p2p-interface.md +++ b/specs/eip4844/p2p-interface.md @@ -185,6 +185,9 @@ No more than `MAX_REQUEST_BLOCKS` may be requested at a time. The response MUST consist of zero or more `response_chunk`. Each _successful_ `response_chunk` MUST contain a single `SignedBeaconBlockAndBlobsSidecar` payload. +If any root in the request content references a block outside of the range `[max(EIP4844_FORK_EPOCH, finalized_epoch, current_epoch - MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS), current_epoch]` + peers SHOULD respond with error code `3: ResourceUnavailable`. + Clients MUST support requesting blocks and sidecars since the latest finalized epoch. Clients MUST respond with at least one block and sidecar, if they have it. From ef2a8b319d963bbe00976a366ac7ded2ea3fcb12 Mon Sep 17 00:00:00 2001 From: Etan Kissling Date: Tue, 13 Dec 2022 12:30:14 +0100 Subject: [PATCH 05/32] Avoid line continuation syntax --- specs/altair/light-client/full-node.md | 8 ++++---- .../altair/light_client/test_single_merkle_proof.py | 12 ++++++------ .../pyspec/eth2spec/test/helpers/light_client.py | 12 ++++++------ 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/specs/altair/light-client/full-node.md b/specs/altair/light-client/full-node.md index 2dd2ded6c..7f0b7bc39 100644 --- a/specs/altair/light-client/full-node.md +++ b/specs/altair/light-client/full-node.md @@ -118,8 +118,8 @@ def create_light_client_update(state: BeaconState, # `next_sync_committee` is only useful if the message is signed by the current sync committee if update_attested_period == update_signature_period: update.next_sync_committee = attested_state.next_sync_committee - update.next_sync_committee_branch = \ - compute_merkle_proof_for_state(attested_state, NEXT_SYNC_COMMITTEE_INDEX) + update.next_sync_committee_branch = compute_merkle_proof_for_state( + attested_state, NEXT_SYNC_COMMITTEE_INDEX) # Indicate finality whenever possible if finalized_block is not None: @@ -128,8 +128,8 @@ def create_light_client_update(state: BeaconState, assert hash_tree_root(update.finalized_header) == attested_state.finalized_checkpoint.root else: assert attested_state.finalized_checkpoint.root == Bytes32() - update.finality_branch = \ - compute_merkle_proof_for_state(attested_state, FINALIZED_ROOT_INDEX) + update.finality_branch = compute_merkle_proof_for_state( + attested_state, FINALIZED_ROOT_INDEX) update.sync_aggregate = block.message.body.sync_aggregate update.signature_slot = block.message.slot diff --git a/tests/core/pyspec/eth2spec/test/altair/light_client/test_single_merkle_proof.py b/tests/core/pyspec/eth2spec/test/altair/light_client/test_single_merkle_proof.py index 465fa629f..5d802bbb3 100644 --- a/tests/core/pyspec/eth2spec/test/altair/light_client/test_single_merkle_proof.py +++ b/tests/core/pyspec/eth2spec/test/altair/light_client/test_single_merkle_proof.py @@ -10,8 +10,8 @@ from eth2spec.test.context import ( @spec_state_test def test_current_sync_committee_merkle_proof(spec, state): yield "object", state - current_sync_committee_branch = \ - spec.compute_merkle_proof_for_state(state, spec.CURRENT_SYNC_COMMITTEE_INDEX) + current_sync_committee_branch = spec.compute_merkle_proof_for_state( + state, spec.CURRENT_SYNC_COMMITTEE_INDEX) yield "proof", { "leaf": "0x" + state.current_sync_committee.hash_tree_root().hex(), "leaf_index": spec.CURRENT_SYNC_COMMITTEE_INDEX, @@ -31,8 +31,8 @@ def test_current_sync_committee_merkle_proof(spec, state): @spec_state_test def test_next_sync_committee_merkle_proof(spec, state): yield "object", state - next_sync_committee_branch = \ - spec.compute_merkle_proof_for_state(state, spec.NEXT_SYNC_COMMITTEE_INDEX) + next_sync_committee_branch = spec.compute_merkle_proof_for_state( + state, spec.NEXT_SYNC_COMMITTEE_INDEX) yield "proof", { "leaf": "0x" + state.next_sync_committee.hash_tree_root().hex(), "leaf_index": spec.NEXT_SYNC_COMMITTEE_INDEX, @@ -52,8 +52,8 @@ def test_next_sync_committee_merkle_proof(spec, state): @spec_state_test def test_finality_root_merkle_proof(spec, state): yield "object", state - finality_branch = \ - spec.compute_merkle_proof_for_state(state, spec.FINALIZED_ROOT_INDEX) + finality_branch = spec.compute_merkle_proof_for_state( + state, spec.FINALIZED_ROOT_INDEX) yield "proof", { "leaf": "0x" + state.finalized_checkpoint.root.hex(), "leaf_index": spec.FINALIZED_ROOT_INDEX, diff --git a/tests/core/pyspec/eth2spec/test/helpers/light_client.py b/tests/core/pyspec/eth2spec/test/helpers/light_client.py index 39175a352..215d174fc 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/light_client.py +++ b/tests/core/pyspec/eth2spec/test/helpers/light_client.py @@ -56,15 +56,15 @@ def create_update(spec, if with_next: update.next_sync_committee = attested_state.next_sync_committee - update.next_sync_committee_branch = \ - spec.compute_merkle_proof_for_state(attested_state, spec.NEXT_SYNC_COMMITTEE_INDEX) + update.next_sync_committee_branch = spec.compute_merkle_proof_for_state( + attested_state, spec.NEXT_SYNC_COMMITTEE_INDEX) if with_finality: update.finalized_header = spec.block_to_light_client_header(finalized_block) - update.finality_branch = \ - spec.compute_merkle_proof_for_state(attested_state, spec.FINALIZED_ROOT_INDEX) + update.finality_branch = spec.compute_merkle_proof_for_state( + attested_state, spec.FINALIZED_ROOT_INDEX) - update.sync_aggregate, update.signature_slot = \ - get_sync_aggregate(spec, attested_state, num_participants) + update.sync_aggregate, update.signature_slot = get_sync_aggregate( + spec, attested_state, num_participants) return update From 29d36d8a78152bc9439505034cf1269f377119b2 Mon Sep 17 00:00:00 2001 From: dapplion <35266934+dapplion@users.noreply.github.com> Date: Thu, 15 Dec 2022 11:11:15 +0700 Subject: [PATCH 06/32] Merge two sentences + drop range --- specs/eip4844/p2p-interface.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/specs/eip4844/p2p-interface.md b/specs/eip4844/p2p-interface.md index 13ada494e..d29d6da07 100644 --- a/specs/eip4844/p2p-interface.md +++ b/specs/eip4844/p2p-interface.md @@ -185,10 +185,7 @@ No more than `MAX_REQUEST_BLOCKS` may be requested at a time. The response MUST consist of zero or more `response_chunk`. Each _successful_ `response_chunk` MUST contain a single `SignedBeaconBlockAndBlobsSidecar` payload. -If any root in the request content references a block outside of the range `[max(EIP4844_FORK_EPOCH, finalized_epoch, current_epoch - MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS), current_epoch]` - peers SHOULD respond with error code `3: ResourceUnavailable`. - -Clients MUST support requesting blocks and sidecars since the latest finalized epoch. +Clients MUST support requesting blocks and sidecars since `minimum_request_epoch`, where `minimum_request_epoch = max(EIP4844_FORK_EPOCH, finalized_epoch, current_epoch - MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS)`. If any root in the request content references a block earlier than `minimum_request_epoch`, peers SHOULD respond with error code `3: ResourceUnavailable`. Clients MUST respond with at least one block and sidecar, if they have it. Clients MAY limit the number of blocks and sidecars in the response. From eaa1268dc421400540f26d4ce0cf2186f52a0021 Mon Sep 17 00:00:00 2001 From: dapplion <35266934+dapplion@users.noreply.github.com> Date: Thu, 15 Dec 2022 11:12:37 +0700 Subject: [PATCH 07/32] Place dominant factor first --- specs/eip4844/p2p-interface.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/eip4844/p2p-interface.md b/specs/eip4844/p2p-interface.md index d29d6da07..001bd6690 100644 --- a/specs/eip4844/p2p-interface.md +++ b/specs/eip4844/p2p-interface.md @@ -185,7 +185,7 @@ No more than `MAX_REQUEST_BLOCKS` may be requested at a time. The response MUST consist of zero or more `response_chunk`. Each _successful_ `response_chunk` MUST contain a single `SignedBeaconBlockAndBlobsSidecar` payload. -Clients MUST support requesting blocks and sidecars since `minimum_request_epoch`, where `minimum_request_epoch = max(EIP4844_FORK_EPOCH, finalized_epoch, current_epoch - MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS)`. If any root in the request content references a block earlier than `minimum_request_epoch`, peers SHOULD respond with error code `3: ResourceUnavailable`. +Clients MUST support requesting blocks and sidecars since `minimum_request_epoch`, where `minimum_request_epoch = max(finalized_epoch, current_epoch - MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS, EIP4844_FORK_EPOCH)`. If any root in the request content references a block earlier than `minimum_request_epoch`, peers SHOULD respond with error code `3: ResourceUnavailable`. Clients MUST respond with at least one block and sidecar, if they have it. Clients MAY limit the number of blocks and sidecars in the response. From fac4b49a9b9e9f83e720d420c6e3e8570045c419 Mon Sep 17 00:00:00 2001 From: dapplion <35266934+dapplion@users.noreply.github.com> Date: Thu, 15 Dec 2022 11:27:33 +0700 Subject: [PATCH 08/32] Change range lower bound to EIP4844_FORK_EPOCH --- specs/eip4844/p2p-interface.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/eip4844/p2p-interface.md b/specs/eip4844/p2p-interface.md index c091d0398..08885bd11 100644 --- a/specs/eip4844/p2p-interface.md +++ b/specs/eip4844/p2p-interface.md @@ -227,7 +227,7 @@ The response MUST consist of zero or more `response_chunk`. Each _successful_ `response_chunk` MUST contain a single `BlobsSidecar` payload. Clients MUST keep a record of signed blobs sidecars seen on the epoch range -`[max(GENESIS_EPOCH, current_epoch - MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS), current_epoch]` +`[max(current_epoch - MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS, EIP4844_FORK_EPOCH), current_epoch]` where `current_epoch` is defined by the current wall-clock time, and clients MUST support serving requests of blocks on this range. From 84592f101b0113f9c5be339eadb15d56128dd3ca Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Fri, 16 Dec 2022 00:49:14 +0800 Subject: [PATCH 09/32] Add tests for `process_historical_batches_update` --- setup.py | 1 + specs/capella/beacon-chain.md | 42 +++++++++++++++++-- .../test/capella/epoch_processing/__init__.py | 0 .../test_process_historical_batches_update.py | 27 ++++++++++++ .../test/eip4844/epoch_processing/__init__.py | 0 .../test_process_historical_batches_update.py | 23 ++++++++++ .../eth2spec/test/helpers/epoch_processing.py | 10 ++++- .../test_process_historical_roots_update.py | 8 +++- .../test/phase0/sanity/test_blocks.py | 22 ++++++++-- .../eth2spec/test/phase0/sanity/test_slots.py | 33 +++++++++++++++ tests/generators/epoch_processing/main.py | 14 ++++--- 11 files changed, 163 insertions(+), 17 deletions(-) create mode 100644 tests/core/pyspec/eth2spec/test/capella/epoch_processing/__init__.py create mode 100644 tests/core/pyspec/eth2spec/test/capella/epoch_processing/test_process_historical_batches_update.py create mode 100644 tests/core/pyspec/eth2spec/test/eip4844/epoch_processing/__init__.py create mode 100644 tests/core/pyspec/eth2spec/test/eip4844/epoch_processing/test_process_historical_batches_update.py diff --git a/setup.py b/setup.py index be8be6d90..b2f2ac8e3 100644 --- a/setup.py +++ b/setup.py @@ -660,6 +660,7 @@ def get_empty_list_result(fn): # type: ignore process_withdrawals = no_op(process_withdrawals) process_bls_to_execution_change = no_op(process_bls_to_execution_change) get_expected_withdrawals = get_empty_list_result(get_expected_withdrawals) +process_historical_batches_update = no_op(process_historical_batches_update) # diff --git a/specs/capella/beacon-chain.md b/specs/capella/beacon-chain.md index 8dfb96b9f..baa06398d 100644 --- a/specs/capella/beacon-chain.md +++ b/specs/capella/beacon-chain.md @@ -30,6 +30,8 @@ - [`is_fully_withdrawable_validator`](#is_fully_withdrawable_validator) - [`is_partially_withdrawable_validator`](#is_partially_withdrawable_validator) - [Beacon chain state transition function](#beacon-chain-state-transition-function) + - [Epoch processing](#epoch-processing) + - [Historical batches updates](#historical-batches-updates) - [Block processing](#block-processing) - [New `get_expected_withdrawals`](#new-get_expected_withdrawals) - [New `process_withdrawals`](#new-process_withdrawals) @@ -209,7 +211,7 @@ class BeaconState(Container): latest_block_header: BeaconBlockHeader block_roots: Vector[Root, SLOTS_PER_HISTORICAL_ROOT] state_roots: Vector[Root, SLOTS_PER_HISTORICAL_ROOT] - historical_roots: List[Root, HISTORICAL_ROOTS_LIMIT] # Frozen in Merge, replaced by historical_batches + historical_roots: List[Root, HISTORICAL_ROOTS_LIMIT] # Frozen in Merge, replaced by historical_batches # Eth1 eth1_data: Eth1Data eth1_data_votes: List[Eth1Data, EPOCHS_PER_ETH1_VOTING_PERIOD * SLOTS_PER_EPOCH] @@ -239,8 +241,8 @@ class BeaconState(Container): # Withdrawals next_withdrawal_index: WithdrawalIndex # [New in Capella] next_withdrawal_validator_index: ValidatorIndex # [New in Capella] - # Deep history - historical_batches: List[HistoricalBatchSummary, HISTORICAL_ROOTS_LIMIT] # Valid from Merge onwards + # Deep history valid from Capella onwards + historical_batches: List[HistoricalBatchSummary, HISTORICAL_ROOTS_LIMIT] # [New in Capella] ``` ## Helpers @@ -285,6 +287,40 @@ def is_partially_withdrawable_validator(validator: Validator, balance: Gwei) -> ## Beacon chain state transition function +### Epoch processing + +*Note*: The function `process_historical_batches_update` replaces `process_historical_roots_update` in Bellatrix. + +```python +def process_epoch(state: BeaconState) -> None: + process_justification_and_finalization(state) + process_inactivity_updates(state) + process_rewards_and_penalties(state) + process_registry_updates(state) + process_slashings(state) + process_eth1_data_reset(state) + process_effective_balance_updates(state) + process_slashings_reset(state) + process_randao_mixes_reset(state) + process_historical_batches_update(state) # [Modified in Capella] + process_participation_flag_updates(state) + process_sync_committee_updates(state) +``` + +#### Historical batches updates + +```python +def process_historical_batches_update(state: BeaconState) -> None: + # Set historical block root accumulator. + next_epoch = Epoch(get_current_epoch(state) + 1) + if next_epoch % (SLOTS_PER_HISTORICAL_ROOT // SLOTS_PER_EPOCH) == 0: + historical_batch = HistoricalBatchSummary( + block_batch_root=hash_tree_root(state.block_roots), + state_batch_root=hash_tree_root(state.state_roots), + ) + state.historical_batches.append(historical_batch) +``` + ### Block processing ```python diff --git a/tests/core/pyspec/eth2spec/test/capella/epoch_processing/__init__.py b/tests/core/pyspec/eth2spec/test/capella/epoch_processing/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/core/pyspec/eth2spec/test/capella/epoch_processing/test_process_historical_batches_update.py b/tests/core/pyspec/eth2spec/test/capella/epoch_processing/test_process_historical_batches_update.py new file mode 100644 index 000000000..ba049272b --- /dev/null +++ b/tests/core/pyspec/eth2spec/test/capella/epoch_processing/test_process_historical_batches_update.py @@ -0,0 +1,27 @@ +from eth2spec.test.context import ( + CAPELLA, + spec_state_test, + with_phases, +) +from eth2spec.test.helpers.epoch_processing import ( + run_epoch_processing_with +) + + +def run_process_historical_batches_update(spec, state): + yield from run_epoch_processing_with(spec, state, 'process_historical_batches_update') + + +@with_phases([CAPELLA]) +@spec_state_test +def test_historical_batches_accumulator(spec, state): + # skip ahead to near the end of the historical batch period (excl block before epoch processing) + state.slot = spec.SLOTS_PER_HISTORICAL_ROOT - 1 + pre_historical_batches = state.historical_batches.copy() + + yield from run_process_historical_batches_update(spec, state) + + assert len(state.historical_batches) == len(pre_historical_batches) + 1 + summary = state.historical_batches[len(state.historical_batches) - 1] + assert summary.block_batch_root == state.block_roots.hash_tree_root() + assert summary.state_batch_root == state.state_roots.hash_tree_root() diff --git a/tests/core/pyspec/eth2spec/test/eip4844/epoch_processing/__init__.py b/tests/core/pyspec/eth2spec/test/eip4844/epoch_processing/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/core/pyspec/eth2spec/test/eip4844/epoch_processing/test_process_historical_batches_update.py b/tests/core/pyspec/eth2spec/test/eip4844/epoch_processing/test_process_historical_batches_update.py new file mode 100644 index 000000000..f6e6f2ce7 --- /dev/null +++ b/tests/core/pyspec/eth2spec/test/eip4844/epoch_processing/test_process_historical_batches_update.py @@ -0,0 +1,23 @@ +from eth2spec.test.context import ( + spec_state_test, + with_eip4844_and_later, +) +from eth2spec.test.helpers.epoch_processing import ( + run_epoch_processing_with +) + + +def run_process_historical_batches_update(spec, state): + yield from run_epoch_processing_with(spec, state, 'process_historical_batches_update') + + +@with_eip4844_and_later +@spec_state_test +def test_no_op(spec, state): + # skip ahead to near the end of the historical batch period (excl block before epoch processing) + state.slot = spec.SLOTS_PER_HISTORICAL_ROOT - 1 + historical_batches_len = len(state.historical_batches) + + yield from run_process_historical_batches_update(spec, state) + + assert len(state.historical_batches) == historical_batches_len diff --git a/tests/core/pyspec/eth2spec/test/helpers/epoch_processing.py b/tests/core/pyspec/eth2spec/test/helpers/epoch_processing.py index ed61f8bdb..399222841 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/epoch_processing.py +++ b/tests/core/pyspec/eth2spec/test/helpers/epoch_processing.py @@ -1,5 +1,8 @@ -from eth2spec.test.helpers.forks import is_post_altair +from eth2spec.test.helpers.forks import ( + is_post_altair, + is_post_capella, +) def get_process_calls(spec): @@ -22,7 +25,10 @@ def get_process_calls(spec): 'process_effective_balance_updates', 'process_slashings_reset', 'process_randao_mixes_reset', - 'process_historical_roots_update', + # Capella replaced `process_historical_roots_update` with `process_historical_batches_update` + 'process_historical_batches_update' if is_post_capella(spec) else ( + 'process_historical_roots_update' + ), # Altair replaced `process_participation_record_updates` with `process_participation_flag_updates` 'process_participation_flag_updates' if is_post_altair(spec) else ( 'process_participation_record_updates' diff --git a/tests/core/pyspec/eth2spec/test/phase0/epoch_processing/test_process_historical_roots_update.py b/tests/core/pyspec/eth2spec/test/phase0/epoch_processing/test_process_historical_roots_update.py index 02ce7ccba..f03d0bd98 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/epoch_processing/test_process_historical_roots_update.py +++ b/tests/core/pyspec/eth2spec/test/phase0/epoch_processing/test_process_historical_roots_update.py @@ -1,4 +1,8 @@ -from eth2spec.test.context import spec_state_test, with_all_phases +from eth2spec.test.context import ( + PHASE0, ALTAIR, BELLATRIX, + spec_state_test, + with_phases, +) from eth2spec.test.helpers.epoch_processing import ( run_epoch_processing_with ) @@ -8,7 +12,7 @@ def run_process_historical_roots_update(spec, state): yield from run_epoch_processing_with(spec, state, 'process_historical_roots_update') -@with_all_phases +@with_phases([PHASE0, ALTAIR, BELLATRIX]) @spec_state_test def test_historical_root_accumulator(spec, state): # skip ahead to near the end of the historical roots period (excl block before epoch processing) diff --git a/tests/core/pyspec/eth2spec/test/phase0/sanity/test_blocks.py b/tests/core/pyspec/eth2spec/test/phase0/sanity/test_blocks.py index 1e9dd2d48..8b35e814f 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/sanity/test_blocks.py +++ b/tests/core/pyspec/eth2spec/test/phase0/sanity/test_blocks.py @@ -29,8 +29,8 @@ from eth2spec.test.helpers.sync_committee import ( compute_committee_indices, compute_sync_committee_participant_reward_and_penalty, ) -from eth2spec.test.helpers.constants import PHASE0, MINIMAL -from eth2spec.test.helpers.forks import is_post_altair, is_post_bellatrix +from eth2spec.test.helpers.constants import PHASE0, EIP4844, MINIMAL +from eth2spec.test.helpers.forks import is_post_altair, is_post_bellatrix, is_post_capella from eth2spec.test.context import ( spec_test, spec_state_test, dump_skipping_message, with_phases, with_all_phases, single_phase, @@ -1026,7 +1026,10 @@ def test_balance_driven_status_transitions(spec, state): @always_bls def test_historical_batch(spec, state): state.slot += spec.SLOTS_PER_HISTORICAL_ROOT - (state.slot % spec.SLOTS_PER_HISTORICAL_ROOT) - 1 - pre_historical_roots_len = len(state.historical_roots) + pre_historical_roots = state.historical_roots.copy() + + if is_post_capella(spec): + pre_historical_batches = state.historical_batches.copy() yield 'pre', state @@ -1038,7 +1041,18 @@ def test_historical_batch(spec, state): assert state.slot == block.slot assert spec.get_current_epoch(state) % (spec.SLOTS_PER_HISTORICAL_ROOT // spec.SLOTS_PER_EPOCH) == 0 - assert len(state.historical_roots) == pre_historical_roots_len + 1 + + # check history update + if is_post_capella(spec): + # Frozen `historical_roots` + assert state.historical_roots == pre_historical_roots + if spec.fork == EIP4844: + # TODO: no-op for now in EIP4844 testnet + assert state.historical_batches == pre_historical_batches + else: + assert len(state.historical_batches) == len(pre_historical_batches) + 1 + else: + assert len(state.historical_roots) == len(pre_historical_roots) + 1 @with_all_phases diff --git a/tests/core/pyspec/eth2spec/test/phase0/sanity/test_slots.py b/tests/core/pyspec/eth2spec/test/phase0/sanity/test_slots.py index 3fa57f0f1..0010898f8 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/sanity/test_slots.py +++ b/tests/core/pyspec/eth2spec/test/phase0/sanity/test_slots.py @@ -1,3 +1,9 @@ +from eth2spec.test.helpers.constants import ( + EIP4844, +) +from eth2spec.test.helpers.forks import ( + is_post_capella, +) from eth2spec.test.helpers.state import get_state_root from eth2spec.test.context import ( spec_state_test, @@ -61,3 +67,30 @@ def test_over_epoch_boundary(spec, state): yield 'slots', int(slots) spec.process_slots(state, state.slot + slots) yield 'post', state + + +@with_all_phases +@spec_state_test +def test_historical_accumulator(spec, state): + pre_historical_roots = state.historical_roots.copy() + + if is_post_capella(spec): + pre_historical_batches = state.historical_batches.copy() + + yield 'pre', state + slots = spec.SLOTS_PER_HISTORICAL_ROOT + yield 'slots', int(slots) + spec.process_slots(state, state.slot + slots) + yield 'post', state + + # check history update + if is_post_capella(spec): + # Frozen `historical_roots` + assert state.historical_roots == pre_historical_roots + if spec.fork == EIP4844: + # TODO: no-op for now in EIP4844 testnet + assert state.historical_batches == pre_historical_batches + else: + assert len(state.historical_batches) == len(pre_historical_batches) + 1 + else: + assert len(state.historical_roots) == len(pre_historical_roots) + 1 diff --git a/tests/generators/epoch_processing/main.py b/tests/generators/epoch_processing/main.py index 84421e749..f8430bc7e 100644 --- a/tests/generators/epoch_processing/main.py +++ b/tests/generators/epoch_processing/main.py @@ -27,13 +27,15 @@ if __name__ == "__main__": # so no additional tests required. bellatrix_mods = altair_mods - # No epoch-processing changes in Capella and previous testing repeats with new types, - # so no additional tests required. - capella_mods = bellatrix_mods + _new_capella_mods = {key: 'eth2spec.test.capella.epoch_processing.test_process_' + key for key in [ + 'historical_batches_update', + ]} + capella_mods = combine_mods(_new_capella_mods, bellatrix_mods) - # No epoch-processing changes in EIP4844 and previous testing repeats with new types, - # so no additional tests required. - eip4844_mods = capella_mods + _new_eip4844_mods = {key: 'eth2spec.test.eip4844.epoch_processing.test_process_' + key for key in [ + 'historical_batches_update', + ]} + eip4844_mods = combine_mods(_new_eip4844_mods, capella_mods) # TODO Custody Game testgen is disabled for now # custody_game_mods = {**{key: 'eth2spec.test.custody_game.epoch_processing.test_process_' + key for key in [ From 4be8f7d669d785a184811776b3ff5ca18ae21e73 Mon Sep 17 00:00:00 2001 From: djrtwo Date: Mon, 19 Dec 2022 10:15:18 -0700 Subject: [PATCH 10/32] clarify how to handle unavailable data outside of the prune window --- specs/eip4844/beacon-chain.md | 7 ++++--- specs/eip4844/p2p-interface.md | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/specs/eip4844/beacon-chain.md b/specs/eip4844/beacon-chain.md index 39b8ed00f..19acb57d8 100644 --- a/specs/eip4844/beacon-chain.md +++ b/specs/eip4844/beacon-chain.md @@ -164,15 +164,16 @@ def validate_blobs_sidecar(slot: Slot, #### `is_data_available` -The implementation of `is_data_available` is meant to change with later sharding upgrades. +The implementation of `is_data_available` will become more sophisticated during later sharding upgrades. Initially, it requires every verifying actor to retrieve the matching `BlobsSidecar`, and validate the sidecar with `validate_blobs_sidecar`. -The block MUST NOT be considered valid until a valid `BlobsSidecar` has been downloaded. +The block MUST NOT be considered valid until a valid `BlobsSidecar` has been downloaded. Blocks that have been previously validated as available SHOULD be considered available even if the associated `BlobsSidecar` has subsequently been pruned. ```python def is_data_available(slot: Slot, beacon_block_root: Root, blob_kzg_commitments: Sequence[KZGCommitment]) -> bool: - # `retrieve_blobs_sidecar` is implementation dependent, raises an exception if not available. + # `retrieve_blobs_sidecar` is implementation and context dependent, raises an exception if not available. + # Note: the p2p network does not guarantee sidecar retrieval outside of `MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS` sidecar = retrieve_blobs_sidecar(slot, beacon_block_root) # For testing, `retrieve_blobs_sidecar` returns "TEST". diff --git a/specs/eip4844/p2p-interface.md b/specs/eip4844/p2p-interface.md index c091d0398..fbf94914c 100644 --- a/specs/eip4844/p2p-interface.md +++ b/specs/eip4844/p2p-interface.md @@ -219,7 +219,7 @@ may not be available beyond the initial distribution via gossip. Before consuming the next response chunk, the response reader SHOULD verify the blobs sidecar is well-formatted and correct w.r.t. the expected KZG commitments through `validate_blobs_sidecar`. -`BlobsSidecarsByRange` is primarily used to sync blobs that may have been missed on gossip. +`BlobsSidecarsByRange` is primarily used to sync blobs that may have been missed on gossip and to sync within the `MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS` window. The request MUST be encoded as an SSZ-container. From b6ac1fa099b074e6566d221775df73d3a39dcd52 Mon Sep 17 00:00:00 2001 From: Jacek Sieka Date: Tue, 20 Dec 2022 07:46:18 +0100 Subject: [PATCH 11/32] Update specs/capella/beacon-chain.md Co-authored-by: Danny Ryan --- specs/capella/beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/capella/beacon-chain.md b/specs/capella/beacon-chain.md index baa06398d..e869d0e5f 100644 --- a/specs/capella/beacon-chain.md +++ b/specs/capella/beacon-chain.md @@ -211,7 +211,7 @@ class BeaconState(Container): latest_block_header: BeaconBlockHeader block_roots: Vector[Root, SLOTS_PER_HISTORICAL_ROOT] state_roots: Vector[Root, SLOTS_PER_HISTORICAL_ROOT] - historical_roots: List[Root, HISTORICAL_ROOTS_LIMIT] # Frozen in Merge, replaced by historical_batches + historical_roots: List[Root, HISTORICAL_ROOTS_LIMIT] # Frozen in Capella, replaced by historical_batches # Eth1 eth1_data: Eth1Data eth1_data_votes: List[Eth1Data, EPOCHS_PER_ETH1_VOTING_PERIOD * SLOTS_PER_EPOCH] From 45b1eb79799afc1e94a81ee3b1d0a1792410d0dd Mon Sep 17 00:00:00 2001 From: terence tsao Date: Wed, 28 Dec 2022 18:42:24 -0800 Subject: [PATCH 12/32] EIP4844: Enable withdrawal --- specs/eip4844/beacon-chain.md | 8 -------- 1 file changed, 8 deletions(-) diff --git a/specs/eip4844/beacon-chain.md b/specs/eip4844/beacon-chain.md index 19acb57d8..afab3fc44 100644 --- a/specs/eip4844/beacon-chain.md +++ b/specs/eip4844/beacon-chain.md @@ -346,11 +346,3 @@ def initialize_beacon_state_from_eth1(eth1_block_hash: Hash32, return state ``` - -### Disabling Withdrawals - -During testing we avoid Capella-specific updates to the state transition. We do this by replacing the following functions with a no-op implementation: -- `process_withdrawals` -- `process_bls_to_execution_change` - -The `get_expected_withdrawals` function is also modified to return an empty withdrawals list. As such, the `PayloadAttributes` used to update forkchoice does not contain withdrawals. From 2afb02ba302254b8a991dc1575bee045c7f5e86f Mon Sep 17 00:00:00 2001 From: terence tsao Date: Wed, 28 Dec 2022 18:49:02 -0800 Subject: [PATCH 13/32] Fix Toc --- specs/eip4844/beacon-chain.md | 1 - 1 file changed, 1 deletion(-) diff --git a/specs/eip4844/beacon-chain.md b/specs/eip4844/beacon-chain.md index afab3fc44..bc5c41a5c 100644 --- a/specs/eip4844/beacon-chain.md +++ b/specs/eip4844/beacon-chain.md @@ -33,7 +33,6 @@ - [`process_execution_payload`](#process_execution_payload) - [Blob KZG commitments](#blob-kzg-commitments) - [Testing](#testing) - - [Disabling Withdrawals](#disabling-withdrawals) From 4c573ff6b96e693a343370903110e7cb3348407c Mon Sep 17 00:00:00 2001 From: Dankrad Feist Date: Sat, 17 Dec 2022 17:14:48 +0000 Subject: [PATCH 14/32] Add compute_kzg_proof to public methods --- specs/eip4844/polynomial-commitments.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/specs/eip4844/polynomial-commitments.md b/specs/eip4844/polynomial-commitments.md index 6aef9c611..8884457ed 100644 --- a/specs/eip4844/polynomial-commitments.md +++ b/specs/eip4844/polynomial-commitments.md @@ -370,8 +370,9 @@ def verify_kzg_proof_impl(polynomial_kzg: KZGCommitment, ```python def compute_kzg_proof(polynomial: Polynomial, z: BLSFieldElement) -> KZGProof: """ - Compute KZG proof at point `z` with `polynomial` being in evaluation form - Do this by computing the quotient polynomial in evaluation form: q(x) = (p(x) - p(z)) / (x - z) + Compute KZG proof at point `z` with `polynomial` being in evaluation form. + Do this by computing the quotient polynomial in evaluation form: q(x) = (p(x) - p(z)) / (x - z). + Public method. """ y = evaluate_polynomial_in_evaluation_form(polynomial, z) polynomial_shifted = [BLSFieldElement((int(p) - int(y)) % BLS_MODULUS) for p in polynomial] From 1cfabcbe54378284319bc6f38c45484b6cefd3fb Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Mon, 2 Jan 2023 23:07:00 +0800 Subject: [PATCH 15/32] Rename `HistoricalBatchSummary` -> `HistoricalSummary` and `historical_batches` -> `historical_summaries` --- setup.py | 2 +- specs/capella/beacon-chain.md | 26 +++++++++---------- .../test_process_historical_batches_update.py | 14 +++++----- .../test_process_historical_batches_update.py | 10 +++---- .../eth2spec/test/helpers/epoch_processing.py | 4 +-- .../test/phase0/sanity/test_blocks.py | 6 ++--- .../eth2spec/test/phase0/sanity/test_slots.py | 6 ++--- tests/generators/epoch_processing/main.py | 4 +-- 8 files changed, 36 insertions(+), 36 deletions(-) diff --git a/setup.py b/setup.py index b2f2ac8e3..f95f85284 100644 --- a/setup.py +++ b/setup.py @@ -660,7 +660,7 @@ def get_empty_list_result(fn): # type: ignore process_withdrawals = no_op(process_withdrawals) process_bls_to_execution_change = no_op(process_bls_to_execution_change) get_expected_withdrawals = get_empty_list_result(get_expected_withdrawals) -process_historical_batches_update = no_op(process_historical_batches_update) +process_historical_summaries_update = no_op(process_historical_summaries_update) # diff --git a/specs/capella/beacon-chain.md b/specs/capella/beacon-chain.md index e869d0e5f..5d322ca92 100644 --- a/specs/capella/beacon-chain.md +++ b/specs/capella/beacon-chain.md @@ -18,7 +18,7 @@ - [`Withdrawal`](#withdrawal) - [`BLSToExecutionChange`](#blstoexecutionchange) - [`SignedBLSToExecutionChange`](#signedblstoexecutionchange) - - [`HistoricalBatchSummary`](#historicalbatchsummary) + - [`HistoricalSummary`](#historicalsummary) - [Extended Containers](#extended-containers) - [`ExecutionPayload`](#executionpayload) - [`ExecutionPayloadHeader`](#executionpayloadheader) @@ -31,7 +31,7 @@ - [`is_partially_withdrawable_validator`](#is_partially_withdrawable_validator) - [Beacon chain state transition function](#beacon-chain-state-transition-function) - [Epoch processing](#epoch-processing) - - [Historical batches updates](#historical-batches-updates) + - [Historical summaries updates](#historical-summaries-updates) - [Block processing](#block-processing) - [New `get_expected_withdrawals`](#new-get_expected_withdrawals) - [New `process_withdrawals`](#new-process_withdrawals) @@ -118,12 +118,12 @@ class SignedBLSToExecutionChange(Container): signature: BLSSignature ``` -#### `HistoricalBatchSummary` +#### `HistoricalSummary` ```python -class HistoricalBatchSummary(Container): +class HistoricalSummary(Container): """ - `HistoricalBatchSummary` matches the components of the phase0 HistoricalBatch + `HistoricalSummary` matches the components of the phase0 `HistoricalBatch` making the two hash_tree_root-compatible. """ block_batch_root: Root @@ -211,7 +211,7 @@ class BeaconState(Container): latest_block_header: BeaconBlockHeader block_roots: Vector[Root, SLOTS_PER_HISTORICAL_ROOT] state_roots: Vector[Root, SLOTS_PER_HISTORICAL_ROOT] - historical_roots: List[Root, HISTORICAL_ROOTS_LIMIT] # Frozen in Capella, replaced by historical_batches + historical_roots: List[Root, HISTORICAL_ROOTS_LIMIT] # Frozen in Capella, replaced by historical_summaries # Eth1 eth1_data: Eth1Data eth1_data_votes: List[Eth1Data, EPOCHS_PER_ETH1_VOTING_PERIOD * SLOTS_PER_EPOCH] @@ -242,7 +242,7 @@ class BeaconState(Container): next_withdrawal_index: WithdrawalIndex # [New in Capella] next_withdrawal_validator_index: ValidatorIndex # [New in Capella] # Deep history valid from Capella onwards - historical_batches: List[HistoricalBatchSummary, HISTORICAL_ROOTS_LIMIT] # [New in Capella] + historical_summaries: List[HistoricalSummary, HISTORICAL_ROOTS_LIMIT] # [New in Capella] ``` ## Helpers @@ -289,7 +289,7 @@ def is_partially_withdrawable_validator(validator: Validator, balance: Gwei) -> ### Epoch processing -*Note*: The function `process_historical_batches_update` replaces `process_historical_roots_update` in Bellatrix. +*Note*: The function `process_historical_summaries_update` replaces `process_historical_roots_update` in Bellatrix. ```python def process_epoch(state: BeaconState) -> None: @@ -302,23 +302,23 @@ def process_epoch(state: BeaconState) -> None: process_effective_balance_updates(state) process_slashings_reset(state) process_randao_mixes_reset(state) - process_historical_batches_update(state) # [Modified in Capella] + process_historical_summaries_update(state) # [Modified in Capella] process_participation_flag_updates(state) process_sync_committee_updates(state) ``` -#### Historical batches updates +#### Historical summaries updates ```python -def process_historical_batches_update(state: BeaconState) -> None: +def process_historical_summaries_update(state: BeaconState) -> None: # Set historical block root accumulator. next_epoch = Epoch(get_current_epoch(state) + 1) if next_epoch % (SLOTS_PER_HISTORICAL_ROOT // SLOTS_PER_EPOCH) == 0: - historical_batch = HistoricalBatchSummary( + historical_summary = HistoricalSummary( block_batch_root=hash_tree_root(state.block_roots), state_batch_root=hash_tree_root(state.state_roots), ) - state.historical_batches.append(historical_batch) + state.historical_summaries.append(historical_summary) ``` ### Block processing diff --git a/tests/core/pyspec/eth2spec/test/capella/epoch_processing/test_process_historical_batches_update.py b/tests/core/pyspec/eth2spec/test/capella/epoch_processing/test_process_historical_batches_update.py index ba049272b..16620b38d 100644 --- a/tests/core/pyspec/eth2spec/test/capella/epoch_processing/test_process_historical_batches_update.py +++ b/tests/core/pyspec/eth2spec/test/capella/epoch_processing/test_process_historical_batches_update.py @@ -8,20 +8,20 @@ from eth2spec.test.helpers.epoch_processing import ( ) -def run_process_historical_batches_update(spec, state): - yield from run_epoch_processing_with(spec, state, 'process_historical_batches_update') +def run_process_historical_summaries_update(spec, state): + yield from run_epoch_processing_with(spec, state, 'process_historical_summaries_update') @with_phases([CAPELLA]) @spec_state_test -def test_historical_batches_accumulator(spec, state): +def test_historical_summaries_accumulator(spec, state): # skip ahead to near the end of the historical batch period (excl block before epoch processing) state.slot = spec.SLOTS_PER_HISTORICAL_ROOT - 1 - pre_historical_batches = state.historical_batches.copy() + pre_historical_summaries = state.historical_summaries.copy() - yield from run_process_historical_batches_update(spec, state) + yield from run_process_historical_summaries_update(spec, state) - assert len(state.historical_batches) == len(pre_historical_batches) + 1 - summary = state.historical_batches[len(state.historical_batches) - 1] + assert len(state.historical_summaries) == len(pre_historical_summaries) + 1 + summary = state.historical_summaries[len(state.historical_summaries) - 1] assert summary.block_batch_root == state.block_roots.hash_tree_root() assert summary.state_batch_root == state.state_roots.hash_tree_root() diff --git a/tests/core/pyspec/eth2spec/test/eip4844/epoch_processing/test_process_historical_batches_update.py b/tests/core/pyspec/eth2spec/test/eip4844/epoch_processing/test_process_historical_batches_update.py index f6e6f2ce7..21865ae38 100644 --- a/tests/core/pyspec/eth2spec/test/eip4844/epoch_processing/test_process_historical_batches_update.py +++ b/tests/core/pyspec/eth2spec/test/eip4844/epoch_processing/test_process_historical_batches_update.py @@ -7,8 +7,8 @@ from eth2spec.test.helpers.epoch_processing import ( ) -def run_process_historical_batches_update(spec, state): - yield from run_epoch_processing_with(spec, state, 'process_historical_batches_update') +def run_process_historical_summaries_update(spec, state): + yield from run_epoch_processing_with(spec, state, 'process_historical_summaries_update') @with_eip4844_and_later @@ -16,8 +16,8 @@ def run_process_historical_batches_update(spec, state): def test_no_op(spec, state): # skip ahead to near the end of the historical batch period (excl block before epoch processing) state.slot = spec.SLOTS_PER_HISTORICAL_ROOT - 1 - historical_batches_len = len(state.historical_batches) + historical_summaries_len = len(state.historical_summaries) - yield from run_process_historical_batches_update(spec, state) + yield from run_process_historical_summaries_update(spec, state) - assert len(state.historical_batches) == historical_batches_len + assert len(state.historical_summaries) == historical_summaries_len diff --git a/tests/core/pyspec/eth2spec/test/helpers/epoch_processing.py b/tests/core/pyspec/eth2spec/test/helpers/epoch_processing.py index 399222841..44b42aff9 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/epoch_processing.py +++ b/tests/core/pyspec/eth2spec/test/helpers/epoch_processing.py @@ -25,8 +25,8 @@ def get_process_calls(spec): 'process_effective_balance_updates', 'process_slashings_reset', 'process_randao_mixes_reset', - # Capella replaced `process_historical_roots_update` with `process_historical_batches_update` - 'process_historical_batches_update' if is_post_capella(spec) else ( + # Capella replaced `process_historical_roots_update` with `process_historical_summaries_update` + 'process_historical_summaries_update' if is_post_capella(spec) else ( 'process_historical_roots_update' ), # Altair replaced `process_participation_record_updates` with `process_participation_flag_updates` diff --git a/tests/core/pyspec/eth2spec/test/phase0/sanity/test_blocks.py b/tests/core/pyspec/eth2spec/test/phase0/sanity/test_blocks.py index 8b35e814f..71c7798a1 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/sanity/test_blocks.py +++ b/tests/core/pyspec/eth2spec/test/phase0/sanity/test_blocks.py @@ -1029,7 +1029,7 @@ def test_historical_batch(spec, state): pre_historical_roots = state.historical_roots.copy() if is_post_capella(spec): - pre_historical_batches = state.historical_batches.copy() + pre_historical_summaries = state.historical_summaries.copy() yield 'pre', state @@ -1048,9 +1048,9 @@ def test_historical_batch(spec, state): assert state.historical_roots == pre_historical_roots if spec.fork == EIP4844: # TODO: no-op for now in EIP4844 testnet - assert state.historical_batches == pre_historical_batches + assert state.historical_summaries == pre_historical_summaries else: - assert len(state.historical_batches) == len(pre_historical_batches) + 1 + assert len(state.historical_summaries) == len(pre_historical_summaries) + 1 else: assert len(state.historical_roots) == len(pre_historical_roots) + 1 diff --git a/tests/core/pyspec/eth2spec/test/phase0/sanity/test_slots.py b/tests/core/pyspec/eth2spec/test/phase0/sanity/test_slots.py index 0010898f8..7b860159a 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/sanity/test_slots.py +++ b/tests/core/pyspec/eth2spec/test/phase0/sanity/test_slots.py @@ -75,7 +75,7 @@ def test_historical_accumulator(spec, state): pre_historical_roots = state.historical_roots.copy() if is_post_capella(spec): - pre_historical_batches = state.historical_batches.copy() + pre_historical_summaries = state.historical_summaries.copy() yield 'pre', state slots = spec.SLOTS_PER_HISTORICAL_ROOT @@ -89,8 +89,8 @@ def test_historical_accumulator(spec, state): assert state.historical_roots == pre_historical_roots if spec.fork == EIP4844: # TODO: no-op for now in EIP4844 testnet - assert state.historical_batches == pre_historical_batches + assert state.historical_summaries == pre_historical_summaries else: - assert len(state.historical_batches) == len(pre_historical_batches) + 1 + assert len(state.historical_summaries) == len(pre_historical_summaries) + 1 else: assert len(state.historical_roots) == len(pre_historical_roots) + 1 diff --git a/tests/generators/epoch_processing/main.py b/tests/generators/epoch_processing/main.py index f8430bc7e..58beb0fd2 100644 --- a/tests/generators/epoch_processing/main.py +++ b/tests/generators/epoch_processing/main.py @@ -28,12 +28,12 @@ if __name__ == "__main__": bellatrix_mods = altair_mods _new_capella_mods = {key: 'eth2spec.test.capella.epoch_processing.test_process_' + key for key in [ - 'historical_batches_update', + 'historical_summaries_update', ]} capella_mods = combine_mods(_new_capella_mods, bellatrix_mods) _new_eip4844_mods = {key: 'eth2spec.test.eip4844.epoch_processing.test_process_' + key for key in [ - 'historical_batches_update', + 'historical_summaries_update', ]} eip4844_mods = combine_mods(_new_eip4844_mods, capella_mods) From dcacb7164f812d1a7161f956615a5123ed266202 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Tue, 3 Jan 2023 21:50:06 +0800 Subject: [PATCH 16/32] Rename `block_batch_root` to `block_summary_root` and `state_batch_root` to `state_summary_root` --- specs/capella/beacon-chain.md | 8 ++++---- .../test_process_historical_batches_update.py | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/specs/capella/beacon-chain.md b/specs/capella/beacon-chain.md index 5d322ca92..bd7a2c50c 100644 --- a/specs/capella/beacon-chain.md +++ b/specs/capella/beacon-chain.md @@ -126,8 +126,8 @@ class HistoricalSummary(Container): `HistoricalSummary` matches the components of the phase0 `HistoricalBatch` making the two hash_tree_root-compatible. """ - block_batch_root: Root - state_batch_root: Root + block_summary_root: Root + state_summary_root: Root ``` ### Extended Containers @@ -315,8 +315,8 @@ def process_historical_summaries_update(state: BeaconState) -> None: next_epoch = Epoch(get_current_epoch(state) + 1) if next_epoch % (SLOTS_PER_HISTORICAL_ROOT // SLOTS_PER_EPOCH) == 0: historical_summary = HistoricalSummary( - block_batch_root=hash_tree_root(state.block_roots), - state_batch_root=hash_tree_root(state.state_roots), + block_summary_root=hash_tree_root(state.block_roots), + state_summary_root=hash_tree_root(state.state_roots), ) state.historical_summaries.append(historical_summary) ``` diff --git a/tests/core/pyspec/eth2spec/test/capella/epoch_processing/test_process_historical_batches_update.py b/tests/core/pyspec/eth2spec/test/capella/epoch_processing/test_process_historical_batches_update.py index 16620b38d..f1f8292eb 100644 --- a/tests/core/pyspec/eth2spec/test/capella/epoch_processing/test_process_historical_batches_update.py +++ b/tests/core/pyspec/eth2spec/test/capella/epoch_processing/test_process_historical_batches_update.py @@ -23,5 +23,5 @@ def test_historical_summaries_accumulator(spec, state): assert len(state.historical_summaries) == len(pre_historical_summaries) + 1 summary = state.historical_summaries[len(state.historical_summaries) - 1] - assert summary.block_batch_root == state.block_roots.hash_tree_root() - assert summary.state_batch_root == state.state_roots.hash_tree_root() + assert summary.block_summary_root == state.block_roots.hash_tree_root() + assert summary.state_summary_root == state.state_roots.hash_tree_root() From bd26c96a8c7c8000695fbf615170f3251f092252 Mon Sep 17 00:00:00 2001 From: Parithosh Jayanthi Date: Tue, 3 Jan 2023 16:23:09 +0100 Subject: [PATCH 17/32] Adds Github Actions CI (#3028) * Squash commits * Rename TEST_TYPE to TEST_PRESET_TYPE * Try python3 -m pytest -n 16 * updating actions versions * adding cleanup * reorder * Add eip4844 Co-authored-by: Hsiao-Wei Wang --- .github/workflows/run-tests.yml | 133 ++++++++++++++++++++++++++++++++ Makefile | 18 ++--- 2 files changed, 142 insertions(+), 9 deletions(-) create mode 100644 .github/workflows/run-tests.yml diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml new file mode 100644 index 000000000..3e23d1910 --- /dev/null +++ b/.github/workflows/run-tests.yml @@ -0,0 +1,133 @@ +name: Run spec tests and linter + +defaults: + run: + shell: zsh {0} + +env: + TEST_PRESET_TYPE: "minimal" + DEFAULT_BRANCH: "dev" + +# Run tests on workflow_Dispatch +on: + push: + branches: + - dev + - master + pull_request: + workflow_dispatch: + inputs: + test_preset_type: + default: minimal + description: Type of test to run, either mainnet or minimal + type: string + required: true + commitRef: + description: The branch, tag or SHA to checkout and build from + default: dev + required: true + schedule: + - cron: '0 0 * * *' + +jobs: + precleanup: + runs-on: self-hosted + if: always() + steps: + - name: 'Cleanup build folder' + run: | + ls -la ./ + rm -rf ./* || true + rm -rf ./.??* || true + ls -la ./ + setup-env: + runs-on: self-hosted + needs: precleanup + steps: + - name: Checkout this repo + uses: actions/checkout@v3.2.0 + with: + ref: ${{ github.event.inputs.commitRef || env.DEFAULT_BRANCH }} + - uses: actions/cache@v3.2.2 + id: cache-git + with: + path: ./* + key: ${{ github.sha }} + + table_of_contents: + runs-on: self-hosted + needs: setup-env + steps: + - uses: actions/cache@v3.2.2 + id: restore-build + with: + path: ./* + key: ${{ github.sha }} + - name: Check table of contents + run: sudo npm install -g doctoc@2 && make check_toc + + codespell: + runs-on: self-hosted + needs: setup-env + steps: + - name: Check codespell + run: pip install 'codespell<3.0.0,>=2.0.0' --user && make codespell + + lint: + runs-on: self-hosted + needs: setup-env + steps: + - name: Run linter for pyspec + run: make lint + - name: Run linter for test generators + run: make lint_generators + + pyspec-tests: + runs-on: self-hosted + needs: [setup-env,lint,codespell,table_of_contents] + strategy: + matrix: + version: ["phase0", "altair", "bellatrix", "capella", "eip4844"] + steps: + - uses: actions/cache@v3.2.2 + id: restore-build + with: + path: ./* + key: ${{ github.sha }} + - name: set TEST_PRESET_TYPE + if: github.event.inputs.test_preset_type != '' + run: | + echo "spec_test_preset_type=${{ github.event.inputs.test_preset_type || env.TEST_PRESET_TYPE }}" >> $GITHUB_ENV + - name: set TEST_PRESET_TYPE + if: ${{ (github.event_name == 'push' && github.ref_name != 'master') || github.event_name == 'pull_request' }} + run: | + echo "spec_test_preset_type=${{ env.TEST_PRESET_TYPE}}" >> $GITHUB_ENV + - name: set TEST_PRESET_TYPE + if: ${{ github.event_name == 'push' && github.ref_name == 'master' }} + run: | + echo "spec_test_preset_type=mainnet" >> $GITHUB_ENV + - name: set TEST_PRESET_TYPE + if: github.event.schedule=='0 0 * * *' + run: | + echo "spec_test_preset_type=mainnet" >> $GITHUB_ENV + - name: Install pyspec requirements + run: make install_test + - name: test-${{ matrix.version }} + run: make citest fork=${{ matrix.version }} TEST_PRESET_TYPE=${{env.spec_test_preset_type}} + - uses: actions/upload-artifact@v3 + if: always() + with: + name: test-${{ matrix.version }} + path: tests/core/pyspec/test-reports + + cleanup: + runs-on: self-hosted + needs: [setup-env,pyspec-tests,codespell,lint,table_of_contents] + if: always() + steps: + - name: 'Cleanup build folder' + run: | + ls -la ./ + rm -rf ./* || true + rm -rf ./.??* || true + ls -la ./ \ No newline at end of file diff --git a/Makefile b/Makefile index 645ed7006..ac769b200 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ SOLIDITY_DEPOSIT_CONTRACT_SOURCE = ${SOLIDITY_DEPOSIT_CONTRACT_DIR}/deposit_cont SOLIDITY_FILE_NAME = deposit_contract.json DEPOSIT_CONTRACT_TESTER_DIR = ${SOLIDITY_DEPOSIT_CONTRACT_DIR}/web3_tester CONFIGS_DIR = ./configs - +TEST_PRESET_TYPE ?= minimal # Collect a list of generator names GENERATORS = $(sort $(dir $(wildcard $(GENERATOR_DIR)/*/.))) # Map this list of generator paths to "gen_{generator name}" entries @@ -96,30 +96,30 @@ generate_tests: $(GENERATOR_TARGETS) # "make pyspec" to create the pyspec for all phases. pyspec: - . venv/bin/activate; python3 setup.py pyspecdev + python3 -m venv venv; . venv/bin/activate; python3 setup.py pyspecdev # installs the packages to run pyspec tests install_test: python3 -m venv venv; . venv/bin/activate; python3 -m pip install -e .[lint]; python3 -m pip install -e .[test] -# Testing against `minimal` config by default +# Testing against `minimal` or `mainnet` config by default test: pyspec . venv/bin/activate; cd $(PY_SPEC_DIR); \ - python3 -m pytest -n 4 --disable-bls --cov=eth2spec.phase0.minimal --cov=eth2spec.altair.minimal --cov=eth2spec.bellatrix.minimal --cov=eth2spec.capella.minimal --cov=eth2spec.eip4844.minimal --cov-report="html:$(COV_HTML_OUT)" --cov-branch eth2spec + python3 -m pytest -n 4 --disable-bls --cov=eth2spec.phase0.$(TEST_PRESET_TYPE) --cov=eth2spec.altair.$(TEST_PRESET_TYPE) --cov=eth2spec.bellatrix.$(TEST_PRESET_TYPE) --cov=eth2spec.capella.$(TEST_PRESET_TYPE) --cov=eth2spec.eip4844.$(TEST_PRESET_TYPE) --cov-report="html:$(COV_HTML_OUT)" --cov-branch eth2spec -# Testing against `minimal` config by default +# Testing against `minimal` or `mainnet` config by default find_test: pyspec . venv/bin/activate; cd $(PY_SPEC_DIR); \ - python3 -m pytest -k=$(K) --disable-bls --cov=eth2spec.phase0.minimal --cov=eth2spec.altair.minimal --cov=eth2spec.bellatrix.minimal --cov=eth2spec.capella.minimal --cov=eth2spec.eip4844.minimal --cov-report="html:$(COV_HTML_OUT)" --cov-branch eth2spec + python3 -m pytest -k=$(K) --disable-bls --cov=eth2spec.phase0.$(TEST_PRESET_TYPE) --cov=eth2spec.altair.$(TEST_PRESET_TYPE) --cov=eth2spec.bellatrix.$(TEST_PRESET_TYPE) --cov=eth2spec.capella.$(TEST_PRESET_TYPE) --cov=eth2spec.eip4844.$(TEST_PRESET_TYPE) --cov-report="html:$(COV_HTML_OUT)" --cov-branch eth2spec citest: pyspec mkdir -p $(TEST_REPORT_DIR); ifdef fork . venv/bin/activate; cd $(PY_SPEC_DIR); \ - python3 -m pytest -n 4 --bls-type=milagro --fork=$(fork) --junitxml=test-reports/test_results.xml eth2spec + python3 -m pytest -n 16 --bls-type=milagro --preset=$(TEST_PRESET_TYPE) --fork=$(fork) --junitxml=test-reports/test_results.xml eth2spec else . venv/bin/activate; cd $(PY_SPEC_DIR); \ - python3 -m pytest -n 4 --bls-type=milagro --junitxml=test-reports/test_results.xml eth2spec + python3 -m pytest -n 16 --bls-type=milagro --preset=$(TEST_PRESET_TYPE) --junitxml=test-reports/test_results.xml eth2spec endif @@ -135,7 +135,7 @@ check_toc: $(MARKDOWN_FILES:=.toc) rm $*.tmp codespell: - codespell . --skip ./.git -I .codespell-whitelist + codespell . --skip "./.git,./venv,$(PY_SPEC_DIR)/.mypy_cache" -I .codespell-whitelist # TODO: add future protocol upgrade patch packages to linting. # NOTE: we use `pylint` just for catching unused arguments in spec code From 6b94aab3af875d97e8574cbc23d6919b6540a235 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Tue, 3 Jan 2023 23:28:37 +0800 Subject: [PATCH 18/32] Move `is_data_available` check to fork-choice `on_block` --- specs/eip4844/beacon-chain.md | 28 --------- specs/eip4844/fork-choice.md | 105 ++++++++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+), 28 deletions(-) create mode 100644 specs/eip4844/fork-choice.md diff --git a/specs/eip4844/beacon-chain.md b/specs/eip4844/beacon-chain.md index 19acb57d8..e62c455d6 100644 --- a/specs/eip4844/beacon-chain.md +++ b/specs/eip4844/beacon-chain.md @@ -23,7 +23,6 @@ - [Helper functions](#helper-functions) - [Misc](#misc) - [`validate_blobs_sidecar`](#validate_blobs_sidecar) - - [`is_data_available`](#is_data_available) - [`kzg_commitment_to_versioned_hash`](#kzg_commitment_to_versioned_hash) - [`tx_peek_blob_versioned_hashes`](#tx_peek_blob_versioned_hashes) - [`verify_kzg_commitments_against_transactions`](#verify_kzg_commitments_against_transactions) @@ -162,30 +161,6 @@ def validate_blobs_sidecar(slot: Slot, assert verify_aggregate_kzg_proof(blobs, expected_kzg_commitments, kzg_aggregated_proof) ``` -#### `is_data_available` - -The implementation of `is_data_available` will become more sophisticated during later sharding upgrades. -Initially, it requires every verifying actor to retrieve the matching `BlobsSidecar`, -and validate the sidecar with `validate_blobs_sidecar`. - -The block MUST NOT be considered valid until a valid `BlobsSidecar` has been downloaded. Blocks that have been previously validated as available SHOULD be considered available even if the associated `BlobsSidecar` has subsequently been pruned. - -```python -def is_data_available(slot: Slot, beacon_block_root: Root, blob_kzg_commitments: Sequence[KZGCommitment]) -> bool: - # `retrieve_blobs_sidecar` is implementation and context dependent, raises an exception if not available. - # Note: the p2p network does not guarantee sidecar retrieval outside of `MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS` - sidecar = retrieve_blobs_sidecar(slot, beacon_block_root) - - # For testing, `retrieve_blobs_sidecar` returns "TEST". - # TODO: Remove it once we have a way to inject `BlobsSidecar` into tests. - if isinstance(sidecar, str): - return True - - validate_blobs_sidecar(slot, beacon_block_root, blob_kzg_commitments, sidecar) - return True -``` - - #### `kzg_commitment_to_versioned_hash` ```python @@ -241,9 +216,6 @@ def process_block(state: BeaconState, block: BeaconBlock) -> None: process_operations(state, block.body) process_sync_aggregate(state, block.body.sync_aggregate) process_blob_kzg_commitments(state, block.body) # [New in EIP-4844] - - # New in EIP-4844 - assert is_data_available(block.slot, hash_tree_root(block), block.body.blob_kzg_commitments) ``` #### Execution payload diff --git a/specs/eip4844/fork-choice.md b/specs/eip4844/fork-choice.md new file mode 100644 index 000000000..bc25eeae7 --- /dev/null +++ b/specs/eip4844/fork-choice.md @@ -0,0 +1,105 @@ +# EIP-4844 -- Fork Choice + +## Table of contents + + + + +- [Introduction](#introduction) +- [Helpers](#helpers) + - [`is_data_available`](#is_data_available) +- [Updated fork-choice handlers](#updated-fork-choice-handlers) + - [`on_block`](#on_block) + + + + +## Introduction + +This is the modification of the fork choice according to the executable beacon chain proposal. + +## Helpers + +#### `is_data_available` + +The implementation of `is_data_available` will become more sophisticated during later sharding upgrades. +Initially, it requires every verifying actor to retrieve the matching `BlobsSidecar`, +and validate the sidecar with `validate_blobs_sidecar`. + +The block MUST NOT be considered valid until a valid `BlobsSidecar` has been downloaded. Blocks that have been previously validated as available SHOULD be considered available even if the associated `BlobsSidecar` has subsequently been pruned. + +```python +def is_data_available(slot: Slot, beacon_block_root: Root, blob_kzg_commitments: Sequence[KZGCommitment]) -> bool: + # `retrieve_blobs_sidecar` is implementation and context dependent, raises an exception if not available. + # Note: the p2p network does not guarantee sidecar retrieval outside of `MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS` + sidecar = retrieve_blobs_sidecar(slot, beacon_block_root) + + # For testing, `retrieve_blobs_sidecar` returns "TEST". + # TODO: Remove it once we have a way to inject `BlobsSidecar` into tests. + if isinstance(sidecar, str): + return True + + validate_blobs_sidecar(slot, beacon_block_root, blob_kzg_commitments, sidecar) + return True +``` + +## Updated fork-choice handlers + +### `on_block` + +*Note*: The only modification is the addition of the verification of transition block conditions. + +```python +def on_block(store: Store, signed_block: SignedBeaconBlock) -> None: + """ + Run ``on_block`` upon receiving a new block. + """ + block = signed_block.message + # Parent block must be known + assert block.parent_root in store.block_states + # Make a copy of the state to avoid mutability issues + pre_state = copy(store.block_states[block.parent_root]) + # Blocks cannot be in the future. If they are, their consideration must be delayed until they are in the past. + assert get_current_slot(store) >= block.slot + + # Check that block is later than the finalized epoch slot (optimization to reduce calls to get_ancestor) + finalized_slot = compute_start_slot_at_epoch(store.finalized_checkpoint.epoch) + assert block.slot > finalized_slot + # Check block is a descendant of the finalized block at the checkpoint finalized slot + assert get_ancestor(store, block.parent_root, finalized_slot) == store.finalized_checkpoint.root + + # [New in EIP-4844] + # Check if the block is available + assert is_data_available(block.slot, hash_tree_root(block), block.body.blob_kzg_commitments) + + # Check the block is valid and compute the post-state + state = pre_state.copy() + state_transition(state, signed_block, True) + + # Check the merge transition + if is_merge_transition_block(pre_state, block.body): + validate_merge_block(block) + + # Add new block to the store + store.blocks[hash_tree_root(block)] = block + # Add new state for this block to the store + store.block_states[hash_tree_root(block)] = state + + # 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: + store.proposer_boost_root = hash_tree_root(block) + + # Update justified checkpoint + if state.current_justified_checkpoint.epoch > store.justified_checkpoint.epoch: + if state.current_justified_checkpoint.epoch > store.best_justified_checkpoint.epoch: + store.best_justified_checkpoint = state.current_justified_checkpoint + if should_update_justified_checkpoint(store, state.current_justified_checkpoint): + store.justified_checkpoint = state.current_justified_checkpoint + + # Update finalized checkpoint + if state.finalized_checkpoint.epoch > store.finalized_checkpoint.epoch: + store.finalized_checkpoint = state.finalized_checkpoint + store.justified_checkpoint = state.current_justified_checkpoint +``` From dba75eece9529ad6bc59c11fcb1382c3eed986b5 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Wed, 4 Jan 2023 00:45:46 +0800 Subject: [PATCH 19/32] Enable Capella feature in eip4844 fork Fix lint --- setup.py | 29 ----- .../test_process_bls_to_execution_change.py | 23 ++-- .../block_processing/test_process_deposit.py | 5 +- .../test_process_withdrawals.py | 110 +++++++++--------- .../test_process_historical_batches_update.py | 5 +- .../test/capella/sanity/test_blocks.py | 21 ++-- .../test/eip4844/block_processing/__init__.py | 0 .../test_process_bls_to_execution_change.py | 40 ------- .../test_process_withdrawals.py | 41 ------- .../test/eip4844/epoch_processing/__init__.py | 0 .../test_process_historical_batches_update.py | 23 ---- .../test/phase0/sanity/test_blocks.py | 8 +- .../eth2spec/test/phase0/sanity/test_slots.py | 9 +- tests/generators/epoch_processing/main.py | 6 +- tests/generators/operations/main.py | 6 +- 15 files changed, 86 insertions(+), 240 deletions(-) delete mode 100644 tests/core/pyspec/eth2spec/test/eip4844/block_processing/__init__.py delete mode 100644 tests/core/pyspec/eth2spec/test/eip4844/block_processing/test_process_bls_to_execution_change.py delete mode 100644 tests/core/pyspec/eth2spec/test/eip4844/block_processing/test_process_withdrawals.py delete mode 100644 tests/core/pyspec/eth2spec/test/eip4844/epoch_processing/__init__.py delete mode 100644 tests/core/pyspec/eth2spec/test/eip4844/epoch_processing/test_process_historical_batches_update.py diff --git a/setup.py b/setup.py index f95f85284..83c7e857e 100644 --- a/setup.py +++ b/setup.py @@ -638,35 +638,6 @@ T = TypeVar('T') # For generic function @classmethod def sundry_functions(cls) -> str: return super().sundry_functions() + '\n\n' + ''' -# -# Temporarily disable Withdrawals functions for EIP4844 testnets -# - - -def no_op(fn): # type: ignore - # pylint: disable=unused-argument - def wrapper(*args, **kw): # type: ignore - return None - return wrapper - - -def get_empty_list_result(fn): # type: ignore - # pylint: disable=unused-argument - def wrapper(*args, **kw): # type: ignore - return [] - return wrapper - - -process_withdrawals = no_op(process_withdrawals) -process_bls_to_execution_change = no_op(process_bls_to_execution_change) -get_expected_withdrawals = get_empty_list_result(get_expected_withdrawals) -process_historical_summaries_update = no_op(process_historical_summaries_update) - - -# -# End -# - def retrieve_blobs_sidecar(slot: Slot, beacon_block_root: Root) -> PyUnion[BlobsSidecar, str]: # pylint: disable=unused-argument return "TEST"''' diff --git a/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_bls_to_execution_change.py b/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_bls_to_execution_change.py index 6a1ba5b36..094de5eeb 100644 --- a/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_bls_to_execution_change.py +++ b/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_bls_to_execution_change.py @@ -1,8 +1,7 @@ -from eth2spec.test.helpers.constants import CAPELLA from eth2spec.test.helpers.keys import pubkeys from eth2spec.test.helpers.bls_to_execution_changes import get_signed_address_change -from eth2spec.test.context import spec_state_test, expect_assertion_error, with_phases, always_bls +from eth2spec.test.context import spec_state_test, expect_assertion_error, with_capella_and_later, always_bls def run_bls_to_execution_change_processing(spec, state, signed_address_change, valid=True): @@ -38,14 +37,14 @@ def run_bls_to_execution_change_processing(spec, state, signed_address_change, v yield 'post', state -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_success(spec, state): signed_address_change = get_signed_address_change(spec, state) yield from run_bls_to_execution_change_processing(spec, state, signed_address_change) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_success_not_activated(spec, state): validator_index = 3 @@ -63,7 +62,7 @@ def test_success_not_activated(spec, state): assert not spec.is_fully_withdrawable_validator(validator, balance, spec.get_current_epoch(state)) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_success_in_activation_queue(spec, state): validator_index = 3 @@ -81,7 +80,7 @@ def test_success_in_activation_queue(spec, state): assert not spec.is_fully_withdrawable_validator(validator, balance, spec.get_current_epoch(state)) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_success_in_exit_queue(spec, state): validator_index = 3 @@ -94,7 +93,7 @@ def test_success_in_exit_queue(spec, state): yield from run_bls_to_execution_change_processing(spec, state, signed_address_change) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_success_exited(spec, state): validator_index = 4 @@ -111,7 +110,7 @@ def test_success_exited(spec, state): assert not spec.is_fully_withdrawable_validator(validator, balance, spec.get_current_epoch(state)) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_success_withdrawable(spec, state): validator_index = 4 @@ -129,7 +128,7 @@ def test_success_withdrawable(spec, state): assert spec.is_fully_withdrawable_validator(validator, balance, spec.get_current_epoch(state)) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_invalid_val_index_out_of_range(spec, state): # Create for one validator beyond the validator list length @@ -138,7 +137,7 @@ def test_invalid_val_index_out_of_range(spec, state): yield from run_bls_to_execution_change_processing(spec, state, signed_address_change, valid=False) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_invalid_already_0x01(spec, state): # Create for one validator beyond the validator list length @@ -150,7 +149,7 @@ def test_invalid_already_0x01(spec, state): yield from run_bls_to_execution_change_processing(spec, state, signed_address_change, valid=False) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_invalid_incorrect_from_bls_pubkey(spec, state): # Create for one validator beyond the validator list length @@ -164,7 +163,7 @@ def test_invalid_incorrect_from_bls_pubkey(spec, state): yield from run_bls_to_execution_change_processing(spec, state, signed_address_change, valid=False) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test @always_bls def test_invalid_bad_signature(spec, state): diff --git a/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_deposit.py b/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_deposit.py index 685d17651..e0603d301 100644 --- a/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_deposit.py +++ b/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_deposit.py @@ -1,8 +1,7 @@ from eth2spec.test.context import ( spec_state_test, - with_phases, + with_capella_and_later, ) -from eth2spec.test.helpers.constants import CAPELLA from eth2spec.test.helpers.state import next_epoch_via_block from eth2spec.test.helpers.deposits import ( prepare_state_and_deposit, @@ -11,7 +10,7 @@ from eth2spec.test.helpers.deposits import ( from eth2spec.test.helpers.withdrawals import set_validator_fully_withdrawable -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_success_top_up_to_withdrawn_validator(spec, state): validator_index = 0 diff --git a/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_withdrawals.py b/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_withdrawals.py index fa3806d2c..674231096 100644 --- a/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_withdrawals.py +++ b/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_withdrawals.py @@ -4,9 +4,9 @@ from eth2spec.test.context import ( spec_state_test, expect_assertion_error, with_presets, - with_phases, + with_capella_and_later, ) -from eth2spec.test.helpers.constants import MAINNET, MINIMAL, CAPELLA +from eth2spec.test.helpers.constants import MAINNET, MINIMAL from eth2spec.test.helpers.execution_payload import ( build_empty_execution_payload, compute_el_block_hash, @@ -97,7 +97,7 @@ def run_withdrawals_processing(spec, state, execution_payload, num_expected_with return expected_withdrawals -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_success_zero_expected_withdrawals(spec, state): assert len(spec.get_expected_withdrawals(state)) == 0 @@ -108,7 +108,7 @@ def test_success_zero_expected_withdrawals(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_success_one_full_withdrawal(spec, state): fully_withdrawable_indices, partial_withdrawals_indices = prepare_expected_withdrawals( @@ -125,7 +125,7 @@ def test_success_one_full_withdrawal(spec, state): partial_withdrawals_indices=partial_withdrawals_indices) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_success_one_partial_withdrawal(spec, state): fully_withdrawable_indices, partial_withdrawals_indices = prepare_expected_withdrawals( @@ -145,7 +145,7 @@ def test_success_one_partial_withdrawal(spec, state): ) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_success_max_per_slot(spec, state): num_full_withdrawals = spec.MAX_WITHDRAWALS_PER_PAYLOAD // 2 @@ -163,7 +163,7 @@ def test_success_max_per_slot(spec, state): partial_withdrawals_indices=partial_withdrawals_indices) -@with_phases([CAPELLA]) +@with_capella_and_later @with_presets([MAINNET], reason="too few validators with minimal config") @spec_state_test def test_success_all_fully_withdrawable_in_one_sweep(spec, state): @@ -182,7 +182,7 @@ def test_success_all_fully_withdrawable_in_one_sweep(spec, state): partial_withdrawals_indices=partial_withdrawals_indices) -@with_phases([CAPELLA]) +@with_capella_and_later @with_presets([MINIMAL], reason="too many validators with mainnet config") @spec_state_test def test_success_all_fully_withdrawable(spec, state): @@ -201,7 +201,7 @@ def test_success_all_fully_withdrawable(spec, state): partial_withdrawals_indices=partial_withdrawals_indices) -@with_phases([CAPELLA]) +@with_capella_and_later @with_presets([MAINNET], reason="too few validators with minimal config") @spec_state_test def test_success_all_partially_withdrawable_in_one_sweep(spec, state): @@ -220,7 +220,7 @@ def test_success_all_partially_withdrawable_in_one_sweep(spec, state): partial_withdrawals_indices=partial_withdrawals_indices) -@with_phases([CAPELLA]) +@with_capella_and_later @with_presets([MINIMAL], reason="too many validators with mainnet config") @spec_state_test def test_success_all_partially_withdrawable(spec, state): @@ -243,7 +243,7 @@ def test_success_all_partially_withdrawable(spec, state): # Failure cases in which the number of withdrawals in the execution_payload is incorrect # -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_invalid_non_withdrawable_non_empty_withdrawals(spec, state): next_slot(spec, state) @@ -260,7 +260,7 @@ def test_invalid_non_withdrawable_non_empty_withdrawals(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_invalid_one_expected_full_withdrawal_and_none_in_withdrawals(spec, state): prepare_expected_withdrawals(spec, state, num_full_withdrawals=1) @@ -273,7 +273,7 @@ def test_invalid_one_expected_full_withdrawal_and_none_in_withdrawals(spec, stat yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_invalid_one_expected_partial_withdrawal_and_none_in_withdrawals(spec, state): prepare_expected_withdrawals(spec, state, num_partial_withdrawals=1) @@ -286,7 +286,7 @@ def test_invalid_one_expected_partial_withdrawal_and_none_in_withdrawals(spec, s yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_invalid_one_expected_full_withdrawal_and_duplicate_in_withdrawals(spec, state): prepare_expected_withdrawals(spec, state, num_full_withdrawals=2) @@ -299,7 +299,7 @@ def test_invalid_one_expected_full_withdrawal_and_duplicate_in_withdrawals(spec, yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_invalid_two_expected_partial_withdrawal_and_duplicate_in_withdrawals(spec, state): prepare_expected_withdrawals(spec, state, num_partial_withdrawals=2) @@ -312,7 +312,7 @@ def test_invalid_two_expected_partial_withdrawal_and_duplicate_in_withdrawals(sp yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_invalid_max_per_slot_full_withdrawals_and_one_less_in_withdrawals(spec, state): prepare_expected_withdrawals(spec, state, num_full_withdrawals=spec.MAX_WITHDRAWALS_PER_PAYLOAD) @@ -325,7 +325,7 @@ def test_invalid_max_per_slot_full_withdrawals_and_one_less_in_withdrawals(spec, yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_invalid_max_per_slot_partial_withdrawals_and_one_less_in_withdrawals(spec, state): prepare_expected_withdrawals(spec, state, num_partial_withdrawals=spec.MAX_WITHDRAWALS_PER_PAYLOAD) @@ -338,7 +338,7 @@ def test_invalid_max_per_slot_partial_withdrawals_and_one_less_in_withdrawals(sp yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_invalid_a_lot_fully_withdrawable_too_few_in_withdrawals(spec, state): prepare_expected_withdrawals(spec, state, num_full_withdrawals=spec.MAX_WITHDRAWALS_PER_PAYLOAD * 4) @@ -351,7 +351,7 @@ def test_invalid_a_lot_fully_withdrawable_too_few_in_withdrawals(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_invalid_a_lot_partially_withdrawable_too_few_in_withdrawals(spec, state): prepare_expected_withdrawals(spec, state, num_partial_withdrawals=spec.MAX_WITHDRAWALS_PER_PAYLOAD * 4) @@ -364,7 +364,7 @@ def test_invalid_a_lot_partially_withdrawable_too_few_in_withdrawals(spec, state yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_invalid_a_lot_mixed_withdrawable_in_queue_too_few_in_withdrawals(spec, state): prepare_expected_withdrawals(spec, state, num_full_withdrawals=spec.MAX_WITHDRAWALS_PER_PAYLOAD, @@ -382,7 +382,7 @@ def test_invalid_a_lot_mixed_withdrawable_in_queue_too_few_in_withdrawals(spec, # Failure cases in which the withdrawals in the execution_payload are incorrect # -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_invalid_incorrect_withdrawal_index(spec, state): prepare_expected_withdrawals(spec, state, num_full_withdrawals=1) @@ -395,7 +395,7 @@ def test_invalid_incorrect_withdrawal_index(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_invalid_incorrect_address_full(spec, state): prepare_expected_withdrawals(spec, state, num_full_withdrawals=1) @@ -408,7 +408,7 @@ def test_invalid_incorrect_address_full(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_invalid_incorrect_address_partial(spec, state): prepare_expected_withdrawals(spec, state, num_partial_withdrawals=1) @@ -421,7 +421,7 @@ def test_invalid_incorrect_address_partial(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_invalid_incorrect_amount_full(spec, state): prepare_expected_withdrawals(spec, state, num_full_withdrawals=1) @@ -434,7 +434,7 @@ def test_invalid_incorrect_amount_full(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_invalid_incorrect_amount_partial(spec, state): prepare_expected_withdrawals(spec, state, num_full_withdrawals=1) @@ -447,7 +447,7 @@ def test_invalid_incorrect_amount_partial(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_invalid_one_of_many_incorrectly_full(spec, state): prepare_expected_withdrawals(spec, state, num_full_withdrawals=spec.MAX_WITHDRAWALS_PER_PAYLOAD * 4) @@ -466,7 +466,7 @@ def test_invalid_one_of_many_incorrectly_full(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_invalid_one_of_many_incorrectly_partial(spec, state): prepare_expected_withdrawals(spec, state, num_partial_withdrawals=spec.MAX_WITHDRAWALS_PER_PAYLOAD * 4) @@ -485,7 +485,7 @@ def test_invalid_one_of_many_incorrectly_partial(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_invalid_many_incorrectly_full(spec, state): prepare_expected_withdrawals(spec, state, num_full_withdrawals=spec.MAX_WITHDRAWALS_PER_PAYLOAD * 4) @@ -504,7 +504,7 @@ def test_invalid_many_incorrectly_full(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_invalid_many_incorrectly_partial(spec, state): prepare_expected_withdrawals(spec, state, num_partial_withdrawals=spec.MAX_WITHDRAWALS_PER_PAYLOAD * 4) @@ -527,7 +527,7 @@ def test_invalid_many_incorrectly_partial(spec, state): # More full withdrawal cases # -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_withdrawable_epoch_but_0_balance(spec, state): current_epoch = spec.get_current_epoch(state) @@ -541,7 +541,7 @@ def test_withdrawable_epoch_but_0_balance(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=0) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_withdrawable_epoch_but_0_effective_balance_0_balance(spec, state): current_epoch = spec.get_current_epoch(state) @@ -555,7 +555,7 @@ def test_withdrawable_epoch_but_0_effective_balance_0_balance(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=0) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_withdrawable_epoch_but_0_effective_balance_nonzero_balance(spec, state): current_epoch = spec.get_current_epoch(state) @@ -569,7 +569,7 @@ def test_withdrawable_epoch_but_0_effective_balance_nonzero_balance(spec, state) yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=1) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_no_withdrawals_but_some_next_epoch(spec, state): current_epoch = spec.get_current_epoch(state) @@ -583,7 +583,7 @@ def test_no_withdrawals_but_some_next_epoch(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=0) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_all_withdrawal(spec, state): # Make all validators withdrawable @@ -619,25 +619,25 @@ def run_random_full_withdrawals_test(spec, state, rng): yield from run_withdrawals_processing(spec, state, execution_payload) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_random_full_withdrawals_0(spec, state): yield from run_random_full_withdrawals_test(spec, state, random.Random(444)) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_random_full_withdrawals_1(spec, state): yield from run_random_full_withdrawals_test(spec, state, random.Random(420)) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_random_full_withdrawals_2(spec, state): yield from run_random_full_withdrawals_test(spec, state, random.Random(200)) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_random_full_withdrawals_3(spec, state): yield from run_random_full_withdrawals_test(spec, state, random.Random(2000000)) @@ -647,7 +647,7 @@ def test_random_full_withdrawals_3(spec, state): # More partial withdrawal cases # -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_success_no_max_effective_balance(spec, state): validator_index = len(state.validators) // 2 @@ -663,7 +663,7 @@ def test_success_no_max_effective_balance(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=0) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_success_no_excess_balance(spec, state): validator_index = len(state.validators) // 2 @@ -679,7 +679,7 @@ def test_success_no_excess_balance(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=0) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_success_excess_balance_but_no_max_effective_balance(spec, state): validator_index = len(state.validators) // 2 @@ -696,7 +696,7 @@ def test_success_excess_balance_but_no_max_effective_balance(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=0) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_success_one_partial_withdrawable_not_yet_active(spec, state): validator_index = min(len(state.validators) // 2, spec.MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP - 1) @@ -710,7 +710,7 @@ def test_success_one_partial_withdrawable_not_yet_active(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=1) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_success_one_partial_withdrawable_in_exit_queue(spec, state): validator_index = min(len(state.validators) // 2, spec.MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP - 1) @@ -725,7 +725,7 @@ def test_success_one_partial_withdrawable_in_exit_queue(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=1) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_success_one_partial_withdrawable_exited(spec, state): validator_index = min(len(state.validators) // 2, spec.MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP - 1) @@ -739,7 +739,7 @@ def test_success_one_partial_withdrawable_exited(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=1) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_success_one_partial_withdrawable_active_and_slashed(spec, state): validator_index = min(len(state.validators) // 2, spec.MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP - 1) @@ -753,7 +753,7 @@ def test_success_one_partial_withdrawable_active_and_slashed(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=1) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_success_one_partial_withdrawable_exited_and_slashed(spec, state): validator_index = min(len(state.validators) // 2, spec.MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP - 1) @@ -768,7 +768,7 @@ def test_success_one_partial_withdrawable_exited_and_slashed(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=1) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_success_two_partial_withdrawable(spec, state): set_validator_partially_withdrawable(spec, state, 0) @@ -779,7 +779,7 @@ def test_success_two_partial_withdrawable(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=2) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_success_max_partial_withdrawable(spec, state): # Sanity check that this test works for this state @@ -794,7 +794,7 @@ def test_success_max_partial_withdrawable(spec, state): spec, state, execution_payload, num_expected_withdrawals=spec.MAX_WITHDRAWALS_PER_PAYLOAD) -@with_phases([CAPELLA]) +@with_capella_and_later @with_presets([MINIMAL], reason="not enough validators with mainnet config") @spec_state_test def test_success_max_plus_one_withdrawable(spec, state): @@ -833,37 +833,37 @@ def run_random_partial_withdrawals_test(spec, state, rng): yield from run_withdrawals_processing(spec, state, execution_payload) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_random_0(spec, state): yield from run_random_partial_withdrawals_test(spec, state, random.Random(0)) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_random_partial_withdrawals_1(spec, state): yield from run_random_partial_withdrawals_test(spec, state, random.Random(1)) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_random_partial_withdrawals_2(spec, state): yield from run_random_partial_withdrawals_test(spec, state, random.Random(2)) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_random_partial_withdrawals_3(spec, state): yield from run_random_partial_withdrawals_test(spec, state, random.Random(3)) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_random_partial_withdrawals_4(spec, state): yield from run_random_partial_withdrawals_test(spec, state, random.Random(4)) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_random_partial_withdrawals_5(spec, state): yield from run_random_partial_withdrawals_test(spec, state, random.Random(5)) diff --git a/tests/core/pyspec/eth2spec/test/capella/epoch_processing/test_process_historical_batches_update.py b/tests/core/pyspec/eth2spec/test/capella/epoch_processing/test_process_historical_batches_update.py index f1f8292eb..c5465d328 100644 --- a/tests/core/pyspec/eth2spec/test/capella/epoch_processing/test_process_historical_batches_update.py +++ b/tests/core/pyspec/eth2spec/test/capella/epoch_processing/test_process_historical_batches_update.py @@ -1,7 +1,6 @@ from eth2spec.test.context import ( - CAPELLA, spec_state_test, - with_phases, + with_capella_and_later, ) from eth2spec.test.helpers.epoch_processing import ( run_epoch_processing_with @@ -12,7 +11,7 @@ def run_process_historical_summaries_update(spec, state): yield from run_epoch_processing_with(spec, state, 'process_historical_summaries_update') -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_historical_summaries_accumulator(spec, state): # skip ahead to near the end of the historical batch period (excl block before epoch processing) diff --git a/tests/core/pyspec/eth2spec/test/capella/sanity/test_blocks.py b/tests/core/pyspec/eth2spec/test/capella/sanity/test_blocks.py index 1df046c9d..1cd1c1317 100644 --- a/tests/core/pyspec/eth2spec/test/capella/sanity/test_blocks.py +++ b/tests/core/pyspec/eth2spec/test/capella/sanity/test_blocks.py @@ -1,7 +1,6 @@ from eth2spec.test.context import ( - with_phases, spec_state_test + with_capella_and_later, spec_state_test ) -from eth2spec.test.helpers.constants import CAPELLA from eth2spec.test.helpers.state import ( state_transition_and_sign_block, ) @@ -25,7 +24,7 @@ from eth2spec.test.helpers.voluntary_exits import prepare_signed_exits # BLSToExecutionChange # -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_success_bls_change(spec, state): index = 0 @@ -48,7 +47,7 @@ def test_success_bls_change(spec, state): assert post_credentials[12:] == signed_address_change.message.to_execution_address -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_success_exit_and_bls_change(spec, state): # move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit @@ -77,7 +76,7 @@ def test_success_exit_and_bls_change(spec, state): assert spec.is_fully_withdrawable_validator(validator, balance, validator.withdrawable_epoch) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_invalid_duplicate_bls_changes_same_block(spec, state): index = 0 @@ -96,7 +95,7 @@ def test_invalid_duplicate_bls_changes_same_block(spec, state): yield 'post', None -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_invalid_two_bls_changes_of_different_addresses_same_validator_same_block(spec, state): index = 0 @@ -124,7 +123,7 @@ def test_invalid_two_bls_changes_of_different_addresses_same_validator_same_bloc # Withdrawals # -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_full_withdrawal_in_epoch_transition(spec, state): index = 0 @@ -145,7 +144,7 @@ def test_full_withdrawal_in_epoch_transition(spec, state): assert len(spec.get_expected_withdrawals(state)) == 0 -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_partial_withdrawal_in_epoch_transition(spec, state): index = state.next_withdrawal_index @@ -169,7 +168,7 @@ def test_partial_withdrawal_in_epoch_transition(spec, state): assert len(spec.get_expected_withdrawals(state)) == 0 -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_many_partial_withdrawals_in_epoch_transition(spec, state): assert len(state.validators) > spec.MAX_WITHDRAWALS_PER_PAYLOAD @@ -221,7 +220,7 @@ def _perform_valid_withdrawal(spec, state): return pre_state, signed_block_1, pre_next_withdrawal_index -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_withdrawal_success_two_blocks(spec, state): pre_state, signed_block_1, pre_next_withdrawal_index = _perform_valid_withdrawal(spec, state) @@ -238,7 +237,7 @@ def test_withdrawal_success_two_blocks(spec, state): yield 'post', state -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_invalid_withdrawal_fail_second_block_payload_isnt_compatible(spec, state): _perform_valid_withdrawal(spec, state) diff --git a/tests/core/pyspec/eth2spec/test/eip4844/block_processing/__init__.py b/tests/core/pyspec/eth2spec/test/eip4844/block_processing/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/core/pyspec/eth2spec/test/eip4844/block_processing/test_process_bls_to_execution_change.py b/tests/core/pyspec/eth2spec/test/eip4844/block_processing/test_process_bls_to_execution_change.py deleted file mode 100644 index d9b93394f..000000000 --- a/tests/core/pyspec/eth2spec/test/eip4844/block_processing/test_process_bls_to_execution_change.py +++ /dev/null @@ -1,40 +0,0 @@ -from eth2spec.test.helpers.bls_to_execution_changes import get_signed_address_change -from eth2spec.test.context import spec_state_test, expect_assertion_error, with_eip4844_and_later - - -def run_bls_to_execution_change_processing_no_op(spec, state, signed_address_change, valid=True): - """ - Run ``process_bls_to_execution_change``, yielding: - - pre-state ('pre') - - address-change ('address_change') - - post-state ('post'). - If ``valid == False``, run expecting ``AssertionError`` - """ - pre_state = state.copy() - - # yield pre-state - yield 'pre', state - - yield 'address_change', signed_address_change - - # If the address_change is invalid, processing is aborted, and there is no post-state. - if not valid: - expect_assertion_error(lambda: spec.process_bls_to_execution_change(state, signed_address_change)) - yield 'post', None - return - - # process address change - spec.process_bls_to_execution_change(state, signed_address_change) - - # yield post-state - yield 'post', state - - # Make sure state has NOT been changed - assert state == pre_state - - -@with_eip4844_and_later -@spec_state_test -def test_no_op(spec, state): - signed_address_change = get_signed_address_change(spec, state) - yield from run_bls_to_execution_change_processing_no_op(spec, state, signed_address_change) diff --git a/tests/core/pyspec/eth2spec/test/eip4844/block_processing/test_process_withdrawals.py b/tests/core/pyspec/eth2spec/test/eip4844/block_processing/test_process_withdrawals.py deleted file mode 100644 index a7db37e42..000000000 --- a/tests/core/pyspec/eth2spec/test/eip4844/block_processing/test_process_withdrawals.py +++ /dev/null @@ -1,41 +0,0 @@ - -from eth2spec.test.context import spec_state_test, expect_assertion_error, with_eip4844_and_later -from eth2spec.test.helpers.execution_payload import ( - build_empty_execution_payload, -) -from eth2spec.test.helpers.state import next_slot - - -def run_withdrawals_processing(spec, state, execution_payload, valid=True): - """ - Run ``process_execution_payload``, yielding: - - pre-state ('pre') - - execution payload ('execution_payload') - - post-state ('post'). - If ``valid == False``, run expecting ``AssertionError`` - """ - pre_state = state.copy() - - yield 'pre', state - yield 'execution_payload', execution_payload - - if not valid: - expect_assertion_error(lambda: spec.process_withdrawals(state, execution_payload)) - yield 'post', None - return - - spec.process_withdrawals(state, execution_payload) - - yield 'post', state - - # Make sure state has NOT been changed - assert state == pre_state - - -@with_eip4844_and_later -@spec_state_test -def test_no_op(spec, state): - next_slot(spec, state) - execution_payload = build_empty_execution_payload(spec, state) - - yield from run_withdrawals_processing(spec, state, execution_payload) diff --git a/tests/core/pyspec/eth2spec/test/eip4844/epoch_processing/__init__.py b/tests/core/pyspec/eth2spec/test/eip4844/epoch_processing/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/core/pyspec/eth2spec/test/eip4844/epoch_processing/test_process_historical_batches_update.py b/tests/core/pyspec/eth2spec/test/eip4844/epoch_processing/test_process_historical_batches_update.py deleted file mode 100644 index 21865ae38..000000000 --- a/tests/core/pyspec/eth2spec/test/eip4844/epoch_processing/test_process_historical_batches_update.py +++ /dev/null @@ -1,23 +0,0 @@ -from eth2spec.test.context import ( - spec_state_test, - with_eip4844_and_later, -) -from eth2spec.test.helpers.epoch_processing import ( - run_epoch_processing_with -) - - -def run_process_historical_summaries_update(spec, state): - yield from run_epoch_processing_with(spec, state, 'process_historical_summaries_update') - - -@with_eip4844_and_later -@spec_state_test -def test_no_op(spec, state): - # skip ahead to near the end of the historical batch period (excl block before epoch processing) - state.slot = spec.SLOTS_PER_HISTORICAL_ROOT - 1 - historical_summaries_len = len(state.historical_summaries) - - yield from run_process_historical_summaries_update(spec, state) - - assert len(state.historical_summaries) == historical_summaries_len diff --git a/tests/core/pyspec/eth2spec/test/phase0/sanity/test_blocks.py b/tests/core/pyspec/eth2spec/test/phase0/sanity/test_blocks.py index 71c7798a1..2e1a2a369 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/sanity/test_blocks.py +++ b/tests/core/pyspec/eth2spec/test/phase0/sanity/test_blocks.py @@ -29,7 +29,7 @@ from eth2spec.test.helpers.sync_committee import ( compute_committee_indices, compute_sync_committee_participant_reward_and_penalty, ) -from eth2spec.test.helpers.constants import PHASE0, EIP4844, MINIMAL +from eth2spec.test.helpers.constants import PHASE0, MINIMAL from eth2spec.test.helpers.forks import is_post_altair, is_post_bellatrix, is_post_capella from eth2spec.test.context import ( spec_test, spec_state_test, dump_skipping_message, @@ -1046,11 +1046,7 @@ def test_historical_batch(spec, state): if is_post_capella(spec): # Frozen `historical_roots` assert state.historical_roots == pre_historical_roots - if spec.fork == EIP4844: - # TODO: no-op for now in EIP4844 testnet - assert state.historical_summaries == pre_historical_summaries - else: - assert len(state.historical_summaries) == len(pre_historical_summaries) + 1 + assert len(state.historical_summaries) == len(pre_historical_summaries) + 1 else: assert len(state.historical_roots) == len(pre_historical_roots) + 1 diff --git a/tests/core/pyspec/eth2spec/test/phase0/sanity/test_slots.py b/tests/core/pyspec/eth2spec/test/phase0/sanity/test_slots.py index 7b860159a..90d332d57 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/sanity/test_slots.py +++ b/tests/core/pyspec/eth2spec/test/phase0/sanity/test_slots.py @@ -1,6 +1,3 @@ -from eth2spec.test.helpers.constants import ( - EIP4844, -) from eth2spec.test.helpers.forks import ( is_post_capella, ) @@ -87,10 +84,6 @@ def test_historical_accumulator(spec, state): if is_post_capella(spec): # Frozen `historical_roots` assert state.historical_roots == pre_historical_roots - if spec.fork == EIP4844: - # TODO: no-op for now in EIP4844 testnet - assert state.historical_summaries == pre_historical_summaries - else: - assert len(state.historical_summaries) == len(pre_historical_summaries) + 1 + assert len(state.historical_summaries) == len(pre_historical_summaries) + 1 else: assert len(state.historical_roots) == len(pre_historical_roots) + 1 diff --git a/tests/generators/epoch_processing/main.py b/tests/generators/epoch_processing/main.py index 58beb0fd2..384d047be 100644 --- a/tests/generators/epoch_processing/main.py +++ b/tests/generators/epoch_processing/main.py @@ -32,10 +32,8 @@ if __name__ == "__main__": ]} capella_mods = combine_mods(_new_capella_mods, bellatrix_mods) - _new_eip4844_mods = {key: 'eth2spec.test.eip4844.epoch_processing.test_process_' + key for key in [ - 'historical_summaries_update', - ]} - eip4844_mods = combine_mods(_new_eip4844_mods, capella_mods) + # TODO: add process_execution_payload tests + eip4844_mods = capella_mods # TODO Custody Game testgen is disabled for now # custody_game_mods = {**{key: 'eth2spec.test.custody_game.epoch_processing.test_process_' + key for key in [ diff --git a/tests/generators/operations/main.py b/tests/generators/operations/main.py index 4c073ebe4..d370a1b85 100644 --- a/tests/generators/operations/main.py +++ b/tests/generators/operations/main.py @@ -36,11 +36,7 @@ if __name__ == "__main__": ]} capella_mods = combine_mods(_new_capella_mods, bellatrix_mods) - _new_eip4844_mods = {key: 'eth2spec.test.eip4844.block_processing.test_process_' + key for key in [ - 'bls_to_execution_change', - 'withdrawals', - ]} - eip4844_mods = combine_mods(_new_eip4844_mods, capella_mods) + eip4844_mods = capella_mods # TODO Custody Game testgen is disabled for now # _new_custody_game_mods = {key: 'eth2spec.test.custody_game.block_processing.test_process_' + key for key in [ From 118daae6d9d62188debcaf34f4f74f21e4705f26 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Wed, 4 Jan 2023 17:54:59 +0800 Subject: [PATCH 20/32] Add notes for new state historical accumulators --- specs/capella/beacon-chain.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/specs/capella/beacon-chain.md b/specs/capella/beacon-chain.md index bd7a2c50c..5d0df0edd 100644 --- a/specs/capella/beacon-chain.md +++ b/specs/capella/beacon-chain.md @@ -53,6 +53,10 @@ to validator withdrawals. Including: * Operation to change from `BLS_WITHDRAWAL_PREFIX` to `ETH1_ADDRESS_WITHDRAWAL_PREFIX` versioned withdrawal credentials to enable withdrawals for a validator. +Another new feature is the new state/block historical accumulators. It becomes possible to validate +the entire block history that led up to that particular state without executing the transitions +and checking them one by one in backward order using a parent chain. + ## Custom types We define the following Python custom types for type hinting and readability: From 9d14dcd8b0171d7e811a2f322174b2d02219ea0d Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Wed, 4 Jan 2023 18:09:38 +0800 Subject: [PATCH 21/32] Ensure that no duplidate block hashes --- .../pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py b/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py index 974719f92..eb56e368a 100644 --- a/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py +++ b/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py @@ -64,6 +64,7 @@ def test_from_syncing_to_invalid(spec, state): block.body.execution_payload.parent_hash = ( block_hashes[f'chain_a_{i - 1}'] if i != 0 else block_hashes['block_0'] ) + block.body.execution_payload.extra_data = spec.hash(bytes(f'chain_a_{i}', 'UTF-8')) block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) block_hashes[f'chain_a_{i}'] = block.body.execution_payload.block_hash @@ -80,6 +81,7 @@ def test_from_syncing_to_invalid(spec, state): block.body.execution_payload.parent_hash = ( block_hashes[f'chain_b_{i - 1}'] if i != 0 else block_hashes['block_0'] ) + block.body.execution_payload.extra_data = spec.hash(bytes(f'chain_b_{i}', 'UTF-8')) block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) block_hashes[f'chain_b_{i}'] = block.body.execution_payload.block_hash @@ -92,9 +94,13 @@ def test_from_syncing_to_invalid(spec, state): # Now add block 4 to chain `b` with INVALID block = build_empty_block_for_next_slot(spec, state) block.body.execution_payload.parent_hash = signed_blocks_b[-1].message.body.execution_payload.block_hash + block.body.execution_payload.extra_data = spec.hash(bytes(f'chain_b_{i}', 'UTF-8')) block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) block_hashes['chain_b_3'] = block.body.execution_payload.block_hash + # Ensure that no duplicate block hashes + assert len(block_hashes) == len(set(block_hashes.values())) + signed_block = state_transition_and_sign_block(spec, state, block) payload_status = PayloadStatusV1( status=PayloadStatusV1Status.INVALID, From 599883644986e85558aac6eaf37e381d92830a02 Mon Sep 17 00:00:00 2001 From: protolambda Date: Wed, 4 Jan 2023 11:20:09 +0100 Subject: [PATCH 22/32] tests/formats: update epoch and operations formats to reflect push-withdrawals (#3184) * tests/formats: update epoch and operations formats to reflect push-withdrawals of PR 3068 * fix typo * Update epoch processing readme Co-authored-by: Hsiao-Wei Wang --- tests/formats/epoch_processing/README.md | 5 ++--- tests/formats/operations/README.md | 23 ++++++++++++----------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/tests/formats/epoch_processing/README.md b/tests/formats/epoch_processing/README.md index 41d2a102a..2951767f2 100644 --- a/tests/formats/epoch_processing/README.md +++ b/tests/formats/epoch_processing/README.md @@ -41,11 +41,10 @@ Sub-transitions: - `effective_balance_updates` - `slashings_reset` - `randao_mixes_reset` -- `historical_roots_update` +- `historical_roots_update` (Phase0, Altair, Bellatrix only) +- `historical_summaries_update` (Capella) - `participation_record_updates` (Phase 0 only) - `participation_flag_updates` (Altair) - `sync_committee_updates` (Altair) -- `full_withdrawals` (Capella) -- `partial_withdrawals` (Capella) The resulting state should match the expected `post` state. diff --git a/tests/formats/operations/README.md b/tests/formats/operations/README.md index ba2c64b3a..810d62578 100644 --- a/tests/formats/operations/README.md +++ b/tests/formats/operations/README.md @@ -33,17 +33,18 @@ This excludes the other parts of the block-transition. Operations: -| *`operation-name`* | *`operation-object`* | *`input name`* | *`processing call`* | -|-------------------------|-----------------------|----------------------|----------------------------------------------------------------------| -| `attestation` | `Attestation` | `attestation` | `process_attestation(state, attestation)` | -| `attester_slashing` | `AttesterSlashing` | `attester_slashing` | `process_attester_slashing(state, attester_slashing)` | -| `block_header` | `BeaconBlock` | **`block`** | `process_block_header(state, block)` | -| `deposit` | `Deposit` | `deposit` | `process_deposit(state, deposit)` | -| `proposer_slashing` | `ProposerSlashing` | `proposer_slashing` | `process_proposer_slashing(state, proposer_slashing)` | -| `voluntary_exit` | `SignedVoluntaryExit` | `voluntary_exit` | `process_voluntary_exit(state, voluntary_exit)` | -| `sync_aggregate` | `SyncAggregate` | `sync_aggregate` | `process_sync_aggregate(state, sync_aggregate)` (new in Altair) | -| `execution_payload` | `ExecutionPayload` | `execution_payload` | `process_execution_payload(state, execution_payload)` (new in Bellatrix) | -| `bls_to_execution_change` | `SignedBLSToExecutionChange` | `signed_address_change` | `process_bls_to_execution_change(state, signed_address_change)` (new in Capella) | +| *`operation-name`* | *`operation-object`* | *`input name`* | *`processing call`* | +|---------------------------|------------------------------|---------------------|----------------------------------------------------------------------------------| +| `attestation` | `Attestation` | `attestation` | `process_attestation(state, attestation)` | +| `attester_slashing` | `AttesterSlashing` | `attester_slashing` | `process_attester_slashing(state, attester_slashing)` | +| `block_header` | `BeaconBlock` | **`block`** | `process_block_header(state, block)` | +| `deposit` | `Deposit` | `deposit` | `process_deposit(state, deposit)` | +| `proposer_slashing` | `ProposerSlashing` | `proposer_slashing` | `process_proposer_slashing(state, proposer_slashing)` | +| `voluntary_exit` | `SignedVoluntaryExit` | `voluntary_exit` | `process_voluntary_exit(state, voluntary_exit)` | +| `sync_aggregate` | `SyncAggregate` | `sync_aggregate` | `process_sync_aggregate(state, sync_aggregate)` (new in Altair) | +| `execution_payload` | `ExecutionPayload` | `execution_payload` | `process_execution_payload(state, execution_payload)` (new in Bellatrix) | +| `withdrawals` | `ExecutionPayload` | `execution_payload` | `process_withdrawals(state, execution_payload)` (new in Capella) | +| `bls_to_execution_change` | `SignedBLSToExecutionChange` | `address_change` | `process_bls_to_execution_change(state, address_change)` (new in Capella) | Note that `block_header` is not strictly an operation (and is a full `Block`), but processed in the same manner, and hence included here. From 5ff877cc26dab8ebdbd83ff08689ca3231a64607 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Wed, 4 Jan 2023 18:24:13 +0800 Subject: [PATCH 23/32] Add link to docs lookup table --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 07b78c0d5..bb69a452a 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ Features are researched and developed in parallel, and then consolidated into se | Code Name or Topic | Specs | Notes | | - | - | - | | Capella (tentative) |
  • Core
    • [Beacon chain changes](specs/capella/beacon-chain.md)
    • [Capella fork](specs/capella/fork.md)
  • Additions
    • [Validator additions](specs/capella/validator.md)
    • [P2P networking](specs/capella/p2p-interface.md)
| -| EIP4844 (tentative) |
  • Core
    • [Beacon Chain changes](specs/eip4844/beacon-chain.md)
    • [EIP-4844 fork](specs/eip4844/fork.md)
    • [Polynomial commitments](specs/eip4844/polynomial-commitments.md)
  • Additions
    • [Honest validator guide changes](specs/eip4844/validator.md)
    • [P2P networking](specs/eip4844/p2p-interface.md)
| +| EIP4844 (tentative) |
  • Core
    • [Beacon Chain changes](specs/eip4844/beacon-chain.md)
    • [EIP-4844 fork](specs/eip4844/fork.md)
    • [Polynomial commitments](specs/eip4844/polynomial-commitments.md)
    • [Fork choice changes](specs/eip4844/fork-choice.md)
  • Additions
    • [Honest validator guide changes](specs/eip4844/validator.md)
    • [P2P networking](specs/eip4844/p2p-interface.md)
| | Sharding (outdated) |
  • Core
    • [Beacon Chain changes](specs/sharding/beacon-chain.md)
  • Additions
    • [P2P networking](specs/sharding/p2p-interface.md)
| | Custody Game (outdated) |
  • Core
    • [Beacon Chain changes](specs/custody_game/beacon-chain.md)
  • Additions
    • [Honest validator guide changes](specs/custody_game/validator.md)
| Dependent on sharding | | Data Availability Sampling (outdated) |
  • Core
    • [Core types and functions](specs/das/das-core.md)
    • [Fork choice changes](specs/das/fork-choice.md)
  • Additions
    • [P2P Networking](specs/das/p2p-interface.md)
    • [Sampling process](specs/das/sampling.md)
|
  • Dependent on sharding
  • [Technical explainer](https://hackmd.io/@HWeNw8hNRimMm2m2GH56Cw/B1YJPGkpD)
| From 9d402dd2d21a55c28fdcd01efeb76fe1d823b066 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Wed, 4 Jan 2023 18:51:51 +0800 Subject: [PATCH 24/32] minor fix --- tests/generators/epoch_processing/main.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/generators/epoch_processing/main.py b/tests/generators/epoch_processing/main.py index 384d047be..dda4345a8 100644 --- a/tests/generators/epoch_processing/main.py +++ b/tests/generators/epoch_processing/main.py @@ -32,7 +32,6 @@ if __name__ == "__main__": ]} capella_mods = combine_mods(_new_capella_mods, bellatrix_mods) - # TODO: add process_execution_payload tests eip4844_mods = capella_mods # TODO Custody Game testgen is disabled for now From 7d2482a025e29261a7b306301977f22590c7f7c9 Mon Sep 17 00:00:00 2001 From: henridf Date: Wed, 4 Jan 2023 16:29:15 +0100 Subject: [PATCH 25/32] Fix typo in eip4844/BeaconBlocksByRoot docs --- specs/eip4844/p2p-interface.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specs/eip4844/p2p-interface.md b/specs/eip4844/p2p-interface.md index 420a6da05..1f5fd55cd 100644 --- a/specs/eip4844/p2p-interface.md +++ b/specs/eip4844/p2p-interface.md @@ -141,8 +141,8 @@ Per `context = compute_fork_digest(fork_version, genesis_validators_root)`: **Protocol ID:** `/eth2/beacon_chain/req/beacon_blocks_by_root/2/` -After `EIP4844_FORK_EPOCH`, `BeaconBlocksByRootV2` is replaced by `BeaconBlockAndBlobsSidecarByRootV1` -clients MUST support requesting blocks by root for pre-fork-epoch blocks. +After `EIP4844_FORK_EPOCH`, `BeaconBlocksByRootV2` is replaced by `BeaconBlockAndBlobsSidecarByRootV1`. +Clients MUST support requesting blocks by root for pre-fork-epoch blocks. Per `context = compute_fork_digest(fork_version, genesis_validators_root)`: From e937e2abbc48aad805d15df64480168ab993b98d Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Fri, 6 Jan 2023 13:08:32 +0800 Subject: [PATCH 26/32] Apply suggestions from code review Co-authored-by: Danny Ryan --- specs/eip4844/fork-choice.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/specs/eip4844/fork-choice.md b/specs/eip4844/fork-choice.md index bc25eeae7..2613bbb7e 100644 --- a/specs/eip4844/fork-choice.md +++ b/specs/eip4844/fork-choice.md @@ -22,8 +22,8 @@ This is the modification of the fork choice according to the executable beacon c #### `is_data_available` -The implementation of `is_data_available` will become more sophisticated during later sharding upgrades. -Initially, it requires every verifying actor to retrieve the matching `BlobsSidecar`, +The implementation of `is_data_available` will become more sophisticated during later scaling upgrades. +Initially, verification requires every verifying actor to retrieve the matching `BlobsSidecar`, and validate the sidecar with `validate_blobs_sidecar`. The block MUST NOT be considered valid until a valid `BlobsSidecar` has been downloaded. Blocks that have been previously validated as available SHOULD be considered available even if the associated `BlobsSidecar` has subsequently been pruned. @@ -69,7 +69,8 @@ def on_block(store: Store, signed_block: SignedBeaconBlock) -> None: assert get_ancestor(store, block.parent_root, finalized_slot) == store.finalized_checkpoint.root # [New in EIP-4844] - # Check if the block is available + # Check if blob data is available + # If not, this block MAY be queued and subsequently considered when blob data becomes available assert is_data_available(block.slot, hash_tree_root(block), block.body.blob_kzg_commitments) # Check the block is valid and compute the post-state From d679b2e80d4b8c0a9ffa7198df89f0213e67b3ef Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Fri, 6 Jan 2023 08:06:39 -0700 Subject: [PATCH 27/32] Update specs/capella/beacon-chain.md --- specs/capella/beacon-chain.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/specs/capella/beacon-chain.md b/specs/capella/beacon-chain.md index 5d0df0edd..9a04f2f38 100644 --- a/specs/capella/beacon-chain.md +++ b/specs/capella/beacon-chain.md @@ -53,9 +53,10 @@ to validator withdrawals. Including: * Operation to change from `BLS_WITHDRAWAL_PREFIX` to `ETH1_ADDRESS_WITHDRAWAL_PREFIX` versioned withdrawal credentials to enable withdrawals for a validator. -Another new feature is the new state/block historical accumulators. It becomes possible to validate -the entire block history that led up to that particular state without executing the transitions -and checking them one by one in backward order using a parent chain. +Another new feature is the new independent state and block historical accumulators +that replace the original singular historical roots. With these accumulators, it becomes possible to validate +the entire block history that led up to that particular state without any additional information +beyond the state and the blocks. ## Custom types From 8255618206fdd9507e1405716918cfb82d5f17f5 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Fri, 6 Jan 2023 08:16:14 -0700 Subject: [PATCH 28/32] bump VERSION.txt to 1.3.0-rc.0 --- 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 79c724242..b73f10e65 100644 --- a/tests/core/pyspec/eth2spec/VERSION.txt +++ b/tests/core/pyspec/eth2spec/VERSION.txt @@ -1 +1 @@ -1.3.0-alpha.2 +1.3.0-rc.0 From e154b3414c30131ce970d8319d00d997ede4a162 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Fri, 6 Jan 2023 23:19:44 +0800 Subject: [PATCH 29/32] Move `validate_blobs_sidecar` to fork-choice and add spec into execution spec scope --- setup.py | 1 + specs/eip4844/beacon-chain.md | 17 ----------------- specs/eip4844/fork-choice.md | 17 +++++++++++++++++ .../{validator => fork_choice}/__init__.py | 0 .../test_validate_blobs_sidecar.py} | 0 5 files changed, 18 insertions(+), 17 deletions(-) rename tests/core/pyspec/eth2spec/test/eip4844/unittests/{validator => fork_choice}/__init__.py (100%) rename tests/core/pyspec/eth2spec/test/eip4844/unittests/{validator/test_validator.py => fork_choice/test_validate_blobs_sidecar.py} (100%) diff --git a/setup.py b/setup.py index be8be6d90..28d55f07e 100644 --- a/setup.py +++ b/setup.py @@ -1020,6 +1020,7 @@ class PySpecCommand(Command): self.md_doc_paths += """ specs/eip4844/beacon-chain.md specs/eip4844/fork.md + specs/eip4844/fork-choice.md specs/eip4844/polynomial-commitments.md specs/eip4844/p2p-interface.md specs/eip4844/validator.md diff --git a/specs/eip4844/beacon-chain.md b/specs/eip4844/beacon-chain.md index e62c455d6..8b0224f86 100644 --- a/specs/eip4844/beacon-chain.md +++ b/specs/eip4844/beacon-chain.md @@ -22,7 +22,6 @@ - [`ExecutionPayloadHeader`](#executionpayloadheader) - [Helper functions](#helper-functions) - [Misc](#misc) - - [`validate_blobs_sidecar`](#validate_blobs_sidecar) - [`kzg_commitment_to_versioned_hash`](#kzg_commitment_to_versioned_hash) - [`tx_peek_blob_versioned_hashes`](#tx_peek_blob_versioned_hashes) - [`verify_kzg_commitments_against_transactions`](#verify_kzg_commitments_against_transactions) @@ -145,22 +144,6 @@ class ExecutionPayloadHeader(Container): ### Misc -#### `validate_blobs_sidecar` - -```python -def validate_blobs_sidecar(slot: Slot, - beacon_block_root: Root, - expected_kzg_commitments: Sequence[KZGCommitment], - blobs_sidecar: BlobsSidecar) -> None: - assert slot == blobs_sidecar.beacon_block_slot - assert beacon_block_root == blobs_sidecar.beacon_block_root - blobs = blobs_sidecar.blobs - kzg_aggregated_proof = blobs_sidecar.kzg_aggregated_proof - assert len(expected_kzg_commitments) == len(blobs) - - assert verify_aggregate_kzg_proof(blobs, expected_kzg_commitments, kzg_aggregated_proof) -``` - #### `kzg_commitment_to_versioned_hash` ```python diff --git a/specs/eip4844/fork-choice.md b/specs/eip4844/fork-choice.md index 2613bbb7e..ead394234 100644 --- a/specs/eip4844/fork-choice.md +++ b/specs/eip4844/fork-choice.md @@ -7,6 +7,7 @@ - [Introduction](#introduction) - [Helpers](#helpers) + - [`validate_blobs_sidecar`](#validate_blobs_sidecar) - [`is_data_available`](#is_data_available) - [Updated fork-choice handlers](#updated-fork-choice-handlers) - [`on_block`](#on_block) @@ -20,6 +21,22 @@ This is the modification of the fork choice according to the executable beacon c ## Helpers +#### `validate_blobs_sidecar` + +```python +def validate_blobs_sidecar(slot: Slot, + beacon_block_root: Root, + expected_kzg_commitments: Sequence[KZGCommitment], + blobs_sidecar: BlobsSidecar) -> None: + assert slot == blobs_sidecar.beacon_block_slot + assert beacon_block_root == blobs_sidecar.beacon_block_root + blobs = blobs_sidecar.blobs + kzg_aggregated_proof = blobs_sidecar.kzg_aggregated_proof + assert len(expected_kzg_commitments) == len(blobs) + + assert verify_aggregate_kzg_proof(blobs, expected_kzg_commitments, kzg_aggregated_proof) +``` + #### `is_data_available` The implementation of `is_data_available` will become more sophisticated during later scaling upgrades. diff --git a/tests/core/pyspec/eth2spec/test/eip4844/unittests/validator/__init__.py b/tests/core/pyspec/eth2spec/test/eip4844/unittests/fork_choice/__init__.py similarity index 100% rename from tests/core/pyspec/eth2spec/test/eip4844/unittests/validator/__init__.py rename to tests/core/pyspec/eth2spec/test/eip4844/unittests/fork_choice/__init__.py diff --git a/tests/core/pyspec/eth2spec/test/eip4844/unittests/validator/test_validator.py b/tests/core/pyspec/eth2spec/test/eip4844/unittests/fork_choice/test_validate_blobs_sidecar.py similarity index 100% rename from tests/core/pyspec/eth2spec/test/eip4844/unittests/validator/test_validator.py rename to tests/core/pyspec/eth2spec/test/eip4844/unittests/fork_choice/test_validate_blobs_sidecar.py From c9f8e4fef845b2b03a7790ede72c10e13f052187 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Fri, 6 Jan 2023 23:39:04 +0800 Subject: [PATCH 30/32] Move `BlobsSidecar` --- specs/eip4844/fork-choice.md | 14 ++++++++++++++ specs/eip4844/p2p-interface.md | 11 ----------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/specs/eip4844/fork-choice.md b/specs/eip4844/fork-choice.md index ead394234..0937b66d7 100644 --- a/specs/eip4844/fork-choice.md +++ b/specs/eip4844/fork-choice.md @@ -6,6 +6,8 @@ - [Introduction](#introduction) +- [Containers](#containers) + - [`BlobsSidecar`](#blobssidecar) - [Helpers](#helpers) - [`validate_blobs_sidecar`](#validate_blobs_sidecar) - [`is_data_available`](#is_data_available) @@ -19,6 +21,18 @@ This is the modification of the fork choice according to the executable beacon chain proposal. +## Containers + +### `BlobsSidecar` + +```python +class BlobsSidecar(Container): + beacon_block_root: Root + beacon_block_slot: Slot + blobs: List[Blob, MAX_BLOBS_PER_BLOCK] + kzg_aggregated_proof: KZGProof +``` + ## Helpers #### `validate_blobs_sidecar` diff --git a/specs/eip4844/p2p-interface.md b/specs/eip4844/p2p-interface.md index 420a6da05..582331203 100644 --- a/specs/eip4844/p2p-interface.md +++ b/specs/eip4844/p2p-interface.md @@ -12,7 +12,6 @@ The specification of these changes continues in the same format as the network s - [Configuration](#configuration) - [Containers](#containers) - - [`BlobsSidecar`](#blobssidecar) - [`SignedBeaconBlockAndBlobsSidecar`](#signedbeaconblockandblobssidecar) - [The gossip domain: gossipsub](#the-gossip-domain-gossipsub) - [Topics and messages](#topics-and-messages) @@ -41,16 +40,6 @@ The specification of these changes continues in the same format as the network s ## Containers -### `BlobsSidecar` - -```python -class BlobsSidecar(Container): - beacon_block_root: Root - beacon_block_slot: Slot - blobs: List[Blob, MAX_BLOBS_PER_BLOCK] - kzg_aggregated_proof: KZGProof -``` - ### `SignedBeaconBlockAndBlobsSidecar` ```python From 4ba2266fd55f5e0bc330ad2a70090443c23871ac Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Fri, 6 Jan 2023 08:45:20 -0700 Subject: [PATCH 31/32] Update specs/eip4844/fork-choice.md --- specs/eip4844/fork-choice.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/eip4844/fork-choice.md b/specs/eip4844/fork-choice.md index 0937b66d7..8dea28ded 100644 --- a/specs/eip4844/fork-choice.md +++ b/specs/eip4844/fork-choice.md @@ -19,7 +19,7 @@ ## Introduction -This is the modification of the fork choice according to the executable beacon chain proposal. +This is the modification of the fork choice accompanying the EIP-4844 upgrade. ## Containers From 8b14345143b65c45f35805fdff3af25965a0f5f1 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Sat, 7 Jan 2023 01:49:23 +0800 Subject: [PATCH 32/32] Fix test file name --- ...ches_update.py => test_process_historical_summaries_update.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/core/pyspec/eth2spec/test/capella/epoch_processing/{test_process_historical_batches_update.py => test_process_historical_summaries_update.py} (100%) diff --git a/tests/core/pyspec/eth2spec/test/capella/epoch_processing/test_process_historical_batches_update.py b/tests/core/pyspec/eth2spec/test/capella/epoch_processing/test_process_historical_summaries_update.py similarity index 100% rename from tests/core/pyspec/eth2spec/test/capella/epoch_processing/test_process_historical_batches_update.py rename to tests/core/pyspec/eth2spec/test/capella/epoch_processing/test_process_historical_summaries_update.py