diff --git a/specs/electra/beacon-chain.md b/specs/electra/beacon-chain.md new file mode 100644 index 0000000000..277483e034 --- /dev/null +++ b/specs/electra/beacon-chain.md @@ -0,0 +1,180 @@ +# Electra -- The Beacon Chain + +**Notice**: This document is a work-in-progress for researchers and implementers. + +## Table of contents + + + + + +- [Containers](#containers) + - [New containers](#new-containers) + - [`SyncData`](#syncdata) + - [Extended containers](#extended-containers) + - [`BeaconState`](#beaconstate) +- [Helper functions](#helper-functions) + - [`default_sync_data`](#default_sync_data) +- [Beacon chain state transition function](#beacon-chain-state-transition-function) + - [Epoch processing](#epoch-processing) + - [Modified `process_sync_committee_updates`](#modified-process_sync_committee_updates) + - [Block processing](#block-processing) + - [New `process_best_sync_data`](#new-process_best_sync_data) + + + + +## Containers + +### New containers + +#### `SyncData` + +```python +class SyncData(Container): + # Sync committee aggregate signature + sync_aggregate: SyncAggregate + # Slot at which the aggregate signature was created + signature_slot: Slot +``` + +### Extended containers + +#### `BeaconState` + +```python +class BeaconState(Container): + # Versioning + genesis_time: uint64 + genesis_validators_root: Root + slot: Slot + fork: Fork + # History + 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_summaries + # Eth1 + eth1_data: Eth1Data + eth1_data_votes: List[Eth1Data, EPOCHS_PER_ETH1_VOTING_PERIOD * SLOTS_PER_EPOCH] + eth1_deposit_index: uint64 + # Registry + validators: List[Validator, VALIDATOR_REGISTRY_LIMIT] + balances: List[Gwei, VALIDATOR_REGISTRY_LIMIT] + # Randomness + randao_mixes: Vector[Bytes32, EPOCHS_PER_HISTORICAL_VECTOR] + # Slashings + slashings: Vector[Gwei, EPOCHS_PER_SLASHINGS_VECTOR] # Per-epoch sums of slashed effective balances + # Participation + previous_epoch_participation: List[ParticipationFlags, VALIDATOR_REGISTRY_LIMIT] + current_epoch_participation: List[ParticipationFlags, VALIDATOR_REGISTRY_LIMIT] + # Finality + justification_bits: Bitvector[JUSTIFICATION_BITS_LENGTH] # Bit set for every recent justified epoch + previous_justified_checkpoint: Checkpoint + current_justified_checkpoint: Checkpoint + finalized_checkpoint: Checkpoint + # Inactivity + inactivity_scores: List[uint64, VALIDATOR_REGISTRY_LIMIT] + # Sync + current_sync_committee: SyncCommittee + next_sync_committee: SyncCommittee + # Execution + latest_execution_payload_header: ExecutionPayloadHeader + # Withdrawals + next_withdrawal_index: WithdrawalIndex + next_withdrawal_validator_index: ValidatorIndex + # Deep history valid from Capella onwards + historical_summaries: List[HistoricalSummary, HISTORICAL_ROOTS_LIMIT] + # Sync history + previous_best_sync_data: SyncData # [New in Electra] + current_best_sync_data: SyncData # [New in Electra] + parent_block_has_sync_committee_finality: bool # [New in Electra] +``` + +## Helper functions + +### `default_sync_data` + +```python +def default_sync_data() -> SyncData: + return SyncData( + sync_aggregate=SyncAggregate( + sync_committee_bits=Bitvector[SYNC_COMMITTEE_SIZE]() + sync_committee_signature=G2_POINT_AT_INFINITY, + ), + signature_slot=GENESIS_SLOT, + ) +``` + +## Beacon chain state transition function + +### Epoch processing + +#### Modified `process_sync_committee_updates` + +```python +def process_sync_committee_updates(state: BeaconState) -> None: + next_epoch = get_current_epoch(state) + Epoch(1) + if next_epoch % EPOCHS_PER_SYNC_COMMITTEE_PERIOD == 0: + state.current_sync_committee = state.next_sync_committee + state.next_sync_committee = get_next_sync_committee(state) + + # [New in Electra] + state.previous_best_sync_data = state.current_best_sync_data + state.current_best_sync_data = default_sync_data() + state.parent_block_has_sync_committee_finality = False +``` + +### Block processing + +```python +def process_block(state: BeaconState, block: BeaconBlock) -> None: + process_best_sync_data(state, block) # [New in Electra] + process_block_header(state, block) + process_withdrawals(state, block.body.execution_payload) + process_execution_payload(state, block.body, EXECUTION_ENGINE) + process_randao(state, block.body) + process_eth1_data(state, block.body) + process_operations(state, block.body) + process_sync_aggregate(state, block.body.sync_aggregate) +``` + +#### New `process_best_sync_data` + +```python +def process_best_sync_data(state: BeaconState, block: BeaconBlock) -> None: + signature_period = compute_sync_committee_period_at_slot(block.slot) + attested_period = compute_sync_committee_period_at_slot(state.latest_block_header.slot) + + # Track sync committee finality + old_has_sync_committee_finality = state.parent_block_has_sync_committee_finality + if state.parent_block_has_sync_committee_finality: + new_has_sync_committee_finality = True + elif state.finalized_checkpoint.epoch < ALTAIR_FORK_EPOCH: + new_has_sync_committee_finality = False + else: + finalized_period = compute_sync_committee_period(state.finalized_checkpoint.epoch) + new_has_sync_committee_finality = (finalized_period == attested_period) + state.parent_block_has_sync_committee_finality = new_has_sync_committee_finality + + # Track best sync data + if attested_period == signature_period: + max_active_participants = len(block.body.sync_aggregate.sync_committee_bits) + new_num_active_participants = sum(block.body.sync_aggregate.sync_committee_bits) + old_num_active_participants = sum(state.current_best_sync_data.sync_aggregate.sync_committee_bits) + new_has_supermajority = new_num_active_participants * 3 >= max_active_participants * 2 + old_has_supermajority = old_num_active_participants * 3 >= max_active_participants * 2 + if new_has_supermajority != old_has_supermajority: + is_better_sync_data = new_has_supermajority + elif not new_has_supermajority and new_num_active_participants != old_num_active_participants: + is_better_sync_data = new_num_active_participants > old_num_active_participants + elif new_has_sync_committee_finality != old_has_sync_committee_finality: + is_better_sync_data = new_has_sync_committee_finality + else: + is_better_sync_data = new_num_active_participants > old_num_active_participants + if is_better_sync_data: + state.current_best_sync_data = SyncData( + sync_aggregate=block.body.sync_aggregate, + signature_slot=block.slot, + ) +``` diff --git a/specs/electra/fork.md b/specs/electra/fork.md new file mode 100644 index 0000000000..62028fe932 --- /dev/null +++ b/specs/electra/fork.md @@ -0,0 +1,144 @@ +# Electra -- Fork Logic + +**Notice**: This document is a work-in-progress for researchers and implementers. + +## Table of contents + + + + +- [Introduction](#introduction) +- [Configuration](#configuration) +- [Helper functions](#helper-functions) + - [Misc](#misc) + - [Modified `compute_fork_version`](#modified-compute_fork_version) +- [Fork to Electra](#fork-to-electra) + - [Fork trigger](#fork-trigger) + - [Upgrading the state](#upgrading-the-state) + + + +## Introduction + +This document describes the process of Electra upgrade. + +## Configuration + +Warning: this configuration is not definitive. + +| Name | Value | +| - | - | +| `ELECTRA_FORK_VERSION` | `Version('0x05000000')` | +| `ELECTRA_FORK_EPOCH` | `Epoch(FAR_FUTURE_EPOCH)` | + +## Helper functions + +### Misc + +#### Modified `compute_fork_version` + +```python +def compute_fork_version(epoch: Epoch) -> Version: + """ + Return the fork version at the given ``epoch``. + """ + if epoch >= ELECTRA_FORK_EPOCH: + return ELECTRA_FORK_VERSION + if epoch >= DENEB_FORK_EPOCH: + return DENEB_FORK_VERSION + if epoch >= CAPELLA_FORK_EPOCH: + return CAPELLA_FORK_VERSION + if epoch >= BELLATRIX_FORK_EPOCH: + return BELLATRIX_FORK_VERSION + if epoch >= ALTAIR_FORK_EPOCH: + return ALTAIR_FORK_VERSION + return GENESIS_FORK_VERSION +``` + +## Fork to Electra + +### Fork trigger + +TBD. This fork is defined for testing purposes. +For now, we assume the condition will be triggered at epoch `ELECTRA_FORK_EPOCH`. + +Note that for the pure Electra networks, we don't apply `upgrade_to_electra` since it starts with Electra version logic. + +### Upgrading the state + +```python +def upgrade_to_electra(pre: deneb.BeaconState) -> BeaconState: + epoch = deneb.get_current_epoch(pre) + latest_execution_payload_header = ExecutionPayloadHeader( + parent_hash=pre.latest_execution_payload_header.parent_hash, + fee_recipient=pre.latest_execution_payload_header.fee_recipient, + state_root=pre.latest_execution_payload_header.state_root, + receipts_root=pre.latest_execution_payload_header.receipts_root, + logs_bloom=pre.latest_execution_payload_header.logs_bloom, + prev_randao=pre.latest_execution_payload_header.prev_randao, + block_number=pre.latest_execution_payload_header.block_number, + gas_limit=pre.latest_execution_payload_header.gas_limit, + gas_used=pre.latest_execution_payload_header.gas_used, + timestamp=pre.latest_execution_payload_header.timestamp, + extra_data=pre.latest_execution_payload_header.extra_data, + base_fee_per_gas=pre.latest_execution_payload_header.base_fee_per_gas, + block_hash=pre.latest_execution_payload_header.block_hash, + transactions_root=pre.latest_execution_payload_header.transactions_root, + withdrawals_root=pre.latest_execution_payload_header.withdrawals_root, + blob_gas_used=pre.latest_execution_payload_header.blob_gas_used, # [Modified in Electra] + excess_blob_gas=pre.latest_execution_payload_header.excess_blob_gas, # [Modified in Electra] + ) + post = BeaconState( + # Versioning + genesis_time=pre.genesis_time, + genesis_validators_root=pre.genesis_validators_root, + slot=pre.slot, + fork=Fork( + previous_version=pre.fork.current_version, + current_version=ELECTRA_FORK_VERSION, # [Modified in Electra] + epoch=epoch, + ), + # History + latest_block_header=pre.latest_block_header, + block_roots=pre.block_roots, + state_roots=pre.state_roots, + historical_roots=pre.historical_roots, + # Eth1 + eth1_data=pre.eth1_data, + eth1_data_votes=pre.eth1_data_votes, + eth1_deposit_index=pre.eth1_deposit_index, + # Registry + validators=pre.validators, + balances=pre.balances, + # Randomness + randao_mixes=pre.randao_mixes, + # Slashings + slashings=pre.slashings, + # Participation + previous_epoch_participation=pre.previous_epoch_participation, + current_epoch_participation=pre.current_epoch_participation, + # Finality + justification_bits=pre.justification_bits, + previous_justified_checkpoint=pre.previous_justified_checkpoint, + current_justified_checkpoint=pre.current_justified_checkpoint, + finalized_checkpoint=pre.finalized_checkpoint, + # Inactivity + inactivity_scores=pre.inactivity_scores, + # Sync + current_sync_committee=pre.current_sync_committee, + next_sync_committee=pre.next_sync_committee, + # Execution-layer + latest_execution_payload_header=latest_execution_payload_header, + # Withdrawals + next_withdrawal_index=pre.next_withdrawal_index, + next_withdrawal_validator_index=pre.next_withdrawal_validator_index, + # Deep history valid from Capella onwards + historical_summaries=pre.historical_summaries, + # Sync history + previous_best_sync_data=default_sync_data(), # [New in Electra] + current_best_sync_data=default_sync_data(), # [New in Electra] + parent_block_has_sync_committee_finality=(pre.slot == GENESIS_SLOT), # [New in Electra] + ) + + return post +```