Part 3: Annotated Specification

Types, Constants, Presets, and Configuration


The distinction between "constants", "presets", and "configuration values" is not always clear, and things have moved back and forth between the sections at times1. In essence, "constants" are things that are expected never to change for the beacon chain, no matter what fork or test network it is running.


Name Value
FAR_FUTURE_EPOCH Epoch(2**64 - 1)

The very first slot number for the beacon chain is zero.

Perhaps this seems uncontroversial, but it actually featured heavily in the Great Signedness Wars mentioned previously. The issue was that calculations on unsigned integers might have negative intermediate values, which would cause problems. A proposed work-around for this was to start the chain at a non-zero slot number. It was initially set to 2^19, then 2^63, then 2^32, and finally back to zero. In my humble opinion, this madness only confirms that we should have been using signed integers all along.


As above. When the chain starts, it starts at epoch zero.


A candidate for the dullest constant. It's used as a default initialiser for validators' activation and exit times before they are properly set. No epoch number will ever be bigger than this one.


DEPOSIT_CONTRACT_TREE_DEPTH specifies the size of the (sparse) Merkle tree used by the Eth1 deposit contract to store deposits made. With a value of 32, this allows for 2322^{32} = 4.3 billion deposits. Given that the minimum deposit it 1 Ether, that number is clearly enough.

Since deposit receipts contain Merkle proofs, their size depends on the value of this constant.


As an optimisation to Casper FFG – the process by which finality is conferred on epochs – the beacon chain uses a "kk-finality" rule. We will describe this more fully when we look at processing justification and finalisation. For now, this constant is just the number of bits we need to store in state to implement kk-finality. With k=2k = 2, we track the justification status of the last four epochs.


This array is just a convenient way to access the various weights given to different validator achievements when calculating rewards. The three weights are defined under incentivization weights, and each weight corresponds to a flag stored in state and defined under participation flag indices.


Endianness refers to the order of bytes in the binary representation of a number: most-significant byte first is big-endian; least-significant byte first is little-endian. For the most part, these details are hidden by compilers, and we don't need to worry about endianness. But endianness matters when converting between integers and bytes, which is relevant to shuffling and proposer selection, the RANDAO, and when serialising with SSZ.

The spec began life as big-endian, but the Nimbus team from Status successfully lobbied for it to be changed to little-endian in order to better match processor hardware implementations, and the endianness of WASM. SSZ was changed first, and then the rest of the spec followed.

Participation flag indices

Name Value

Validators making attestations that get included on-chain are rewarded for three things:

  • getting attestations included with the correct source checkpoint within 5 slots (integer_squareroot(SLOTS_PER_EPOCH));
  • getting attestations included with the correct target checkpoint within 32 slots (SLOTS_PER_EPOCH); and,
  • getting attestations included with the correct head within 1 slot (MIN_ATTESTATION_INCLUSION_DELAY), basically immediately.

These flags are temporarily recorded in the BeaconState when attestations are processed, then used at the ends of epochs to update finality and to calculate validator rewards for making attestations.

The mechanism for rewarding timely inclusion of attestations (thus penalising late attestations) differs between Altair and Phase 0. In Phase 0, attestations included within 32 slots would receive the full reward for the votes they got correct (source, target, head), plus a declining reward based on the delay in inclusion: 12\frac{1}{2} for a two slot delay, 13\frac{1}{3} for a three slot delay, and so on. With Altair, for each vote, we now have a cliff before which the validator receives the full reward and after which a penalty. The cliffs differ in duration, which is intended to more accurately target incentives at behaviours that genuinely help the chain (there is little value in rewarding a correct head vote made 30 slots late, for example). See get_attestation_participation_flag_indices() for how this is implemented in code.

Incentivization weights

Name Value

