eth2.0-specs/specs/phase0/beacon-chain.md

1634 lines
59 KiB
Markdown
Raw Normal View History

# Ethereum 2.0 Phase 0 -- The Beacon Chain
2018-09-20 05:20:49 +00:00
2019-05-06 15:30:32 +00:00
**Notice**: This document is a work-in-progress for researchers and implementers.
2018-09-20 05:20:49 +00:00
2018-11-28 07:23:37 +00:00
## Table of contents
2018-12-01 05:29:19 +00:00
<!-- TOC -->
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
2018-12-01 05:29:19 +00:00
- [Introduction](#introduction)
- [Notation](#notation)
- [Custom types](#custom-types)
- [Constants](#constants)
- [Configuration](#configuration)
- [Misc](#misc)
- [Gwei values](#gwei-values)
- [Initial values](#initial-values)
- [Time parameters](#time-parameters)
- [State list lengths](#state-list-lengths)
- [Rewards and penalties](#rewards-and-penalties)
- [Max operations per block](#max-operations-per-block)
- [Domain types](#domain-types)
- [Containers](#containers)
- [Misc dependencies](#misc-dependencies)
- [`Fork`](#fork)
- [`Checkpoint`](#checkpoint)
- [`Validator`](#validator)
- [`AttestationData`](#attestationdata)
- [`IndexedAttestation`](#indexedattestation)
- [`PendingAttestation`](#pendingattestation)
- [`Eth1Data`](#eth1data)
- [`HistoricalBatch`](#historicalbatch)
- [`DepositMessage`](#depositmessage)
- [`DepositData`](#depositdata)
- [`BeaconBlockHeader`](#beaconblockheader)
2020-01-03 06:46:27 +00:00
- [`SigningRoot`](#signingroot)
- [Beacon operations](#beacon-operations)
- [`ProposerSlashing`](#proposerslashing)
- [`AttesterSlashing`](#attesterslashing)
- [`Attestation`](#attestation)
- [`Deposit`](#deposit)
- [`VoluntaryExit`](#voluntaryexit)
- [Beacon blocks](#beacon-blocks)
- [`BeaconBlockBody`](#beaconblockbody)
- [`BeaconBlock`](#beaconblock)
- [Beacon state](#beacon-state)
- [`BeaconState`](#beaconstate)
- [Signed envelopes](#signed-envelopes)
- [`SignedVoluntaryExit`](#signedvoluntaryexit)
- [`SignedBeaconBlock`](#signedbeaconblock)
- [`SignedBeaconBlockHeader`](#signedbeaconblockheader)
- [Helper functions](#helper-functions)
- [Math](#math)
- [`integer_squareroot`](#integer_squareroot)
- [`xor`](#xor)
- [`int_to_bytes`](#int_to_bytes)
- [`bytes_to_int`](#bytes_to_int)
- [Crypto](#crypto)
- [`hash`](#hash)
- [`hash_tree_root`](#hash_tree_root)
2019-12-17 13:40:26 +00:00
- [BLS Signatures](#bls-signatures)
- [Predicates](#predicates)
- [`is_active_validator`](#is_active_validator)
2019-12-12 16:48:53 +00:00
- [`is_eligible_for_activation_queue`](#is_eligible_for_activation_queue)
- [`is_eligible_for_activation`](#is_eligible_for_activation)
- [`is_slashable_validator`](#is_slashable_validator)
- [`is_slashable_attestation_data`](#is_slashable_attestation_data)
- [`is_valid_indexed_attestation`](#is_valid_indexed_attestation)
- [`is_valid_merkle_branch`](#is_valid_merkle_branch)
- [Misc](#misc-1)
- [`compute_shuffled_index`](#compute_shuffled_index)
- [`compute_proposer_index`](#compute_proposer_index)
- [`compute_committee`](#compute_committee)
- [`compute_epoch_at_slot`](#compute_epoch_at_slot)
- [`compute_start_slot_at_epoch`](#compute_start_slot_at_epoch)
- [`compute_activation_exit_epoch`](#compute_activation_exit_epoch)
- [`compute_domain`](#compute_domain)
2020-01-03 06:46:27 +00:00
- [`compute_signing_root`](#compute_signing_root)
- [Beacon state accessors](#beacon-state-accessors)
- [`get_current_epoch`](#get_current_epoch)
- [`get_previous_epoch`](#get_previous_epoch)
- [`get_block_root`](#get_block_root)
- [`get_block_root_at_slot`](#get_block_root_at_slot)
- [`get_randao_mix`](#get_randao_mix)
- [`get_active_validator_indices`](#get_active_validator_indices)
- [`get_validator_churn_limit`](#get_validator_churn_limit)
- [`get_seed`](#get_seed)
- [`get_committee_count_at_slot`](#get_committee_count_at_slot)
- [`get_beacon_committee`](#get_beacon_committee)
- [`get_beacon_proposer_index`](#get_beacon_proposer_index)
- [`get_total_balance`](#get_total_balance)
- [`get_total_active_balance`](#get_total_active_balance)
- [`get_domain`](#get_domain)
- [`get_indexed_attestation`](#get_indexed_attestation)
- [`get_attesting_indices`](#get_attesting_indices)
- [Beacon state mutators](#beacon-state-mutators)
- [`increase_balance`](#increase_balance)
- [`decrease_balance`](#decrease_balance)
- [`initiate_validator_exit`](#initiate_validator_exit)
- [`slash_validator`](#slash_validator)
- [Genesis](#genesis)
- [Genesis state](#genesis-state)
- [Genesis block](#genesis-block)
- [Beacon chain state transition function](#beacon-chain-state-transition-function)
- [Epoch processing](#epoch-processing)
- [Helper functions](#helper-functions-1)
- [Justification and finalization](#justification-and-finalization)
- [Rewards and penalties](#rewards-and-penalties-1)
- [Registry updates](#registry-updates)
- [Slashings](#slashings)
- [Final updates](#final-updates)
- [Block processing](#block-processing)
- [Block header](#block-header)
- [RANDAO](#randao)
- [Eth1 data](#eth1-data)
- [Operations](#operations)
- [Proposer slashings](#proposer-slashings)
- [Attester slashings](#attester-slashings)
- [Attestations](#attestations)
- [Deposits](#deposits)
- [Voluntary exits](#voluntary-exits)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
2018-12-01 05:29:19 +00:00
<!-- /TOC -->
2018-11-28 07:23:37 +00:00
## Introduction
2018-09-20 05:20:49 +00:00
This document represents the specification for Phase 0 of Ethereum 2.0 -- The Beacon Chain.
2019-06-30 13:45:36 +00:00
At the core of Ethereum 2.0 is a system chain called the "beacon chain". The beacon chain stores and manages the registry of validators. In the initial deployment phases of Ethereum 2.0, the only mechanism to become a validator is to make a one-way ETH transaction to a deposit contract on Ethereum 1.0. Activation as a validator happens when Ethereum 1.0 deposit receipts are processed by the beacon chain, the activation balance is reached, and a queuing process is completed. Exit is either voluntary or done forcibly as a penalty for misbehavior.
2019-10-17 01:47:39 +00:00
The primary source of load on the beacon chain is "attestations". Attestations are simultaneously availability votes for a shard block (Phase 1) and proof-of-stake votes for a beacon block (Phase 0).
2018-09-20 05:20:49 +00:00
2018-11-29 01:56:25 +00:00
## Notation
2019-06-30 13:45:36 +00:00
Code snippets appearing in `this style` are to be interpreted as Python 3 code.
2018-09-20 05:20:49 +00:00
## Custom types
We define the following Python custom types for type hinting and readability:
| Name | SSZ equivalent | Description |
| - | - | - |
| `Slot` | `uint64` | a slot number |
| `Epoch` | `uint64` | an epoch number |
2019-10-17 08:47:51 +00:00
| `CommitteeIndex` | `uint64` | a committee index at a slot |
| `ValidatorIndex` | `uint64` | a validator registry index |
| `Gwei` | `uint64` | an amount in Gwei |
| `Root` | `Bytes32` | a Merkle root |
2019-06-15 21:32:52 +00:00
| `Version` | `Bytes4` | a fork version number |
2019-09-22 19:27:42 +00:00
| `DomainType` | `Bytes4` | a domain type |
2019-06-30 20:12:02 +00:00
| `Domain` | `Bytes8` | a signature domain |
| `BLSPubkey` | `Bytes48` | a BLS12-381 public key |
| `BLSSignature` | `Bytes96` | a BLS12-381 signature |
2018-11-28 07:23:37 +00:00
## Constants
2018-09-20 05:20:49 +00:00
2019-06-17 21:19:44 +00:00
The following values are (non-configurable) constants used throughout the specification.
| Name | Value |
| - | - |
| `GENESIS_SLOT` | `Slot(0)` |
| `GENESIS_EPOCH` | `Epoch(0)` |
2019-06-18 14:24:23 +00:00
| `FAR_FUTURE_EPOCH` | `Epoch(2**64 - 1)` |
| `BASE_REWARDS_PER_EPOCH` | `4` |
2019-06-17 21:19:44 +00:00
| `DEPOSIT_CONTRACT_TREE_DEPTH` | `2**5` (= 32) |
2019-06-30 19:51:10 +00:00
| `JUSTIFICATION_BITS_LENGTH` | `4` |
2019-06-30 22:21:19 +00:00
| `ENDIANNESS` | `'little'` |
2019-06-17 21:19:44 +00:00
## Configuration
2019-08-19 11:47:09 +00:00
*Note*: The default mainnet configuration values are included here for spec-design purposes. The different configurations for mainnet, testnets, and YAML-based testing can be found in the [`configs/constant_presets`](../../configs) directory. These configurations are updated for releases and may be out of sync during `dev` changes.
2019-04-03 02:35:40 +00:00
### Misc
2019-02-12 13:37:30 +00:00
| Name | Value |
| - | - |
2019-10-17 08:47:51 +00:00
| `MAX_COMMITTEES_PER_SLOT` | `2**6` (= 64) |
2019-02-12 13:37:30 +00:00
| `TARGET_COMMITTEE_SIZE` | `2**7` (= 128) |
| `MAX_VALIDATORS_PER_COMMITTEE` | `2**11` (= 2,048) |
2019-04-14 03:14:05 +00:00
| `MIN_PER_EPOCH_CHURN_LIMIT` | `2**2` (= 4) |
2019-04-14 07:28:45 +00:00
| `CHURN_LIMIT_QUOTIENT` | `2**16` (= 65,536) |
2019-05-06 15:30:32 +00:00
| `SHUFFLE_ROUND_COUNT` | `90` |
| `MIN_GENESIS_ACTIVE_VALIDATOR_COUNT` | `2**14` (= 16,384) |
| `MIN_GENESIS_TIME` | `1578009600` (Jan 3, 2020) |
2019-12-12 02:17:20 +00:00
- For the safety of committees, `TARGET_COMMITTEE_SIZE` exceeds [the recommended minimum committee size of 111](http://web.archive.org/web/20190504131341/https://vitalik.ca/files/Ithaca201807_Sharding.pdf); with sufficient active validators (at least `SLOTS_PER_EPOCH * TARGET_COMMITTEE_SIZE`), the shuffling algorithm ensures committee sizes of at least `TARGET_COMMITTEE_SIZE`. (Unbiasable randomness with a Verifiable Delay Function (VDF) will improve committee robustness and lower the safe minimum committee size.)
2019-02-06 16:34:19 +00:00
### Gwei values
| Name | Value |
2019-06-21 20:30:22 +00:00
| - | - |
| `MIN_DEPOSIT_AMOUNT` | `Gwei(2**0 * 10**9)` (= 1,000,000,000) |
| `MAX_EFFECTIVE_BALANCE` | `Gwei(2**5 * 10**9)` (= 32,000,000,000) |
| `EJECTION_BALANCE` | `Gwei(2**4 * 10**9)` (= 16,000,000,000) |
| `EFFECTIVE_BALANCE_INCREMENT` | `Gwei(2**0 * 10**9)` (= 1,000,000,000) |
### Initial values
| Name | Value |
| - | - |
| `GENESIS_FORK_VERSION` | `Version('0x00000000')` |
2019-12-27 08:37:26 +00:00
| `BLS_WITHDRAWAL_PREFIX` | `Bytes1('0x00')` |
### Time parameters
2019-07-01 03:13:41 +00:00
| Name | Value | Unit | Duration |
| - | - | :-: | :-: |
2020-01-05 22:29:23 +00:00
| `MIN_GENESIS_DELAY` | `86400` | seconds | 1 day |
| `SECONDS_PER_SLOT` | `12` | seconds | 12 seconds |
| `MIN_ATTESTATION_INCLUSION_DELAY` | `2**0` (= 1) | slots | 12 seconds |
| `SLOTS_PER_EPOCH` | `2**5` (= 32) | slots | 6.4 minutes |
2019-07-01 03:13:41 +00:00
| `MIN_SEED_LOOKAHEAD` | `2**0` (= 1) | epochs | 6.4 minutes |
| `MAX_SEED_LOOKAHEAD` | `2**2` (= 4) | epochs | 25.6 minutes |
| `MIN_EPOCHS_TO_INACTIVITY_PENALTY` | `2**2` (= 4) | epochs | 25.6 minutes |
2019-11-01 04:31:08 +00:00
| `SLOTS_PER_ETH1_VOTING_PERIOD` | `2**10` (= 1,024) | slots | ~3.4 hours |
| `SLOTS_PER_HISTORICAL_ROOT` | `2**13` (= 8,192) | slots | ~27 hours |
2019-07-01 03:13:41 +00:00
| `MIN_VALIDATOR_WITHDRAWABILITY_DELAY` | `2**8` (= 256) | epochs | ~27 hours |
| `PERSISTENT_COMMITTEE_PERIOD` | `2**11` (= 2,048) | epochs | 9 days |
2019-03-19 16:21:17 +00:00
2019-01-28 00:25:29 +00:00
### State list lengths
2019-07-01 01:17:03 +00:00
| Name | Value | Unit | Duration |
| - | - | :-: | :-: |
2019-07-01 01:17:03 +00:00
| `EPOCHS_PER_HISTORICAL_VECTOR` | `2**16` (= 65,536) | epochs | ~0.8 years |
| `EPOCHS_PER_SLASHINGS_VECTOR` | `2**13` (= 8,192) | epochs | ~36 days |
| `HISTORICAL_ROOTS_LIMIT` | `2**24` (= 16,777,216) | historical roots | ~26,131 years |
| `VALIDATOR_REGISTRY_LIMIT` | `2**40` (= 1,099,511,627,776) | validators |
2019-05-29 20:40:46 +00:00
### Rewards and penalties
2018-10-03 07:27:39 +00:00
| Name | Value |
| - | - |
Tweak inclusion delay rewards and set BASE_REWARD_FACTOR Substantive changes: 1) Split the inclusion delay reward between attester and proposer to add up to at most one base reward. This is analogous to the reward logic in `slash_validator`, and makes the `BASE_REWARDS_PER_EPOCH` constant include proposer rewards. 2) Double `BASE_REWARD_FACTOR` to 2^6 (addressing item 4 in #1054). When the total effective balance is 2^17 ETH then maximum annual issuance is a bit below 2^21 ETH. Maximum annual issuance happens when a) all validators make perfect attestations (matching source, target, head, as well as consistent crosslink data), b) all attestations are included as fast as possible (in particular, no skip blocks), and c) there are no slashings. ```python BASE_REWARD_FACTOR = 2**6 SLOTS_PER_EPOCH = 2**6 SECONDS_PER_SLOT = 6 BASE_REWARDS_PER_EPOCH = 5 GWEI_PER_ETH = 10**9 MAX_TOTAL_EFFECTIVE_BALANCE = 2**27 * GWEI_PER_ETH TARGET_MAX_ISSUANCE = 2**21 * GWEI_PER_ETH def integer_squareroot(n: int) -> int: """ The largest integer ``x`` such that ``x**2`` is less than or equal to ``n``. """ assert n >= 0 x = n y = (x + 1) // 2 while y < x: x = y y = (x + n // x) // 2 return x MAX_REWARDS_PER_EPOCH = MAX_TOTAL_EFFECTIVE_BALANCE * BASE_REWARD_FACTOR // integer_squareroot(MAX_TOTAL_EFFECTIVE_BALANCE) // BASE_REWARDS_PER_EPOCH EPOCHS_PER_YEAR = 365.25*24*60*60 / (SECONDS_PER_SLOT * SLOTS_PER_EPOCH) MAX_REWARDS_PER_YEAR = EPOCHS_PER_YEAR * MAX_REWARDS_PER_EPOCH * BASE_REWARDS_PER_EPOCH print(MAX_REWARDS_PER_YEAR / TARGET_MAX_ISSUANCE) ```
2019-06-10 14:14:32 +00:00
| `BASE_REWARD_FACTOR` | `2**6` (= 64) |
| `WHISTLEBLOWER_REWARD_QUOTIENT` | `2**9` (= 512) |
2019-03-26 13:21:49 +00:00
| `PROPOSER_REWARD_QUOTIENT` | `2**3` (= 8) |
| `INACTIVITY_PENALTY_QUOTIENT` | `2**25` (= 33,554,432) |
2019-04-22 06:34:50 +00:00
| `MIN_SLASHING_PENALTY_QUOTIENT` | `2**5` (= 32) |
2018-10-03 07:27:39 +00:00
2019-06-30 19:25:58 +00:00
- The `INACTIVITY_PENALTY_QUOTIENT` equals `INVERSE_SQRT_E_DROP_TIME**2` where `INVERSE_SQRT_E_DROP_TIME := 2**12 epochs` (about 18 days) is the time it takes the inactivity penalty to reduce the balance of non-participating validators to about `1/sqrt(e) ~= 60.6%`. Indeed, the balance retained by offline validators after `n` epochs is about `(1 - 1/INACTIVITY_PENALTY_QUOTIENT)**(n**2/2)`; so after `INVERSE_SQRT_E_DROP_TIME` epochs, it is roughly `(1 - 1/INACTIVITY_PENALTY_QUOTIENT)**(INACTIVITY_PENALTY_QUOTIENT/2) ~= 1/sqrt(e)`.
### Max operations per block
2018-10-04 13:16:31 +00:00
2018-12-07 01:02:23 +00:00
| Name | Value |
2018-12-07 01:07:26 +00:00
| - | - |
2018-12-07 01:02:23 +00:00
| `MAX_PROPOSER_SLASHINGS` | `2**4` (= 16) |
| `MAX_ATTESTER_SLASHINGS` | `2**0` (= 1) |
2018-12-07 01:02:23 +00:00
| `MAX_ATTESTATIONS` | `2**7` (= 128) |
| `MAX_DEPOSITS` | `2**4` (= 16) |
2019-02-12 12:24:19 +00:00
| `MAX_VOLUNTARY_EXITS` | `2**4` (= 16) |
2018-10-04 13:16:31 +00:00
2019-09-22 19:27:42 +00:00
### Domain types
2019-06-30 20:12:02 +00:00
2018-12-10 19:55:11 +00:00
| Name | Value |
| - | - |
2019-12-30 05:52:00 +00:00
| `DOMAIN_BEACON_PROPOSER` | `DomainType('0x00000000')` |
| `DOMAIN_BEACON_ATTESTER` | `DomainType('0x01000000')` |
| `DOMAIN_RANDAO` | `DomainType('0x02000000')` |
| `DOMAIN_DEPOSIT` | `DomainType('0x03000000')` |
| `DOMAIN_VOLUNTARY_EXIT` | `DomainType('0x04000000')` |
2019-06-09 19:41:21 +00:00
## Containers
The following types are [SimpleSerialize (SSZ)](../../ssz/simple-serialize.md) containers.
2019-06-09 19:41:21 +00:00
*Note*: The definitions are ordered topologically to facilitate execution of the spec.
*Note*: Fields missing in container instantiations default to their zero value.
2019-03-07 18:02:13 +00:00
### Misc dependencies
2019-03-07 18:02:13 +00:00
#### `Fork`
```python
class Fork(Container):
2019-06-15 21:32:52 +00:00
previous_version: Version
current_version: Version
epoch: Epoch # Epoch of latest fork
```
#### `Checkpoint`
```python
class Checkpoint(Container):
epoch: Epoch
root: Root
```
#### `Validator`
2018-12-06 23:51:01 +00:00
```python
class Validator(Container):
2019-06-15 21:23:44 +00:00
pubkey: BLSPubkey
withdrawal_credentials: Bytes32 # Commitment to pubkey for withdrawals
effective_balance: Gwei # Balance at stake
slashed: boolean
2019-06-09 19:41:21 +00:00
# Status epochs
activation_eligibility_epoch: Epoch # When criteria for activation were met
activation_epoch: Epoch
exit_epoch: Epoch
2019-10-24 12:49:07 +00:00
withdrawable_epoch: Epoch # When validator can withdraw funds
2018-12-06 23:51:01 +00:00
```
2019-03-07 18:02:13 +00:00
#### `AttestationData`
```python
class AttestationData(Container):
slot: Slot
2019-10-17 08:47:51 +00:00
index: CommitteeIndex
2019-03-12 10:17:34 +00:00
# LMD GHOST vote
beacon_block_root: Root
2019-03-12 10:17:34 +00:00
# FFG vote
2019-06-22 20:49:53 +00:00
source: Checkpoint
target: Checkpoint
```
2019-03-26 19:40:19 +00:00
#### `IndexedAttestation`
2018-12-07 01:02:23 +00:00
2019-03-07 18:02:13 +00:00
```python
class IndexedAttestation(Container):
2019-11-02 03:02:53 +00:00
attesting_indices: List[ValidatorIndex, MAX_VALIDATORS_PER_COMMITTEE]
data: AttestationData
2019-06-15 21:23:44 +00:00
signature: BLSSignature
2019-03-07 18:02:13 +00:00
```
#### `PendingAttestation`
```python
class PendingAttestation(Container):
aggregation_bits: Bitlist[MAX_VALIDATORS_PER_COMMITTEE]
data: AttestationData
2019-06-09 19:41:21 +00:00
inclusion_delay: Slot
proposer_index: ValidatorIndex
2019-03-07 18:02:13 +00:00
```
#### `Eth1Data`
```python
class Eth1Data(Container):
deposit_root: Root
deposit_count: uint64
block_hash: Bytes32
```
2019-03-08 17:13:05 +00:00
#### `HistoricalBatch`
```python
class HistoricalBatch(Container):
block_roots: Vector[Root, SLOTS_PER_HISTORICAL_ROOT]
state_roots: Vector[Root, SLOTS_PER_HISTORICAL_ROOT]
2019-03-08 17:13:05 +00:00
```
2019-11-21 22:13:45 +00:00
#### `DepositMessage`
```python
class DepositMessage(Container):
pubkey: BLSPubkey
withdrawal_credentials: Bytes32
amount: Gwei
```
2019-03-15 11:18:06 +00:00
#### `DepositData`
```python
class DepositData(Container):
2019-06-15 21:23:44 +00:00
pubkey: BLSPubkey
withdrawal_credentials: Bytes32
amount: Gwei
signature: BLSSignature # Signing over DepositMessage
```
2019-03-07 18:02:13 +00:00
#### `BeaconBlockHeader`
```python
class BeaconBlockHeader(Container):
slot: Slot
parent_root: Root
state_root: Root
body_root: Root
2019-11-21 22:13:45 +00:00
```
2020-01-03 06:46:27 +00:00
#### `SigningRoot`
2019-12-17 10:04:56 +00:00
```python
2020-01-03 06:46:27 +00:00
class SigningRoot(Container):
object_root: Root
2019-12-17 10:04:56 +00:00
domain: Domain
```
### Beacon operations
2019-03-07 18:02:13 +00:00
#### `ProposerSlashing`
```python
class ProposerSlashing(Container):
proposer_index: ValidatorIndex
2019-11-21 22:13:45 +00:00
signed_header_1: SignedBeaconBlockHeader
signed_header_2: SignedBeaconBlockHeader
2019-03-07 18:02:13 +00:00
```
#### `AttesterSlashing`
```python
class AttesterSlashing(Container):
attestation_1: IndexedAttestation
attestation_2: IndexedAttestation
2019-03-07 18:02:13 +00:00
```
#### `Attestation`
```python
class Attestation(Container):
aggregation_bits: Bitlist[MAX_VALIDATORS_PER_COMMITTEE]
data: AttestationData
2019-06-15 21:23:44 +00:00
signature: BLSSignature
```
2019-03-07 18:02:13 +00:00
#### `Deposit`
2019-03-07 18:02:13 +00:00
```python
class Deposit(Container):
proof: Vector[Bytes32, DEPOSIT_CONTRACT_TREE_DEPTH + 1] # Merkle path to deposit root
data: DepositData
2019-03-07 18:02:13 +00:00
```
#### `VoluntaryExit`
2018-12-07 01:02:23 +00:00
```python
class VoluntaryExit(Container):
epoch: Epoch # Earliest epoch when voluntary exit can be processed
validator_index: ValidatorIndex
2019-11-21 22:13:45 +00:00
```
2019-03-07 18:02:13 +00:00
### Beacon blocks
2018-12-07 01:02:23 +00:00
2019-03-07 18:02:13 +00:00
#### `BeaconBlockBody`
```python
class BeaconBlockBody(Container):
2019-06-15 21:23:44 +00:00
randao_reveal: BLSSignature
eth1_data: Eth1Data # Eth1 data vote
graffiti: Bytes32 # Arbitrary data
2019-06-09 19:41:21 +00:00
# Operations
2019-06-20 18:03:11 +00:00
proposer_slashings: List[ProposerSlashing, MAX_PROPOSER_SLASHINGS]
attester_slashings: List[AttesterSlashing, MAX_ATTESTER_SLASHINGS]
attestations: List[Attestation, MAX_ATTESTATIONS]
deposits: List[Deposit, MAX_DEPOSITS]
2019-11-21 22:13:45 +00:00
voluntary_exits: List[SignedVoluntaryExit, MAX_VOLUNTARY_EXITS]
2019-03-03 11:04:28 +00:00
```
2019-03-07 18:02:13 +00:00
#### `BeaconBlock`
2019-03-03 11:04:28 +00:00
```python
class BeaconBlock(Container):
slot: Slot
parent_root: Root
state_root: Root
body: BeaconBlockBody
2019-11-21 22:13:45 +00:00
```
2019-03-07 18:02:13 +00:00
### Beacon state
2018-09-20 05:20:49 +00:00
#### `BeaconState`
2018-09-20 05:20:49 +00:00
```python
class BeaconState(Container):
2019-06-09 19:41:21 +00:00
# Versioning
genesis_time: uint64
2019-06-09 19:41:21 +00:00
slot: Slot
fork: Fork
# History
2019-06-15 14:09:50 +00:00
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]
2019-06-09 19:41:21 +00:00
# Eth1
eth1_data: Eth1Data
eth1_data_votes: List[Eth1Data, SLOTS_PER_ETH1_VOTING_PERIOD]
2019-06-09 19:41:21 +00:00
eth1_deposit_index: uint64
# Registry
2019-06-24 21:38:36 +00:00
validators: List[Validator, VALIDATOR_REGISTRY_LIMIT]
balances: List[Gwei, VALIDATOR_REGISTRY_LIMIT]
2019-10-17 08:47:51 +00:00
# Randomness
randao_mixes: Vector[Bytes32, EPOCHS_PER_HISTORICAL_VECTOR]
2019-06-09 19:41:21 +00:00
# Slashings
slashings: Vector[Gwei, EPOCHS_PER_SLASHINGS_VECTOR] # Per-epoch sums of slashed effective balances
2019-06-09 19:41:21 +00:00
# Attestations
previous_epoch_attestations: List[PendingAttestation, MAX_ATTESTATIONS * SLOTS_PER_EPOCH]
current_epoch_attestations: List[PendingAttestation, MAX_ATTESTATIONS * SLOTS_PER_EPOCH]
2019-06-09 19:41:21 +00:00
# Finality
2019-06-28 11:23:22 +00:00
justification_bits: Bitvector[JUSTIFICATION_BITS_LENGTH] # Bit set for every recent justified epoch
previous_justified_checkpoint: Checkpoint # Previous epoch snapshot
current_justified_checkpoint: Checkpoint
finalized_checkpoint: Checkpoint
2018-09-20 05:20:49 +00:00
```
2019-12-03 20:34:48 +00:00
### Signed envelopes
#### `SignedVoluntaryExit`
```python
class SignedVoluntaryExit(Container):
message: VoluntaryExit
signature: BLSSignature
```
#### `SignedBeaconBlock`
```python
class SignedBeaconBlock(Container):
message: BeaconBlock
signature: BLSSignature
```
#### `SignedBeaconBlockHeader`
```python
class SignedBeaconBlockHeader(Container):
message: BeaconBlockHeader
signature: BLSSignature
```
## Helper functions
2019-05-06 15:30:32 +00:00
*Note*: The definitions below are for specification purposes and are not necessarily optimal implementations.
2019-06-30 09:02:18 +00:00
### Math
#### `integer_squareroot`
```python
2019-06-30 19:51:10 +00:00
def integer_squareroot(n: uint64) -> uint64:
2019-06-30 09:02:18 +00:00
"""
Return the largest integer ``x`` such that ``x**2 <= n``.
"""
x = n
y = (x + 1) // 2
while y < x:
x = y
y = (x + n // x) // 2
return x
```
#### `xor`
2019-03-04 22:49:21 +00:00
```python
2019-07-05 14:03:37 +00:00
def xor(bytes_1: Bytes32, bytes_2: Bytes32) -> Bytes32:
2019-06-30 09:02:18 +00:00
"""
2019-06-30 17:42:24 +00:00
Return the exclusive-or of two 32-byte strings.
2019-06-30 09:02:18 +00:00
"""
2019-07-05 14:03:37 +00:00
return Bytes32(a ^ b for a, b in zip(bytes_1, bytes_2))
2019-03-04 22:49:21 +00:00
```
2019-07-10 17:11:34 +00:00
#### `int_to_bytes`
2019-06-30 09:56:14 +00:00
```python
2019-06-30 19:51:10 +00:00
def int_to_bytes(n: uint64, length: uint64) -> bytes:
2019-06-30 09:56:14 +00:00
"""
2019-09-16 14:58:09 +00:00
Return the ``length``-byte serialization of ``n`` in ``ENDIANNESS``-endian.
2019-06-30 09:56:14 +00:00
"""
2019-06-30 19:51:10 +00:00
return n.to_bytes(length, ENDIANNESS)
2019-06-30 09:56:14 +00:00
```
#### `bytes_to_int`
```python
2019-06-30 19:51:10 +00:00
def bytes_to_int(data: bytes) -> uint64:
2019-06-30 09:56:14 +00:00
"""
2019-12-16 11:55:18 +00:00
Return the integer deserialization of ``data`` interpreted as ``ENDIANNESS``-endian.
2019-06-30 09:56:14 +00:00
"""
return int.from_bytes(data, ENDIANNESS)
```
2019-06-30 09:02:18 +00:00
### Crypto
#### `hash`
`def hash(data: bytes) -> Bytes32` is SHA256.
2019-06-30 09:02:18 +00:00
#### `hash_tree_root`
`def hash_tree_root(object: SSZSerializable) -> Root` is a function for hashing objects into a single root by utilizing a hash tree structure, as defined in the [SSZ spec](../../ssz/simple-serialize.md#merkleization).
2019-12-17 10:04:56 +00:00
#### BLS Signatures
2019-02-15 00:23:03 +00:00
2019-12-20 06:41:46 +00:00
Eth2 makes use of BLS signatures as specified in the [IETF draft BLS specification](https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-00). Specifically, eth2 uses the `BLS_SIG_BLS12381G2-SHA256-SSWU-RO-_POP_` ciphersuite which implements the following interfaces:
2019-02-15 00:23:03 +00:00
2020-01-03 06:46:27 +00:00
- `def Sign(SK: int, message: Bytes) -> BLSSignature`
- `def Verify(PK: BLSPubkey, message: Bytes, signature: BLSSignature) -> bool`
- `def Aggregate(signatures: Sequence[BLSSignature]) -> BLSSignature`
- `def FastAggregateVerify(PKs: Sequence[BLSSignature], message: Bytes, signature: BLSSignature) -> bool`
2020-01-07 19:52:20 +00:00
- `def AggregateVerify(pairs: Sequence[PK: BLSSignature, message: Bytes], signature: BLSSignature) -> bool`
2019-12-20 06:41:46 +00:00
Within these specifications, BLS signatures are treated as a module for notational clarity, thus to verify a signature `bls.Verify(...)` is used.
2019-06-30 09:02:18 +00:00
2019-06-30 09:11:23 +00:00
### Predicates
#### `is_active_validator`
```python
def is_active_validator(validator: Validator, epoch: Epoch) -> bool:
"""
Check if ``validator`` is active.
"""
return validator.activation_epoch <= epoch < validator.exit_epoch
```
2019-12-11 23:10:18 +00:00
#### `is_eligible_for_activation_queue`
```python
def is_eligible_for_activation_queue(validator: Validator) -> bool:
"""
Check if ``validator`` is eligible to be placed into the activation queue.
"""
return (
validator.activation_eligibility_epoch == FAR_FUTURE_EPOCH
and validator.effective_balance == MAX_EFFECTIVE_BALANCE
)
```
2019-12-11 23:00:46 +00:00
#### `is_eligible_for_activation`
```python
def is_eligible_for_activation(state: BeaconState, validator: Validator) -> bool:
"""
Check if ``validator`` is eligible for activation.
"""
return (
# Placement in queue is finalized
validator.activation_eligibility_epoch <= state.finalized_checkpoint.epoch
2019-12-11 23:00:46 +00:00
# Has not yet been activated
and validator.activation_epoch == FAR_FUTURE_EPOCH
)
```
2019-06-30 09:11:23 +00:00
#### `is_slashable_validator`
```python
def is_slashable_validator(validator: Validator, epoch: Epoch) -> bool:
"""
Check if ``validator`` is slashable.
"""
return (not validator.slashed) and (validator.activation_epoch <= epoch < validator.withdrawable_epoch)
```
#### `is_slashable_attestation_data`
```python
def is_slashable_attestation_data(data_1: AttestationData, data_2: AttestationData) -> bool:
"""
Check if ``data_1`` and ``data_2`` are slashable according to Casper FFG rules.
"""
return (
# Double vote
(data_1 != data_2 and data_1.target.epoch == data_2.target.epoch) or
# Surround vote
(data_1.source.epoch < data_2.source.epoch and data_2.target.epoch < data_1.target.epoch)
)
```
#### `is_valid_indexed_attestation`
```python
def is_valid_indexed_attestation(state: BeaconState, indexed_attestation: IndexedAttestation) -> bool:
"""
2019-07-05 14:04:57 +00:00
Check if ``indexed_attestation`` has valid indices and signature.
"""
2019-11-02 03:02:53 +00:00
indices = indexed_attestation.attesting_indices
# Verify max number of indices
2019-11-02 03:02:53 +00:00
if not len(indices) <= MAX_VALIDATORS_PER_COMMITTEE:
return False
# Verify indices are sorted and unique
if not indices == sorted(set(indices)):
return False
# Verify aggregate signature
2019-12-17 10:04:56 +00:00
pubkeys = [state.validators[i].pubkey for i in indices]
domain = get_domain(state, DOMAIN_BEACON_ATTESTER, indexed_attestation.data.target.epoch)
2020-01-03 07:18:34 +00:00
signing_root = compute_signing_root(indexed_attestation.data, domain)
return bls.FastAggregateVerify(pubkeys, signing_root, indexed_attestation.signature)
```
2019-06-30 09:56:14 +00:00
#### `is_valid_merkle_branch`
```python
def is_valid_merkle_branch(leaf: Bytes32, branch: Sequence[Bytes32], depth: uint64, index: uint64, root: Root) -> bool:
2019-06-30 09:56:14 +00:00
"""
Check if ``leaf`` at ``index`` verifies against the Merkle ``root`` and ``branch``.
"""
value = leaf
for i in range(depth):
if index // (2**i) % 2:
value = hash(branch[i] + value)
else:
value = hash(value + branch[i])
return value == root
```
### Misc
2019-06-30 19:49:29 +00:00
#### `compute_shuffled_index`
2019-06-30 09:56:14 +00:00
```python
def compute_shuffled_index(index: ValidatorIndex, index_count: uint64, seed: Bytes32) -> ValidatorIndex:
2019-06-30 09:56:14 +00:00
"""
Return the shuffled validator index corresponding to ``seed`` (and ``index_count``).
"""
assert index < index_count
# Swap or not (https://link.springer.com/content/pdf/10.1007%2F978-3-642-32009-5_1.pdf)
# See the 'generalized domain' algorithm on page 3
for current_round in range(SHUFFLE_ROUND_COUNT):
pivot = bytes_to_int(hash(seed + int_to_bytes(current_round, length=1))[0:8]) % index_count
flip = ValidatorIndex((pivot + index_count - index) % index_count)
position = max(index, flip)
2019-06-30 13:32:50 +00:00
source = hash(seed + int_to_bytes(current_round, length=1) + int_to_bytes(position // 256, length=4))
2019-06-30 09:56:14 +00:00
byte = source[(position % 256) // 8]
bit = (byte >> (position % 8)) % 2
index = flip if bit else index
return ValidatorIndex(index)
```
#### `compute_proposer_index`
```python
def compute_proposer_index(state: BeaconState, indices: Sequence[ValidatorIndex], seed: Bytes32) -> ValidatorIndex:
"""
Return from ``indices`` a random index sampled by effective balance.
"""
assert len(indices) > 0
MAX_RANDOM_BYTE = 2**8 - 1
i = 0
while True:
candidate_index = indices[compute_shuffled_index(ValidatorIndex(i % len(indices)), len(indices), seed)]
random_byte = hash(seed + int_to_bytes(i // 32, length=8))[i % 32]
effective_balance = state.validators[candidate_index].effective_balance
if effective_balance * MAX_RANDOM_BYTE >= MAX_EFFECTIVE_BALANCE * random_byte:
return ValidatorIndex(candidate_index)
i += 1
```
2019-06-30 09:56:14 +00:00
#### `compute_committee`
```python
def compute_committee(indices: Sequence[ValidatorIndex],
seed: Bytes32,
index: uint64,
count: uint64) -> Sequence[ValidatorIndex]:
2019-06-30 09:56:14 +00:00
"""
2019-06-30 13:11:46 +00:00
Return the committee corresponding to ``indices``, ``seed``, ``index``, and committee ``count``.
2019-06-30 09:56:14 +00:00
"""
start = (len(indices) * index) // count
end = (len(indices) * (index + 1)) // count
2019-06-30 19:49:29 +00:00
return [indices[compute_shuffled_index(ValidatorIndex(i), len(indices), seed)] for i in range(start, end)]
2019-06-30 09:56:14 +00:00
```
2019-10-17 08:47:51 +00:00
#### `compute_epoch_at_slot`
2019-06-30 09:56:14 +00:00
```python
2019-10-17 08:47:51 +00:00
def compute_epoch_at_slot(slot: Slot) -> Epoch:
2019-06-30 09:56:14 +00:00
"""
2019-10-17 08:47:51 +00:00
Return the epoch number at ``slot``.
2019-06-30 09:56:14 +00:00
"""
return Epoch(slot // SLOTS_PER_EPOCH)
```
2019-10-23 00:37:15 +00:00
#### `compute_start_slot_at_epoch`
2019-06-30 09:56:14 +00:00
```python
2019-10-23 00:37:15 +00:00
def compute_start_slot_at_epoch(epoch: Epoch) -> Slot:
2019-06-30 09:56:14 +00:00
"""
2019-06-30 17:42:24 +00:00
Return the start slot of ``epoch``.
2019-06-30 09:56:14 +00:00
"""
return Slot(epoch * SLOTS_PER_EPOCH)
```
#### `compute_activation_exit_epoch`
2019-06-30 09:56:14 +00:00
```python
def compute_activation_exit_epoch(epoch: Epoch) -> Epoch:
2019-06-30 09:56:14 +00:00
"""
2019-06-30 17:42:24 +00:00
Return the epoch during which validator activations and exits initiated in ``epoch`` take effect.
2019-06-30 09:56:14 +00:00
"""
return Epoch(epoch + 1 + MAX_SEED_LOOKAHEAD)
2019-06-30 09:56:14 +00:00
```
#### `compute_domain`
2019-06-30 09:56:14 +00:00
```python
def compute_domain(domain_type: DomainType, fork_version: Version=GENESIS_FORK_VERSION) -> Domain:
2019-06-30 09:56:14 +00:00
"""
2019-06-30 23:36:19 +00:00
Return the domain for the ``domain_type`` and ``fork_version``.
2019-06-30 09:56:14 +00:00
"""
2019-06-30 20:12:02 +00:00
return Domain(domain_type + fork_version)
2019-06-30 09:56:14 +00:00
```
2020-01-03 06:46:27 +00:00
### `compute_signing_root`
2019-12-17 10:04:56 +00:00
```python
2020-01-03 06:46:27 +00:00
def compute_signing_root(ssz_object: SSZObject, domain: Domain) -> Root:
2020-01-07 12:07:09 +00:00
"""
Return the signing root of an object by calculating the root of the object-domain tree.
"""
2020-01-03 06:46:27 +00:00
domain_wrapped_object = SigningRoot(
object_root=hash_tree_root(ssz_object),
2019-12-17 10:04:56 +00:00
domain=domain,
)
return hash_tree_root(domain_wrapped_object)
```
### Beacon state accessors
2019-06-30 09:02:18 +00:00
#### `get_current_epoch`
```python
2019-06-30 09:02:18 +00:00
def get_current_epoch(state: BeaconState) -> Epoch:
2019-01-30 09:25:39 +00:00
"""
2019-06-30 09:02:18 +00:00
Return the current epoch.
2019-01-30 09:25:39 +00:00
"""
2019-10-17 08:47:51 +00:00
return compute_epoch_at_slot(state.slot)
```
2019-06-30 09:02:18 +00:00
#### `get_previous_epoch`
2019-02-03 09:36:21 +00:00
```python
2019-02-12 12:24:19 +00:00
def get_previous_epoch(state: BeaconState) -> Epoch:
2019-02-03 09:36:21 +00:00
"""`
2019-06-30 13:11:46 +00:00
Return the previous epoch (unless the current epoch is ``GENESIS_EPOCH``).
2019-02-03 09:36:21 +00:00
"""
current_epoch = get_current_epoch(state)
return GENESIS_EPOCH if current_epoch == GENESIS_EPOCH else Epoch(current_epoch - 1)
2019-02-03 09:36:21 +00:00
```
2019-06-30 09:02:18 +00:00
#### `get_block_root`
2018-09-20 05:20:49 +00:00
2019-01-26 14:31:09 +00:00
```python
def get_block_root(state: BeaconState, epoch: Epoch) -> Root:
2019-01-30 09:25:39 +00:00
"""
2019-06-30 17:42:24 +00:00
Return the block root at the start of a recent ``epoch``.
2019-01-30 09:25:39 +00:00
"""
2019-10-23 00:37:15 +00:00
return get_block_root_at_slot(state, compute_start_slot_at_epoch(epoch))
2019-01-27 14:54:46 +00:00
```
2018-12-14 15:29:49 +00:00
2019-06-30 09:02:18 +00:00
#### `get_block_root_at_slot`
2019-04-24 04:23:51 +00:00
2018-12-12 19:00:53 +00:00
```python
def get_block_root_at_slot(state: BeaconState, slot: Slot) -> Root:
"""
2019-06-30 09:02:18 +00:00
Return the block root at a recent ``slot``.
"""
2019-06-30 09:02:18 +00:00
assert slot < state.slot <= slot + SLOTS_PER_HISTORICAL_ROOT
return state.block_roots[slot % SLOTS_PER_HISTORICAL_ROOT]
```
2019-06-30 09:02:18 +00:00
#### `get_randao_mix`
2019-04-24 04:23:51 +00:00
2019-03-19 11:08:17 +00:00
```python
def get_randao_mix(state: BeaconState, epoch: Epoch) -> Bytes32:
2019-03-19 11:08:17 +00:00
"""
2019-06-30 09:02:18 +00:00
Return the randao mix at a recent ``epoch``.
2019-03-19 11:08:17 +00:00
"""
2019-06-30 09:02:18 +00:00
return state.randao_mixes[epoch % EPOCHS_PER_HISTORICAL_VECTOR]
2019-03-19 11:08:17 +00:00
```
2019-06-30 09:02:18 +00:00
#### `get_active_validator_indices`
2018-09-20 05:20:49 +00:00
```python
2019-06-22 16:12:42 +00:00
def get_active_validator_indices(state: BeaconState, epoch: Epoch) -> Sequence[ValidatorIndex]:
"""
2019-06-30 09:02:18 +00:00
Return the sequence of active validator indices at ``epoch``.
"""
2019-06-17 21:51:00 +00:00
return [ValidatorIndex(i) for i, v in enumerate(state.validators) if is_active_validator(v, epoch)]
2018-09-20 05:20:49 +00:00
```
2019-06-30 13:11:46 +00:00
#### `get_validator_churn_limit`
2019-03-21 16:04:20 +00:00
```python
2019-06-30 19:51:10 +00:00
def get_validator_churn_limit(state: BeaconState) -> uint64:
2019-03-22 04:56:54 +00:00
"""
2019-06-30 09:02:18 +00:00
Return the validator churn limit for the current epoch.
2019-03-22 04:56:54 +00:00
"""
2019-06-30 09:02:18 +00:00
active_validator_indices = get_active_validator_indices(state, get_current_epoch(state))
return max(MIN_PER_EPOCH_CHURN_LIMIT, len(active_validator_indices) // CHURN_LIMIT_QUOTIENT)
2019-03-21 16:04:20 +00:00
```
2019-06-30 13:11:46 +00:00
#### `get_seed`
```python
def get_seed(state: BeaconState, epoch: Epoch, domain_type: DomainType) -> Bytes32:
2019-06-30 13:11:46 +00:00
"""
Return the seed at ``epoch``.
"""
mix = get_randao_mix(state, Epoch(epoch + EPOCHS_PER_HISTORICAL_VECTOR - MIN_SEED_LOOKAHEAD - 1)) # Avoid underflow
return hash(domain_type + int_to_bytes(epoch, length=8) + mix)
2019-06-30 13:11:46 +00:00
```
2019-10-17 08:47:51 +00:00
#### `get_committee_count_at_slot`
2019-01-08 00:53:33 +00:00
```python
2019-10-17 08:47:51 +00:00
def get_committee_count_at_slot(state: BeaconState, slot: Slot) -> uint64:
2019-01-30 09:25:39 +00:00
"""
2019-10-13 12:52:58 +00:00
Return the number of committees at ``slot``.
2019-01-30 09:25:39 +00:00
"""
2019-10-17 08:47:51 +00:00
epoch = compute_epoch_at_slot(slot)
2019-10-13 12:52:58 +00:00
return max(1, min(
2019-10-13 04:53:43 +00:00
MAX_COMMITTEES_PER_SLOT,
2019-06-30 13:32:50 +00:00
len(get_active_validator_indices(state, epoch)) // SLOTS_PER_EPOCH // TARGET_COMMITTEE_SIZE,
2019-06-30 17:42:24 +00:00
))
2019-01-08 00:53:33 +00:00
```
#### `get_beacon_committee`
2018-09-20 05:20:49 +00:00
```python
def get_beacon_committee(state: BeaconState, slot: Slot, index: CommitteeIndex) -> Sequence[ValidatorIndex]:
"""
2019-10-17 01:47:39 +00:00
Return the beacon committee at ``slot`` for ``index``.
2019-06-30 09:02:18 +00:00
"""
2019-10-17 08:47:51 +00:00
epoch = compute_epoch_at_slot(slot)
committees_per_slot = get_committee_count_at_slot(state, slot)
2019-06-30 09:02:18 +00:00
return compute_committee(
indices=get_active_validator_indices(state, epoch),
2019-09-22 19:27:42 +00:00
seed=get_seed(state, epoch, DOMAIN_BEACON_ATTESTER),
2019-10-18 03:17:46 +00:00
index=(slot % SLOTS_PER_EPOCH) * committees_per_slot + index,
2019-10-13 12:52:58 +00:00
count=committees_per_slot * SLOTS_PER_EPOCH,
2019-06-30 09:02:18 +00:00
)
```
2019-06-30 17:42:24 +00:00
#### `get_beacon_proposer_index`
2019-03-04 18:45:41 +00:00
```python
2019-06-30 17:42:24 +00:00
def get_beacon_proposer_index(state: BeaconState) -> ValidatorIndex:
2019-03-04 18:45:41 +00:00
"""
2019-06-30 17:42:24 +00:00
Return the beacon proposer index at the current slot.
2019-03-04 18:45:41 +00:00
"""
2019-06-30 09:02:18 +00:00
epoch = get_current_epoch(state)
2019-09-22 19:27:42 +00:00
seed = hash(get_seed(state, epoch, DOMAIN_BEACON_PROPOSER) + int_to_bytes(state.slot, length=8))
indices = get_active_validator_indices(state, epoch)
return compute_proposer_index(state, indices, seed)
2019-03-04 18:45:41 +00:00
```
2018-09-20 05:20:49 +00:00
2019-06-30 09:02:18 +00:00
#### `get_total_balance`
```python
2019-06-30 09:02:18 +00:00
def get_total_balance(state: BeaconState, indices: Set[ValidatorIndex]) -> Gwei:
"""
2019-06-30 09:02:18 +00:00
Return the combined effective balance of the ``indices``. (1 Gwei minimum to avoid divisions by zero.)
"""
2019-07-05 14:03:37 +00:00
return Gwei(max(1, sum([state.validators[index].effective_balance for index in indices])))
2019-06-30 09:02:18 +00:00
```
2019-06-30 13:11:46 +00:00
#### `get_total_active_balance`
```python
def get_total_active_balance(state: BeaconState) -> Gwei:
"""
Return the combined effective balance of the active validators.
"""
return get_total_balance(state, set(get_active_validator_indices(state, get_current_epoch(state))))
```
2019-06-30 09:02:18 +00:00
#### `get_domain`
```python
2020-01-07 12:07:09 +00:00
def get_domain(state: BeaconState, domain_type: DomainType, epoch: Epoch=None) -> Domain:
2019-06-30 09:02:18 +00:00
"""
Return the signature domain (fork version concatenated with domain type) of a message.
"""
2020-01-07 12:07:09 +00:00
epoch = get_current_epoch(state) if epoch is None else epoch
2019-06-30 09:02:18 +00:00
fork_version = state.fork.previous_version if epoch < state.fork.epoch else state.fork.current_version
return compute_domain(domain_type, fork_version)
2019-06-30 09:02:18 +00:00
```
#### `get_indexed_attestation`
```python
def get_indexed_attestation(state: BeaconState, attestation: Attestation) -> IndexedAttestation:
"""
Return the indexed attestation corresponding to ``attestation``.
"""
attesting_indices = get_attesting_indices(state, attestation.data, attestation.aggregation_bits)
return IndexedAttestation(
2019-11-02 03:02:53 +00:00
attesting_indices=sorted(attesting_indices),
2019-06-30 09:02:18 +00:00
data=attestation.data,
signature=attestation.signature,
)
```
2019-06-30 09:02:18 +00:00
#### `get_attesting_indices`
```python
2019-06-30 09:02:18 +00:00
def get_attesting_indices(state: BeaconState,
data: AttestationData,
bits: Bitlist[MAX_VALIDATORS_PER_COMMITTEE]) -> Set[ValidatorIndex]:
"""
2019-06-30 09:02:18 +00:00
Return the set of attesting indices corresponding to ``data`` and ``bits``.
"""
committee = get_beacon_committee(state, data.slot, data.index)
2019-06-30 09:02:18 +00:00
return set(index for i, index in enumerate(committee) if bits[i])
```
2019-06-30 10:01:54 +00:00
### Beacon state mutators
2018-09-20 05:20:49 +00:00
2019-06-30 09:02:18 +00:00
#### `increase_balance`
2019-02-07 21:12:58 +00:00
```python
2019-06-30 09:02:18 +00:00
def increase_balance(state: BeaconState, index: ValidatorIndex, delta: Gwei) -> None:
"""
2019-06-30 09:56:14 +00:00
Increase the validator balance at index ``index`` by ``delta``.
2019-06-30 09:02:18 +00:00
"""
state.balances[index] += delta
2019-02-07 21:12:58 +00:00
```
2018-09-20 05:20:49 +00:00
2019-06-30 09:02:18 +00:00
#### `decrease_balance`
2019-02-03 10:14:02 +00:00
```python
2019-06-30 09:02:18 +00:00
def decrease_balance(state: BeaconState, index: ValidatorIndex, delta: Gwei) -> None:
2019-02-03 10:14:02 +00:00
"""
2019-06-30 09:56:14 +00:00
Decrease the validator balance at index ``index`` by ``delta``, with underflow protection.
2019-02-03 10:14:02 +00:00
"""
2019-06-30 09:02:18 +00:00
state.balances[index] = 0 if delta > state.balances[index] else state.balances[index] - delta
2019-02-03 10:14:02 +00:00
```
#### `initiate_validator_exit`
2018-12-10 21:16:06 +00:00
```python
2019-01-19 07:58:24 +00:00
def initiate_validator_exit(state: BeaconState, index: ValidatorIndex) -> None:
2019-01-30 09:25:39 +00:00
"""
2019-06-30 09:56:14 +00:00
Initiate the exit of the validator with index ``index``.
2019-01-30 09:25:39 +00:00
"""
2019-04-14 09:01:53 +00:00
# Return if validator already initiated exit
2019-06-09 19:41:21 +00:00
validator = state.validators[index]
2019-04-13 23:13:53 +00:00
if validator.exit_epoch != FAR_FUTURE_EPOCH:
return
2019-04-14 09:01:53 +00:00
# Compute exit queue epoch
2019-06-09 19:41:21 +00:00
exit_epochs = [v.exit_epoch for v in state.validators if v.exit_epoch != FAR_FUTURE_EPOCH]
exit_queue_epoch = max(exit_epochs, default=compute_activation_exit_epoch(get_current_epoch(state)))
2019-06-09 19:41:21 +00:00
exit_queue_churn = len([v for v in state.validators if v.exit_epoch == exit_queue_epoch])
2019-06-30 13:11:46 +00:00
if exit_queue_churn >= get_validator_churn_limit(state):
exit_queue_epoch += Epoch(1)
2018-12-11 15:49:50 +00:00
2019-04-13 23:13:53 +00:00
# Set validator exit epoch and withdrawable epoch
2019-04-14 08:10:44 +00:00
validator.exit_epoch = exit_queue_epoch
validator.withdrawable_epoch = Epoch(validator.exit_epoch + MIN_VALIDATOR_WITHDRAWABILITY_DELAY)
```
2019-02-12 12:24:19 +00:00
#### `slash_validator`
```python
def slash_validator(state: BeaconState,
slashed_index: ValidatorIndex,
whistleblower_index: ValidatorIndex=None) -> None:
2019-01-30 09:25:39 +00:00
"""
Slash the validator with index ``slashed_index``.
2019-01-30 09:25:39 +00:00
"""
epoch = get_current_epoch(state)
2019-03-29 07:26:26 +00:00
initiate_validator_exit(state, slashed_index)
validator = state.validators[slashed_index]
validator.slashed = True
validator.withdrawable_epoch = max(validator.withdrawable_epoch, Epoch(epoch + EPOCHS_PER_SLASHINGS_VECTOR))
state.slashings[epoch % EPOCHS_PER_SLASHINGS_VECTOR] += validator.effective_balance
decrease_balance(state, slashed_index, validator.effective_balance // MIN_SLASHING_PENALTY_QUOTIENT)
# Apply proposer and whistleblower rewards
2019-06-30 17:42:24 +00:00
proposer_index = get_beacon_proposer_index(state)
if whistleblower_index is None:
whistleblower_index = proposer_index
whistleblower_reward = Gwei(validator.effective_balance // WHISTLEBLOWER_REWARD_QUOTIENT)
proposer_reward = Gwei(whistleblower_reward // PROPOSER_REWARD_QUOTIENT)
increase_balance(state, proposer_index, proposer_reward)
increase_balance(state, whistleblower_index, whistleblower_reward - proposer_reward)
```
2019-05-05 16:15:05 +00:00
## Genesis
2018-09-20 05:20:49 +00:00
2019-06-30 09:56:14 +00:00
Before the Ethereum 2.0 genesis has been triggered, and for every Ethereum 1.0 block, let `candidate_state = initialize_beacon_state_from_eth1(eth1_block_hash, eth1_timestamp, deposits)` where:
2019-05-05 16:15:05 +00:00
2019-06-30 19:25:58 +00:00
- `eth1_block_hash` is the hash of the Ethereum 1.0 block
- `eth1_timestamp` is the Unix timestamp corresponding to `eth1_block_hash`
- `deposits` is the sequence of all deposits, ordered chronologically, up to (and including) the block with hash `eth1_block_hash`
2019-03-03 11:04:28 +00:00
```python
def initialize_beacon_state_from_eth1(eth1_block_hash: Bytes32,
eth1_timestamp: uint64,
deposits: Sequence[Deposit]) -> BeaconState:
fork = Fork(
previous_version=GENESIS_FORK_VERSION,
current_version=GENESIS_FORK_VERSION,
epoch=GENESIS_EPOCH,
)
state = BeaconState(
2020-01-05 22:29:23 +00:00
genesis_time=eth1_timestamp - eth1_timestamp % MIN_GENESIS_DELAY + 2 * MIN_GENESIS_DELAY,
fork=fork,
2019-06-29 21:06:17 +00:00
eth1_data=Eth1Data(block_hash=eth1_block_hash, deposit_count=len(deposits)),
latest_block_header=BeaconBlockHeader(body_root=hash_tree_root(BeaconBlockBody())),
2019-10-27 18:18:48 +00:00
randao_mixes=[eth1_block_hash] * EPOCHS_PER_HISTORICAL_VECTOR, # Seed RANDAO with Eth1 entropy
)
2018-10-09 07:33:22 +00:00
# Process deposits
leaves = list(map(lambda deposit: deposit.data, deposits))
2019-06-29 20:29:10 +00:00
for index, deposit in enumerate(deposits):
2019-06-30 13:32:50 +00:00
deposit_data_list = List[DepositData, 2**DEPOSIT_CONTRACT_TREE_DEPTH](*leaves[:index + 1])
state.eth1_data.deposit_root = hash_tree_root(deposit_data_list)
2019-02-15 00:23:03 +00:00
process_deposit(state, deposit)
2018-12-10 21:16:06 +00:00
2019-06-29 20:29:10 +00:00
# Process activations
2019-06-29 20:36:27 +00:00
for index, validator in enumerate(state.validators):
2019-06-30 20:27:01 +00:00
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
2018-12-10 21:16:06 +00:00
return state
```
2018-09-20 05:20:49 +00:00
2019-06-30 09:56:14 +00:00
### Genesis state
Let `genesis_state = candidate_state` whenever `is_valid_genesis_state(candidate_state) is True` for the first time.
```python
def is_valid_genesis_state(state: BeaconState) -> bool:
if state.genesis_time < MIN_GENESIS_TIME:
return False
2019-06-30 09:56:14 +00:00
if len(get_active_validator_indices(state, GENESIS_EPOCH)) < MIN_GENESIS_ACTIVE_VALIDATOR_COUNT:
return False
2019-06-30 09:56:14 +00:00
return True
```
2019-06-30 09:56:14 +00:00
*Note*: The `is_valid_genesis_state` function (including `MIN_GENESIS_TIME` and `MIN_GENESIS_ACTIVE_VALIDATOR_COUNT`) is a placeholder for testing. It has yet to be finalized by the community, and can be updated as necessary.
2019-05-05 16:15:05 +00:00
### Genesis block
Let `genesis_block = BeaconBlock(state_root=hash_tree_root(genesis_state))`.
## Beacon chain state transition function
The post-state corresponding to a pre-state `state` and a signed block `signed_block` is defined as `state_transition(state, signed_block)`. State transitions that trigger an unhandled exception (e.g. a failed `assert` or an out-of-range list access) are considered invalid.
2019-05-01 12:14:10 +00:00
```python
2019-11-21 22:13:45 +00:00
def state_transition(state: BeaconState, signed_block: SignedBeaconBlock, validate_result: bool=True) -> BeaconState:
block = signed_block.message
2019-05-08 18:15:23 +00:00
# Process slots (including those with no blocks) since block
process_slots(state, block.slot)
2019-11-21 22:13:45 +00:00
# Verify signature
if validate_result:
assert verify_block_signature(state, signed_block)
2019-05-08 18:15:23 +00:00
# Process block
process_block(state, block)
# Verify state root
2019-11-21 22:13:45 +00:00
if validate_result:
assert block.state_root == hash_tree_root(state)
2019-05-08 18:15:23 +00:00
# Return post-state
2019-05-02 10:07:25 +00:00
return state
2019-05-01 12:14:10 +00:00
```
2018-09-20 05:20:49 +00:00
2019-11-21 22:13:45 +00:00
```python
def verify_block_signature(state: BeaconState, signed_block: SignedBeaconBlock) -> bool:
proposer = state.validators[get_beacon_proposer_index(state)]
2020-01-03 07:18:34 +00:00
signing_root = compute_signing_root(signed_block.message, get_domain(state, DOMAIN_BEACON_PROPOSER))
return bls.Verify(proposer.pubkey, signing_root, signed_block.signature)
2019-11-21 22:13:45 +00:00
```
2019-05-03 20:21:42 +00:00
```python
def process_slots(state: BeaconState, slot: Slot) -> None:
assert state.slot <= slot
2019-05-03 20:21:42 +00:00
while state.slot < slot:
process_slot(state)
2019-06-30 17:42:24 +00:00
# Process epoch on the start slot of the next epoch
2019-05-03 20:21:42 +00:00
if (state.slot + 1) % SLOTS_PER_EPOCH == 0:
process_epoch(state)
state.slot += Slot(1)
2019-05-03 20:21:42 +00:00
```
2019-03-03 11:04:28 +00:00
```python
2019-05-03 20:21:42 +00:00
def process_slot(state: BeaconState) -> None:
# Cache state root
2019-05-01 13:16:55 +00:00
previous_state_root = hash_tree_root(state)
2019-06-09 19:41:21 +00:00
state.state_roots[state.slot % SLOTS_PER_HISTORICAL_ROOT] = previous_state_root
2019-05-03 20:21:42 +00:00
# Cache latest block header state root
if state.latest_block_header.state_root == Bytes32():
2019-05-01 13:16:55 +00:00
state.latest_block_header.state_root = previous_state_root
2019-05-03 20:21:42 +00:00
# Cache block root
2019-11-21 22:13:45 +00:00
previous_block_root = hash_tree_root(state.latest_block_header)
2019-06-09 19:41:21 +00:00
state.block_roots[state.slot % SLOTS_PER_HISTORICAL_ROOT] = previous_block_root
2019-03-03 11:04:28 +00:00
```
2018-09-20 05:20:49 +00:00
2019-05-01 12:29:03 +00:00
### Epoch processing
*Note*: The `# @LabelHere` lines below are placeholders to show that code will be inserted here in a future phase.
2019-05-01 12:14:10 +00:00
```python
2019-05-01 12:29:03 +00:00
def process_epoch(state: BeaconState) -> None:
2019-05-01 12:14:10 +00:00
process_justification_and_finalization(state)
process_rewards_and_penalties(state)
process_registry_updates(state)
2019-05-24 14:51:21 +00:00
# @process_reveal_deadlines
# @process_challenge_deadlines
2019-05-01 12:14:10 +00:00
process_slashings(state)
2019-08-24 11:55:35 +00:00
# @update_period_committee
2019-05-01 12:14:10 +00:00
process_final_updates(state)
2019-05-24 14:51:21 +00:00
# @after_process_final_updates
2019-05-01 12:14:10 +00:00
```
2019-03-07 19:05:34 +00:00
#### Helper functions
```python
2019-06-22 16:12:42 +00:00
def get_matching_source_attestations(state: BeaconState, epoch: Epoch) -> Sequence[PendingAttestation]:
2019-06-23 09:09:09 +00:00
assert epoch in (get_previous_epoch(state), get_current_epoch(state))
2019-04-20 05:17:33 +00:00
return state.current_epoch_attestations if epoch == get_current_epoch(state) else state.previous_epoch_attestations
2019-03-07 19:05:34 +00:00
```
2019-03-03 11:04:28 +00:00
```python
2019-06-22 16:12:42 +00:00
def get_matching_target_attestations(state: BeaconState, epoch: Epoch) -> Sequence[PendingAttestation]:
2019-03-07 19:05:34 +00:00
return [
2019-04-20 05:17:33 +00:00
a for a in get_matching_source_attestations(state, epoch)
2019-06-22 20:49:53 +00:00
if a.data.target.root == get_block_root(state, epoch)
2019-03-07 19:05:34 +00:00
]
2019-03-03 11:04:28 +00:00
```
2019-03-07 19:05:34 +00:00
```python
2019-06-22 16:12:42 +00:00
def get_matching_head_attestations(state: BeaconState, epoch: Epoch) -> Sequence[PendingAttestation]:
2019-03-07 19:05:34 +00:00
return [
2019-04-20 05:17:33 +00:00
a for a in get_matching_source_attestations(state, epoch)
if a.data.beacon_block_root == get_block_root_at_slot(state, a.data.slot)
2019-03-07 19:05:34 +00:00
]
```
2019-03-07 19:05:34 +00:00
```python
def get_unslashed_attesting_indices(state: BeaconState,
2019-06-22 16:12:42 +00:00
attestations: Sequence[PendingAttestation]) -> Set[ValidatorIndex]:
2019-06-30 13:11:46 +00:00
output = set() # type: Set[ValidatorIndex]
for a in attestations:
2019-06-28 11:23:22 +00:00
output = output.union(get_attesting_indices(state, a.data, a.aggregation_bits))
return set(filter(lambda index: not state.validators[index].slashed, output))
2019-03-07 19:05:34 +00:00
```
2018-11-16 15:48:57 +00:00
```python
2019-06-22 16:12:42 +00:00
def get_attesting_balance(state: BeaconState, attestations: Sequence[PendingAttestation]) -> Gwei:
return get_total_balance(state, get_unslashed_attesting_indices(state, attestations))
```
#### Justification and finalization
2019-03-07 19:05:34 +00:00
```python
def process_justification_and_finalization(state: BeaconState) -> None:
if get_current_epoch(state) <= GENESIS_EPOCH + 1:
2019-04-11 12:28:42 +00:00
return
2019-04-20 06:10:25 +00:00
previous_epoch = get_previous_epoch(state)
current_epoch = get_current_epoch(state)
old_previous_justified_checkpoint = state.previous_justified_checkpoint
old_current_justified_checkpoint = state.current_justified_checkpoint
2019-03-12 18:32:11 +00:00
# Process justifications
state.previous_justified_checkpoint = state.current_justified_checkpoint
state.justification_bits[1:] = state.justification_bits[:-1]
2019-06-28 14:26:02 +00:00
state.justification_bits[0] = 0b0
2019-06-28 11:34:01 +00:00
matching_target_attestations = get_matching_target_attestations(state, previous_epoch) # Previous epoch
if get_attesting_balance(state, matching_target_attestations) * 3 >= get_total_active_balance(state) * 2:
2019-06-25 03:43:05 +00:00
state.current_justified_checkpoint = Checkpoint(epoch=previous_epoch,
root=get_block_root(state, previous_epoch))
2019-06-28 11:23:22 +00:00
state.justification_bits[1] = 0b1
2019-06-28 11:34:01 +00:00
matching_target_attestations = get_matching_target_attestations(state, current_epoch) # Current epoch
if get_attesting_balance(state, matching_target_attestations) * 3 >= get_total_active_balance(state) * 2:
state.current_justified_checkpoint = Checkpoint(epoch=current_epoch,
root=get_block_root(state, current_epoch))
2019-06-28 11:23:22 +00:00
state.justification_bits[0] = 0b1
2019-03-07 19:05:34 +00:00
# Process finalizations
2019-06-28 11:23:22 +00:00
bits = state.justification_bits
# The 2nd/3rd/4th most recent epochs are justified, the 2nd using the 4th as source
2019-06-28 11:23:22 +00:00
if all(bits[1:4]) and old_previous_justified_checkpoint.epoch + 3 == current_epoch:
state.finalized_checkpoint = old_previous_justified_checkpoint
# The 2nd/3rd most recent epochs are justified, the 2nd using the 3rd as source
2019-06-28 11:23:22 +00:00
if all(bits[1:3]) and old_previous_justified_checkpoint.epoch + 2 == current_epoch:
state.finalized_checkpoint = old_previous_justified_checkpoint
# The 1st/2nd/3rd most recent epochs are justified, the 1st using the 3rd as source
2019-06-28 11:23:22 +00:00
if all(bits[0:3]) and old_current_justified_checkpoint.epoch + 2 == current_epoch:
state.finalized_checkpoint = old_current_justified_checkpoint
# The 1st/2nd most recent epochs are justified, the 1st using the 2nd as source
2019-06-28 11:23:22 +00:00
if all(bits[0:2]) and old_current_justified_checkpoint.epoch + 1 == current_epoch:
state.finalized_checkpoint = old_current_justified_checkpoint
```
2018-09-20 05:20:49 +00:00
#### Rewards and penalties
```python
def get_base_reward(state: BeaconState, index: ValidatorIndex) -> Gwei:
total_balance = get_total_active_balance(state)
2019-06-09 19:41:21 +00:00
effective_balance = state.validators[index].effective_balance
return Gwei(effective_balance * BASE_REWARD_FACTOR // integer_squareroot(total_balance) // BASE_REWARDS_PER_EPOCH)
```
```python
2019-06-22 16:12:42 +00:00
def get_attestation_deltas(state: BeaconState) -> Tuple[Sequence[Gwei], Sequence[Gwei]]:
2019-04-20 06:10:25 +00:00
previous_epoch = get_previous_epoch(state)
total_balance = get_total_active_balance(state)
2019-06-17 21:51:00 +00:00
rewards = [Gwei(0) for _ in range(len(state.validators))]
penalties = [Gwei(0) for _ in range(len(state.validators))]
2019-04-24 04:23:51 +00:00
eligible_validator_indices = [
2019-06-17 21:51:00 +00:00
ValidatorIndex(index) for index, v in enumerate(state.validators)
2019-04-24 04:23:51 +00:00
if is_active_validator(v, previous_epoch) or (v.slashed and previous_epoch + 1 < v.withdrawable_epoch)
]
2019-04-24 04:23:51 +00:00
# Micro-incentives for matching FFG source, FFG target, and head
matching_source_attestations = get_matching_source_attestations(state, previous_epoch)
matching_target_attestations = get_matching_target_attestations(state, previous_epoch)
matching_head_attestations = get_matching_head_attestations(state, previous_epoch)
for attestations in (matching_source_attestations, matching_target_attestations, matching_head_attestations):
unslashed_attesting_indices = get_unslashed_attesting_indices(state, attestations)
2019-06-02 20:13:52 +00:00
attesting_balance = get_total_balance(state, unslashed_attesting_indices)
2019-04-24 04:23:51 +00:00
for index in eligible_validator_indices:
if index in unslashed_attesting_indices:
rewards[index] += get_base_reward(state, index) * attesting_balance // total_balance
else:
2019-04-24 04:23:51 +00:00
penalties[index] += get_base_reward(state, index)
# Proposer and inclusion delay micro-rewards
2019-04-24 05:32:43 +00:00
for index in get_unslashed_attesting_indices(state, matching_source_attestations):
2019-04-30 11:27:45 +00:00
attestation = min([
2019-05-03 16:34:16 +00:00
a for a in matching_source_attestations
2019-06-28 11:23:22 +00:00
if index in get_attesting_indices(state, a.data, a.aggregation_bits)
2019-04-30 11:27:45 +00:00
], key=lambda a: a.inclusion_delay)
2019-06-18 14:24:23 +00:00
proposer_reward = Gwei(get_base_reward(state, index) // PROPOSER_REWARD_QUOTIENT)
Tweak inclusion delay rewards and set BASE_REWARD_FACTOR Substantive changes: 1) Split the inclusion delay reward between attester and proposer to add up to at most one base reward. This is analogous to the reward logic in `slash_validator`, and makes the `BASE_REWARDS_PER_EPOCH` constant include proposer rewards. 2) Double `BASE_REWARD_FACTOR` to 2^6 (addressing item 4 in #1054). When the total effective balance is 2^17 ETH then maximum annual issuance is a bit below 2^21 ETH. Maximum annual issuance happens when a) all validators make perfect attestations (matching source, target, head, as well as consistent crosslink data), b) all attestations are included as fast as possible (in particular, no skip blocks), and c) there are no slashings. ```python BASE_REWARD_FACTOR = 2**6 SLOTS_PER_EPOCH = 2**6 SECONDS_PER_SLOT = 6 BASE_REWARDS_PER_EPOCH = 5 GWEI_PER_ETH = 10**9 MAX_TOTAL_EFFECTIVE_BALANCE = 2**27 * GWEI_PER_ETH TARGET_MAX_ISSUANCE = 2**21 * GWEI_PER_ETH def integer_squareroot(n: int) -> int: """ The largest integer ``x`` such that ``x**2`` is less than or equal to ``n``. """ assert n >= 0 x = n y = (x + 1) // 2 while y < x: x = y y = (x + n // x) // 2 return x MAX_REWARDS_PER_EPOCH = MAX_TOTAL_EFFECTIVE_BALANCE * BASE_REWARD_FACTOR // integer_squareroot(MAX_TOTAL_EFFECTIVE_BALANCE) // BASE_REWARDS_PER_EPOCH EPOCHS_PER_YEAR = 365.25*24*60*60 / (SECONDS_PER_SLOT * SLOTS_PER_EPOCH) MAX_REWARDS_PER_YEAR = EPOCHS_PER_YEAR * MAX_REWARDS_PER_EPOCH * BASE_REWARDS_PER_EPOCH print(MAX_REWARDS_PER_YEAR / TARGET_MAX_ISSUANCE) ```
2019-06-10 14:14:32 +00:00
rewards[attestation.proposer_index] += proposer_reward
max_attester_reward = get_base_reward(state, index) - proposer_reward
rewards[index] += Gwei(max_attester_reward // attestation.inclusion_delay)
2019-04-24 04:23:51 +00:00
# Inactivity penalty
finality_delay = previous_epoch - state.finalized_checkpoint.epoch
2019-04-24 04:23:51 +00:00
if finality_delay > MIN_EPOCHS_TO_INACTIVITY_PENALTY:
matching_target_attesting_indices = get_unslashed_attesting_indices(state, matching_target_attestations)
for index in eligible_validator_indices:
penalties[index] += Gwei(BASE_REWARDS_PER_EPOCH * get_base_reward(state, index))
2019-04-24 04:23:51 +00:00
if index not in matching_target_attesting_indices:
effective_balance = state.validators[index].effective_balance
penalties[index] += Gwei(effective_balance * finality_delay // INACTIVITY_PENALTY_QUOTIENT)
return rewards, penalties
```
2018-09-20 05:20:49 +00:00
```python
def process_rewards_and_penalties(state: BeaconState) -> None:
2019-04-11 12:28:42 +00:00
if get_current_epoch(state) == GENESIS_EPOCH:
return
2019-10-18 03:17:46 +00:00
rewards, penalties = get_attestation_deltas(state)
2019-06-17 21:51:00 +00:00
for index in range(len(state.validators)):
2019-10-18 03:17:46 +00:00
increase_balance(state, ValidatorIndex(index), rewards[index])
decrease_balance(state, ValidatorIndex(index), penalties[index])
```
2018-11-23 04:54:11 +00:00
#### Registry updates
2018-12-05 11:22:15 +00:00
```python
def process_registry_updates(state: BeaconState) -> None:
# Process activation eligibility and ejections
2019-06-09 19:41:21 +00:00
for index, validator in enumerate(state.validators):
2019-12-11 23:10:18 +00:00
if is_eligible_for_activation_queue(validator):
validator.activation_eligibility_epoch = get_current_epoch(state) + 1
2019-04-20 06:32:41 +00:00
if is_active_validator(validator, get_current_epoch(state)) and validator.effective_balance <= EJECTION_BALANCE:
initiate_validator_exit(state, ValidatorIndex(index))
2019-03-07 19:05:34 +00:00
2019-12-10 18:49:26 +00:00
# Queue validators eligible for activation and not yet dequeued for activation
activation_queue = sorted([
2019-06-30 20:59:12 +00:00
index for index, validator in enumerate(state.validators)
2019-12-11 23:00:46 +00:00
if is_eligible_for_activation(state, validator)
# Order by the sequence of activation_eligibility_epoch setting and then index
], key=lambda index: (state.validators[index].activation_eligibility_epoch, index))
2019-12-10 18:25:55 +00:00
# Dequeued validators for activation up to churn limit
2019-06-30 13:11:46 +00:00
for index in activation_queue[:get_validator_churn_limit(state)]:
2019-06-09 19:41:21 +00:00
validator = state.validators[index]
2019-12-10 18:25:55 +00:00
validator.activation_epoch = compute_activation_exit_epoch(get_current_epoch(state))
2019-03-07 19:05:34 +00:00
```
2019-04-06 10:07:03 +00:00
#### Slashings
2019-03-07 19:05:34 +00:00
```python
def process_slashings(state: BeaconState) -> None:
2019-06-22 05:34:02 +00:00
epoch = get_current_epoch(state)
total_balance = get_total_active_balance(state)
2019-06-09 19:41:21 +00:00
for index, validator in enumerate(state.validators):
if validator.slashed and epoch + EPOCHS_PER_SLASHINGS_VECTOR // 2 == validator.withdrawable_epoch:
increment = EFFECTIVE_BALANCE_INCREMENT # Factored out from penalty numerator to avoid uint64 overflow
penalty_numerator = validator.effective_balance // increment * min(sum(state.slashings) * 3, total_balance)
penalty = penalty_numerator // total_balance * increment
decrease_balance(state, ValidatorIndex(index), penalty)
2019-03-07 19:05:34 +00:00
```
#### Final updates
```python
def process_final_updates(state: BeaconState) -> None:
2019-03-07 19:05:34 +00:00
current_epoch = get_current_epoch(state)
2019-06-30 10:01:54 +00:00
next_epoch = Epoch(current_epoch + 1)
# Reset eth1 data votes
if (state.slot + 1) % SLOTS_PER_ETH1_VOTING_PERIOD == 0:
state.eth1_data_votes = []
# Update effective balances with hysteresis
2019-06-09 19:41:21 +00:00
for index, validator in enumerate(state.validators):
balance = state.balances[index]
HALF_INCREMENT = EFFECTIVE_BALANCE_INCREMENT // 2
if balance < validator.effective_balance or validator.effective_balance + 3 * HALF_INCREMENT < balance:
validator.effective_balance = min(balance - balance % EFFECTIVE_BALANCE_INCREMENT, MAX_EFFECTIVE_BALANCE)
# Reset slashings
state.slashings[next_epoch % EPOCHS_PER_SLASHINGS_VECTOR] = Gwei(0)
2019-03-07 19:05:34 +00:00
# Set randao mix
state.randao_mixes[next_epoch % EPOCHS_PER_HISTORICAL_VECTOR] = get_randao_mix(state, current_epoch)
2019-03-07 19:05:34 +00:00
# Set historical root accumulator
if next_epoch % (SLOTS_PER_HISTORICAL_ROOT // SLOTS_PER_EPOCH) == 0:
2019-06-30 13:45:36 +00:00
historical_batch = HistoricalBatch(block_roots=state.block_roots, state_roots=state.state_roots)
2019-03-08 17:13:05 +00:00
state.historical_roots.append(hash_tree_root(historical_batch))
2019-03-07 19:05:34 +00:00
# Rotate current/previous epoch attestations
state.previous_epoch_attestations = state.current_epoch_attestations
state.current_epoch_attestations = []
```
2019-05-01 12:29:03 +00:00
### Block processing
2019-03-07 19:05:34 +00:00
```python
2019-05-01 12:14:10 +00:00
def process_block(state: BeaconState, block: BeaconBlock) -> None:
process_block_header(state, block)
2019-05-03 20:21:42 +00:00
process_randao(state, block.body)
process_eth1_data(state, block.body)
2019-05-01 12:14:10 +00:00
process_operations(state, block.body)
2019-03-07 19:05:34 +00:00
```
#### Block header
```python
def process_block_header(state: BeaconState, block: BeaconBlock) -> None:
# Verify that the slots match
assert block.slot == state.slot
# Verify that the parent matches
2019-11-21 22:13:45 +00:00
assert block.parent_root == hash_tree_root(state.latest_block_header)
# Cache current block as the new latest block
2019-04-19 08:26:54 +00:00
state.latest_block_header = BeaconBlockHeader(
slot=block.slot,
2019-05-06 19:49:46 +00:00
parent_root=block.parent_root,
state_root=Bytes32(), # Overwritten in the next process_slot call
2019-05-07 07:52:56 +00:00
body_root=hash_tree_root(block.body),
2019-04-19 08:26:54 +00:00
)
2019-03-17 11:48:47 +00:00
# Verify proposer is not slashed
2019-06-30 17:42:24 +00:00
proposer = state.validators[get_beacon_proposer_index(state)]
2019-03-11 22:07:34 +00:00
assert not proposer.slashed
2019-03-07 19:05:34 +00:00
```
#### RANDAO
```python
2019-05-03 20:21:42 +00:00
def process_randao(state: BeaconState, body: BeaconBlockBody) -> None:
epoch = get_current_epoch(state)
# Verify RANDAO reveal
2019-06-30 17:42:24 +00:00
proposer = state.validators[get_beacon_proposer_index(state)]
2020-01-03 07:18:34 +00:00
signing_root = compute_signing_root(epoch, get_domain(state, DOMAIN_RANDAO))
assert bls.Verify(proposer.pubkey, signing_root, body.randao_reveal)
# Mix in RANDAO reveal
mix = xor(get_randao_mix(state, epoch), hash(body.randao_reveal))
state.randao_mixes[epoch % EPOCHS_PER_HISTORICAL_VECTOR] = mix
2019-03-07 19:05:34 +00:00
```
#### Eth1 data
```python
2019-05-03 20:21:42 +00:00
def process_eth1_data(state: BeaconState, body: BeaconBlockBody) -> None:
state.eth1_data_votes.append(body.eth1_data)
if state.eth1_data_votes.count(body.eth1_data) * 2 > SLOTS_PER_ETH1_VOTING_PERIOD:
2019-06-09 19:41:21 +00:00
state.eth1_data = body.eth1_data
2019-03-07 19:05:34 +00:00
```
#### Operations
2019-03-07 19:05:34 +00:00
```python
def process_operations(state: BeaconState, body: BeaconBlockBody) -> None:
2019-05-03 20:21:42 +00:00
# Verify that outstanding deposits are processed up to the maximum number of deposits
2019-06-09 19:41:21 +00:00
assert len(body.deposits) == min(MAX_DEPOSITS, state.eth1_data.deposit_count - state.eth1_deposit_index)
2019-06-18 00:54:08 +00:00
2019-06-30 15:10:22 +00:00
for operations, function in (
(body.proposer_slashings, process_proposer_slashing),
(body.attester_slashings, process_attester_slashing),
(body.attestations, process_attestation),
(body.deposits, process_deposit),
(body.voluntary_exits, process_voluntary_exit),
2019-08-24 11:55:18 +00:00
# @process_shard_receipt_proofs
2019-06-30 15:10:22 +00:00
):
for operation in operations:
function(state, operation)
```
2019-03-07 19:05:34 +00:00
##### Proposer slashings
2019-03-07 19:05:34 +00:00
```python
2019-05-03 20:21:42 +00:00
def process_proposer_slashing(state: BeaconState, proposer_slashing: ProposerSlashing) -> None:
# Verify header slots match
2019-11-21 22:13:45 +00:00
assert proposer_slashing.signed_header_1.message.slot == proposer_slashing.signed_header_2.message.slot
# Verify the headers are different
assert proposer_slashing.signed_header_1 != proposer_slashing.signed_header_2
# Verify the proposer is slashable
proposer = state.validators[proposer_slashing.proposer_index]
2019-03-19 11:12:50 +00:00
assert is_slashable_validator(proposer, get_current_epoch(state))
# Verify signatures
2019-11-21 22:13:45 +00:00
for signed_header in (proposer_slashing.signed_header_1, proposer_slashing.signed_header_2):
domain = get_domain(state, DOMAIN_BEACON_PROPOSER, compute_epoch_at_slot(signed_header.message.slot))
2020-01-03 07:18:34 +00:00
signing_root = compute_signing_root(signed_header.message, domain)
assert bls.Verify(proposer.pubkey, signing_root, signed_header.signature)
2019-03-07 19:05:34 +00:00
slash_validator(state, proposer_slashing.proposer_index)
```
##### Attester slashings
2018-12-10 21:16:06 +00:00
2019-03-02 01:49:28 +00:00
```python
2019-05-03 20:21:42 +00:00
def process_attester_slashing(state: BeaconState, attester_slashing: AttesterSlashing) -> None:
2019-04-30 09:39:18 +00:00
attestation_1 = attester_slashing.attestation_1
attestation_2 = attester_slashing.attestation_2
2019-05-01 06:42:49 +00:00
assert is_slashable_attestation_data(attestation_1.data, attestation_2.data)
assert is_valid_indexed_attestation(state, attestation_1)
assert is_valid_indexed_attestation(state, attestation_2)
2019-05-01 06:42:49 +00:00
slashed_any = False
2019-11-05 16:00:04 +00:00
indices = set(attestation_1.attesting_indices).intersection(attestation_2.attesting_indices)
for index in sorted(indices):
2019-06-09 19:41:21 +00:00
if is_slashable_validator(state.validators[index], get_current_epoch(state)):
2019-05-01 06:42:49 +00:00
slash_validator(state, index)
slashed_any = True
assert slashed_any
2019-03-02 01:49:28 +00:00
```
2019-03-07 19:05:34 +00:00
##### Attestations
2019-01-08 00:53:33 +00:00
```python
2019-03-07 19:05:34 +00:00
def process_attestation(state: BeaconState, attestation: Attestation) -> None:
2019-05-05 18:30:55 +00:00
data = attestation.data
2019-10-17 08:47:51 +00:00
assert data.index < get_committee_count_at_slot(state, data.slot)
2019-06-22 20:49:53 +00:00
assert data.target.epoch in (get_previous_epoch(state), get_current_epoch(state))
assert data.target.epoch == compute_epoch_at_slot(data.slot)
assert data.slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot <= data.slot + SLOTS_PER_EPOCH
committee = get_beacon_committee(state, data.slot, data.index)
2019-11-02 03:02:53 +00:00
assert len(attestation.aggregation_bits) == len(committee)
2019-07-22 13:13:07 +00:00
2019-03-07 19:05:34 +00:00
pending_attestation = PendingAttestation(
2019-04-03 19:40:54 +00:00
data=data,
2019-06-28 11:23:22 +00:00
aggregation_bits=attestation.aggregation_bits,
inclusion_delay=state.slot - data.slot,
2019-06-30 17:42:24 +00:00
proposer_index=get_beacon_proposer_index(state),
2019-03-07 19:05:34 +00:00
)
2019-05-05 11:04:34 +00:00
2019-06-22 20:49:53 +00:00
if data.target.epoch == get_current_epoch(state):
2019-06-23 09:18:24 +00:00
assert data.source == state.current_justified_checkpoint
2019-03-07 19:05:34 +00:00
state.current_epoch_attestations.append(pending_attestation)
else:
2019-06-23 09:18:24 +00:00
assert data.source == state.previous_justified_checkpoint
2019-03-07 19:05:34 +00:00
state.previous_epoch_attestations.append(pending_attestation)
2019-05-05 11:04:34 +00:00
# Verify signature
assert is_valid_indexed_attestation(state, get_indexed_attestation(state, attestation))
2019-02-12 12:24:19 +00:00
```
2019-03-07 19:05:34 +00:00
##### Deposits
```python
def process_deposit(state: BeaconState, deposit: Deposit) -> None:
# Verify the Merkle branch
2019-06-30 09:11:23 +00:00
assert is_valid_merkle_branch(
leaf=hash_tree_root(deposit.data),
2019-06-30 09:02:18 +00:00
branch=deposit.proof,
depth=DEPOSIT_CONTRACT_TREE_DEPTH + 1, # Add 1 for the List length mix-in
2019-06-09 19:41:21 +00:00
index=state.eth1_deposit_index,
root=state.eth1_data.deposit_root,
)
2019-04-20 06:35:02 +00:00
# Deposits must be processed in order
2019-06-09 19:41:21 +00:00
state.eth1_deposit_index += 1
pubkey = deposit.data.pubkey
amount = deposit.data.amount
2019-06-09 19:41:21 +00:00
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
2019-11-21 22:13:45 +00:00
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)
2020-01-03 07:18:34 +00:00
if not bls.Verify(pubkey, signing_root, deposit.data.signature):
return
# Add validator and balance entries
2019-06-09 19:41:21 +00:00
state.validators.append(Validator(
pubkey=pubkey,
withdrawal_credentials=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=min(amount - amount % EFFECTIVE_BALANCE_INCREMENT, MAX_EFFECTIVE_BALANCE),
))
2019-04-20 05:17:33 +00:00
state.balances.append(amount)
else:
# Increase balance by deposit amount
index = ValidatorIndex(validator_pubkeys.index(pubkey))
increase_balance(state, index, amount)
```
2019-03-07 19:05:34 +00:00
##### Voluntary exits
2019-02-12 12:24:19 +00:00
```python
2019-11-21 22:13:45 +00:00
def process_voluntary_exit(state: BeaconState, signed_voluntary_exit: SignedVoluntaryExit) -> None:
voluntary_exit = signed_voluntary_exit.message
validator = state.validators[voluntary_exit.validator_index]
2019-03-19 21:49:01 +00:00
# Verify the validator is active
assert is_active_validator(validator, get_current_epoch(state))
# Verify exit has not been initiated
assert validator.exit_epoch == FAR_FUTURE_EPOCH
2019-03-07 19:05:34 +00:00
# Exits must specify an epoch when they become valid; they are not valid before then
2019-11-21 22:13:45 +00:00
assert get_current_epoch(state) >= voluntary_exit.epoch
2019-03-19 20:43:05 +00:00
# Verify the validator has been active long enough
assert get_current_epoch(state) >= validator.activation_epoch + PERSISTENT_COMMITTEE_PERIOD
2019-03-07 19:05:34 +00:00
# Verify signature
2019-11-21 22:13:45 +00:00
domain = get_domain(state, DOMAIN_VOLUNTARY_EXIT, voluntary_exit.epoch)
2020-01-03 07:18:34 +00:00
signing_root = compute_signing_root(voluntary_exit, domain)
assert bls.Verify(validator.pubkey, signing_root, signed_voluntary_exit.signature)
2019-03-19 20:43:05 +00:00
# Initiate exit
2019-11-21 22:13:45 +00:00
initiate_validator_exit(state, voluntary_exit.validator_index)
```