2021-12-02 17:52:56 +00:00
|
|
|
# Capella -- The Beacon Chain
|
2021-12-01 18:37:30 +00:00
|
|
|
|
|
|
|
## Table of contents
|
|
|
|
|
|
|
|
<!-- TOC -->
|
|
|
|
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
|
|
|
|
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
|
|
|
|
|
2022-03-22 14:22:35 +00:00
|
|
|
- [Introduction](#introduction)
|
|
|
|
- [Custom types](#custom-types)
|
2022-03-22 14:23:59 +00:00
|
|
|
- [Domain types](#domain-types)
|
2022-03-22 14:22:35 +00:00
|
|
|
- [Preset](#preset)
|
2022-03-22 14:23:59 +00:00
|
|
|
- [Max operations per block](#max-operations-per-block)
|
2022-03-22 14:22:35 +00:00
|
|
|
- [Execution](#execution)
|
2022-11-11 17:47:53 +00:00
|
|
|
- [Withdrawals processing](#withdrawals-processing)
|
2022-03-22 14:22:35 +00:00
|
|
|
- [Containers](#containers)
|
|
|
|
- [New containers](#new-containers)
|
|
|
|
- [`Withdrawal`](#withdrawal)
|
2022-03-22 14:23:59 +00:00
|
|
|
- [`BLSToExecutionChange`](#blstoexecutionchange)
|
|
|
|
- [`SignedBLSToExecutionChange`](#signedblstoexecutionchange)
|
2023-01-02 15:07:00 +00:00
|
|
|
- [`HistoricalSummary`](#historicalsummary)
|
2022-03-22 14:22:35 +00:00
|
|
|
- [Extended Containers](#extended-containers)
|
|
|
|
- [`ExecutionPayload`](#executionpayload)
|
|
|
|
- [`ExecutionPayloadHeader`](#executionpayloadheader)
|
2022-03-22 14:23:59 +00:00
|
|
|
- [`BeaconBlockBody`](#beaconblockbody)
|
2022-03-22 14:22:35 +00:00
|
|
|
- [`BeaconState`](#beaconstate)
|
|
|
|
- [Helpers](#helpers)
|
|
|
|
- [Predicates](#predicates)
|
2022-06-08 19:16:12 +00:00
|
|
|
- [`has_eth1_withdrawal_credential`](#has_eth1_withdrawal_credential)
|
2022-03-22 14:22:35 +00:00
|
|
|
- [`is_fully_withdrawable_validator`](#is_fully_withdrawable_validator)
|
2022-06-08 19:16:12 +00:00
|
|
|
- [`is_partially_withdrawable_validator`](#is_partially_withdrawable_validator)
|
2022-03-22 14:22:35 +00:00
|
|
|
- [Beacon chain state transition function](#beacon-chain-state-transition-function)
|
2022-12-15 16:49:14 +00:00
|
|
|
- [Epoch processing](#epoch-processing)
|
2023-01-02 15:07:00 +00:00
|
|
|
- [Historical summaries updates](#historical-summaries-updates)
|
2022-03-22 14:22:35 +00:00
|
|
|
- [Block processing](#block-processing)
|
2022-10-31 18:32:16 +00:00
|
|
|
- [New `get_expected_withdrawals`](#new-get_expected_withdrawals)
|
2022-03-22 14:22:35 +00:00
|
|
|
- [New `process_withdrawals`](#new-process_withdrawals)
|
|
|
|
- [Modified `process_execution_payload`](#modified-process_execution_payload)
|
2022-03-22 14:23:59 +00:00
|
|
|
- [Modified `process_operations`](#modified-process_operations)
|
|
|
|
- [New `process_bls_to_execution_change`](#new-process_bls_to_execution_change)
|
2022-07-15 14:18:33 +00:00
|
|
|
- [Testing](#testing)
|
2022-03-22 14:22:35 +00:00
|
|
|
|
2021-12-01 18:37:30 +00:00
|
|
|
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
|
|
|
<!-- /TOC -->
|
|
|
|
|
|
|
|
## Introduction
|
|
|
|
|
2022-02-24 19:43:57 +00:00
|
|
|
Capella is a consensus-layer upgrade containing a number of features related
|
2021-12-01 18:37:30 +00:00
|
|
|
to validator withdrawals. Including:
|
2022-09-20 19:43:38 +00:00
|
|
|
* Automatic withdrawals of `withdrawable` validators.
|
2022-06-08 19:16:12 +00:00
|
|
|
* Partial withdrawals sweep for validators with 0x01 withdrawal
|
2022-09-20 19:43:38 +00:00
|
|
|
credentials and balances in excess of `MAX_EFFECTIVE_BALANCE`.
|
2021-12-01 18:37:30 +00:00
|
|
|
* Operation to change from `BLS_WITHDRAWAL_PREFIX` to
|
2022-09-20 19:43:38 +00:00
|
|
|
`ETH1_ADDRESS_WITHDRAWAL_PREFIX` versioned withdrawal credentials to enable withdrawals for a validator.
|
2021-12-01 18:37:30 +00:00
|
|
|
|
2023-01-06 15:06:39 +00:00
|
|
|
Another new feature is the new independent state and block historical accumulators
|
|
|
|
that replace the original singular historical roots. With these accumulators, it becomes possible to validate
|
|
|
|
the entire block history that led up to that particular state without any additional information
|
|
|
|
beyond the state and the blocks.
|
2023-01-04 09:54:59 +00:00
|
|
|
|
2021-12-01 18:37:30 +00:00
|
|
|
## Custom types
|
|
|
|
|
2022-02-23 22:15:24 +00:00
|
|
|
We define the following Python custom types for type hinting and readability:
|
|
|
|
|
|
|
|
| Name | SSZ equivalent | Description |
|
|
|
|
| - | - | - |
|
2022-09-20 19:43:38 +00:00
|
|
|
| `WithdrawalIndex` | `uint64` | an index of a `Withdrawal` |
|
2022-02-23 22:15:24 +00:00
|
|
|
|
2022-03-16 21:41:37 +00:00
|
|
|
### Domain types
|
|
|
|
|
|
|
|
| Name | Value |
|
|
|
|
| - | - |
|
|
|
|
| `DOMAIN_BLS_TO_EXECUTION_CHANGE` | `DomainType('0x0A000000')` |
|
|
|
|
|
2021-12-01 18:37:30 +00:00
|
|
|
## Preset
|
|
|
|
|
2022-03-16 21:41:37 +00:00
|
|
|
### Max operations per block
|
|
|
|
|
|
|
|
| Name | Value |
|
|
|
|
| - | - |
|
|
|
|
| `MAX_BLS_TO_EXECUTION_CHANGES` | `2**4` (= 16) |
|
|
|
|
|
2022-02-23 22:05:55 +00:00
|
|
|
### Execution
|
|
|
|
|
|
|
|
| Name | Value | Description |
|
|
|
|
| - | - | - |
|
2022-03-10 17:38:47 +00:00
|
|
|
| `MAX_WITHDRAWALS_PER_PAYLOAD` | `uint64(2**4)` (= 16) | Maximum amount of withdrawals allowed in each payload |
|
2021-12-01 18:37:30 +00:00
|
|
|
|
2022-11-11 17:47:53 +00:00
|
|
|
### Withdrawals processing
|
|
|
|
|
|
|
|
| Name | Value |
|
|
|
|
| - | - |
|
2022-12-12 21:22:58 +00:00
|
|
|
| `MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP` | `16384` (= 2**14 ) |
|
2021-12-01 18:37:30 +00:00
|
|
|
|
|
|
|
## Containers
|
|
|
|
|
2022-03-22 14:00:53 +00:00
|
|
|
### New containers
|
|
|
|
|
|
|
|
#### `Withdrawal`
|
|
|
|
|
|
|
|
```python
|
|
|
|
class Withdrawal(Container):
|
|
|
|
index: WithdrawalIndex
|
2022-10-18 14:35:06 +00:00
|
|
|
validator_index: ValidatorIndex
|
2022-03-22 14:00:53 +00:00
|
|
|
address: ExecutionAddress
|
|
|
|
amount: Gwei
|
|
|
|
```
|
|
|
|
|
2022-03-22 14:19:31 +00:00
|
|
|
#### `BLSToExecutionChange`
|
|
|
|
|
|
|
|
```python
|
|
|
|
class BLSToExecutionChange(Container):
|
|
|
|
validator_index: ValidatorIndex
|
|
|
|
from_bls_pubkey: BLSPubkey
|
|
|
|
to_execution_address: ExecutionAddress
|
|
|
|
```
|
|
|
|
|
|
|
|
#### `SignedBLSToExecutionChange`
|
|
|
|
|
|
|
|
```python
|
|
|
|
class SignedBLSToExecutionChange(Container):
|
|
|
|
message: BLSToExecutionChange
|
|
|
|
signature: BLSSignature
|
|
|
|
```
|
|
|
|
|
2023-01-02 15:07:00 +00:00
|
|
|
#### `HistoricalSummary`
|
Historical batches
This PR, a continuation of
replaces `historical_roots` with
`historical_block_roots`.
By keeping an accumulator of historical block roots in the state, it
becomes possible to validate the entire block history that led up to
that particular state without executing the transitions, and without
checking them one by one in backwards order using a parent chain.
This is interesting for archival purposes as well as when implementing
sync protocols that can verify chunks of blocks quickly, meaning they
can be downloaded in any order.
It's also useful as it provides a canonical hash by which such chunks of
blocks can be named, with a direct reference in the state.
In this PR, `historical_roots` is frozen at its current value and
`historical_batches` are computed from the merge epoch onwards.
After this PR, `block_batch_root` in the state can be used to verify an
era of blocks against the state with a simple root check.
The `historical_roots` values on the other hand can be used to verify
that a constant distributed with clients is valid for a particular
state, and therefore extends the block validation all the way back to
genesis without backfilling `block_batch_root` and without introducing
any new security assumptions in the client.
As far as naming goes, it's convenient to talk about an "era" being 8192
slots ~= 1.14 days. The 8192 number comes from the
SLOTS_PER_HISTORICAL_ROOT constant.
With multiple easily verifable blocks in a file, it becomes trivial to
offload block history to out-of-protocol transfer methods (bittorrent /
ftp / whatever) - including execution payloads, paving the way for a
future in which clients purge block history in p2p.
This PR can be applied along with the merge which simplifies payload
distribution from the get-go. Both execution and consensus clients
benefit because from the merge onwards, they both need to be able to
supply ranges of blocks in the sync protocol from what effectively is
"cold storage".
Another possibility is to include it in a future cleanup PR - this
complicates the "cold storage" mode above by not covering exection
payloads from start.
2022-10-27 08:33:38 +00:00
|
|
|
|
|
|
|
```python
|
2023-01-02 15:07:00 +00:00
|
|
|
class HistoricalSummary(Container):
|
Historical batches
This PR, a continuation of
replaces `historical_roots` with
`historical_block_roots`.
By keeping an accumulator of historical block roots in the state, it
becomes possible to validate the entire block history that led up to
that particular state without executing the transitions, and without
checking them one by one in backwards order using a parent chain.
This is interesting for archival purposes as well as when implementing
sync protocols that can verify chunks of blocks quickly, meaning they
can be downloaded in any order.
It's also useful as it provides a canonical hash by which such chunks of
blocks can be named, with a direct reference in the state.
In this PR, `historical_roots` is frozen at its current value and
`historical_batches` are computed from the merge epoch onwards.
After this PR, `block_batch_root` in the state can be used to verify an
era of blocks against the state with a simple root check.
The `historical_roots` values on the other hand can be used to verify
that a constant distributed with clients is valid for a particular
state, and therefore extends the block validation all the way back to
genesis without backfilling `block_batch_root` and without introducing
any new security assumptions in the client.
As far as naming goes, it's convenient to talk about an "era" being 8192
slots ~= 1.14 days. The 8192 number comes from the
SLOTS_PER_HISTORICAL_ROOT constant.
With multiple easily verifable blocks in a file, it becomes trivial to
offload block history to out-of-protocol transfer methods (bittorrent /
ftp / whatever) - including execution payloads, paving the way for a
future in which clients purge block history in p2p.
This PR can be applied along with the merge which simplifies payload
distribution from the get-go. Both execution and consensus clients
benefit because from the merge onwards, they both need to be able to
supply ranges of blocks in the sync protocol from what effectively is
"cold storage".
Another possibility is to include it in a future cleanup PR - this
complicates the "cold storage" mode above by not covering exection
payloads from start.
2022-10-27 08:33:38 +00:00
|
|
|
"""
|
2023-01-02 15:07:00 +00:00
|
|
|
`HistoricalSummary` matches the components of the phase0 `HistoricalBatch`
|
Historical batches
This PR, a continuation of
replaces `historical_roots` with
`historical_block_roots`.
By keeping an accumulator of historical block roots in the state, it
becomes possible to validate the entire block history that led up to
that particular state without executing the transitions, and without
checking them one by one in backwards order using a parent chain.
This is interesting for archival purposes as well as when implementing
sync protocols that can verify chunks of blocks quickly, meaning they
can be downloaded in any order.
It's also useful as it provides a canonical hash by which such chunks of
blocks can be named, with a direct reference in the state.
In this PR, `historical_roots` is frozen at its current value and
`historical_batches` are computed from the merge epoch onwards.
After this PR, `block_batch_root` in the state can be used to verify an
era of blocks against the state with a simple root check.
The `historical_roots` values on the other hand can be used to verify
that a constant distributed with clients is valid for a particular
state, and therefore extends the block validation all the way back to
genesis without backfilling `block_batch_root` and without introducing
any new security assumptions in the client.
As far as naming goes, it's convenient to talk about an "era" being 8192
slots ~= 1.14 days. The 8192 number comes from the
SLOTS_PER_HISTORICAL_ROOT constant.
With multiple easily verifable blocks in a file, it becomes trivial to
offload block history to out-of-protocol transfer methods (bittorrent /
ftp / whatever) - including execution payloads, paving the way for a
future in which clients purge block history in p2p.
This PR can be applied along with the merge which simplifies payload
distribution from the get-go. Both execution and consensus clients
benefit because from the merge onwards, they both need to be able to
supply ranges of blocks in the sync protocol from what effectively is
"cold storage".
Another possibility is to include it in a future cleanup PR - this
complicates the "cold storage" mode above by not covering exection
payloads from start.
2022-10-27 08:33:38 +00:00
|
|
|
making the two hash_tree_root-compatible.
|
|
|
|
"""
|
2023-01-03 13:50:06 +00:00
|
|
|
block_summary_root: Root
|
|
|
|
state_summary_root: Root
|
Historical batches
This PR, a continuation of
replaces `historical_roots` with
`historical_block_roots`.
By keeping an accumulator of historical block roots in the state, it
becomes possible to validate the entire block history that led up to
that particular state without executing the transitions, and without
checking them one by one in backwards order using a parent chain.
This is interesting for archival purposes as well as when implementing
sync protocols that can verify chunks of blocks quickly, meaning they
can be downloaded in any order.
It's also useful as it provides a canonical hash by which such chunks of
blocks can be named, with a direct reference in the state.
In this PR, `historical_roots` is frozen at its current value and
`historical_batches` are computed from the merge epoch onwards.
After this PR, `block_batch_root` in the state can be used to verify an
era of blocks against the state with a simple root check.
The `historical_roots` values on the other hand can be used to verify
that a constant distributed with clients is valid for a particular
state, and therefore extends the block validation all the way back to
genesis without backfilling `block_batch_root` and without introducing
any new security assumptions in the client.
As far as naming goes, it's convenient to talk about an "era" being 8192
slots ~= 1.14 days. The 8192 number comes from the
SLOTS_PER_HISTORICAL_ROOT constant.
With multiple easily verifable blocks in a file, it becomes trivial to
offload block history to out-of-protocol transfer methods (bittorrent /
ftp / whatever) - including execution payloads, paving the way for a
future in which clients purge block history in p2p.
This PR can be applied along with the merge which simplifies payload
distribution from the get-go. Both execution and consensus clients
benefit because from the merge onwards, they both need to be able to
supply ranges of blocks in the sync protocol from what effectively is
"cold storage".
Another possibility is to include it in a future cleanup PR - this
complicates the "cold storage" mode above by not covering exection
payloads from start.
2022-10-27 08:33:38 +00:00
|
|
|
```
|
|
|
|
|
2021-12-01 18:37:30 +00:00
|
|
|
### Extended Containers
|
|
|
|
|
2022-03-22 14:00:53 +00:00
|
|
|
#### `ExecutionPayload`
|
|
|
|
|
|
|
|
```python
|
|
|
|
class ExecutionPayload(Container):
|
|
|
|
# Execution block header fields
|
|
|
|
parent_hash: Hash32
|
|
|
|
fee_recipient: ExecutionAddress # 'beneficiary' in the yellow paper
|
|
|
|
state_root: Bytes32
|
|
|
|
receipts_root: Bytes32
|
|
|
|
logs_bloom: ByteVector[BYTES_PER_LOGS_BLOOM]
|
|
|
|
prev_randao: Bytes32 # 'difficulty' in the yellow paper
|
|
|
|
block_number: uint64 # 'number' in the yellow paper
|
|
|
|
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 # Hash of execution block
|
|
|
|
transactions: List[Transaction, MAX_TRANSACTIONS_PER_PAYLOAD]
|
|
|
|
withdrawals: List[Withdrawal, MAX_WITHDRAWALS_PER_PAYLOAD] # [New in Capella]
|
|
|
|
```
|
|
|
|
|
|
|
|
#### `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 # Hash of execution block
|
|
|
|
transactions_root: Root
|
|
|
|
withdrawals_root: Root # [New in Capella]
|
|
|
|
```
|
|
|
|
|
2022-03-16 21:41:37 +00:00
|
|
|
#### `BeaconBlockBody`
|
|
|
|
|
|
|
|
```python
|
|
|
|
class BeaconBlockBody(Container):
|
|
|
|
randao_reveal: BLSSignature
|
|
|
|
eth1_data: Eth1Data # Eth1 data vote
|
|
|
|
graffiti: Bytes32 # Arbitrary data
|
|
|
|
# Operations
|
|
|
|
proposer_slashings: List[ProposerSlashing, MAX_PROPOSER_SLASHINGS]
|
|
|
|
attester_slashings: List[AttesterSlashing, MAX_ATTESTER_SLASHINGS]
|
|
|
|
attestations: List[Attestation, MAX_ATTESTATIONS]
|
|
|
|
deposits: List[Deposit, MAX_DEPOSITS]
|
|
|
|
voluntary_exits: List[SignedVoluntaryExit, MAX_VOLUNTARY_EXITS]
|
|
|
|
sync_aggregate: SyncAggregate
|
|
|
|
# Execution
|
|
|
|
execution_payload: ExecutionPayload
|
2022-03-23 16:54:40 +00:00
|
|
|
# Capella operations
|
|
|
|
bls_to_execution_changes: List[SignedBLSToExecutionChange, MAX_BLS_TO_EXECUTION_CHANGES] # [New in Capella]
|
2022-03-16 21:41:37 +00:00
|
|
|
```
|
|
|
|
|
2021-12-01 18:37:30 +00:00
|
|
|
#### `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]
|
2023-01-02 15:07:00 +00:00
|
|
|
historical_roots: List[Root, HISTORICAL_ROOTS_LIMIT] # Frozen in Capella, replaced by historical_summaries
|
2021-12-01 18:37:30 +00:00
|
|
|
# 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
|
2023-01-27 10:00:37 +00:00
|
|
|
latest_execution_payload_header: ExecutionPayloadHeader # [Modified in Capella]
|
2021-12-01 18:37:30 +00:00
|
|
|
# Withdrawals
|
2022-06-08 19:16:12 +00:00
|
|
|
next_withdrawal_index: WithdrawalIndex # [New in Capella]
|
2022-11-10 11:33:11 +00:00
|
|
|
next_withdrawal_validator_index: ValidatorIndex # [New in Capella]
|
2022-12-15 16:49:14 +00:00
|
|
|
# Deep history valid from Capella onwards
|
2023-01-02 15:07:00 +00:00
|
|
|
historical_summaries: List[HistoricalSummary, HISTORICAL_ROOTS_LIMIT] # [New in Capella]
|
2022-02-23 22:05:55 +00:00
|
|
|
```
|
|
|
|
|
2021-12-01 18:37:30 +00:00
|
|
|
## Helpers
|
|
|
|
|
|
|
|
### Predicates
|
|
|
|
|
2022-06-08 19:16:12 +00:00
|
|
|
#### `has_eth1_withdrawal_credential`
|
|
|
|
|
|
|
|
```python
|
|
|
|
def has_eth1_withdrawal_credential(validator: Validator) -> bool:
|
|
|
|
"""
|
2022-09-20 19:43:38 +00:00
|
|
|
Check if ``validator`` has an 0x01 prefixed "eth1" withdrawal credential.
|
2022-06-08 19:16:12 +00:00
|
|
|
"""
|
|
|
|
return validator.withdrawal_credentials[:1] == ETH1_ADDRESS_WITHDRAWAL_PREFIX
|
|
|
|
```
|
|
|
|
|
2022-02-24 22:06:31 +00:00
|
|
|
#### `is_fully_withdrawable_validator`
|
2021-12-01 18:37:30 +00:00
|
|
|
|
|
|
|
```python
|
2022-09-19 17:05:47 +00:00
|
|
|
def is_fully_withdrawable_validator(validator: Validator, balance: Gwei, epoch: Epoch) -> bool:
|
2021-12-01 18:37:30 +00:00
|
|
|
"""
|
2022-02-24 22:06:31 +00:00
|
|
|
Check if ``validator`` is fully withdrawable.
|
2021-12-01 18:37:30 +00:00
|
|
|
"""
|
2022-06-08 19:16:12 +00:00
|
|
|
return (
|
|
|
|
has_eth1_withdrawal_credential(validator)
|
2022-09-19 17:05:47 +00:00
|
|
|
and validator.withdrawable_epoch <= epoch
|
|
|
|
and balance > 0
|
2022-06-08 19:16:12 +00:00
|
|
|
)
|
|
|
|
```
|
|
|
|
|
|
|
|
#### `is_partially_withdrawable_validator`
|
|
|
|
|
|
|
|
```python
|
|
|
|
def is_partially_withdrawable_validator(validator: Validator, balance: Gwei) -> bool:
|
|
|
|
"""
|
|
|
|
Check if ``validator`` is partially withdrawable.
|
|
|
|
"""
|
|
|
|
has_max_effective_balance = validator.effective_balance == MAX_EFFECTIVE_BALANCE
|
|
|
|
has_excess_balance = balance > MAX_EFFECTIVE_BALANCE
|
|
|
|
return has_eth1_withdrawal_credential(validator) and has_max_effective_balance and has_excess_balance
|
2021-12-01 18:37:30 +00:00
|
|
|
```
|
|
|
|
|
|
|
|
## Beacon chain state transition function
|
|
|
|
|
2022-12-15 16:49:14 +00:00
|
|
|
### Epoch processing
|
|
|
|
|
2023-01-02 15:07:00 +00:00
|
|
|
*Note*: The function `process_historical_summaries_update` replaces `process_historical_roots_update` in Bellatrix.
|
2022-12-15 16:49:14 +00:00
|
|
|
|
|
|
|
```python
|
|
|
|
def process_epoch(state: BeaconState) -> None:
|
|
|
|
process_justification_and_finalization(state)
|
|
|
|
process_inactivity_updates(state)
|
|
|
|
process_rewards_and_penalties(state)
|
|
|
|
process_registry_updates(state)
|
|
|
|
process_slashings(state)
|
|
|
|
process_eth1_data_reset(state)
|
|
|
|
process_effective_balance_updates(state)
|
|
|
|
process_slashings_reset(state)
|
|
|
|
process_randao_mixes_reset(state)
|
2023-01-02 15:07:00 +00:00
|
|
|
process_historical_summaries_update(state) # [Modified in Capella]
|
2022-12-15 16:49:14 +00:00
|
|
|
process_participation_flag_updates(state)
|
|
|
|
process_sync_committee_updates(state)
|
|
|
|
```
|
|
|
|
|
2023-01-02 15:07:00 +00:00
|
|
|
#### Historical summaries updates
|
2022-12-15 16:49:14 +00:00
|
|
|
|
|
|
|
```python
|
2023-01-02 15:07:00 +00:00
|
|
|
def process_historical_summaries_update(state: BeaconState) -> None:
|
2022-12-15 16:49:14 +00:00
|
|
|
# Set historical block root accumulator.
|
|
|
|
next_epoch = Epoch(get_current_epoch(state) + 1)
|
|
|
|
if next_epoch % (SLOTS_PER_HISTORICAL_ROOT // SLOTS_PER_EPOCH) == 0:
|
2023-01-02 15:07:00 +00:00
|
|
|
historical_summary = HistoricalSummary(
|
2023-01-03 13:50:06 +00:00
|
|
|
block_summary_root=hash_tree_root(state.block_roots),
|
|
|
|
state_summary_root=hash_tree_root(state.state_roots),
|
2022-12-15 16:49:14 +00:00
|
|
|
)
|
2023-01-02 15:07:00 +00:00
|
|
|
state.historical_summaries.append(historical_summary)
|
2022-12-15 16:49:14 +00:00
|
|
|
```
|
|
|
|
|
2022-02-23 22:05:55 +00:00
|
|
|
### Block processing
|
|
|
|
|
|
|
|
```python
|
|
|
|
def process_block(state: BeaconState, block: BeaconBlock) -> None:
|
|
|
|
process_block_header(state, block)
|
2022-02-24 21:26:15 +00:00
|
|
|
if is_execution_enabled(state, block.body):
|
2022-03-10 17:38:47 +00:00
|
|
|
process_withdrawals(state, block.body.execution_payload) # [New in Capella]
|
2023-05-15 16:27:00 +00:00
|
|
|
process_execution_payload(state, block.body, EXECUTION_ENGINE) # [Modified in Capella]
|
2022-02-23 22:05:55 +00:00
|
|
|
process_randao(state, block.body)
|
|
|
|
process_eth1_data(state, block.body)
|
2022-11-07 14:09:09 +00:00
|
|
|
process_operations(state, block.body) # [Modified in Capella]
|
2022-02-23 22:05:55 +00:00
|
|
|
process_sync_aggregate(state, block.body.sync_aggregate)
|
|
|
|
```
|
|
|
|
|
2022-10-31 18:32:16 +00:00
|
|
|
#### New `get_expected_withdrawals`
|
2022-02-23 22:05:55 +00:00
|
|
|
|
|
|
|
```python
|
2022-10-31 18:32:16 +00:00
|
|
|
def get_expected_withdrawals(state: BeaconState) -> Sequence[Withdrawal]:
|
2022-10-28 14:38:32 +00:00
|
|
|
epoch = get_current_epoch(state)
|
2022-10-31 18:32:16 +00:00
|
|
|
withdrawal_index = state.next_withdrawal_index
|
2022-11-10 11:33:11 +00:00
|
|
|
validator_index = state.next_withdrawal_validator_index
|
2022-11-03 17:58:55 +00:00
|
|
|
withdrawals: List[Withdrawal] = []
|
2022-11-11 17:47:53 +00:00
|
|
|
bound = min(len(state.validators), MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP)
|
|
|
|
for _ in range(bound):
|
2022-11-04 21:47:56 +00:00
|
|
|
validator = state.validators[validator_index]
|
2022-11-04 10:20:26 +00:00
|
|
|
balance = state.balances[validator_index]
|
2022-11-04 21:47:56 +00:00
|
|
|
if is_fully_withdrawable_validator(validator, balance, epoch):
|
2022-11-03 17:58:55 +00:00
|
|
|
withdrawals.append(Withdrawal(
|
2022-10-31 18:32:16 +00:00
|
|
|
index=withdrawal_index,
|
2022-11-04 10:20:26 +00:00
|
|
|
validator_index=validator_index,
|
2022-11-04 21:47:56 +00:00
|
|
|
address=ExecutionAddress(validator.withdrawal_credentials[12:]),
|
2022-10-31 18:32:16 +00:00
|
|
|
amount=balance,
|
2022-11-03 17:58:55 +00:00
|
|
|
))
|
2022-11-04 12:13:21 +00:00
|
|
|
withdrawal_index += WithdrawalIndex(1)
|
2022-11-04 21:47:56 +00:00
|
|
|
elif is_partially_withdrawable_validator(validator, balance):
|
2022-11-03 17:58:55 +00:00
|
|
|
withdrawals.append(Withdrawal(
|
2022-10-31 18:32:16 +00:00
|
|
|
index=withdrawal_index,
|
2022-11-04 10:20:26 +00:00
|
|
|
validator_index=validator_index,
|
2022-11-04 21:47:56 +00:00
|
|
|
address=ExecutionAddress(validator.withdrawal_credentials[12:]),
|
2022-10-31 19:52:07 +00:00
|
|
|
amount=balance - MAX_EFFECTIVE_BALANCE,
|
2022-11-03 17:58:55 +00:00
|
|
|
))
|
2022-11-04 12:13:21 +00:00
|
|
|
withdrawal_index += WithdrawalIndex(1)
|
2022-11-03 16:21:29 +00:00
|
|
|
if len(withdrawals) == MAX_WITHDRAWALS_PER_PAYLOAD:
|
2022-11-01 20:06:35 +00:00
|
|
|
break
|
2022-11-08 22:53:58 +00:00
|
|
|
validator_index = ValidatorIndex((validator_index + 1) % len(state.validators))
|
2022-11-03 18:07:03 +00:00
|
|
|
return withdrawals
|
2022-10-31 18:32:16 +00:00
|
|
|
```
|
2022-02-23 22:05:55 +00:00
|
|
|
|
2022-10-31 18:32:16 +00:00
|
|
|
#### New `process_withdrawals`
|
2022-02-23 22:05:55 +00:00
|
|
|
|
2022-10-31 18:32:16 +00:00
|
|
|
```python
|
|
|
|
def process_withdrawals(state: BeaconState, payload: ExecutionPayload) -> None:
|
|
|
|
expected_withdrawals = get_expected_withdrawals(state)
|
|
|
|
assert len(payload.withdrawals) == len(expected_withdrawals)
|
|
|
|
|
2022-11-03 16:21:29 +00:00
|
|
|
for expected_withdrawal, withdrawal in zip(expected_withdrawals, payload.withdrawals):
|
2022-10-31 18:32:16 +00:00
|
|
|
assert withdrawal == expected_withdrawal
|
|
|
|
decrease_balance(state, withdrawal.validator_index, withdrawal.amount)
|
2022-11-11 17:47:53 +00:00
|
|
|
|
|
|
|
# Update the next withdrawal index if this block contained withdrawals
|
|
|
|
if len(expected_withdrawals) != 0:
|
2022-11-07 20:56:01 +00:00
|
|
|
latest_withdrawal = expected_withdrawals[-1]
|
2022-11-07 20:29:56 +00:00
|
|
|
state.next_withdrawal_index = WithdrawalIndex(latest_withdrawal.index + 1)
|
2022-11-11 17:47:53 +00:00
|
|
|
|
|
|
|
# Update the next validator index to start the next withdrawal sweep
|
|
|
|
if len(expected_withdrawals) == MAX_WITHDRAWALS_PER_PAYLOAD:
|
|
|
|
# Next sweep starts after the latest withdrawal's validator index
|
|
|
|
next_validator_index = ValidatorIndex((expected_withdrawals[-1].validator_index + 1) % len(state.validators))
|
|
|
|
state.next_withdrawal_validator_index = next_validator_index
|
|
|
|
else:
|
|
|
|
# Advance sweep by the max length of the sweep if there was not a full set of withdrawals
|
|
|
|
next_index = state.next_withdrawal_validator_index + MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP
|
|
|
|
next_validator_index = ValidatorIndex(next_index % len(state.validators))
|
2022-11-10 11:33:11 +00:00
|
|
|
state.next_withdrawal_validator_index = next_validator_index
|
2022-02-23 22:05:55 +00:00
|
|
|
```
|
|
|
|
|
|
|
|
#### Modified `process_execution_payload`
|
|
|
|
|
|
|
|
*Note*: The function `process_execution_payload` is modified to use the new `ExecutionPayloadHeader` type.
|
|
|
|
|
|
|
|
```python
|
2023-05-15 16:27:00 +00:00
|
|
|
def process_execution_payload(state: BeaconState, body: BeaconBlockBody, execution_engine: ExecutionEngine) -> None:
|
|
|
|
payload = body.execution_payload
|
|
|
|
|
2022-02-23 22:05:55 +00:00
|
|
|
# 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
|
2022-02-24 21:26:15 +00:00
|
|
|
# Verify prev_randao
|
|
|
|
assert payload.prev_randao == get_randao_mix(state, get_current_epoch(state))
|
2022-02-23 22:05:55 +00:00
|
|
|
# Verify timestamp
|
|
|
|
assert payload.timestamp == compute_timestamp_at_slot(state, state.slot)
|
|
|
|
# Verify the execution payload is valid
|
2023-05-24 03:12:03 +00:00
|
|
|
assert execution_engine.verify_and_notify_new_payload(NewPayloadRequest(execution_payload=payload))
|
2022-02-23 22:05:55 +00:00
|
|
|
# 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,
|
2022-02-24 21:26:15 +00:00
|
|
|
receipts_root=payload.receipts_root,
|
2022-02-23 22:05:55 +00:00
|
|
|
logs_bloom=payload.logs_bloom,
|
2022-02-24 21:26:15 +00:00
|
|
|
prev_randao=payload.prev_randao,
|
2022-02-23 22:05:55 +00:00
|
|
|
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),
|
2022-03-22 14:00:53 +00:00
|
|
|
withdrawals_root=hash_tree_root(payload.withdrawals), # [New in Capella]
|
2022-02-23 22:05:55 +00:00
|
|
|
)
|
|
|
|
```
|
2022-03-16 21:41:37 +00:00
|
|
|
|
|
|
|
#### 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
|
|
|
|
assert len(body.deposits) == min(MAX_DEPOSITS, state.eth1_data.deposit_count - state.eth1_deposit_index)
|
|
|
|
|
|
|
|
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)
|
2022-03-23 16:54:40 +00:00
|
|
|
for_ops(body.bls_to_execution_changes, process_bls_to_execution_change) # [New in Capella]
|
2022-03-16 21:41:37 +00:00
|
|
|
```
|
|
|
|
|
|
|
|
#### New `process_bls_to_execution_change`
|
|
|
|
|
|
|
|
```python
|
|
|
|
def process_bls_to_execution_change(state: BeaconState,
|
|
|
|
signed_address_change: SignedBLSToExecutionChange) -> None:
|
|
|
|
address_change = signed_address_change.message
|
|
|
|
|
|
|
|
assert address_change.validator_index < len(state.validators)
|
|
|
|
|
|
|
|
validator = state.validators[address_change.validator_index]
|
|
|
|
|
|
|
|
assert validator.withdrawal_credentials[:1] == BLS_WITHDRAWAL_PREFIX
|
|
|
|
assert validator.withdrawal_credentials[1:] == hash(address_change.from_bls_pubkey)[1:]
|
|
|
|
|
2023-01-12 15:10:22 +00:00
|
|
|
# Fork-agnostic domain since address changes are valid across forks
|
|
|
|
domain = compute_domain(DOMAIN_BLS_TO_EXECUTION_CHANGE, genesis_validators_root=state.genesis_validators_root)
|
2022-03-16 21:41:37 +00:00
|
|
|
signing_root = compute_signing_root(address_change, domain)
|
|
|
|
assert bls.Verify(address_change.from_bls_pubkey, signing_root, signed_address_change.signature)
|
|
|
|
|
|
|
|
validator.withdrawal_credentials = (
|
|
|
|
ETH1_ADDRESS_WITHDRAWAL_PREFIX
|
2022-05-10 15:37:01 +00:00
|
|
|
+ b'\x00' * 11
|
2022-03-16 21:41:37 +00:00
|
|
|
+ address_change.to_execution_address
|
|
|
|
)
|
|
|
|
```
|
2022-07-15 14:18:33 +00:00
|
|
|
|
|
|
|
## Testing
|
|
|
|
|
|
|
|
*Note*: The function `initialize_beacon_state_from_eth1` is modified for pure Capella testing only.
|
|
|
|
Modifications include:
|
2022-07-18 06:45:00 +00:00
|
|
|
1. Use `CAPELLA_FORK_VERSION` as the previous and current fork version.
|
2022-07-15 14:18:33 +00:00
|
|
|
2. Utilize the Capella `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
|
|
|
|
```
|