These weights are used to calculate the reward earned by a validator for performing its duties. There are five duties in total. Three relate to making attestations: attesting to the source epoch, attesting to the target epoch, and attesting to the head block. There are also rewards for proposing blocks, and for participating in sync committees. Note that the sum of the five weights is equal to WEIGHT_DENOMINATOR.

On a long-term average, a validator can expect to earn a total amount of get_base_reward() per epoch, with these weights being the relative portions for each of the duties comprising that total. Proposing blocks and participating in sync committees do not happen in every epoch, but are randomly assigned, so over small periods of time validator earnings may differ from get_base_reward().

The apportioning of rewards was overhauled in the Altair upgrade to better reflect the importance of each activity within the protocol. The total reward amount remains the same, but sync committee rewards were added, and the relative weights were adjusted. Previously, the weights corresponded to 16 for correct source, 16 for correct target, 16 for correct head, 14 for inclusion (equivalent to correct source), and 2 for block proposals. The factor of four increase in the proposer reward addressed a long-standing spec bug.

A piechart of the proportion of a validator's total reward derived from each of the micro-rewards.

The proportion of the total reward derived from each of the micro-rewards.

Withdrawal Prefixes

Name Value

Withdrawal prefixes relate to the withdrawal credentials provided when deposits are made for validators.

Two ways to specify the withdrawal credentials are currently available, versioned with these prefixes, with others such as 0x02 and 0x03 under discussion.

When processing deposits onto the consensus layer, the withdrawal_credential of the deposit is not checked in any way. It's up to the depositor to ensure that they are using the correct prefix and contents to be able to receive their rewards and retrieve their stake back after exiting the consensus layer. This also means that we can potentially introduce new types of withdrawal credentials at any time, enabling them later with a hard fork, just as we did with 0x01 credentials ahead of the Capella upgrade that began using them.


The beacon chain launched with only BLS-style withdrawal credentials available, so all early stakers used this.

It was not at all clear in the early days what accounts on Ethereum 2.0 would look like, and what addressing scheme they might use. The 0x00 credential created a placeholder or commitment to a future withdrawal credential change.

With this type of credential, in addition to a BLS signing key, stakers have a second BLS "withdrawal" key. Since the Capella upgrade, stakers have been able to use their withdrawal key to sign a message instructing the consensus layer to change its withdrawal credential from type 0x00 to type 0x01.

The credential registered in the deposit data is the 32 byte SHA256 hash of the validator's withdrawal public key, with the first byte set to 0x00 (BLS_WITHDRAWAL_PREFIX).


Eth1 withdrawal credentials are much simpler, and were adopted once it became clear that Ethereum 2.0 would not be using a BLS-based address scheme for accounts at any time soon. The Capella upgrade enables automatic partial and full withdrawals of validators' balances from the beacon chain to normal Ethereum accounts and wallets.

An Eth1 withdrawal credential looks like the byte 0x01 (ETH1_ADDRESS_WITHDRAWAL_PREFIX), followed by eleven 0x00 bytes, followed by the 20-byte Ethereum address of the destination account.

In addition to enabling withdrawal transactions for validators having 0x01 Eth1 credentials, the Capella upgrade gives validators with old 0x00 BLS style credentials the opportunity to make a one-time change from BLS to Eth1 withdrawal credentials2.

Domain types

Name Value
DOMAIN_BEACON_PROPOSER DomainType('0x00000000')
DOMAIN_BEACON_ATTESTER DomainType('0x01000000')
DOMAIN_RANDAO DomainType('0x02000000')
DOMAIN_DEPOSIT DomainType('0x03000000')
DOMAIN_VOLUNTARY_EXIT DomainType('0x04000000')
DOMAIN_SELECTION_PROOF DomainType('0x05000000')
DOMAIN_AGGREGATE_AND_PROOF DomainType('0x06000000')
DOMAIN_SYNC_COMMITTEE DomainType('0x07000000')

These domain types are used in three ways: for seeds, for signatures, and for selecting aggregators.

As seeds

