The beacon chain is the core system-level blockchain for Ethereum 2.0. The name comes from that fact that it serves as a random beacon, but could just as easily be called the “system chain”, “spine chain”, etc. This chain is where the validators exist, where they are assigned their duties, where they perform the RNG MPC, where they vote on the head of the chain for the fork choice, where they finalize checkpoints, and where they link back in references to shard chains (crosslinks) to serve as the root of each shard chain and to faciliate cross-shard communication. It is both the brains behind the operation and the scaffolding upon which the rest of the sharded system is built.
The beacon chain’s state (BeaconState
) is the core object around which the specification is built. The BeaconState
encapsulates all of the information pertaining to: who the validators are, in what state each of them is in, which chain in the tree this state belongs to and a hash-reference to the Ethereum 1 chain.
Beginning with the genesis state, the post state of a block is considered valid if it passes all of the guards within the state transition function. Thus, the precondition of a block is recursively defined as being a valid postcondition of running the state transition function on the previous block and its state all the way back to the genesis state.
Given a block tree, the fork choice rule provides a single chain (the canonical chain) and resulting state based upon recent messages from validators. The fork choice rule consumes the block-tree along with the set of most recent attestations from the validator set and returns a block as the current head of the chain. LMD GHOST, the fork choice rule of Eth2.0, considers which block the latest attestation from each validator points to and uses this to calculate the total balance that recursively attests to each block in the tree. This is done by setting the weight of a node in the tree to be the sum of the balances of the validators whose latest attestations were for that node or any descendent of that node. The GHOST algorithm then starts at the base of the tree, selecting the child of the current node with the heaviest weight until reaching a leaf node. This leaf node is the head of the chain and recursively defines the canonical chain.
Concretely, validators are given the opportunity to produce a single attestation during an assigned slot at each epoch. The committed to attestation.data.beacon_block_root
serves as a fork choice vote. A view takes into account all of the most recent of these votes from the current active validator set when computing the fork choice.
The fork choice rule allows us to choose a single canonical blockchain through the block tree whereas “finality” provides guarantees that certain blocks will remain within the canonical chain. The beacon chain uses a modified version of Casper FFG for finality. Casper provides “accountable safety” that certain blocks will always remain in the canonical chain unless some threshold of validators burn their staked up capital. This is what we call a “crypto-economic” claim of safety rather than a more traditional safety found in traditional consensus algorithms.
Concretely, validators are given the opportunity to produce a single attestation during an assigned slot at each epoch. The committed to (attestation.data.source_root, attestation.data.source_epoch)
serves as the FFG source pair discussed in depth in “Combining GHOST and Casper”, while the committed to (attestation.data.target_root, attestation.data.target_epoch)
serves as the FFG target pair.
Crosslinks are references in the beacon chain to the recent state/blocks for each shard chain. These references serve both as the root of the shard chain fork choice as well as a means for asynchronous communication between shards. In the normal case, each shard can be crosslinked into the beacon chain once per epoch (with a low validator count, this is sometimes once every N epochs).
Although shard chains are not added until phase 1, crosslink committees in phase 0 are still assigned to a shard and attempt to form a crosslink each epoch. In phase 0, data roots inside of crosslinks are simply stubbed as 0x00
.
A detailed discussion of the responsibilities of validators in phase 0 can be found here.
The two primary responsibilities are (1) attesting to the beacon chain at each epoch and (2) infrequently creating beacon blocks when selected. Validators are split into “crosslink committees” at each epoch. Each committee is assigned to a slot and shard. The validator attests to the head of the beacon chain and the recent data of the shard (phase 1) at the assigned slot. At each slot, a single beacon block proposer is selected (via get_beacon_proposer_index
) from one of the committees assigned to the slot.
Validators gain rewards by regularly attesting to the canonical beacon chain and shard chains and incur penalties if they fail to do so. A validator can be slashed (a more severe penalty) if they violate the Casper FFG rules or if they create two beacon blocks in one epoch. More details on slashing can be found here.
Data structures and hashes of data structures within the beacon chain are encoded as Simple SerialiZe (SSZ) objects. One of the benefits of SSZ-hashing is its support for non-homgenous tree depths when merkleizing the underlying data. As a result of this and the rest of SSZ’s clever design, the hash tree root of an SSZ object is the same whether the object’s sub-objects are represented by the full sub-objects or just their partial merkle root.
Beacon operations are datastructures that can be added to a BeaconBlock
by a block proposer. This is how various messages related to system level validation/construction of the chain are incorporated. They are essentially validator-level transactions to the beacon chain state machine. Each operation has a maximum allowed per block defined in the constants in max operations per block.
ProposerSlashing
hash_tree_root(block) == hash_tree_root(block_header)
and thus the signature is valid for both data structures. BeaconBlockHeader
is thus used as proof to reduce message size.AttesterSlashing
is_slashable_attestation_data
which checks for the Casper FFG “double” and “surround” vote conditions.Attestation
AttestationData
is the main component that is signed. The outer datastructure contains the aggregate signature and the participation bitfields requisite for verification of the signature.aggregation_bitfield
stores a single bit for each member of the committee with a value of 1
if the validator participated in this aggregate signaturedata
is the AttestationData
that was signed by the validator or committee of validators
beacon_block_root
- block root of the beacon block seen as the head of the chain at the assigned slotsource_epoch
- the most recent justified epoch in the BeaconState
at the assigned slotsource_root
- the most recent justified root in the BeaconState
at the assigned slottarget_epoch
- the epoch attempting to be justified (the current epoch)target_root
- the block root attempting to be justified (the epoch boundary block)crosslink
- the crosslink attemping to be formed for the assigned shardcustody_bitfield
represents each participating validator’s “proof of custody” bit. In phase 0, this must be set to 0 bits. (proof of custody is to be enabled during phase 1)signature
is the aggregate BLS signature of the data.AttestationDataAndCustodyBit
is the actual message signed by validators. For a given Attestation
there are two potential messages signed – AttestationDataAndCustodyBit
with a 0 or 1 custody bit. The custody_bitfield
allows recovery of the expected signed message (custody_bit 0/1) from each participating validator. In phase 0, all custody bits are 0.Deposit
proof
- the merkle proof against the BeaconState
current eth1_data.root
data
- the DepositData
submit to the deposit contract to be verified using the proof against the deposit root.
pubkey
- BLS12-381 pubkey to be used to sign messages by the validatorwithdrawal_credentials
- the hash of an offline pubkey to be used to withdraw the staked funds after exiting. This key is not used actively in validation and can be kept in cold storage.amount
- amount in Gwei that was depositedsignature
- Signature of the DepositData
using the privkey
pair of the pubkey
. This is used as a one-time “proof of custody” required for securely using BLS keys.VoluntaryExit
epoch
- minimum epoch at which this exit can be included on chain. Helps prevent accidental/nefarious use in chain reorgs/forks.validator_index
- index of validator exitingsignature
- signature of the VoluntaryExit
by the pubkey associated with the Validator
defined by validator_index
.Transfer
slot
specified to avoid replay attacks.MAX_TRANSFERS
is expected to be set to 0 at the initial launch of phase 0 and only to be increased once phase 0 is seen as stable in production.sender
- validator index of the sender of fundsrecipient
- validator index of the recipient of fundsamount
- amount in Gweifee
- fee in Gwei to be paid to the block proposer for including the transferslot
- the specific slot that this signed Transfer
can be included on chain. prevents replay attackspubkey
- the withdrawal pubkey
of the sender
. The hash
of this pubkey must match the sender
s withdrawal_credentials.signature
- the signature of the Transfer
signed by the transfer.pubkey
BeaconBlock
hash_tree_root(BeaconBlock) == hash_tree_root(BeaconBlockHeader)
and so signatures of each are equivalent.slot
- the slot for which this block is created. Must be greater than the slot of the block defined by parent_root
parent_root
- the block root of the parent block forming a block chainstate_root
- the hash root of the post state of running the state transition through this blockbody
- contain all of the aforementioned beacon operations objects as well as a few supplemental fieldssignature
- signature of the block by the block proposerBeaconBlockBody
randao_reveal
is the signature of the current epoch (by the block proposer) and, when mixed in with the other validators’ reveals, consitutes the seed for randomness.Eth1Data
is the pointer from the beacon chain back to the Eth1 chain. It maintains the following fields of note:
deposit_root
- the merkle-root of the deposits in the deposit contract.deposit_count
- the number of deposits that have occured thusfarblock_hash
- the eth1 block hash that contained the deposit_root
. This block_hash
is expected to be used for finilization of the Eth1 chain in the future (similar to how the FFG contract was going to be used to finalize the eth1 chain).graffiti
- This is a space for validators to decorate as they choose. It has no define in-protocol use.The BeaconState
is the resulting state of running the state transition function on a series of blocks starting from the genesis state. It contains the validator registry, information about randomness, information about finality, relevant caches, and data about the eth1 chain.
slot
– time is divided into “slots” of fixed length at which actions occur and state transitions happen. This field tracks the slot of the state.genesis_time
– tracks when genesis event occured. This allows a client to calculate what the slot should be according to wall clock timefork
– a mechanism for handling forking (upgrading) the beacon chain. The main purpose here is to handle versioning of signatures and handle objects of different signatures across fork boundariesvalidator_registry
– a list of Validator
records, tracking the current full registry. Each validator contains data relevant to it such as pubkey, effective balance, and status (pending, active, exited, etc)balances
– a list mapping 1:1 with the validator_registry
. The granular/frequently changing balances are pulled out to reduce the amount of re-hashing that needs to be performed at each epoch.latest_randao_mixes
– The randao mix from each epoch. At the start of every epoch, the randao_mix from the previous epoch is copied over as the base of the current epoch. At each block, the hash
of the block.randao_reveal
is xor’d into the running mix.latest_start_shard
– Tracks the “start shard” of the current epoch which is used as the base to calculate assigned shard for each committee in an epoch – (latest_shard_shard + committee_offset) % SHARD_COUNT
PendingAttestations
Attestations
from blocks are converted to PendingAttestations
and stored in state for bulk accounting at epoch boundaries. We store two separate listsprevious_epoch_attesations
– List of PendingAttestations
for slots from the previous epoch. note: these are attestations attesting to slots in the previous epoch, not those included in blocks during the previous epoch.current_epoch_attesations
– List of PendingAttestations
for slots from the current epoch. Moved to previous_epoch_attestations
at the end of the current epoch processing.previous_justifed_epoch
– Most recent justified epoch during the previous epoch.current_justifed_epoch
– Most recent justified epoch during the current epoch.previous_justified_root
– Most recent justified block root during the previous epoch.current_justified_root
– Most recent justified block root during the current epoch.justification_bitfield
– bits tracking the justified state of the previous 64 epochs. Note that this is not alone sufficient in calculating finality as source
of the justified epoch must be taken into account.finalized_epoch
– the latest finalized epochfinalized_root
– the latest finalized block rootAttestation
references the crosslink as it was during the creation of the attestation. To validate an crosslink reference within attestation from the previous epoch during the current epoch, we rely upon previous_crosslinks
.current_crosslinks
– The list of crosslinks (one per shard) during the current epochprevious_crosslinks
– The list of crosslinks as they were during the previous epochlatest_block_roots
– per-slot store of the recent block roots. The block root for a slot is added at the start of the next slot to avoid the circular dependency due to the state root being embedded in the block. For slots that are skipped (no block in the chain for the given slot), the previous available block root is stored.latest_state_roots
– per-slot store of the recent state roots. The state root for a slot is stored at the start of the next slot.latest_active_index_roots
– per-epoch store of the hash-root of the active validator registry. This is primarily used to better serve light clients.latest_slashed_balances
– per-epoch store of the running total of slashed balances for all time. The difference between the current and some previous epoch gives us the “recent slashed balance” is used to calculate the proportion of balance that should be slashed for slashable validators.latest_block_header
– Store of the latest block header seen. On the slot of the block, the header is stored without the state root. At the start of the next slot, the state root is added to the block header. This is done to eliminate the circular dependency of the state root being embedded in the block header.historical_roots
– a double batch merkle accumulator of the latest block and state roots to make historic merkle proofs againstlatest_eth1_data
– the latest eth1 data that has been come to a consensus on by the eth2 validators. This data is then used to process incoming deposits from the eth1 chain.eth1_data_votes
– the latest eth1 data votes since the start of the recent eth1 data voting period. Each processed block contains an Eth1Data
vote. At the end of the voting period, if >50% agree on a single vote, the Eth1Data
is enshrined in latest_eth1_data
.deposit_index
– running counter of the latest deposit processed. Deposits must be processed in order and blocks must contain deposits if deposit_index < latest_eth1_data.deposit_count
, with a maximum of MAX_DEPOSITS
per block.state_transition
process_slots
process_slots
up to the block slot.process_slot
is called for each slot.process_epoch
is only called at epoch boundaries. Note that because state.slot
has not yet been incremented, this occurs at the state transition at slot
that is the start of slot % SLOTS_PER_EPOCH == 0
process_slot
state
has not yet been modified so still represents the post state of the previous slot. Also state.slot
is still equal to the previous slot (state.slot += 1
happens after process_slot
from within process_slots
).hash_tree_root(state) == root_of_previous_state
because the state
has not yet been modified from the post state of the previous slot.previous_state_root
into the cached block header. This block header remains in state through any skipped slots until the next block occurs.latest_block_roots
at each slot. If the slot is skipped, then the block from the latest non-skipped slot is cached.Epoch processing occurs at the start of the 0th slot (slot % EPOCH_LENGTH == 0
) of each epoch. Note that state.slot
is still equal to the previous slot when process_epoch
is run, only incremented after.
process_epoch
is the primary container function that calls the rest of the epoch processing sub-functions.
process_justification_and_finilization
k=1
and k=2
finality rules discussed in section 5.5 in the draft paper.process_crosslinks
crosslink.parent_root
. The only crosslinks considered in get_winning_crosslink_and_attesting_indices
are those that meet this chain criterion.process_rewards_and_penalties
get_crosslink_deltas
and get_attestation_deltas
which return the rewards and penalties for each validator as tuples of lists. Rewards and penalties are separated to avoid signed integers.get_base_reward
get_attestation_deltas
get_crosslink_deltas
process_registry_updates
process_slashings
process_slashings
goes through the recent slashings and penalizes slashed validators proportionately.process_final_updates
process_block
is the main function that calls the sub-functions of block processing. If any asserts or exceptions are thrown in block processing, then the block is seen as invalid and should be discarded.
process_block_header
determines whether, at a high level, a block is valid. It does so by verifying that:
process_block_header
also stores the block header in state
for later uses in the state transition function.
process_randao
verifies that the block’s RANDAO reveal is the valid signature of the current epoch by the proposer and if so, mixes it into the epoch’s mix.
process_eth1_data
adds the block’s eth1-data to state’s eth1-data-votes. If more than half of the votes in the voting period are for the same eth1-data, update latest_eth1_data
.
process_operations
makes sure that the appropriate number of slashings, attestations, depoists, exits and transfers occur as determined by the functions in the following sections. It does so by processing the ProposerSlashing
, AttesterSlashing
, Attestation
, Deposit
, VoluntaryExit
, and Transfer
objects as described above.