Part 3: Annotated Specification

Types, Constants, Presets, and Configuration


Genesis Settings

Beacon chain genesis is long behind us. Nevertheless, the ability to spin-up testnets is useful in all sorts of scenarios, so the spec retains genesis functionality, now called initialisation.

The following parameters refer to the actual mainnet beacon chain genesis, and I'll explain them in that context. When starting up new testnets, these will of course be changed. For example, see the configuration file for the Prater testnet.

Name Value
MIN_GENESIS_TIME uint64(1606824000) (Dec 1, 2020, 12pm UTC)
GENESIS_FORK_VERSION Version('0x00000000')
GENESIS_DELAY uint64(604800) (7 days)

MIN_GENESIS_ACTIVE_VALIDATOR_COUNT is the minimum number of full validator stakes that must have been deposited before the beacon chain can start producing blocks. The number is chosen to ensure a degree of security. It allows for four 128 member committees per slot, rather than the 64 committees per slot eventually desired to support the fully operational data shards that were on the roadmap at that time (but no longer). Fewer validators means higher rewards per validator, so it is designed to attract early participants to get things bootstrapped.

MIN_GENESIS_ACTIVE_VALIDATOR_COUNT used to be much higher (65,536 = 2 million Ether staked), but was reduced when MIN_GENESIS_TIME, below, was added.

In the actual event of beacon chain genesis, there were 21,063 participating validators, comfortably exceeding the minimum necessary count.


MIN_GENESIS_TIME is the earliest date that the beacon chain can start.

Having a MIN_GENESIS_TIME allows us to start the chain with fewer validators than was previously thought necessary. The previous plan was to start the chain as soon as there were MIN_GENESIS_ACTIVE_VALIDATOR_COUNT validators staked. But there were concerns that with a lowish initial validator count, a single entity could form the majority of them and then act to prevent other validators from entering (a "gatekeeper attack"). A minimum genesis time allows time for all those who wish to make deposits to do so before they could be excluded by a gatekeeper attack.

The beacon chain actually started at 12:00:23 UTC on the 1st of December 2020. The extra 23 seconds comes from the timestamp of the first Eth1 block to meet the genesis criteria, block 11320899. I like to think of this as a little remnant of proof of work forever embedded in the beacon chain's history.


Unlike Ethereum 1.0, the beacon chain gives in-protocol versions to its forks. See the Version custom type for more explanation.

GENESIS_FORK_VERSION is the fork version the beacon chain starts with at its "genesis" event: the point at which the chain first starts producing blocks. Nowadays, this value is used only when computing the cryptographic domain for deposit messages and BLS credential change messages, which are valid across all forks.

Fork versions and timings for the Altair, Bellatrix, and Capella upgrades are defined in their respective specifications as follows.

Name Value
ALTAIR_FORK_VERSION Version('0x01000000')
ALTAIR_FORK_EPOCH Epoch(74240) (Oct 27, 2021, 10:56:23am UTC)
BELLATRIX_FORK_VERSION Version('0x02000000')
BELLATRIX_FORK_EPOCH Epoch(144896) (Sept 6, 2022, 11:34:47am UTC)
CAPELLA_FORK_VERSION Version('0x03000000')
CAPELLA_FORK_EPOCH Epoch(194048) (April 12, 2023, 10:27:35pm UTC)

The GENESIS_DELAY is a grace period to allow nodes and node operators time to prepare for the genesis event. The genesis event cannot occur before MIN_GENESIS_TIME. If MIN_GENESIS_ACTIVE_VALIDATOR_COUNT validators are not registered sufficiently in advance of MIN_GENESIS_TIME, then Genesis will occur GENESIS_DELAY seconds after enough validators have been registered.

Seven days' notice was regarded as sufficient to allow client dev teams time to make a release once the genesis parameters were known, and for node operators to upgrade to that release. And, of course, to organise some parties. It was increased from 2 days over time due to lessons learned on some of the pre-genesis testnets.

Time parameters