When random numbers are required in-protocol, one way they are generated is by hashing the RANDAO mix with other quantities, one of them being a domain type (see get_seed()). The original motivation was to avoid occasional collisions between Phase 0 committees and Phase 1 persistent committees, back when they were a thing. So, when computing the beacon block proposer, DOMAIN_BEACON_PROPOSER is hashed into the seed, when computing attestation committees, DOMAIN_BEACON_ATTESTER is hashed in, and when computing sync committees, DOMAIN_SYNC_COMMITTEE is hashed in.

See the Randomness chapter for more information.

As signatures

In addition, as a cryptographic nicety, each of the protocol's signature types is augmented with the appropriate domain before being signed:

  • Signed block proposals incorporate DOMAIN_BEACON_PROPOSER
  • Signed attestations incorporate DOMAIN_BEACON_ATTESTER
  • RANDAO reveals are BLS signatures, and use DOMAIN_RANDAO
  • Deposit data messages incorporate DOMAIN_DEPOSIT
  • Validator voluntary exit messages incorporate DOMAIN_VOLUNTARY_EXIT
  • Sync committee signatures incorporate DOMAIN_SYNC_COMMITTEE
  • BLS withdrawal credential change messages incorporate DOMAIN_BLS_TO_EXECUTION_CHANGE

For most of these, the fork version is also incorporated before signing. This allows validators to participate, if they wish, in two independent forks of the beacon chain without fear of being slashed.

However, the user-signed messages for deposits (DOMAIN_DEPOSIT) and for BLS withdrawal credential changes (DOMAIN_BLS_TO_EXECUTION_CHANGE) do not incorporate the fork version when signed. This makes them valid across all forks, which is a usability enhancement.

Voluntary exit messages (DOMAIN_VOLUNTARY_EXIT) are a bit of an anomaly in that they are user signed but also incorporate the fork version, meaning that they expire after two upgrades (voluntary exit messages signed in Phase 0 or Altair are no longer valid in Capella). There is some discussion about making voluntary exits non-expiring in future.

See the BLS signatures chapter for more information.

Aggregator selection

The remaining four types, suffixed _PROOF are not used directly in the beacon chain specification. They were introduced to implement attestation subnet validations for denial of service resistance. The technique was extended to sync committees with the Altair upgrade.

Briefly, at each slot, validators are selected to aggregate attestations from their committees. The selection is done based on the validator's signature over the slot number, mixing in DOMAIN_SELECTION_PROOF. The validator then signs the whole aggregated attestation, including the previous signature as proof that it was selected to be a validator, using DOMAIN_AGGREGATE_AND_PROOF. And similarly for sync committees. In this way, everything is verifiable and attributable, making it hard to flood the network with fake messages.

These four are not part of the consensus-critical state-transition, but are nonetheless important to the healthy functioning of the chain.

This mechanism is described in the Phase 0 honest validator spec for attestation aggregation, and in the Altair honest validator spec for sync committee aggregation.

See the Aggregator Selection chapter for more information.


Name Value
G2_POINT_AT_INFINITY BLSSignature(b'\xc0' + b'\x00' * 95)

This is the compressed serialisation of the "point at infinity", the identity point, of the G2 group of the BLS12-381 curve that we are using for signatures. Note that it is in big-endian format (unlike all other constants in the spec).

It was introduced as a convenience when verifying aggregate signatures that contain no public keys in eth_fast_aggregate_verify(). The underlying FastAggregateVerify function from the BLS signature standard would reject these.

G2_POINT_AT_INFINITY is described in the separate BLS Extensions document, but included here for convenience.

  1. See Issue 2390 for discussion and a rationale for the current categorisation into constants, presets, and configuration variables.
  2. Fun fact: at the point of the Capella upgrade, out of 567,144 total validators, 322,491 (56.9%) had 0x00 BLS withdrawal credentials and 244,653 (43.1%) had 0x01 Eth1 withdrawal credentials.

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