From c53ab45e19ce99b5c207cf9f4f2ef0c0c0c72bdb Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Thu, 24 Nov 2022 17:29:09 +0600 Subject: [PATCH 01/24] Add in-protocol deposit processing --- specs/deposits/beacon-chain.md | 416 +++++++++++++++++++++++++++++++++ 1 file changed, 416 insertions(+) create mode 100644 specs/deposits/beacon-chain.md diff --git a/specs/deposits/beacon-chain.md b/specs/deposits/beacon-chain.md new file mode 100644 index 0000000000..bb6ebd2e8c --- /dev/null +++ b/specs/deposits/beacon-chain.md @@ -0,0 +1,416 @@ +# DepositEIP -- The Beacon Chain + +## Table of contents + + + + + +- [Introduction](#introduction) +- [Preset](#preset) + - [State list lengths](#state-list-lengths) + - [Execution](#execution) +- [Containers](#containers) + - [New containers](#new-containers) + - [`DepositReceipt`](#depositreceipt) + - [`IndexedDepositData`](#indexeddepositdata) + - [Extended Containers](#extended-containers) + - [`ExecutionPayload`](#executionpayload) + - [`ExecutionPayloadHeader`](#executionpayloadheader) + - [`BeaconState`](#beaconstate) +- [Beacon chain state transition function](#beacon-chain-state-transition-function) + - [Epoch processing](#epoch-processing) + - [Helper functions](#helper-functions) + - [New `get_validator_from_indexed_deposit_data`](#new-get_validator_from_indexed_deposit_data) + - [New `apply_pending_deposit`](#new-apply_pending_deposit) + - [New `process_pending_deposits`](#new-process_pending_deposits) + - [Block processing](#block-processing) + - [New `process_deposit_receipts`](#new-process_deposit_receipts) + - [Modified `process_execution_payload`](#modified-process_execution_payload) + - [Modified `process_operations`](#modified-process_operations) +- [Testing](#testing) + + + + +## Introduction + +This is the beacon chain specification of in-protocol deposits processing mechanism. +This mechanism relies on the changes proposed by the corresponding EIP. + +*Note:* This specification is under development and should be used with care. + +## Preset + +### State list lengths + +| Name | Value | +| - | - | +| `PENDING_DEPOSITS_LIMIT` | `2**32` (= 4,294,967,296) | + +### Execution + +| Name | Value | Description | +| - | - | - | +| `MAX_DEPOSIT_RECEIPTS_PER_PAYLOAD` | `uint64(2**10)` (= 1,024) | Maximum number of deposit receipts allowed in each payload | + +## Containers + +### New containers + +#### `DepositReceipt` + +```python +class DepositReceipt(Container): + pubkey: BLSPubkey + withdrawal_credentials: Bytes32 + amount: Gwei + signature: BLSSignature + index: uint64 +``` + +#### `IndexedDepositData` + +```python +class IndexedDepositData(Container): + pubkey: BLSPubkey + withdrawal_credentials: Bytes32 + amount: Gwei + index: uint64 + epoch: Epoch +``` + +### Extended Containers + +#### `ExecutionPayload` + +```python +class ExecutionPayload(Container): + # Execution block header fields + parent_hash: Hash32 + fee_recipient: ExecutionAddress + state_root: Bytes32 + receipts_root: Bytes32 + logs_bloom: ByteVector[BYTES_PER_LOGS_BLOOM] + prev_randao: Bytes32 + block_number: uint64 + gas_limit: uint64 + gas_used: uint64 + timestamp: uint64 + extra_data: ByteList[MAX_EXTRA_DATA_BYTES] + base_fee_per_gas: uint256 + # Extra payload fields + block_hash: Hash32 + transactions: List[Transaction, MAX_TRANSACTIONS_PER_PAYLOAD] + withdrawals: List[Withdrawal, MAX_WITHDRAWALS_PER_PAYLOAD] + deposit_receipts: List[DepositReceipt, MAX_DEPOSIT_RECEIPTS_PER_PAYLOAD] # [New in DepositEIP] +``` + +#### `ExecutionPayloadHeader` + +```python +class ExecutionPayloadHeader(Container): + # Execution block header fields + parent_hash: Hash32 + fee_recipient: ExecutionAddress + state_root: Bytes32 + receipts_root: Bytes32 + logs_bloom: ByteVector[BYTES_PER_LOGS_BLOOM] + prev_randao: Bytes32 + block_number: uint64 + gas_limit: uint64 + gas_used: uint64 + timestamp: uint64 + extra_data: ByteList[MAX_EXTRA_DATA_BYTES] + base_fee_per_gas: uint256 + # Extra payload fields + block_hash: Hash32 + transactions_root: Root + withdrawals_root: Root + deposit_receipts_root: Root # [New in DepositEIP] +``` + +#### `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] + # 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 + # DepositsEIP + pending_deposits: List[IndexedDepositData, PENDING_DEPOSITS_LIMIT] +``` + +## Beacon chain state transition function + +### Epoch processing + +```python +def process_epoch(state: BeaconState) -> None: + process_justification_and_finalization(state) + process_inactivity_updates(state) + process_rewards_and_penalties(state) + # Run before registry and after finality updates + process_pending_deposits(state) # [New in DepositsEIP] + 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_roots_update(state) + process_participation_flag_updates(state) + process_sync_committee_updates(state) +``` + +#### Helper functions + +##### New `get_validator_from_indexed_deposit_data` + +```python +def get_validator_from_indexed_deposit_data(indexed_deposit_data: IndexedDepositData) -> Validator: + amount = indexed_deposit_data.amount + effective_balance = min(amount - amount % EFFECTIVE_BALANCE_INCREMENT, MAX_EFFECTIVE_BALANCE) + + return Validator( + pubkey=indexed_deposit_data.pubkey, + withdrawal_credentials=indexed_deposit_data.withdrawal_credentials, + activation_eligibility_epoch=FAR_FUTURE_EPOCH, + activation_epoch=FAR_FUTURE_EPOCH, + exit_epoch=FAR_FUTURE_EPOCH, + withdrawable_epoch=FAR_FUTURE_EPOCH, + effective_balance=effective_balance, + ) +``` + +##### New `apply_pending_deposit` + +```python +def apply_pending_deposit(state: BeaconState, indexed_deposit_data: IndexedDepositData) -> None: + pubkey = indexed_deposit_data.pubkey + amount = indexed_deposit_data.amount + validator_pubkeys = [v.pubkey for v in state.validators] + if pubkey not in validator_pubkeys: + # Add validator and balance entries + state.validators.append(get_validator_from_indexed_deposit_data(indexed_deposit_data)) + state.balances.append(amount) + else: + # Increase balance by deposit amount + index = ValidatorIndex(validator_pubkeys.index(pubkey)) + increase_balance(state, index, amount) +``` + +#### New `process_pending_deposits` + +```python +def process_pending_deposits(state: BeaconState) -> None: + finalized_epoch = state.finalized_checkpoint.epoch + + next_pending_deposit_index = 0 + for pending_deposit in state.pending_deposits: + # Apply only finalized deposits + if pending_deposit.epoch > finalized_epoch + break + + # Skip already applied deposits + if pending_deposit.index >= state.eth1_deposit_index: + apply_pending_deposit(state, pending_deposit) + state.eth1_deposit_index += 1 + + next_pending_deposit_index += 1 + + state.pending_deposit = state.pending_deposit[next_pending_deposit_index:] +``` + +### Block processing + +```python +def process_block(state: BeaconState, block: BeaconBlock) -> None: + process_block_header(state, block) + if is_execution_enabled(state, block.body): + process_withdrawals(state, block.body.execution_payload) + process_execution_payload(state, block.body.execution_payload, EXECUTION_ENGINE) # [Modified in DepositsEIP] + process_deposit_receipts(state, block.body.execution_payload) # [New in DepositsEIP] + process_randao(state, block.body) + process_eth1_data(state, block.body) + process_operations(state, block.body) # [Modified in DepositsEIP] + process_sync_aggregate(state, block.body.sync_aggregate) +``` + +#### New `process_deposit_receipts` + +```python +def process_deposit_receipts(state: BeaconState, payload: ExecutionPayload) -> None: + current_epoch = get_current_epoch(state) + + for deposit_receipt in payload.deposit_receipts: + if pubkey not in validator_pubkeys: + # Verify the deposit signature (proof of possession) which is not checked by the deposit contract + deposit_message = DepositMessage( + pubkey=deposit_receipt.pubkey, + withdrawal_credentials=deposit_receipt.withdrawal_credentials, + amount=deposit_receipt.amount, + ) + domain = compute_domain(DOMAIN_DEPOSIT) # Fork-agnostic domain since deposits are valid across forks + signing_root = compute_signing_root(deposit_message, domain) + if not bls.Verify(pubkey, signing_root, deposit.data.signature): + continue + + pending_deposit = IndexedDepositData( + pubkey=deposit_receipt.pubkey, + withdrawal_credentials=deposit_receipt.withdrawal_credentials, + amount=deposit_receipt.amount, + index=deposit_receipt.index, + epoch=current_epoch, + ) + state.pending_deposits.append(pending_deposit) +``` + +#### Modified `process_execution_payload` + +*Note*: The function `process_execution_payload` is modified to use the new `ExecutionPayloadHeader` type. + +```python +def process_execution_payload(state: BeaconState, payload: ExecutionPayload, execution_engine: ExecutionEngine) -> None: + # Verify consistency of the parent hash with respect to the previous execution payload header + if is_merge_transition_complete(state): + assert payload.parent_hash == state.latest_execution_payload_header.block_hash + # Verify prev_randao + assert payload.prev_randao == get_randao_mix(state, get_current_epoch(state)) + # Verify timestamp + assert payload.timestamp == compute_timestamp_at_slot(state, state.slot) + # Verify the execution payload is valid + assert execution_engine.notify_new_payload(payload) + # Cache execution payload header + state.latest_execution_payload_header = ExecutionPayloadHeader( + parent_hash=payload.parent_hash, + fee_recipient=payload.fee_recipient, + state_root=payload.state_root, + receipts_root=payload.receipts_root, + logs_bloom=payload.logs_bloom, + prev_randao=payload.prev_randao, + block_number=payload.block_number, + gas_limit=payload.gas_limit, + gas_used=payload.gas_used, + timestamp=payload.timestamp, + extra_data=payload.extra_data, + base_fee_per_gas=payload.base_fee_per_gas, + block_hash=payload.block_hash, + transactions_root=hash_tree_root(payload.transactions), + withdrawals_root=hash_tree_root(payload.withdrawals), + deposit_receipts_root=hash_tree_root(payload.deposit_receipts), # [New in DepositsEIP] + ) +``` + +#### Modified `process_operations` + +*Note*: The function `process_operations` is modified to process `BLSToExecutionChange` operations included in the block. + +```python +def process_operations(state: BeaconState, body: BeaconBlockBody) -> None: + # Verify that outstanding deposits are processed up to the maximum number of deposits + unprocessed_deposits_count = max(0, state.eth1_data.deposit_count - state.eth1_deposit_index) # [New in DepositsEIP] + assert len(body.deposits) == min(MAX_DEPOSITS, unprocessed_deposits_count) # [Modified in DepositsEIP] + + def for_ops(operations: Sequence[Any], fn: Callable[[BeaconState, Any], None]) -> None: + for operation in operations: + fn(state, operation) + + for_ops(body.proposer_slashings, process_proposer_slashing) + for_ops(body.attester_slashings, process_attester_slashing) + for_ops(body.attestations, process_attestation) + for_ops(body.deposits, process_deposit) + for_ops(body.voluntary_exits, process_voluntary_exit) + for_ops(body.bls_to_execution_changes, process_bls_to_execution_change) # [New in Capella] +``` + +## Testing + +*Note*: The function `initialize_beacon_state_from_eth1` is modified for pure DepositsEIP testing only. +Modifications include: +1. Use `DEPOSITS_EIP_FORK_VERSION` as the previous and current fork version. +2. Utilize the DepositsEIP `BeaconBlockBody` when constructing the initial `latest_block_header`. + +```python +def initialize_beacon_state_from_eth1(eth1_block_hash: Hash32, + eth1_timestamp: uint64, + deposits: Sequence[Deposit], + execution_payload_header: ExecutionPayloadHeader=ExecutionPayloadHeader() + ) -> BeaconState: + fork = Fork( + previous_version=CAPELLA_FORK_VERSION, # [Modified in Capella] for testing only + current_version=CAPELLA_FORK_VERSION, # [Modified in Capella] + epoch=GENESIS_EPOCH, + ) + state = BeaconState( + genesis_time=eth1_timestamp + GENESIS_DELAY, + fork=fork, + eth1_data=Eth1Data(block_hash=eth1_block_hash, deposit_count=uint64(len(deposits))), + latest_block_header=BeaconBlockHeader(body_root=hash_tree_root(BeaconBlockBody())), + randao_mixes=[eth1_block_hash] * EPOCHS_PER_HISTORICAL_VECTOR, # Seed RANDAO with Eth1 entropy + ) + + # Process deposits + leaves = list(map(lambda deposit: deposit.data, deposits)) + for index, deposit in enumerate(deposits): + deposit_data_list = List[DepositData, 2**DEPOSIT_CONTRACT_TREE_DEPTH](*leaves[:index + 1]) + state.eth1_data.deposit_root = hash_tree_root(deposit_data_list) + process_deposit(state, deposit) + + # Process activations + for index, validator in enumerate(state.validators): + balance = state.balances[index] + validator.effective_balance = min(balance - balance % EFFECTIVE_BALANCE_INCREMENT, MAX_EFFECTIVE_BALANCE) + if validator.effective_balance == MAX_EFFECTIVE_BALANCE: + validator.activation_eligibility_epoch = GENESIS_EPOCH + validator.activation_epoch = GENESIS_EPOCH + + # Set genesis validators root for domain separation and chain versioning + state.genesis_validators_root = hash_tree_root(state.validators) + + # Fill in sync committees + # Note: A duplicate committee is assigned for the current and next committee at genesis + state.current_sync_committee = get_next_sync_committee(state) + state.next_sync_committee = get_next_sync_committee(state) + + # Initialize the execution payload header + state.latest_execution_payload_header = execution_payload_header + + return state +``` From b3c771c46db891812422e113e227b39c6757620c Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Wed, 7 Dec 2022 12:05:24 +0600 Subject: [PATCH 02/24] Preserve deposits per epoch boundary --- specs/deposits/beacon-chain.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/specs/deposits/beacon-chain.md b/specs/deposits/beacon-chain.md index bb6ebd2e8c..7e8b5bf82d 100644 --- a/specs/deposits/beacon-chain.md +++ b/specs/deposits/beacon-chain.md @@ -244,10 +244,14 @@ def process_pending_deposits(state: BeaconState) -> None: next_pending_deposit_index = 0 for pending_deposit in state.pending_deposits: + # Preserve deposits per epoch boundary + if next_pending_deposit_index >= MAX_DEPOSITS * SLOTS_PER_EPOCH: + break + # Apply only finalized deposits - if pending_deposit.epoch > finalized_epoch + if pending_deposit.epoch > finalized_epoch: break - + # Skip already applied deposits if pending_deposit.index >= state.eth1_deposit_index: apply_pending_deposit(state, pending_deposit) From 5ea983ac33e16963b845318fbfd1f178a516a8cb Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Wed, 7 Dec 2022 22:25:22 +0600 Subject: [PATCH 03/24] Fix toc, add more comments --- specs/deposits/beacon-chain.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/specs/deposits/beacon-chain.md b/specs/deposits/beacon-chain.md index 7e8b5bf82d..40d9864c2d 100644 --- a/specs/deposits/beacon-chain.md +++ b/specs/deposits/beacon-chain.md @@ -22,7 +22,7 @@ - [Epoch processing](#epoch-processing) - [Helper functions](#helper-functions) - [New `get_validator_from_indexed_deposit_data`](#new-get_validator_from_indexed_deposit_data) - - [New `apply_pending_deposit`](#new-apply_pending_deposit) + - [New `apply_indexed_deposit_data`](#new-apply_indexed_deposit_data) - [New `process_pending_deposits`](#new-process_pending_deposits) - [Block processing](#block-processing) - [New `process_deposit_receipts`](#new-process_deposit_receipts) @@ -219,10 +219,10 @@ def get_validator_from_indexed_deposit_data(indexed_deposit_data: IndexedDeposit ) ``` -##### New `apply_pending_deposit` +##### New `apply_indexed_deposit_data` ```python -def apply_pending_deposit(state: BeaconState, indexed_deposit_data: IndexedDepositData) -> None: +def apply_indexed_deposit_data(state: BeaconState, indexed_deposit_data: IndexedDepositData) -> None: pubkey = indexed_deposit_data.pubkey amount = indexed_deposit_data.amount validator_pubkeys = [v.pubkey for v in state.validators] @@ -254,7 +254,7 @@ def process_pending_deposits(state: BeaconState) -> None: # Skip already applied deposits if pending_deposit.index >= state.eth1_deposit_index: - apply_pending_deposit(state, pending_deposit) + apply_indexed_deposit_data(state, pending_deposit) state.eth1_deposit_index += 1 next_pending_deposit_index += 1 @@ -348,8 +348,9 @@ def process_execution_payload(state: BeaconState, payload: ExecutionPayload, exe ```python def process_operations(state: BeaconState, body: BeaconBlockBody) -> None: - # Verify that outstanding deposits are processed up to the maximum number of deposits + # Prevent potential underflow that is introduced by the mix of two deposit processing flows unprocessed_deposits_count = max(0, state.eth1_data.deposit_count - state.eth1_deposit_index) # [New in DepositsEIP] + # Verify that outstanding deposits are processed up to the maximum number of deposits assert len(body.deposits) == min(MAX_DEPOSITS, unprocessed_deposits_count) # [Modified in DepositsEIP] def for_ops(operations: Sequence[Any], fn: Callable[[BeaconState, Any], None]) -> None: From 8cc293c8697376bb594cca9f753f0b93c2aef634 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Wed, 7 Dec 2022 22:29:21 +0600 Subject: [PATCH 04/24] Fix wording --- specs/deposits/beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/deposits/beacon-chain.md b/specs/deposits/beacon-chain.md index 40d9864c2d..76423d656d 100644 --- a/specs/deposits/beacon-chain.md +++ b/specs/deposits/beacon-chain.md @@ -348,7 +348,7 @@ def process_execution_payload(state: BeaconState, payload: ExecutionPayload, exe ```python def process_operations(state: BeaconState, body: BeaconBlockBody) -> None: - # Prevent potential underflow that is introduced by the mix of two deposit processing flows + # Prevent potential underflow introduced by mixing two deposit processing flows unprocessed_deposits_count = max(0, state.eth1_data.deposit_count - state.eth1_deposit_index) # [New in DepositsEIP] # Verify that outstanding deposits are processed up to the maximum number of deposits assert len(body.deposits) == min(MAX_DEPOSITS, unprocessed_deposits_count) # [Modified in DepositsEIP] From 9d2a8f7d63f8a720c7723f04190972fc6e542e96 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Mon, 19 Dec 2022 17:47:56 +0600 Subject: [PATCH 05/24] Give deposits EIP a name --- specs/{deposits => eip6110}/beacon-chain.md | 28 ++++++++++----------- 1 file changed, 14 insertions(+), 14 deletions(-) rename specs/{deposits => eip6110}/beacon-chain.md (95%) diff --git a/specs/deposits/beacon-chain.md b/specs/eip6110/beacon-chain.md similarity index 95% rename from specs/deposits/beacon-chain.md rename to specs/eip6110/beacon-chain.md index 76423d656d..d1b64f5a68 100644 --- a/specs/deposits/beacon-chain.md +++ b/specs/eip6110/beacon-chain.md @@ -1,4 +1,4 @@ -# DepositEIP -- The Beacon Chain +# EIP-6110 -- The Beacon Chain ## Table of contents @@ -36,7 +36,7 @@ ## Introduction This is the beacon chain specification of in-protocol deposits processing mechanism. -This mechanism relies on the changes proposed by the corresponding EIP. +This mechanism relies on the changes proposed by [EIP-6110](http://eips.ethereum.org/EIPS/eip-6110). *Note:* This specification is under development and should be used with care. @@ -103,7 +103,7 @@ class ExecutionPayload(Container): block_hash: Hash32 transactions: List[Transaction, MAX_TRANSACTIONS_PER_PAYLOAD] withdrawals: List[Withdrawal, MAX_WITHDRAWALS_PER_PAYLOAD] - deposit_receipts: List[DepositReceipt, MAX_DEPOSIT_RECEIPTS_PER_PAYLOAD] # [New in DepositEIP] + deposit_receipts: List[DepositReceipt, MAX_DEPOSIT_RECEIPTS_PER_PAYLOAD] # [New in EIP-6110] ``` #### `ExecutionPayloadHeader` @@ -127,7 +127,7 @@ class ExecutionPayloadHeader(Container): block_hash: Hash32 transactions_root: Root withdrawals_root: Root - deposit_receipts_root: Root # [New in DepositEIP] + deposit_receipts_root: Root # [New in EIP-6110] ``` #### `BeaconState` @@ -173,7 +173,7 @@ class BeaconState(Container): # Withdrawals next_withdrawal_index: WithdrawalIndex next_withdrawal_validator_index: ValidatorIndex - # DepositsEIP + # EIP-6110 pending_deposits: List[IndexedDepositData, PENDING_DEPOSITS_LIMIT] ``` @@ -187,7 +187,7 @@ def process_epoch(state: BeaconState) -> None: process_inactivity_updates(state) process_rewards_and_penalties(state) # Run before registry and after finality updates - process_pending_deposits(state) # [New in DepositsEIP] + process_pending_deposits(state) # [New in EIP-6110] process_registry_updates(state) process_slashings(state) process_eth1_data_reset(state) @@ -269,11 +269,11 @@ def process_block(state: BeaconState, block: BeaconBlock) -> None: process_block_header(state, block) if is_execution_enabled(state, block.body): process_withdrawals(state, block.body.execution_payload) - process_execution_payload(state, block.body.execution_payload, EXECUTION_ENGINE) # [Modified in DepositsEIP] - process_deposit_receipts(state, block.body.execution_payload) # [New in DepositsEIP] + process_execution_payload(state, block.body.execution_payload, EXECUTION_ENGINE) # [Modified in EIP-6110] + process_deposit_receipts(state, block.body.execution_payload) # [New in EIP-6110] process_randao(state, block.body) process_eth1_data(state, block.body) - process_operations(state, block.body) # [Modified in DepositsEIP] + process_operations(state, block.body) # [Modified in EIP-6110] process_sync_aggregate(state, block.body.sync_aggregate) ``` @@ -338,7 +338,7 @@ def process_execution_payload(state: BeaconState, payload: ExecutionPayload, exe block_hash=payload.block_hash, transactions_root=hash_tree_root(payload.transactions), withdrawals_root=hash_tree_root(payload.withdrawals), - deposit_receipts_root=hash_tree_root(payload.deposit_receipts), # [New in DepositsEIP] + deposit_receipts_root=hash_tree_root(payload.deposit_receipts), # [New in EIP-6110] ) ``` @@ -349,9 +349,9 @@ def process_execution_payload(state: BeaconState, payload: ExecutionPayload, exe ```python def process_operations(state: BeaconState, body: BeaconBlockBody) -> None: # Prevent potential underflow introduced by mixing two deposit processing flows - unprocessed_deposits_count = max(0, state.eth1_data.deposit_count - state.eth1_deposit_index) # [New in DepositsEIP] + unprocessed_deposits_count = max(0, state.eth1_data.deposit_count - state.eth1_deposit_index) # [New in EIP-6110] # Verify that outstanding deposits are processed up to the maximum number of deposits - assert len(body.deposits) == min(MAX_DEPOSITS, unprocessed_deposits_count) # [Modified in DepositsEIP] + assert len(body.deposits) == min(MAX_DEPOSITS, unprocessed_deposits_count) # [Modified in EIP-6110] def for_ops(operations: Sequence[Any], fn: Callable[[BeaconState, Any], None]) -> None: for operation in operations: @@ -367,10 +367,10 @@ def process_operations(state: BeaconState, body: BeaconBlockBody) -> None: ## Testing -*Note*: The function `initialize_beacon_state_from_eth1` is modified for pure DepositsEIP testing only. +*Note*: The function `initialize_beacon_state_from_eth1` is modified for pure EIP-6110 testing only. Modifications include: 1. Use `DEPOSITS_EIP_FORK_VERSION` as the previous and current fork version. -2. Utilize the DepositsEIP `BeaconBlockBody` when constructing the initial `latest_block_header`. +2. Utilize the EIP-6110 `BeaconBlockBody` when constructing the initial `latest_block_header`. ```python def initialize_beacon_state_from_eth1(eth1_block_hash: Hash32, From 48f120c90e77dadcbce51d8ac08df0e23b5e6db1 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Wed, 21 Dec 2022 19:19:13 +0600 Subject: [PATCH 06/24] Set a higher limit to deposit receipts list --- specs/eip6110/beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/eip6110/beacon-chain.md b/specs/eip6110/beacon-chain.md index d1b64f5a68..cf9c046af3 100644 --- a/specs/eip6110/beacon-chain.md +++ b/specs/eip6110/beacon-chain.md @@ -52,7 +52,7 @@ This mechanism relies on the changes proposed by [EIP-6110](http://eips.ethereum | Name | Value | Description | | - | - | - | -| `MAX_DEPOSIT_RECEIPTS_PER_PAYLOAD` | `uint64(2**10)` (= 1,024) | Maximum number of deposit receipts allowed in each payload | +| `MAX_DEPOSIT_RECEIPTS_PER_PAYLOAD` | `uint64(2**13)` (= 8,192) | Maximum number of deposit receipts allowed in each payload | ## Containers From 6eb118d34afad8f8e01d2164e5d8d06ea0cae663 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Thu, 22 Dec 2022 16:58:34 +0600 Subject: [PATCH 07/24] Fix finality check in deposit processing --- specs/eip6110/beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/eip6110/beacon-chain.md b/specs/eip6110/beacon-chain.md index cf9c046af3..4a95fa95f7 100644 --- a/specs/eip6110/beacon-chain.md +++ b/specs/eip6110/beacon-chain.md @@ -249,7 +249,7 @@ def process_pending_deposits(state: BeaconState) -> None: break # Apply only finalized deposits - if pending_deposit.epoch > finalized_epoch: + if pending_deposit.epoch >= finalized_epoch: break # Skip already applied deposits From d5c7474d4d50982f9b6b714b0ba2a03103d0e1be Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Tue, 21 Feb 2023 17:31:27 +0600 Subject: [PATCH 08/24] Move EIP6110 to features --- specs/{ => _features}/eip6110/beacon-chain.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename specs/{ => _features}/eip6110/beacon-chain.md (100%) diff --git a/specs/eip6110/beacon-chain.md b/specs/_features/eip6110/beacon-chain.md similarity index 100% rename from specs/eip6110/beacon-chain.md rename to specs/_features/eip6110/beacon-chain.md From 08c7287387d456b96bfe952ab4952c6be1af1612 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Wed, 22 Feb 2023 18:33:05 +0600 Subject: [PATCH 09/24] Get rid of pending_deposits queue --- specs/_features/eip6110/beacon-chain.md | 254 ++++++++++++------------ 1 file changed, 130 insertions(+), 124 deletions(-) diff --git a/specs/_features/eip6110/beacon-chain.md b/specs/_features/eip6110/beacon-chain.md index 4a95fa95f7..3c81cfb891 100644 --- a/specs/_features/eip6110/beacon-chain.md +++ b/specs/_features/eip6110/beacon-chain.md @@ -7,6 +7,8 @@ - [Introduction](#introduction) +- [Constants](#constants) + - [Misc](#misc) - [Preset](#preset) - [State list lengths](#state-list-lengths) - [Execution](#execution) @@ -19,15 +21,12 @@ - [`ExecutionPayloadHeader`](#executionpayloadheader) - [`BeaconState`](#beaconstate) - [Beacon chain state transition function](#beacon-chain-state-transition-function) - - [Epoch processing](#epoch-processing) - - [Helper functions](#helper-functions) - - [New `get_validator_from_indexed_deposit_data`](#new-get_validator_from_indexed_deposit_data) - - [New `apply_indexed_deposit_data`](#new-apply_indexed_deposit_data) - - [New `process_pending_deposits`](#new-process_pending_deposits) - [Block processing](#block-processing) - - [New `process_deposit_receipts`](#new-process_deposit_receipts) - - [Modified `process_execution_payload`](#modified-process_execution_payload) - [Modified `process_operations`](#modified-process_operations) + - [New `get_validator_from_deposit_receipt`](#new-get_validator_from_deposit_receipt) + - [New `process_deposit_receipt`](#new-process_deposit_receipt) + - [Modified `process_deposit`](#modified-process_deposit) + - [Modified `process_execution_payload`](#modified-process_execution_payload) - [Testing](#testing) @@ -40,6 +39,16 @@ This mechanism relies on the changes proposed by [EIP-6110](http://eips.ethereum *Note:* This specification is under development and should be used with care. +## Constants + +The following values are (non-configurable) constants used throughout the specification. + +### Misc + +| Name | Value | +| - | - | +| `NOT_SET_DEPOSIT_RECEIPT_START_INDEX` | `2**64 - 1` | + ## Preset ### State list lengths @@ -174,43 +183,66 @@ class BeaconState(Container): next_withdrawal_index: WithdrawalIndex next_withdrawal_validator_index: ValidatorIndex # EIP-6110 - pending_deposits: List[IndexedDepositData, PENDING_DEPOSITS_LIMIT] + deposit_receipt_start_index: uint64 + deposit_receipt_next_index: uint64 ``` ## Beacon chain state transition function -### Epoch processing +### Block processing ```python -def process_epoch(state: BeaconState) -> None: - process_justification_and_finalization(state) - process_inactivity_updates(state) - process_rewards_and_penalties(state) - # Run before registry and after finality updates - process_pending_deposits(state) # [New in EIP-6110] - 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_roots_update(state) - process_participation_flag_updates(state) - process_sync_committee_updates(state) +def process_block(state: BeaconState, block: BeaconBlock) -> None: + process_block_header(state, block) + if is_execution_enabled(state, block.body): + process_withdrawals(state, block.body.execution_payload) + process_execution_payload(state, block.body.execution_payload, EXECUTION_ENGINE) # [Modified in EIP-6110] + process_randao(state, block.body) + process_eth1_data(state, block.body) + process_operations(state, block.body) # [Modified in EIP-6110] + process_sync_aggregate(state, block.body.sync_aggregate) ``` -#### Helper functions +#### Modified `process_operations` + +*Note*: The function `process_operations` is modified to process `DepositReceipt` operations included in the payload. + +```python +def process_operations(state: BeaconState, body: BeaconBlockBody) -> None: + # Prevent potential underflow introduced by mixing two deposit processing flows + unprocessed_deposits_count = max(0, state.eth1_data.deposit_count - state.eth1_deposit_index) # [New in EIP-6110] + # Verify that outstanding deposits are processed up to the maximum number of deposits + assert len(body.deposits) == min(MAX_DEPOSITS, unprocessed_deposits_count) # [Modified in EIP-6110] + + def for_ops(operations: Sequence[Any], fn: Callable[[BeaconState, Any], None]) -> None: + for operation in operations: + fn(state, operation) + + for_ops(body.proposer_slashings, process_proposer_slashing) + for_ops(body.attester_slashings, process_attester_slashing) + for_ops(body.attestations, process_attestation) + for_ops(body.deposits, process_deposit) # [Modified in EIP-6110] + for_ops(body.voluntary_exits, process_voluntary_exit) + for_ops(body.bls_to_execution_changes, process_bls_to_execution_change) + + # [New in EIP-6110] + if is_execution_enabled(state, body): + for_ops(body.execution_payload.deposit_receipts, process_deposit_receipt) + # Signify the end of transition to in-protocol deposits logic + if state.eth1_deposit_index >= state.deposit_receipt_start_index + state.eth1_deposit_index = state.deposit_receipt_next_index +``` -##### New `get_validator_from_indexed_deposit_data` +#### New `get_validator_from_deposit_receipt` ```python -def get_validator_from_indexed_deposit_data(indexed_deposit_data: IndexedDepositData) -> Validator: - amount = indexed_deposit_data.amount +def get_validator_from_deposit_receipt(deposit_receipt: DepositReceipt) -> Validator: + amount = deposit_receipt.amount effective_balance = min(amount - amount % EFFECTIVE_BALANCE_INCREMENT, MAX_EFFECTIVE_BALANCE) return Validator( - pubkey=indexed_deposit_data.pubkey, - withdrawal_credentials=indexed_deposit_data.withdrawal_credentials, + pubkey=deposit_receipt.pubkey, + withdrawal_credentials=deposit_receipt.withdrawal_credentials, activation_eligibility_epoch=FAR_FUTURE_EPOCH, activation_epoch=FAR_FUTURE_EPOCH, exit_epoch=FAR_FUTURE_EPOCH, @@ -219,91 +251,87 @@ def get_validator_from_indexed_deposit_data(indexed_deposit_data: IndexedDeposit ) ``` -##### New `apply_indexed_deposit_data` +#### New `process_deposit_receipt` ```python -def apply_indexed_deposit_data(state: BeaconState, indexed_deposit_data: IndexedDepositData) -> None: - pubkey = indexed_deposit_data.pubkey - amount = indexed_deposit_data.amount - validator_pubkeys = [v.pubkey for v in state.validators] +def process_deposit_receipt(state: BeaconState, deposit_receipt: DepositReceipt) -> None: + # Set deposit receipt start index + if state.deposit_receipt_start_index == NOT_SET_DEPOSIT_RECEIPT_START_INDEX: + state.deposit_receipt_start_index = deposit_receipt.index + + state.deposit_receipt_next_index = deposit_receipt.index + 1 + + pubkey = deposit_receipt.pubkey + amount = deposit_receipt.amount + validator_pubkeys = [validator.pubkey for validator in state.validators] if pubkey not in validator_pubkeys: - # Add validator and balance entries - state.validators.append(get_validator_from_indexed_deposit_data(indexed_deposit_data)) - state.balances.append(amount) + # Verify the deposit signature (proof of possession) which is not checked by the deposit contract + deposit_message = DepositMessage( + pubkey=deposit.data.pubkey, + withdrawal_credentials=deposit.data.withdrawal_credentials, + amount=deposit.data.amount, + ) + domain = compute_domain(DOMAIN_DEPOSIT) # Fork-agnostic domain since deposits are valid across forks + signing_root = compute_signing_root(deposit_message, domain) + # Initialize validator if the deposit signature is valid + if bls.Verify(pubkey, signing_root, deposit.data.signature): + state.validators.append(get_validator_from_deposit_receipt(deposit)) + state.balances.append(amount) + state.previous_epoch_participation.append(ParticipationFlags(0b0000_0000)) + state.current_epoch_participation.append(ParticipationFlags(0b0000_0000)) + state.inactivity_scores.append(uint64(0)) else: # Increase balance by deposit amount index = ValidatorIndex(validator_pubkeys.index(pubkey)) increase_balance(state, index, amount) ``` -#### New `process_pending_deposits` - -```python -def process_pending_deposits(state: BeaconState) -> None: - finalized_epoch = state.finalized_checkpoint.epoch - - next_pending_deposit_index = 0 - for pending_deposit in state.pending_deposits: - # Preserve deposits per epoch boundary - if next_pending_deposit_index >= MAX_DEPOSITS * SLOTS_PER_EPOCH: - break - - # Apply only finalized deposits - if pending_deposit.epoch >= finalized_epoch: - break +#### Modified `process_deposit` - # Skip already applied deposits - if pending_deposit.index >= state.eth1_deposit_index: - apply_indexed_deposit_data(state, pending_deposit) - state.eth1_deposit_index += 1 - - next_pending_deposit_index += 1 - - state.pending_deposit = state.pending_deposit[next_pending_deposit_index:] -``` - -### Block processing +*Note*: The function `process_deposit` is modified to prevent deposits from being processed in the second time (due to `process_deposit_receipt`). ```python -def process_block(state: BeaconState, block: BeaconBlock) -> None: - process_block_header(state, block) - if is_execution_enabled(state, block.body): - process_withdrawals(state, block.body.execution_payload) - process_execution_payload(state, block.body.execution_payload, EXECUTION_ENGINE) # [Modified in EIP-6110] - process_deposit_receipts(state, block.body.execution_payload) # [New in EIP-6110] - process_randao(state, block.body) - process_eth1_data(state, block.body) - process_operations(state, block.body) # [Modified in EIP-6110] - process_sync_aggregate(state, block.body.sync_aggregate) -``` - -#### New `process_deposit_receipts` +def process_deposit(state: BeaconState, deposit: Deposit) -> None: + # Skip already processed deposits + if state.eth1_deposit_index >= state.deposit_receipt_start_index: + return + + # Verify the Merkle branch + assert is_valid_merkle_branch( + leaf=hash_tree_root(deposit.data), + branch=deposit.proof, + depth=DEPOSIT_CONTRACT_TREE_DEPTH + 1, # Add 1 for the List length mix-in + index=state.eth1_deposit_index, + root=state.eth1_data.deposit_root, + ) -```python -def process_deposit_receipts(state: BeaconState, payload: ExecutionPayload) -> None: - current_epoch = get_current_epoch(state) - - for deposit_receipt in payload.deposit_receipts: - if pubkey not in validator_pubkeys: - # Verify the deposit signature (proof of possession) which is not checked by the deposit contract - deposit_message = DepositMessage( - pubkey=deposit_receipt.pubkey, - withdrawal_credentials=deposit_receipt.withdrawal_credentials, - amount=deposit_receipt.amount, - ) - domain = compute_domain(DOMAIN_DEPOSIT) # Fork-agnostic domain since deposits are valid across forks - signing_root = compute_signing_root(deposit_message, domain) - if not bls.Verify(pubkey, signing_root, deposit.data.signature): - continue - - pending_deposit = IndexedDepositData( - pubkey=deposit_receipt.pubkey, - withdrawal_credentials=deposit_receipt.withdrawal_credentials, - amount=deposit_receipt.amount, - index=deposit_receipt.index, - epoch=current_epoch, + # Deposits must be processed in order + state.eth1_deposit_index += 1 + + pubkey = deposit.data.pubkey + amount = deposit.data.amount + validator_pubkeys = [validator.pubkey for validator in state.validators] + if pubkey not in validator_pubkeys: + # Verify the deposit signature (proof of possession) which is not checked by the deposit contract + deposit_message = DepositMessage( + pubkey=deposit.data.pubkey, + withdrawal_credentials=deposit.data.withdrawal_credentials, + amount=deposit.data.amount, ) - state.pending_deposits.append(pending_deposit) + domain = compute_domain(DOMAIN_DEPOSIT) # Fork-agnostic domain since deposits are valid across forks + signing_root = compute_signing_root(deposit_message, domain) + # Initialize validator if the deposit signature is valid + if bls.Verify(pubkey, signing_root, deposit.data.signature): + state.validators.append(get_validator_from_deposit(deposit)) + state.balances.append(amount) + # [New in Altair] + state.previous_epoch_participation.append(ParticipationFlags(0b0000_0000)) + state.current_epoch_participation.append(ParticipationFlags(0b0000_0000)) + state.inactivity_scores.append(uint64(0)) + else: + # Increase balance by deposit amount + index = ValidatorIndex(validator_pubkeys.index(pubkey)) + increase_balance(state, index, amount) ``` #### Modified `process_execution_payload` @@ -342,29 +370,6 @@ def process_execution_payload(state: BeaconState, payload: ExecutionPayload, exe ) ``` -#### Modified `process_operations` - -*Note*: The function `process_operations` is modified to process `BLSToExecutionChange` operations included in the block. - -```python -def process_operations(state: BeaconState, body: BeaconBlockBody) -> None: - # Prevent potential underflow introduced by mixing two deposit processing flows - unprocessed_deposits_count = max(0, state.eth1_data.deposit_count - state.eth1_deposit_index) # [New in EIP-6110] - # Verify that outstanding deposits are processed up to the maximum number of deposits - assert len(body.deposits) == min(MAX_DEPOSITS, unprocessed_deposits_count) # [Modified in EIP-6110] - - def for_ops(operations: Sequence[Any], fn: Callable[[BeaconState, Any], None]) -> None: - for operation in operations: - fn(state, operation) - - for_ops(body.proposer_slashings, process_proposer_slashing) - for_ops(body.attester_slashings, process_attester_slashing) - for_ops(body.attestations, process_attestation) - for_ops(body.deposits, process_deposit) - for_ops(body.voluntary_exits, process_voluntary_exit) - for_ops(body.bls_to_execution_changes, process_bls_to_execution_change) # [New in Capella] -``` - ## Testing *Note*: The function `initialize_beacon_state_from_eth1` is modified for pure EIP-6110 testing only. @@ -389,6 +394,7 @@ def initialize_beacon_state_from_eth1(eth1_block_hash: Hash32, eth1_data=Eth1Data(block_hash=eth1_block_hash, deposit_count=uint64(len(deposits))), latest_block_header=BeaconBlockHeader(body_root=hash_tree_root(BeaconBlockBody())), randao_mixes=[eth1_block_hash] * EPOCHS_PER_HISTORICAL_VECTOR, # Seed RANDAO with Eth1 entropy + deposit_receipt_start_index = NOT_SET_DEPOSIT_RECEIPT_START_INDEX, ) # Process deposits From 23c10cfd7fc7c72127b2a7bc2f7e3561c978b511 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Thu, 23 Feb 2023 13:53:15 +0600 Subject: [PATCH 10/24] Remove state.deposit_receipt_next_index variable --- specs/_features/eip6110/beacon-chain.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/specs/_features/eip6110/beacon-chain.md b/specs/_features/eip6110/beacon-chain.md index 3c81cfb891..df92fb103a 100644 --- a/specs/_features/eip6110/beacon-chain.md +++ b/specs/_features/eip6110/beacon-chain.md @@ -184,7 +184,6 @@ class BeaconState(Container): next_withdrawal_validator_index: ValidatorIndex # EIP-6110 deposit_receipt_start_index: uint64 - deposit_receipt_next_index: uint64 ``` ## Beacon chain state transition function @@ -228,9 +227,6 @@ def process_operations(state: BeaconState, body: BeaconBlockBody) -> None: # [New in EIP-6110] if is_execution_enabled(state, body): for_ops(body.execution_payload.deposit_receipts, process_deposit_receipt) - # Signify the end of transition to in-protocol deposits logic - if state.eth1_deposit_index >= state.deposit_receipt_start_index - state.eth1_deposit_index = state.deposit_receipt_next_index ``` #### New `get_validator_from_deposit_receipt` @@ -259,7 +255,9 @@ def process_deposit_receipt(state: BeaconState, deposit_receipt: DepositReceipt) if state.deposit_receipt_start_index == NOT_SET_DEPOSIT_RECEIPT_START_INDEX: state.deposit_receipt_start_index = deposit_receipt.index - state.deposit_receipt_next_index = deposit_receipt.index + 1 + # Signify the end of transition to in-protocol deposit logic + if state.eth1_deposit_index >= state.deposit_receipt_start_index + state.eth1_deposit_index = deposit_receipt.index + 1 pubkey = deposit_receipt.pubkey amount = deposit_receipt.amount @@ -294,6 +292,7 @@ def process_deposit_receipt(state: BeaconState, deposit_receipt: DepositReceipt) def process_deposit(state: BeaconState, deposit: Deposit) -> None: # Skip already processed deposits if state.eth1_deposit_index >= state.deposit_receipt_start_index: + state.eth1_deposit_index += 1 return # Verify the Merkle branch From b22c89244a88a20853c82fbc2df00af545728e81 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Thu, 23 Feb 2023 14:09:01 +0600 Subject: [PATCH 11/24] Cosmetic renaming --- specs/_features/eip6110/beacon-chain.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/specs/_features/eip6110/beacon-chain.md b/specs/_features/eip6110/beacon-chain.md index df92fb103a..e7e0630b06 100644 --- a/specs/_features/eip6110/beacon-chain.md +++ b/specs/_features/eip6110/beacon-chain.md @@ -47,7 +47,7 @@ The following values are (non-configurable) constants used throughout the specif | Name | Value | | - | - | -| `NOT_SET_DEPOSIT_RECEIPT_START_INDEX` | `2**64 - 1` | +| `NOT_SET_DEPOSIT_RECEIPTS_START_INDEX` | `2**64 - 1` | ## Preset @@ -183,7 +183,7 @@ class BeaconState(Container): next_withdrawal_index: WithdrawalIndex next_withdrawal_validator_index: ValidatorIndex # EIP-6110 - deposit_receipt_start_index: uint64 + deposit_receipts_start_index: uint64 ``` ## Beacon chain state transition function @@ -252,11 +252,11 @@ def get_validator_from_deposit_receipt(deposit_receipt: DepositReceipt) -> Valid ```python def process_deposit_receipt(state: BeaconState, deposit_receipt: DepositReceipt) -> None: # Set deposit receipt start index - if state.deposit_receipt_start_index == NOT_SET_DEPOSIT_RECEIPT_START_INDEX: - state.deposit_receipt_start_index = deposit_receipt.index + if state.deposit_receipts_start_index == NOT_SET_DEPOSIT_RECEIPTS_START_INDEX: + state.deposit_receipts_start_index = deposit_receipt.index # Signify the end of transition to in-protocol deposit logic - if state.eth1_deposit_index >= state.deposit_receipt_start_index + if state.eth1_deposit_index >= state.deposit_receipts_start_index state.eth1_deposit_index = deposit_receipt.index + 1 pubkey = deposit_receipt.pubkey @@ -291,7 +291,7 @@ def process_deposit_receipt(state: BeaconState, deposit_receipt: DepositReceipt) ```python def process_deposit(state: BeaconState, deposit: Deposit) -> None: # Skip already processed deposits - if state.eth1_deposit_index >= state.deposit_receipt_start_index: + if state.eth1_deposit_index >= state.deposit_receipts_start_index: state.eth1_deposit_index += 1 return @@ -393,7 +393,7 @@ def initialize_beacon_state_from_eth1(eth1_block_hash: Hash32, eth1_data=Eth1Data(block_hash=eth1_block_hash, deposit_count=uint64(len(deposits))), latest_block_header=BeaconBlockHeader(body_root=hash_tree_root(BeaconBlockBody())), randao_mixes=[eth1_block_hash] * EPOCHS_PER_HISTORICAL_VECTOR, # Seed RANDAO with Eth1 entropy - deposit_receipt_start_index = NOT_SET_DEPOSIT_RECEIPT_START_INDEX, + deposit_receipts_start_index = NOT_SET_DEPOSIT_RECEIPTS_START_INDEX, ) # Process deposits From a1daac098ce4458acad75d0385a89e7146d3d20b Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Thu, 23 Feb 2023 22:34:32 +0800 Subject: [PATCH 12/24] Make EIP-6110 executable and fix linter errors --- .gitignore | 1 + setup.py | 29 ++++- specs/_features/eip6110/beacon-chain.md | 16 +-- specs/_features/eip6110/fork.md | 142 ++++++++++++++++++++++++ 4 files changed, 176 insertions(+), 12 deletions(-) create mode 100644 specs/_features/eip6110/fork.md diff --git a/.gitignore b/.gitignore index c49e6c006c..c56a658ce2 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,7 @@ tests/core/pyspec/eth2spec/altair/ tests/core/pyspec/eth2spec/bellatrix/ tests/core/pyspec/eth2spec/capella/ tests/core/pyspec/eth2spec/deneb/ +tests/core/pyspec/eth2spec/eip6110/ # coverage reports .htmlcov diff --git a/setup.py b/setup.py index 9c5488f126..b1158e440f 100644 --- a/setup.py +++ b/setup.py @@ -47,6 +47,7 @@ def installPackage(package: str): BELLATRIX = 'bellatrix' CAPELLA = 'capella' DENEB = 'deneb' +EIP6110 = 'eip6110' # The helper functions that are used when defining constants @@ -667,9 +668,22 @@ def hardcoded_custom_type_dep_constants(cls, spec_object) -> str: return {**super().hardcoded_custom_type_dep_constants(spec_object), **constants} +# +# EIP6110SpecBuilder +# +class EIP6110SpecBuilder(CapellaSpecBuilder): + fork: str = EIP6110 + + @classmethod + def imports(cls, preset_name: str): + return super().imports(preset_name) + f''' +from eth2spec.capella import {preset_name} as capella +''' + + spec_builders = { builder.fork: builder - for builder in (Phase0SpecBuilder, AltairSpecBuilder, BellatrixSpecBuilder, CapellaSpecBuilder, DenebSpecBuilder) + for builder in (Phase0SpecBuilder, AltairSpecBuilder, BellatrixSpecBuilder, CapellaSpecBuilder, DenebSpecBuilder, EIP6110SpecBuilder) } @@ -968,14 +982,14 @@ def finalize_options(self): if len(self.md_doc_paths) == 0: print("no paths were specified, using default markdown file paths for pyspec" " build (spec fork: %s)" % self.spec_fork) - if self.spec_fork in (PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB): + if self.spec_fork in (PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB, EIP6110): self.md_doc_paths = """ specs/phase0/beacon-chain.md specs/phase0/fork-choice.md specs/phase0/validator.md specs/phase0/weak-subjectivity.md """ - if self.spec_fork in (ALTAIR, BELLATRIX, CAPELLA, DENEB): + if self.spec_fork in (ALTAIR, BELLATRIX, CAPELLA, DENEB, EIP6110): self.md_doc_paths += """ specs/altair/light-client/full-node.md specs/altair/light-client/light-client.md @@ -987,7 +1001,7 @@ def finalize_options(self): specs/altair/validator.md specs/altair/p2p-interface.md """ - if self.spec_fork in (BELLATRIX, CAPELLA, DENEB): + if self.spec_fork in (BELLATRIX, CAPELLA, DENEB, EIP6110): self.md_doc_paths += """ specs/bellatrix/beacon-chain.md specs/bellatrix/fork.md @@ -996,7 +1010,7 @@ def finalize_options(self): specs/bellatrix/p2p-interface.md sync/optimistic.md """ - if self.spec_fork in (CAPELLA, DENEB): + if self.spec_fork in (CAPELLA, DENEB, EIP6110): self.md_doc_paths += """ specs/capella/light-client/fork.md specs/capella/light-client/full-node.md @@ -1021,6 +1035,11 @@ def finalize_options(self): specs/deneb/p2p-interface.md specs/deneb/validator.md """ + if self.spec_fork == EIP6110: + self.md_doc_paths += """ + specs/_features/eip6110/beacon-chain.md + specs/_features/eip6110/fork.md + """ if len(self.md_doc_paths) == 0: raise Exception('no markdown files specified, and spec fork "%s" is unknown', self.spec_fork) diff --git a/specs/_features/eip6110/beacon-chain.md b/specs/_features/eip6110/beacon-chain.md index e7e0630b06..1daa44fb14 100644 --- a/specs/_features/eip6110/beacon-chain.md +++ b/specs/_features/eip6110/beacon-chain.md @@ -182,6 +182,8 @@ class BeaconState(Container): # Withdrawals next_withdrawal_index: WithdrawalIndex next_withdrawal_validator_index: ValidatorIndex + # Deep history valid from Capella onwards + historical_summaries: List[HistoricalSummary, HISTORICAL_ROOTS_LIMIT] # EIP-6110 deposit_receipts_start_index: uint64 ``` @@ -256,7 +258,7 @@ def process_deposit_receipt(state: BeaconState, deposit_receipt: DepositReceipt) state.deposit_receipts_start_index = deposit_receipt.index # Signify the end of transition to in-protocol deposit logic - if state.eth1_deposit_index >= state.deposit_receipts_start_index + if state.eth1_deposit_index >= state.deposit_receipts_start_index: state.eth1_deposit_index = deposit_receipt.index + 1 pubkey = deposit_receipt.pubkey @@ -265,15 +267,15 @@ def process_deposit_receipt(state: BeaconState, deposit_receipt: DepositReceipt) if pubkey not in validator_pubkeys: # Verify the deposit signature (proof of possession) which is not checked by the deposit contract deposit_message = DepositMessage( - pubkey=deposit.data.pubkey, - withdrawal_credentials=deposit.data.withdrawal_credentials, - amount=deposit.data.amount, + pubkey=deposit_receipt.pubkey, + withdrawal_credentials=deposit_receipt.withdrawal_credentials, + amount=deposit_receipt.amount, ) domain = compute_domain(DOMAIN_DEPOSIT) # Fork-agnostic domain since deposits are valid across forks signing_root = compute_signing_root(deposit_message, domain) # Initialize validator if the deposit signature is valid - if bls.Verify(pubkey, signing_root, deposit.data.signature): - state.validators.append(get_validator_from_deposit_receipt(deposit)) + if bls.Verify(pubkey, signing_root, deposit_receipt.signature): + state.validators.append(get_validator_from_deposit_receipt(deposit_receipt)) state.balances.append(amount) state.previous_epoch_participation.append(ParticipationFlags(0b0000_0000)) state.current_epoch_participation.append(ParticipationFlags(0b0000_0000)) @@ -393,7 +395,7 @@ def initialize_beacon_state_from_eth1(eth1_block_hash: Hash32, eth1_data=Eth1Data(block_hash=eth1_block_hash, deposit_count=uint64(len(deposits))), latest_block_header=BeaconBlockHeader(body_root=hash_tree_root(BeaconBlockBody())), randao_mixes=[eth1_block_hash] * EPOCHS_PER_HISTORICAL_VECTOR, # Seed RANDAO with Eth1 entropy - deposit_receipts_start_index = NOT_SET_DEPOSIT_RECEIPTS_START_INDEX, + deposit_receipts_start_index=NOT_SET_DEPOSIT_RECEIPTS_START_INDEX, ) # Process deposits diff --git a/specs/_features/eip6110/fork.md b/specs/_features/eip6110/fork.md new file mode 100644 index 0000000000..3a8de1b8dc --- /dev/null +++ b/specs/_features/eip6110/fork.md @@ -0,0 +1,142 @@ +# EIP-6110 -- 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 Deneb](#fork-to-deneb) + - [Fork trigger](#fork-trigger) + - [Upgrading the state](#upgrading-the-state) + + + +## Introduction + +This document describes the process of Deneb upgrade. + +## Configuration + +Warning: this configuration is not definitive. + +| Name | Value | +| - | - | +| `EIP6110_FORK_VERSION` | `Version('0x05000000')` | +| `EIP6110_FORK_EPOCH` | `Epoch(18446744073709551615)` **TBD** | + +## Helper functions + +### Misc + +#### Modified `compute_fork_version` + +```python +def compute_fork_version(epoch: Epoch) -> Version: + """ + Return the fork version at the given ``epoch``. + """ + if epoch >= EIP6110_FORK_EPOCH: + return EIP6110_FORK_EPOCH + 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 Deneb + +### Fork trigger + +TBD. This fork is defined for testing purposes, the EIP may be combined with other consensus-layer upgrade. +For now, we assume the condition will be triggered at epoch `EIP6110_FORK_EPOCH`. + +Note that for the pure Deneb networks, we don't apply `upgrade_to_eip6110` since it starts with Deneb version logic. + +### Upgrading the state + +If `state.slot % SLOTS_PER_EPOCH == 0` and `compute_epoch_at_slot(state.slot) == EIP6110_FORK_EPOCH`, +an irregular state change is made to upgrade to EIP-6110. + +```python +def upgrade_to_eip6110(pre: capella.BeaconState) -> BeaconState: + epoch = capella.get_current_epoch(pre) + latest_execution_payload_header = ExecutionPayloadHeader( + parent_hash=pre.latest_execution_payload_header.parent_hash, + fee_recipient=pre.latest_execution_payload_header.fee_recipient, + state_root=pre.latest_execution_payload_header.state_root, + receipts_root=pre.latest_execution_payload_header.receipts_root, + logs_bloom=pre.latest_execution_payload_header.logs_bloom, + prev_randao=pre.latest_execution_payload_header.prev_randao, + block_number=pre.latest_execution_payload_header.block_number, + gas_limit=pre.latest_execution_payload_header.gas_limit, + gas_used=pre.latest_execution_payload_header.gas_used, + timestamp=pre.latest_execution_payload_header.timestamp, + extra_data=pre.latest_execution_payload_header.extra_data, + base_fee_per_gas=pre.latest_execution_payload_header.base_fee_per_gas, + block_hash=pre.latest_execution_payload_header.block_hash, + transactions_root=pre.latest_execution_payload_header.transactions_root, + withdrawals_root=pre.latest_execution_payload_header.withdrawals_root, + deposit_receipts_root=Root(), # [New in EIP-6110] + ) + 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=EIP6110_FORK_VERSION, # [Modified in EIP-6110] + 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, # [Modified in EIP-6110] + # 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, + # EIP-6110 + deposit_receipts_start_index=0, # [New in EIP-6110] + ) + + return post +``` From 7d6831ec8691607b1b4b30d4b1b9cd0af69b16a5 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Thu, 23 Feb 2023 21:23:52 +0600 Subject: [PATCH 13/24] Fix initialize_beacon_state_from_eth1 definition --- specs/_features/eip6110/beacon-chain.md | 9 +++++---- specs/_features/eip6110/fork.md | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/specs/_features/eip6110/beacon-chain.md b/specs/_features/eip6110/beacon-chain.md index 1daa44fb14..62a8664769 100644 --- a/specs/_features/eip6110/beacon-chain.md +++ b/specs/_features/eip6110/beacon-chain.md @@ -375,8 +375,9 @@ def process_execution_payload(state: BeaconState, payload: ExecutionPayload, exe *Note*: The function `initialize_beacon_state_from_eth1` is modified for pure EIP-6110 testing only. Modifications include: -1. Use `DEPOSITS_EIP_FORK_VERSION` as the previous and current fork version. +1. Use `EIP6110_FORK_VERSION` as the previous and current fork version. 2. Utilize the EIP-6110 `BeaconBlockBody` when constructing the initial `latest_block_header`. +3. Add `deposit_receipts_start_index` variable to the genesis state initialization. ```python def initialize_beacon_state_from_eth1(eth1_block_hash: Hash32, @@ -385,8 +386,8 @@ def initialize_beacon_state_from_eth1(eth1_block_hash: Hash32, execution_payload_header: ExecutionPayloadHeader=ExecutionPayloadHeader() ) -> BeaconState: fork = Fork( - previous_version=CAPELLA_FORK_VERSION, # [Modified in Capella] for testing only - current_version=CAPELLA_FORK_VERSION, # [Modified in Capella] + previous_version=EIP6110_FORK_VERSION, # [Modified in EIP6110] for testing only + current_version=EIP6110_FORK_VERSION, # [Modified in EIP6110] epoch=GENESIS_EPOCH, ) state = BeaconState( @@ -395,7 +396,7 @@ def initialize_beacon_state_from_eth1(eth1_block_hash: Hash32, eth1_data=Eth1Data(block_hash=eth1_block_hash, deposit_count=uint64(len(deposits))), latest_block_header=BeaconBlockHeader(body_root=hash_tree_root(BeaconBlockBody())), randao_mixes=[eth1_block_hash] * EPOCHS_PER_HISTORICAL_VECTOR, # Seed RANDAO with Eth1 entropy - deposit_receipts_start_index=NOT_SET_DEPOSIT_RECEIPTS_START_INDEX, + deposit_receipts_start_index=NOT_SET_DEPOSIT_RECEIPTS_START_INDEX, # [New in EIP6110] ) # Process deposits diff --git a/specs/_features/eip6110/fork.md b/specs/_features/eip6110/fork.md index 3a8de1b8dc..6e89788872 100644 --- a/specs/_features/eip6110/fork.md +++ b/specs/_features/eip6110/fork.md @@ -135,7 +135,7 @@ def upgrade_to_eip6110(pre: capella.BeaconState) -> BeaconState: # Deep history valid from Capella onwards historical_summaries=pre.historical_summaries, # EIP-6110 - deposit_receipts_start_index=0, # [New in EIP-6110] + deposit_receipts_start_index=NOT_SET_DEPOSIT_RECEIPTS_START_INDEX, # [New in EIP-6110] ) return post From 703fdfc7c7cc5945fa8d451498164b1e641b625a Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Thu, 23 Feb 2023 21:31:19 +0600 Subject: [PATCH 14/24] Fix linter --- specs/_features/eip6110/beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/_features/eip6110/beacon-chain.md b/specs/_features/eip6110/beacon-chain.md index 62a8664769..827637221c 100644 --- a/specs/_features/eip6110/beacon-chain.md +++ b/specs/_features/eip6110/beacon-chain.md @@ -396,7 +396,7 @@ def initialize_beacon_state_from_eth1(eth1_block_hash: Hash32, eth1_data=Eth1Data(block_hash=eth1_block_hash, deposit_count=uint64(len(deposits))), latest_block_header=BeaconBlockHeader(body_root=hash_tree_root(BeaconBlockBody())), randao_mixes=[eth1_block_hash] * EPOCHS_PER_HISTORICAL_VECTOR, # Seed RANDAO with Eth1 entropy - deposit_receipts_start_index=NOT_SET_DEPOSIT_RECEIPTS_START_INDEX, # [New in EIP6110] + deposit_receipts_start_index=NOT_SET_DEPOSIT_RECEIPTS_START_INDEX, # [New in EIP6110] ) # Process deposits From fda0eae70af88658f1bff373e3bb7bb7b2fb8c67 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Thu, 23 Feb 2023 23:41:57 +0800 Subject: [PATCH 15/24] Add EIP6110 to pylint and mypy scope --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index d4259b2fe9..b17baf7c89 100644 --- a/Makefile +++ b/Makefile @@ -142,8 +142,8 @@ codespell: lint: pyspec . venv/bin/activate; cd $(PY_SPEC_DIR); \ flake8 --config $(LINTER_CONFIG_FILE) ./eth2spec \ - && pylint --rcfile $(LINTER_CONFIG_FILE) ./eth2spec/phase0 ./eth2spec/altair ./eth2spec/bellatrix ./eth2spec/capella ./eth2spec/deneb \ - && mypy --config-file $(LINTER_CONFIG_FILE) -p eth2spec.phase0 -p eth2spec.altair -p eth2spec.bellatrix -p eth2spec.capella -p eth2spec.deneb + && pylint --rcfile $(LINTER_CONFIG_FILE) ./eth2spec/phase0 ./eth2spec/altair ./eth2spec/bellatrix ./eth2spec/capella ./eth2spec/deneb ./eth2spec/eip6110 \ + && mypy --config-file $(LINTER_CONFIG_FILE) -p eth2spec.phase0 -p eth2spec.altair -p eth2spec.bellatrix -p eth2spec.capella -p eth2spec.deneb -p eth2spec.eip6110 lint_generators: pyspec . venv/bin/activate; cd $(TEST_GENERATORS_DIR); \ From 9d690a4cb298b34da59960cdf24891608b817ad0 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Fri, 24 Feb 2023 17:58:10 +0800 Subject: [PATCH 16/24] Fix typo --- specs/_features/eip6110/fork.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/specs/_features/eip6110/fork.md b/specs/_features/eip6110/fork.md index 6e89788872..a932ecb24e 100644 --- a/specs/_features/eip6110/fork.md +++ b/specs/_features/eip6110/fork.md @@ -12,7 +12,7 @@ - [Helper functions](#helper-functions) - [Misc](#misc) - [Modified `compute_fork_version`](#modified-compute_fork_version) -- [Fork to Deneb](#fork-to-deneb) +- [Fork to EIP-6110](#fork-to-eip-6110) - [Fork trigger](#fork-trigger) - [Upgrading the state](#upgrading-the-state) @@ -20,7 +20,7 @@ ## Introduction -This document describes the process of Deneb upgrade. +This document describes the process of EIP-6110 upgrade. ## Configuration @@ -53,14 +53,14 @@ def compute_fork_version(epoch: Epoch) -> Version: return GENESIS_FORK_VERSION ``` -## Fork to Deneb +## Fork to EIP-6110 ### Fork trigger TBD. This fork is defined for testing purposes, the EIP may be combined with other consensus-layer upgrade. For now, we assume the condition will be triggered at epoch `EIP6110_FORK_EPOCH`. -Note that for the pure Deneb networks, we don't apply `upgrade_to_eip6110` since it starts with Deneb version logic. +Note that for the pure EIP-6110 networks, we don't apply `upgrade_to_eip6110` since it starts with EIP-6110 version logic. ### Upgrading the state From de5be633996fca73355a84200f21c58bfaafa656 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Tue, 28 Feb 2023 16:14:47 +0600 Subject: [PATCH 17/24] Apply suggestions from code review Co-authored-by: Hsiao-Wei Wang --- specs/_features/eip6110/beacon-chain.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specs/_features/eip6110/beacon-chain.md b/specs/_features/eip6110/beacon-chain.md index 827637221c..7fe2218ae9 100644 --- a/specs/_features/eip6110/beacon-chain.md +++ b/specs/_features/eip6110/beacon-chain.md @@ -47,7 +47,7 @@ The following values are (non-configurable) constants used throughout the specif | Name | Value | | - | - | -| `NOT_SET_DEPOSIT_RECEIPTS_START_INDEX` | `2**64 - 1` | +| `NOT_SET_DEPOSIT_RECEIPTS_START_INDEX` | `uint64(2**64 - 1)` | ## Preset @@ -292,6 +292,7 @@ def process_deposit_receipt(state: BeaconState, deposit_receipt: DepositReceipt) ```python def process_deposit(state: BeaconState, deposit: Deposit) -> None: + # [New in EIP-6110] # Skip already processed deposits if state.eth1_deposit_index >= state.deposit_receipts_start_index: state.eth1_deposit_index += 1 @@ -325,7 +326,6 @@ def process_deposit(state: BeaconState, deposit: Deposit) -> None: if bls.Verify(pubkey, signing_root, deposit.data.signature): state.validators.append(get_validator_from_deposit(deposit)) state.balances.append(amount) - # [New in Altair] state.previous_epoch_participation.append(ParticipationFlags(0b0000_0000)) state.current_epoch_participation.append(ParticipationFlags(0b0000_0000)) state.inactivity_scores.append(uint64(0)) From fae77eb53dc45be4e2a346cc5a57e7f6a1e007e9 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Tue, 28 Feb 2023 16:36:46 +0600 Subject: [PATCH 18/24] Apply @hwwhww suggestions --- specs/_features/eip6110/beacon-chain.md | 122 ++++++++++-------------- 1 file changed, 53 insertions(+), 69 deletions(-) diff --git a/specs/_features/eip6110/beacon-chain.md b/specs/_features/eip6110/beacon-chain.md index 7fe2218ae9..160eee6585 100644 --- a/specs/_features/eip6110/beacon-chain.md +++ b/specs/_features/eip6110/beacon-chain.md @@ -10,12 +10,10 @@ - [Constants](#constants) - [Misc](#misc) - [Preset](#preset) - - [State list lengths](#state-list-lengths) - [Execution](#execution) - [Containers](#containers) - [New containers](#new-containers) - [`DepositReceipt`](#depositreceipt) - - [`IndexedDepositData`](#indexeddepositdata) - [Extended Containers](#extended-containers) - [`ExecutionPayload`](#executionpayload) - [`ExecutionPayloadHeader`](#executionpayloadheader) @@ -23,7 +21,8 @@ - [Beacon chain state transition function](#beacon-chain-state-transition-function) - [Block processing](#block-processing) - [Modified `process_operations`](#modified-process_operations) - - [New `get_validator_from_deposit_receipt`](#new-get_validator_from_deposit_receipt) + - [New `get_validator_from_deposit_data`](#new-get_validator_from_deposit_data) + - [New `apply_deposit`](#new-apply_deposit) - [New `process_deposit_receipt`](#new-process_deposit_receipt) - [Modified `process_deposit`](#modified-process_deposit) - [Modified `process_execution_payload`](#modified-process_execution_payload) @@ -51,12 +50,6 @@ The following values are (non-configurable) constants used throughout the specif ## Preset -### State list lengths - -| Name | Value | -| - | - | -| `PENDING_DEPOSITS_LIMIT` | `2**32` (= 4,294,967,296) | - ### Execution | Name | Value | Description | @@ -78,17 +71,6 @@ class DepositReceipt(Container): index: uint64 ``` -#### `IndexedDepositData` - -```python -class IndexedDepositData(Container): - pubkey: BLSPubkey - withdrawal_credentials: Bytes32 - amount: Gwei - index: uint64 - epoch: Epoch -``` - ### Extended Containers #### `ExecutionPayload` @@ -211,9 +193,11 @@ def process_block(state: BeaconState, block: BeaconBlock) -> None: ```python def process_operations(state: BeaconState, body: BeaconBlockBody) -> None: # Prevent potential underflow introduced by mixing two deposit processing flows - unprocessed_deposits_count = max(0, state.eth1_data.deposit_count - state.eth1_deposit_index) # [New in EIP-6110] - # Verify that outstanding deposits are processed up to the maximum number of deposits - assert len(body.deposits) == min(MAX_DEPOSITS, unprocessed_deposits_count) # [Modified in EIP-6110] + # [New in EIP-6110] + if state.eth1_data.deposit_count > state.eth1_deposit_index: + assert len(body.deposits) == min(MAX_DEPOSITS, state.eth1_data.deposit_count - state.eth1_deposit_index) + else: + assert len(body.deposits) == 0 def for_ops(operations: Sequence[Any], fn: Callable[[BeaconState, Any], None]) -> None: for operation in operations: @@ -231,16 +215,15 @@ def process_operations(state: BeaconState, body: BeaconBlockBody) -> None: for_ops(body.execution_payload.deposit_receipts, process_deposit_receipt) ``` -#### New `get_validator_from_deposit_receipt` +#### New `get_validator_from_deposit_data` ```python -def get_validator_from_deposit_receipt(deposit_receipt: DepositReceipt) -> Validator: - amount = deposit_receipt.amount +def get_validator_from_deposit_data(pubkey: BLSPubkey, withdrawal_credentials: Bytes32, amount: uint64) -> Validator: effective_balance = min(amount - amount % EFFECTIVE_BALANCE_INCREMENT, MAX_EFFECTIVE_BALANCE) return Validator( - pubkey=deposit_receipt.pubkey, - withdrawal_credentials=deposit_receipt.withdrawal_credentials, + pubkey=pubkey, + withdrawal_credentials=withdrawal_credentials, activation_eligibility_epoch=FAR_FUTURE_EPOCH, activation_epoch=FAR_FUTURE_EPOCH, exit_epoch=FAR_FUTURE_EPOCH, @@ -249,33 +232,28 @@ def get_validator_from_deposit_receipt(deposit_receipt: DepositReceipt) -> Valid ) ``` -#### New `process_deposit_receipt` +#### New `apply_deposit` ```python -def process_deposit_receipt(state: BeaconState, deposit_receipt: DepositReceipt) -> None: - # Set deposit receipt start index - if state.deposit_receipts_start_index == NOT_SET_DEPOSIT_RECEIPTS_START_INDEX: - state.deposit_receipts_start_index = deposit_receipt.index - - # Signify the end of transition to in-protocol deposit logic - if state.eth1_deposit_index >= state.deposit_receipts_start_index: - state.eth1_deposit_index = deposit_receipt.index + 1 - - pubkey = deposit_receipt.pubkey - amount = deposit_receipt.amount +def apply_deposit(state: BeaconState, + pubkey: BLSPubkey, + withdrawal_credentials: Bytes32, + amount: uint64, + signature: BLSSignature, + ) -> None: validator_pubkeys = [validator.pubkey for validator in state.validators] if pubkey not in validator_pubkeys: # Verify the deposit signature (proof of possession) which is not checked by the deposit contract deposit_message = DepositMessage( - pubkey=deposit_receipt.pubkey, - withdrawal_credentials=deposit_receipt.withdrawal_credentials, - amount=deposit_receipt.amount, + pubkey=pubkey, + withdrawal_credentials=withdrawal_credentials, + amount=amount, ) domain = compute_domain(DOMAIN_DEPOSIT) # Fork-agnostic domain since deposits are valid across forks signing_root = compute_signing_root(deposit_message, domain) # Initialize validator if the deposit signature is valid - if bls.Verify(pubkey, signing_root, deposit_receipt.signature): - state.validators.append(get_validator_from_deposit_receipt(deposit_receipt)) + if bls.Verify(pubkey, signing_root, signature): + state.validators.append(get_validator_from_deposit_data(pubkey, withdrawal_credentials, amount)) state.balances.append(amount) state.previous_epoch_participation.append(ParticipationFlags(0b0000_0000)) state.current_epoch_participation.append(ParticipationFlags(0b0000_0000)) @@ -286,9 +264,31 @@ def process_deposit_receipt(state: BeaconState, deposit_receipt: DepositReceipt) increase_balance(state, index, amount) ``` + +#### New `process_deposit_receipt` + +```python +def process_deposit_receipt(state: BeaconState, deposit_receipt: DepositReceipt) -> None: + # Set deposit receipt start index + if state.deposit_receipts_start_index == NOT_SET_DEPOSIT_RECEIPTS_START_INDEX: + state.deposit_receipts_start_index = deposit_receipt.index + + # Signify the end of transition to in-protocol deposit logic + if state.eth1_deposit_index >= state.deposit_receipts_start_index: + state.eth1_deposit_index = deposit_receipt.index + 1 + + apply_deposit( + state=state, + pubkey=deposit_receipt.pubkey, + withdrawal_credentials=deposit_receipt.withdrawal_credentials, + amount=deposit_receipt.amount, + signature=deposit_receipt.signature, + ) +``` + #### Modified `process_deposit` -*Note*: The function `process_deposit` is modified to prevent deposits from being processed in the second time (due to `process_deposit_receipt`). +*Note*: The function `process_deposit` is modified to prevent deposits from being processed the second time (due to `process_deposit_receipt`). ```python def process_deposit(state: BeaconState, deposit: Deposit) -> None: @@ -310,29 +310,13 @@ def process_deposit(state: BeaconState, deposit: Deposit) -> None: # Deposits must be processed in order state.eth1_deposit_index += 1 - pubkey = deposit.data.pubkey - amount = deposit.data.amount - validator_pubkeys = [validator.pubkey for validator in state.validators] - if pubkey not in validator_pubkeys: - # Verify the deposit signature (proof of possession) which is not checked by the deposit contract - deposit_message = DepositMessage( - pubkey=deposit.data.pubkey, - withdrawal_credentials=deposit.data.withdrawal_credentials, - amount=deposit.data.amount, - ) - domain = compute_domain(DOMAIN_DEPOSIT) # Fork-agnostic domain since deposits are valid across forks - signing_root = compute_signing_root(deposit_message, domain) - # Initialize validator if the deposit signature is valid - if bls.Verify(pubkey, signing_root, deposit.data.signature): - state.validators.append(get_validator_from_deposit(deposit)) - state.balances.append(amount) - state.previous_epoch_participation.append(ParticipationFlags(0b0000_0000)) - state.current_epoch_participation.append(ParticipationFlags(0b0000_0000)) - state.inactivity_scores.append(uint64(0)) - else: - # Increase balance by deposit amount - index = ValidatorIndex(validator_pubkeys.index(pubkey)) - increase_balance(state, index, amount) + apply_deposit( + state=state, + pubkey=deposit.data.pubkey, + withdrawal_credentials=deposit.data.withdrawal_credentials, + amount=deposit.data.amount, + signature=deposit.data.signature, + ) ``` #### Modified `process_execution_payload` From 7bb65f88d91f5f00ec0d19baf665f0f59c9134e0 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Tue, 28 Feb 2023 17:18:07 +0600 Subject: [PATCH 19/24] Cosmetic fix --- specs/_features/eip6110/beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/_features/eip6110/beacon-chain.md b/specs/_features/eip6110/beacon-chain.md index 160eee6585..4c7ca790d5 100644 --- a/specs/_features/eip6110/beacon-chain.md +++ b/specs/_features/eip6110/beacon-chain.md @@ -192,8 +192,8 @@ def process_block(state: BeaconState, block: BeaconBlock) -> None: ```python def process_operations(state: BeaconState, body: BeaconBlockBody) -> None: - # Prevent potential underflow introduced by mixing two deposit processing flows # [New in EIP-6110] + # Prevent potential underflow introduced by mixing two deposit processing flows if state.eth1_data.deposit_count > state.eth1_deposit_index: assert len(body.deposits) == min(MAX_DEPOSITS, state.eth1_data.deposit_count - state.eth1_deposit_index) else: From c445fa9b3762f665e08a2d558c80abf78178dc0d Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Thu, 2 Mar 2023 15:50:08 +0600 Subject: [PATCH 20/24] Apply suggestions from code review Co-authored-by: Danny Ryan --- specs/_features/eip6110/beacon-chain.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/specs/_features/eip6110/beacon-chain.md b/specs/_features/eip6110/beacon-chain.md index 4c7ca790d5..ddaca21a24 100644 --- a/specs/_features/eip6110/beacon-chain.md +++ b/specs/_features/eip6110/beacon-chain.md @@ -46,7 +46,7 @@ The following values are (non-configurable) constants used throughout the specif | Name | Value | | - | - | -| `NOT_SET_DEPOSIT_RECEIPTS_START_INDEX` | `uint64(2**64 - 1)` | +| `UNSET_DEPOSIT_RECEIPTS_START_INDEX` | `uint64(2**64 - 1)` | ## Preset @@ -166,7 +166,7 @@ class BeaconState(Container): next_withdrawal_validator_index: ValidatorIndex # Deep history valid from Capella onwards historical_summaries: List[HistoricalSummary, HISTORICAL_ROOTS_LIMIT] - # EIP-6110 + # [New in EIP-6110] deposit_receipts_start_index: uint64 ``` @@ -239,8 +239,7 @@ def apply_deposit(state: BeaconState, pubkey: BLSPubkey, withdrawal_credentials: Bytes32, amount: uint64, - signature: BLSSignature, - ) -> None: + signature: BLSSignature) -> None: validator_pubkeys = [validator.pubkey for validator in state.validators] if pubkey not in validator_pubkeys: # Verify the deposit signature (proof of possession) which is not checked by the deposit contract From 13f3654296546f63a1b60c5b9263a6516125f5e5 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Thu, 2 Mar 2023 17:29:22 +0600 Subject: [PATCH 21/24] Apply suggestions from @djrtwo --- specs/_features/eip6110/beacon-chain.md | 100 ++---------------------- specs/_features/eip6110/fork.md | 2 +- specs/altair/beacon-chain.md | 36 +++------ specs/phase0/beacon-chain.md | 60 ++++++++------ 4 files changed, 58 insertions(+), 140 deletions(-) diff --git a/specs/_features/eip6110/beacon-chain.md b/specs/_features/eip6110/beacon-chain.md index ddaca21a24..95655d32b3 100644 --- a/specs/_features/eip6110/beacon-chain.md +++ b/specs/_features/eip6110/beacon-chain.md @@ -21,10 +21,7 @@ - [Beacon chain state transition function](#beacon-chain-state-transition-function) - [Block processing](#block-processing) - [Modified `process_operations`](#modified-process_operations) - - [New `get_validator_from_deposit_data`](#new-get_validator_from_deposit_data) - - [New `apply_deposit`](#new-apply_deposit) - [New `process_deposit_receipt`](#new-process_deposit_receipt) - - [Modified `process_deposit`](#modified-process_deposit) - [Modified `process_execution_payload`](#modified-process_execution_payload) - [Testing](#testing) @@ -36,7 +33,7 @@ This is the beacon chain specification of in-protocol deposits processing mechanism. This mechanism relies on the changes proposed by [EIP-6110](http://eips.ethereum.org/EIPS/eip-6110). -*Note:* This specification is under development and should be used with care. +*Note:* This specification is built upon [Capella](../../capella/beacon_chain.md) and is under active development. ## Constants @@ -192,10 +189,11 @@ def process_block(state: BeaconState, block: BeaconBlock) -> None: ```python def process_operations(state: BeaconState, body: BeaconBlockBody) -> None: - # [New in EIP-6110] - # Prevent potential underflow introduced by mixing two deposit processing flows - if state.eth1_data.deposit_count > state.eth1_deposit_index: - assert len(body.deposits) == min(MAX_DEPOSITS, state.eth1_data.deposit_count - state.eth1_deposit_index) + # [Modified in EIP-6110] + # Disable former deposit mechanism once all prior deposits are processed + eth1_deposit_index_limit = min(state.eth1_data.deposit_count, state.deposit_receipts_start_index) + if state.eth1_deposit_index < eth1_deposit_index_limit: + assert len(body.deposits) == min(MAX_DEPOSITS, eth1_deposit_index_limit - state.eth1_deposit_index) else: assert len(body.deposits) == 0 @@ -215,61 +213,12 @@ def process_operations(state: BeaconState, body: BeaconBlockBody) -> None: for_ops(body.execution_payload.deposit_receipts, process_deposit_receipt) ``` -#### New `get_validator_from_deposit_data` - -```python -def get_validator_from_deposit_data(pubkey: BLSPubkey, withdrawal_credentials: Bytes32, amount: uint64) -> Validator: - effective_balance = min(amount - amount % EFFECTIVE_BALANCE_INCREMENT, MAX_EFFECTIVE_BALANCE) - - return Validator( - pubkey=pubkey, - withdrawal_credentials=withdrawal_credentials, - activation_eligibility_epoch=FAR_FUTURE_EPOCH, - activation_epoch=FAR_FUTURE_EPOCH, - exit_epoch=FAR_FUTURE_EPOCH, - withdrawable_epoch=FAR_FUTURE_EPOCH, - effective_balance=effective_balance, - ) -``` - -#### New `apply_deposit` - -```python -def apply_deposit(state: BeaconState, - pubkey: BLSPubkey, - withdrawal_credentials: Bytes32, - amount: uint64, - signature: BLSSignature) -> None: - validator_pubkeys = [validator.pubkey for validator in state.validators] - if pubkey not in validator_pubkeys: - # Verify the deposit signature (proof of possession) which is not checked by the deposit contract - deposit_message = DepositMessage( - pubkey=pubkey, - withdrawal_credentials=withdrawal_credentials, - amount=amount, - ) - domain = compute_domain(DOMAIN_DEPOSIT) # Fork-agnostic domain since deposits are valid across forks - signing_root = compute_signing_root(deposit_message, domain) - # Initialize validator if the deposit signature is valid - if bls.Verify(pubkey, signing_root, signature): - state.validators.append(get_validator_from_deposit_data(pubkey, withdrawal_credentials, amount)) - state.balances.append(amount) - state.previous_epoch_participation.append(ParticipationFlags(0b0000_0000)) - state.current_epoch_participation.append(ParticipationFlags(0b0000_0000)) - state.inactivity_scores.append(uint64(0)) - else: - # Increase balance by deposit amount - index = ValidatorIndex(validator_pubkeys.index(pubkey)) - increase_balance(state, index, amount) -``` - - #### New `process_deposit_receipt` ```python def process_deposit_receipt(state: BeaconState, deposit_receipt: DepositReceipt) -> None: # Set deposit receipt start index - if state.deposit_receipts_start_index == NOT_SET_DEPOSIT_RECEIPTS_START_INDEX: + if state.deposit_receipts_start_index == UNSET_DEPOSIT_RECEIPTS_START_INDEX: state.deposit_receipts_start_index = deposit_receipt.index # Signify the end of transition to in-protocol deposit logic @@ -285,39 +234,6 @@ def process_deposit_receipt(state: BeaconState, deposit_receipt: DepositReceipt) ) ``` -#### Modified `process_deposit` - -*Note*: The function `process_deposit` is modified to prevent deposits from being processed the second time (due to `process_deposit_receipt`). - -```python -def process_deposit(state: BeaconState, deposit: Deposit) -> None: - # [New in EIP-6110] - # Skip already processed deposits - if state.eth1_deposit_index >= state.deposit_receipts_start_index: - state.eth1_deposit_index += 1 - return - - # Verify the Merkle branch - assert is_valid_merkle_branch( - leaf=hash_tree_root(deposit.data), - branch=deposit.proof, - depth=DEPOSIT_CONTRACT_TREE_DEPTH + 1, # Add 1 for the List length mix-in - index=state.eth1_deposit_index, - root=state.eth1_data.deposit_root, - ) - - # Deposits must be processed in order - state.eth1_deposit_index += 1 - - apply_deposit( - state=state, - pubkey=deposit.data.pubkey, - withdrawal_credentials=deposit.data.withdrawal_credentials, - amount=deposit.data.amount, - signature=deposit.data.signature, - ) -``` - #### Modified `process_execution_payload` *Note*: The function `process_execution_payload` is modified to use the new `ExecutionPayloadHeader` type. @@ -379,7 +295,7 @@ def initialize_beacon_state_from_eth1(eth1_block_hash: Hash32, eth1_data=Eth1Data(block_hash=eth1_block_hash, deposit_count=uint64(len(deposits))), latest_block_header=BeaconBlockHeader(body_root=hash_tree_root(BeaconBlockBody())), randao_mixes=[eth1_block_hash] * EPOCHS_PER_HISTORICAL_VECTOR, # Seed RANDAO with Eth1 entropy - deposit_receipts_start_index=NOT_SET_DEPOSIT_RECEIPTS_START_INDEX, # [New in EIP6110] + deposit_receipts_start_index=UNSET_DEPOSIT_RECEIPTS_START_INDEX, # [New in EIP6110] ) # Process deposits diff --git a/specs/_features/eip6110/fork.md b/specs/_features/eip6110/fork.md index a932ecb24e..b08661e5fa 100644 --- a/specs/_features/eip6110/fork.md +++ b/specs/_features/eip6110/fork.md @@ -135,7 +135,7 @@ def upgrade_to_eip6110(pre: capella.BeaconState) -> BeaconState: # Deep history valid from Capella onwards historical_summaries=pre.historical_summaries, # EIP-6110 - deposit_receipts_start_index=NOT_SET_DEPOSIT_RECEIPTS_START_INDEX, # [New in EIP-6110] + deposit_receipts_start_index=UNSET_DEPOSIT_RECEIPTS_START_INDEX, # [New in EIP-6110] ) return post diff --git a/specs/altair/beacon-chain.md b/specs/altair/beacon-chain.md index fe71a5ff83..58dfad608a 100644 --- a/specs/altair/beacon-chain.md +++ b/specs/altair/beacon-chain.md @@ -43,7 +43,7 @@ - [Modified `slash_validator`](#modified-slash_validator) - [Block processing](#block-processing) - [Modified `process_attestation`](#modified-process_attestation) - - [Modified `process_deposit`](#modified-process_deposit) + - [Modified `apply_deposit`](#modified-apply_deposit) - [Sync aggregate processing](#sync-aggregate-processing) - [Epoch processing](#epoch-processing) - [Justification and finalization](#justification-and-finalization) @@ -489,39 +489,29 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None: increase_balance(state, get_beacon_proposer_index(state), proposer_reward) ``` -#### Modified `process_deposit` +#### Modified `apply_deposit` -*Note*: The function `process_deposit` is modified to initialize `inactivity_scores`, `previous_epoch_participation`, and `current_epoch_participation`. +*Note*: The function `apply_deposit` is modified to initialize `inactivity_scores`, `previous_epoch_participation`, and `current_epoch_participation`. ```python -def process_deposit(state: BeaconState, deposit: Deposit) -> None: - # Verify the Merkle branch - assert is_valid_merkle_branch( - leaf=hash_tree_root(deposit.data), - branch=deposit.proof, - depth=DEPOSIT_CONTRACT_TREE_DEPTH + 1, # Add 1 for the List length mix-in - index=state.eth1_deposit_index, - root=state.eth1_data.deposit_root, - ) - - # Deposits must be processed in order - state.eth1_deposit_index += 1 - - pubkey = deposit.data.pubkey - amount = deposit.data.amount +def apply_deposit(state: BeaconState, + pubkey: BLSPubkey, + withdrawal_credentials: Bytes32, + amount: uint64, + signature: BLSSignature) -> None: validator_pubkeys = [validator.pubkey for validator in state.validators] if pubkey not in validator_pubkeys: # Verify the deposit signature (proof of possession) which is not checked by the deposit contract deposit_message = DepositMessage( - pubkey=deposit.data.pubkey, - withdrawal_credentials=deposit.data.withdrawal_credentials, - amount=deposit.data.amount, + pubkey=pubkey, + withdrawal_credentials=withdrawal_credentials, + amount=amount, ) domain = compute_domain(DOMAIN_DEPOSIT) # Fork-agnostic domain since deposits are valid across forks signing_root = compute_signing_root(deposit_message, domain) # Initialize validator if the deposit signature is valid - if bls.Verify(pubkey, signing_root, deposit.data.signature): - state.validators.append(get_validator_from_deposit(deposit)) + if bls.Verify(pubkey, signing_root, signature): + state.validators.append(get_validator_from_deposit(pubkey, withdrawal_credentials, amount)) state.balances.append(amount) # [New in Altair] state.previous_epoch_participation.append(ParticipationFlags(0b0000_0000)) diff --git a/specs/phase0/beacon-chain.md b/specs/phase0/beacon-chain.md index 7e14fa951a..3794cd6be3 100644 --- a/specs/phase0/beacon-chain.md +++ b/specs/phase0/beacon-chain.md @@ -1835,13 +1835,12 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None: ##### Deposits ```python -def get_validator_from_deposit(deposit: Deposit) -> Validator: - amount = deposit.data.amount +def get_validator_from_deposit(pubkey: BLSPubkey, withdrawal_credentials: Bytes32, amount: uint64) -> Validator: effective_balance = min(amount - amount % EFFECTIVE_BALANCE_INCREMENT, MAX_EFFECTIVE_BALANCE) return Validator( - pubkey=deposit.data.pubkey, - withdrawal_credentials=deposit.data.withdrawal_credentials, + pubkey=pubkey, + withdrawal_credentials=withdrawal_credentials, activation_eligibility_epoch=FAR_FUTURE_EPOCH, activation_epoch=FAR_FUTURE_EPOCH, exit_epoch=FAR_FUTURE_EPOCH, @@ -1851,36 +1850,26 @@ def get_validator_from_deposit(deposit: Deposit) -> Validator: ``` ```python -def process_deposit(state: BeaconState, deposit: Deposit) -> None: - # Verify the Merkle branch - assert is_valid_merkle_branch( - leaf=hash_tree_root(deposit.data), - branch=deposit.proof, - depth=DEPOSIT_CONTRACT_TREE_DEPTH + 1, # Add 1 for the List length mix-in - index=state.eth1_deposit_index, - root=state.eth1_data.deposit_root, - ) - - # Deposits must be processed in order - state.eth1_deposit_index += 1 - - pubkey = deposit.data.pubkey - amount = deposit.data.amount +def apply_deposit(state: BeaconState, + pubkey: BLSPubkey, + withdrawal_credentials: Bytes32, + amount: uint64, + signature: BLSSignature) -> None: validator_pubkeys = [v.pubkey for v in state.validators] if pubkey not in validator_pubkeys: # Verify the deposit signature (proof of possession) which is not checked by the deposit contract deposit_message = DepositMessage( - pubkey=deposit.data.pubkey, - withdrawal_credentials=deposit.data.withdrawal_credentials, - amount=deposit.data.amount, + pubkey=pubkey, + withdrawal_credentials=withdrawal_credentials, + amount=amount, ) domain = compute_domain(DOMAIN_DEPOSIT) # Fork-agnostic domain since deposits are valid across forks signing_root = compute_signing_root(deposit_message, domain) - if not bls.Verify(pubkey, signing_root, deposit.data.signature): + if not bls.Verify(pubkey, signing_root, signature): return # Add validator and balance entries - state.validators.append(get_validator_from_deposit(deposit)) + state.validators.append(get_validator_from_deposit(pubkey, withdrawal_credentials, amount)) state.balances.append(amount) else: # Increase balance by deposit amount @@ -1888,6 +1877,29 @@ def process_deposit(state: BeaconState, deposit: Deposit) -> None: increase_balance(state, index, amount) ``` +```python +def process_deposit(state: BeaconState, deposit: Deposit) -> None: + # Verify the Merkle branch + assert is_valid_merkle_branch( + leaf=hash_tree_root(deposit.data), + branch=deposit.proof, + depth=DEPOSIT_CONTRACT_TREE_DEPTH + 1, # Add 1 for the List length mix-in + index=state.eth1_deposit_index, + root=state.eth1_data.deposit_root, + ) + + # Deposits must be processed in order + state.eth1_deposit_index += 1 + + apply_deposit( + state=state, + pubkey=deposit.data.pubkey, + withdrawal_credentials=deposit.data.withdrawal_credentials, + amount=deposit.data.amount, + signature=deposit.data.signature, + ) +``` + ##### Voluntary exits ```python From 00557c56492171dbaa41853e5c658b0789ff3206 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Thu, 2 Mar 2023 17:31:12 +0600 Subject: [PATCH 22/24] Remove unnecessary eth1_deposit_index bump --- specs/_features/eip6110/beacon-chain.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/specs/_features/eip6110/beacon-chain.md b/specs/_features/eip6110/beacon-chain.md index 95655d32b3..70a72a5f45 100644 --- a/specs/_features/eip6110/beacon-chain.md +++ b/specs/_features/eip6110/beacon-chain.md @@ -221,10 +221,6 @@ def process_deposit_receipt(state: BeaconState, deposit_receipt: DepositReceipt) if state.deposit_receipts_start_index == UNSET_DEPOSIT_RECEIPTS_START_INDEX: state.deposit_receipts_start_index = deposit_receipt.index - # Signify the end of transition to in-protocol deposit logic - if state.eth1_deposit_index >= state.deposit_receipts_start_index: - state.eth1_deposit_index = deposit_receipt.index + 1 - apply_deposit( state=state, pubkey=deposit_receipt.pubkey, From 0da79bdbfd127be24102b1dc0454f5ba200499d5 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Thu, 9 Mar 2023 21:05:07 +0600 Subject: [PATCH 23/24] Provide validator guide for EIP6110 --- specs/_features/eip6110/validator.md | 42 ++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 specs/_features/eip6110/validator.md diff --git a/specs/_features/eip6110/validator.md b/specs/_features/eip6110/validator.md new file mode 100644 index 0000000000..dcaaf11041 --- /dev/null +++ b/specs/_features/eip6110/validator.md @@ -0,0 +1,42 @@ +# EIP-6110 -- Honest Validator + +## Table of contents + + + + + +- [Introduction](#introduction) +- [Prerequisites](#prerequisites) +- [Block proposal](#block-proposal) + - [Deposits](#deposits) + + + + +## Introduction + +This document represents the changes to be made in the code of an "honest validator" to implement EIP-6110. + +## Prerequisites + +This document is an extension of the [Capella -- Honest Validator](../../capella/validator.md) guide. +All behaviors and definitions defined in this document, and documents it extends, carry over unless explicitly noted or overridden. + +All terminology, constants, functions, and protocol mechanics defined in the updated Beacon Chain doc of [EIP-6110](./beacon-chain.md) are requisite for this document and used throughout. +Please see related Beacon Chain doc before continuing and use them as a reference throughout. + +## Block proposal + +### Deposits + +The expected number of deposits MUST be changed from `min(MAX_DEPOSITS, eth1_data.deposit_count - state.eth1_deposit_index)` to the result of the following function: + +```python +def get_eth1_deposit_count(state: BeaconState) -> uint64: + eth1_deposit_index_limit = min(state.eth1_data.deposit_count, state.deposit_receipts_start_index) + if state.eth1_deposit_index < eth1_deposit_index_limit: + return min(MAX_DEPOSITS, eth1_deposit_index_limit - state.eth1_deposit_index) + else: + return 0 +``` From 0ae18d86e3fee6ace2187d92da0afca78e43b75d Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Tue, 14 Mar 2023 11:22:12 -0600 Subject: [PATCH 24/24] Update specs/_features/eip6110/validator.md Co-authored-by: Hsiao-Wei Wang --- specs/_features/eip6110/validator.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/_features/eip6110/validator.md b/specs/_features/eip6110/validator.md index dcaaf11041..ae9d493a6f 100644 --- a/specs/_features/eip6110/validator.md +++ b/specs/_features/eip6110/validator.md @@ -38,5 +38,5 @@ def get_eth1_deposit_count(state: BeaconState) -> uint64: if state.eth1_deposit_index < eth1_deposit_index_limit: return min(MAX_DEPOSITS, eth1_deposit_index_limit - state.eth1_deposit_index) else: - return 0 + return uint64(0) ```