Name Value Unit Duration
SECONDS_PER_SLOT uint64(12) seconds 12 seconds
SECONDS_PER_ETH1_BLOCK uint64(14) seconds 14 seconds
MIN_VALIDATOR_WITHDRAWABILITY_DELAY uint64(2**8) (= 256) epochs ~27 hours
SHARD_COMMITTEE_PERIOD uint64(2**8) (= 256) epochs ~27 hours
ETH1_FOLLOW_DISTANCE uint64(2**11) (= 2,048) Eth1 blocks ~8 hours

This was originally six seconds, but is now twelve, and has been other values in between.

Network delays are the main limiting factor in shortening the slot length. Three communication activities need to be accomplished within a slot, and it is supposed that four seconds is enough for the vast majority of nodes to have participated in each:

  1. Blocks are proposed at the start of a slot and should have propagated to most of the network within the first four seconds.
  2. At four seconds into a slot, committee members create and broadcast attestations, including attesting to this slot's block. During the next four seconds, these attestations are collected by aggregators in each committee.
  3. At eight seconds into the slot, the aggregators broadcast their aggregate attestations which then have four seconds to reach the validator who is proposing the next block.

There is a general intention to shorten the slot time in future, perhaps to 8 seconds, if it proves possible to do this in practice. Or perhaps to lengthen it to 16 seconds.

Post-Merge, the time taken by the execution client to validate the execution payload contents (that is, the normal Ethereum transactions) is now on the critical path for validators during step 1, the first four seconds. In order for the validator to attest correctly, the beacon block must first be broadcast, propagated and received, then validated by the consensus client, and also validated by the execution client, all within that initial four-second window. In borderline cases, the extra time taken by execution validation can push the whole process beyond the four-second point at which attestations must be made. This can lead to voting incorrectly for an empty slot. See Adrian Sutton's article Understanding Attestation Misses for further explanation.


The assumed block interval on the Eth1 chain, used in conjunction with ETH1_FOLLOW_DISTANCE when considering blocks on the Eth1 chain, either at genesis, or when voting on the deposit contract state.

The average Eth1 block time since January 2020 has actually been nearer 13 seconds, but never mind. The net effect is that we will be going a little deeper back in the Eth1 chain than ETH1_FOLLOW_DISTANCE would suggest, which ought to be safer.


A validator can stop participating once it has made it through the exit queue. However, its stake remains locked for the duration of MIN_VALIDATOR_WITHDRAWABILITY_DELAY. This is to allow some time for any slashable behaviour to be detected and reported so that the validator can still be penalised (in which case the validator's withdrawable time is pushed EPOCHS_PER_SLASHINGS_VECTOR into the future).

Once the MIN_VALIDATOR_WITHDRAWABILITY_DELAY period has passed, the validator becomes eligible for a full withdrawal of its stake and rewards on the next withdrawals sweep, as long as it has ETH1_ADDRESS_WITHDRAWAL_PREFIX (0x01) withdrawal credentials set. In any case, being in a "withdrawable" state means that a validator has now fully exited from the protocol.


This really anticipates the implementation of data shards, which is no longer planned, at least in its originally envisaged form. The idea is that it's bad for the stability of longer-lived committees if validators can appear and disappear very rapidly. Therefore, a validator cannot initiate a voluntary exit until SHARD_COMMITTEE_PERIOD epochs after it has been activated. However, it could still be ejected by slashing before this time.


This is used to calculate the minimum depth of block on the Ethereum 1 chain that can be considered by the Eth2 chain: it applies to the Genesis process and the processing of deposits by validators. The Eth1 chain depth is estimated by multiplying this value by the target average Eth1 block time, SECONDS_PER_ETH1_BLOCK.

The value of ETH1_FOLLOW_DISTANCE is not based on the expected depth of any reorgs of the Eth1 chain, which are rarely if ever more than 2-3 blocks deep. It is about providing time to respond to an incident on the Eth1 chain such as a consensus failure between clients.

This parameter was increased from 1024 to 2048 blocks for the beacon chain mainnet, to allow devs more time to respond if there were any trouble on the Eth1 chain.

The whole follow distance concept has been made redundant by the Merge and may be removed in a future upgrade, so that validators can make deposits and become active more-or-less instantly.

Validator Cycle

Name Value
EJECTION_BALANCE Gwei(2**4 * 10**9) (= 16,000,000,000)
MIN_PER_EPOCH_CHURN_LIMIT uint64(2**2) (= 4)
CHURN_LIMIT_QUOTIENT uint64(2**16) (= 65,536)

If a validator's effective balance falls to 16 Ether or below then it is exited from the system. This is most likely to happen as a result of the "inactivity leak", which gradually reduces the balances of inactive validators in order to maintain the liveness of the beacon chain.

This mechanism is intended to protect stakers who no longer have access to their keys. If a validator has been offline for long enough to lose half of its balance, it is unlikely to be coming back. To save the staker from losing everything, we choose to eject the validator before its balance reaches zero.

Note that the dependence on effective balance means that the validator is queued for ejection as soon as its actual balance falls to 16.75 Ether.


Validators are allowed to exit the system and cease validating, and new validators may apply to join at any time. For interesting reasons, a design decision was made to apply a rate-limit to entries (activations) and exits. Basically, it is important in proof of stake protocols that the validator set not change too quickly.

In the normal case, a validator is able to exit fairly swiftly: it just needs to wait MAX_SEED_LOOKAHEAD (currently four) epochs. However, if a large number of validators wishes to exit at the same time, a queue forms with a limited number of exits allowed per epoch. The minimum number of exits per epoch (the minimum "churn") is MIN_PER_EPOCH_CHURN_LIMIT, so that validators can always eventually exit. The actual allowed churn per epoch is calculated in conjunction with CHURN_LIMIT_QUOTIENT.

The same applies to new validator activations, once a validator has been marked as eligible for activation.

The rate at which validators can exit is strongly related to the concept of weak subjectivity, and the weak subjectivity period.


This is used in conjunction with MIN_PER_EPOCH_CHURN_LIMIT to calculate the actual number of validator exits and activations allowed per epoch. The number of exits allowed is max(MIN_PER_EPOCH_CHURN_LIMIT, n // CHURN_LIMIT_QUOTIENT), where n is the number of active validators. The same applies to activations.

Inactivity penalties

Name Value Description
INACTIVITY_SCORE_BIAS uint64(2**2) (= 4) score points per inactive epoch
INACTIVITY_SCORE_RECOVERY_RATE uint64(2**4) (= 16) score points per leak-free epoch

If the beacon chain hasn't finalised an epoch for longer than MIN_EPOCHS_TO_INACTIVITY_PENALTY epochs, then it enters "leak" mode. In this mode, any validator that does not vote (or votes for an incorrect target) is penalised an amount each epoch of (effective_balance * inactivity_score) // (INACTIVITY_SCORE_BIAS * INACTIVITY_PENALTY_QUOTIENT_BELLATRIX). See INACTIVITY_PENALTY_QUOTIENT_BELLATRIX for discussion of the inactivity leak itself.

The per-validator inactivity-score was introduced in the Altair upgrade. During Phase 0, inactivity penalties were an increasing global amount applied to all validators that did not participate in an epoch, regardless of their individual track records of participation. So a validator that was able to participate for a significant fraction of the time nevertheless could be quite severely penalised due to the growth of the per-epoch inactivity penalty. Vitalik gives a simplified example: "if fully [off]line validators get leaked and lose 40% of their balance, someone who has been trying hard to stay online and succeeds at 90% of their duties would still lose 4% of their balance. Arguably this is unfair."

In addition, if many validators are able to participate intermittently, it indicates that whatever event has befallen the chain is potentially recoverable (unlike a permanent network partition, or a super-majority network fork, for example). The inactivity leak is intended to bring finality to irrecoverable situations, so prolonging the time to finality if it's not irrecoverable is likely a good thing.

Each validator has an individual inactivity score in the beacon state which is updated by process_inactivity_updates() as follows.

  • Every epoch, irrespective of the inactivity leak,
    • decrease the score by one when the validator makes a correct timely target vote, and
    • increase the score by INACTIVITY_SCORE_BIAS otherwise.
  • When not in an inactivity leak
    • decrease every validator's score by INACTIVITY_SCORE_RECOVERY_RATE.

There is a floor of zero on the score. So, outside a leak, validators' scores will rapidly return to zero and stay there, since INACTIVITY_SCORE_RECOVERY_RATE is greater than INACTIVITY_SCORE_BIAS.

When in a leak, if pp is the participation rate between 00 and 11, and λ\lambda is INACTIVITY_SCORE_BIAS, then the expected score after NN epochs is max(0,N((1p)λp))\max (0, N((1-p)\lambda - p)). For λ=4\lambda = 4 this is max(0,N(45p))\max (0, N(4 - 5p)). So a validator that is participating 80% of the time or more can maintain a score that is bounded near zero. With less than 80% average participation, its score will increase unboundedly.


When not in an inactivity leak, validators' inactivity scores are reduced by INACTIVITY_SCORE_RECOVERY_RATE + 1 per epoch when they make a timely target vote, and by INACTIVITY_SCORE_RECOVERY_RATE - INACTIVITY_SCORE_BIAS when they don't. So, even for non-performing validators, scores decrease three times faster than they increase.

The new scoring system means that some validators will continue to be penalised due to the leak, even after finalisation starts again. This is intentional. When the leak causes the beacon chain to finalise, at that point we have just 2/3 of the stake online. If we immediately stop the leak (as we used to), then the amount of stake online would remain close to 2/3 and the chain would be vulnerable to flipping in and out of finality as small numbers of validators come and go. We saw this behaviour on some of the testnets prior to launch. Continuing the leak after finalisation serves to increase the balances of participating validators to greater than 2/3, providing a margin that should help to prevent such behaviour.

See the section on the Inactivity Leak for some more analysis of the inactivity score and some graphs of its effect.

Transition settings

Name Value
TERMINAL_TOTAL_DIFFICULTY 58750000000000000000000

These values are not used in the main beacon chain specification, but are used in the Bellatrix fork choice and validator guide to determine the point of handover from proof of work to proof of stake for the execution chain.

All previous upgrades to the Ethereum proof of work chain took place at a pre-defined block height. That approach was deemed to be insecure for the Merge due to the irreversible dynamics of the switch to proof of stake. The rationale is given in the Security Considerations section of EIP-3675.

Using a pre-defined block number for the hardfork is unsafe in this context due to the PoS fork choice taking priority during the transition.

An attacker may use a minority of hash power to build a malicious chain fork that would satisfy the block height requirement. Then the first PoS block may be maliciously proposed on top of the PoW block from this adversarial fork, becoming the head and subverting the security of the transition.

To protect the network from this attack scenario, difficulty accumulated by the chain (total difficulty) is used to trigger the upgrade.

Thus, the Bellatrix upgrade defined a terminal total difficulty (TTD) at which the Merge would take place. Each block on the Ethereum proof of work chain has a "difficulty" associated with it, which corresponds to the expected number of hashes it would take to mine it. The total difficulty is the monotonically increasing accumulated difficulty of all the blocks so far.

The first block to exceed TERMINAL_TOTAL_DIFFICULTY was Ethereum block number 15537393. That block became the last canonical block to be produced under proof of work. The next execution payload was included in the beacon chain at slot 4700013, which was produced at 06:42:59 UTC on September the 15th, 2022.

TERMINAL_BLOCK_HASH and TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH are present in case a need arose to manually select a particular proof of work fork to follow in case of trouble. TERMINAL_BLOCK_HASH would have been set in clients, by a manual override or a client update, to point to a specific proof of work block chosen by agreement to be the terminal block. In the event this functionality was not needed.

Created by Ben Edgington. Licensed under CC BY-SA 4.0. Published 2023-09-29 14:16 UTC. Commit ebfcf50.