Part 3: Annotated Specification

Types, Constants, Presets, and Configuration


The "presets" are consistent collections of configuration variables that are bundled together. The specs repo currently defines two sets of presets, mainnet and minimal. The mainnet configuration is running in production on the beacon chain; minimal is often used for testing. Other configurations are possible. For example, Teku uses a swift configuration for acceptance testing.

All the values discussed below are from the mainnet configuration.

You'll notice that most of these values are powers of two. There's no huge significance to this. Computer scientists think it's neat, and it ensures that things cleanly divide other things in general. There is a view that this practice helps to minimise bike-shedding (endless arguments over trivial matters).

Some of the configuration parameters below are quite technical and perhaps obscure. I'll take the opportunity here to introduce some concepts, and give more detailed explanations when they appear in later chapters.


Name Value
MAX_COMMITTEES_PER_SLOT uint64(2**6) (= 64)
TARGET_COMMITTEE_SIZE uint64(2**7) (= 128)
MAX_VALIDATORS_PER_COMMITTEE uint64(2**11) (= 2,048)

Validators are organised into committees to do their work. At any one time, each validator is a member of exactly one beacon chain committee, and is called on to make an attestation exactly once per epoch. An attestation is a vote for, or a statement of, the validator's view of the chain at that point in time.

On the beacon chain, up to 64 committees are active in a slot and effectively act as a single committee as far as the fork-choice rule is concerned. They all vote on the proposed block for the slot, and their votes/attestations are pooled. In a similar way, all committees active during an epoch (that is, the whole active validator set) act effectively as a single committee as far as justification and finalisation are concerned.

The number 64 was intended to map to one committee per shard once data shards were deployed in the now abandoned Phase 1 of the Ethereum 2.0 roadmap. The plan was for each committee to also vote on one shard crosslink, for a total of 64 shards. We are no longer going down that path, but the committees remain at each slot.

All the above is discussed further in the section on Committees.

Note that sync committees are a different thing: there is only one sync committee active at any time.


To achieve a desirable level of security, committees need to be larger than a certain size. This makes it infeasible for an attacker to randomly end up with a super-majority in a committee even if they control a significant number of validators. The target here is a kind of lower-bound on committee size. If there are not enough validators for all committees to have at least 128 members, then, as a first measure, the number of committees per slot is reduced to maintain this minimum. Only if there are fewer than SLOTS_PER_EPOCH * TARGET_COMMITTEE_SIZE = 4096 validators in total will the committee size be reduced below TARGET_COMMITTEE_SIZE. With so few validators, the system would be insecure in any case.

For further discussion and an explanation of how the value of TARGET_COMMITTEE_SIZE was set, see the section on committees.


This is just used for sizing some data structures, and is not particularly interesting. Reaching this limit would imply over 4 million active validators, staked with a total of 128 million Ether, which exceeds the total supply today.


The beacon chain implements a rather interesting way of shuffling validators in order to select committees, called the "swap-or-not shuffle". This shuffle proceeds in rounds, and the degree of shuffling is determined by the number of rounds, SHUFFLE_ROUND_COUNT. The time taken to shuffle is linear in the number of rounds, so for light-weight, non-mainnet configurations, the number of rounds can be reduced.

The value 90 was introduced in Vitalik's initial commit without explanation. The original paper describing the shuffling technique seems to suggest that a cryptographically safe number of rounds is 6logN6\log{N}. With 90 rounds, then, we should be good for shuffling 3.3 million validators, which is close to the maximum number possible (given the Ether supply).

Hysteresis parameters

Name Value

The parameters prefixed HYSTERESIS_ control the way that effective balance is changed (see EFFECTIVE_BALANCE_INCREMENT). As described there, the effective balance of a validator follows changes to the actual balance in a step-wise way, with hysteresis applied. This ensures that the effective balance does not change often.

The original hysteresis design had an unintended effect that might have encouraged stakers to over-deposit or make multiple deposits in order to maintain a balance above 32 Ether at all times. If a validator's balance were to drop below 32 Ether soon after depositing, however briefly, the effective balance would have immediately dropped to 31 Ether and taken a long time to recover. This would have resulted in a 3% reduction in rewards for a period.

This problem was addressed by making the hysteresis configurable via these parameters. Specifically, these settings mean:

  1. if a validators' balance falls 0.25 Ether below its effective balance, then its effective balance is reduced by 1 Ether
  2. if a validator's balance rises 1.25 Ether above its effective balance, then its effective balance is increased by 1 Ether

These calculations are done in process_effective_balance_updates() during end of epoch processing.

Gwei values

Name Value
MIN_DEPOSIT_AMOUNT Gwei(2**0 * 10**9) (= 1,000,000,000)
MAX_EFFECTIVE_BALANCE Gwei(2**5 * 10**9) (= 32,000,000,000)
EFFECTIVE_BALANCE_INCREMENT Gwei(2**0 * 10**9) (= 1,000,000,000)

MIN_DEPOSIT_AMOUNT is not actually used anywhere within the beacon chain specification document. Rather, it is enforced in the deposit contract that was deployed to the Ethereum 1 chain. Any amount less than this value sent to the deposit contract is reverted.

Allowing stakers to make deposits smaller than a full stake is useful for topping-up a validator's balance if its effective balance has dropped below 32 Ether in order to maintain full productivity. However, this actually led to a vulnerability for some staking pools, involving the front-running of deposits. In some circumstances, a front-running attacker could change a genuine depositor's withdrawal credentials to their own.


There is a concept of "effective balance" for validators: whatever a validator's total balance, its voting power is weighted by its effective balance, even if its actual balance is higher. Effective balance is also the amount on which all rewards, penalties, and slashings are calculated - it's used a lot in the protocol

The MAX_EFFECTIVE_BALANCE is the highest effective balance that a validator can have: 32 Ether. Any balance above this is ignored. Note that this means that staking rewards don't compound in the usual case (unless a validator's effective balance somehow falls below 32 Ether, in which case rewards kind of compound).

There is a discussion in the Design Rationale of why 32 Ether was chosen as the staking amount. In short, we want enough validators to keep the chain both alive and secure under attack, but not so many that the message overhead on the network becomes too high.


Throughout the protocol, a quantity called "effective balance" is used instead of the validators' actual balances. Effective balance tracks the actual balance, with two differences: (1) effective balance is capped at MAX_EFFECTIVE_BALANCE no matter how high the actual balance of a validator is, and (2) effective balance is much more granular - it changes only in steps of EFFECTIVE_BALANCE_INCREMENT rather than Gwei.

This discretisation of effective balance is intended to reduce the amount of hashing required when making state updates. The goal is to avoid having to re-calculate the hash tree root of validator records too often. Validators' actual balances, which change frequently, are stored as a contiguous list in BeaconState, outside validators' records. Effective balances are stored inside validators' individual records, which are more costly to update (more hashing required). So we try to update effective balances relatively infrequently.

Effective balance is changed according to a process with hysteresis to avoid situations where it might change frequently. See HYSTERESIS_QUOTIENT.

You can read more about effective balance in the Design Rationale and in this article.

Time parameters

Name Value Unit Duration
MIN_ATTESTATION_INCLUSION_DELAY uint64(2**0) (= 1) slots 12 seconds
SLOTS_PER_EPOCH uint64(2**5) (= 32) slots 6.4 minutes
MIN_SEED_LOOKAHEAD uint64(2**0) (= 1) epochs 6.4 minutes
MAX_SEED_LOOKAHEAD uint64(2**2) (= 4) epochs 25.6 minutes
MIN_EPOCHS_TO_INACTIVITY_PENALTY uint64(2**2) (= 4) epochs 25.6 minutes
EPOCHS_PER_ETH1_VOTING_PERIOD uint64(2**6) (= 64) epochs ~6.8 hours
SLOTS_PER_HISTORICAL_ROOT uint64(2**13) (= 8,192) slots ~27 hours

A design goal of Ethereum 2.0 is not to heavily disadvantage validators that are running on lower-spec systems, or, conversely, to reduce any advantage gained by running on high-spec systems.

One aspect of performance is network bandwidth. When a validator becomes the block proposer, it needs to gather attestations from the rest of its committee. On a low-bandwidth link, this takes longer, and could result in the proposer not being able to include as many past attestations as other better-connected validators might, thus receiving lower rewards.

MIN_ATTESTATION_INCLUSION_DELAY was an attempt to "level the playing field" by setting a minimum number of slots before an attestation can be included in a beacon block. It was originally set at 4, with a 6-second slot time, allowing 24 seconds for attestations to propagate around the network.

It was later set to one – attestations are included as early as possible – and MIN_ATTESTATION_INCLUSION_DELAY exists today as a relic of the earlier design. The current slot time of 12 seconds is assumed to allow sufficient time for attestations to propagate and be aggregated sufficiently within one slot.


We currently have 12-second slots and 32-slot epochs. In earlier designs, slots were 6 seconds and there were 64 slots per epoch. So the time between epoch boundaries was unchanged when slots were lengthened.

The choice of 32 slots per epoch is a trade-off between time to finality (we need two epochs to finalise, so we prefer to keep them as short as we can) and being as certain as possible that at least one honest proposer per epoch will make a block to update the RANDAO (for which we prefer longer epochs).

In addition, epoch boundaries are where the heaviest part of the beacon chain state-transition calculation occurs, so that's another reason for not having them too close together.

Since every validator attests one every epoch, there is an interplay between the number of slots per epoch, the number of committees per slot, committee sizes, and the total number of validators.


A random seed is used to select all the committees and proposers for an epoch. During each epoch, the beacon chain accumulates randomness from proposers via the RANDAO and stores it. The seed for the current epoch is based on the RANDAO output from the epoch MIN_SEED_LOOKAHEAD + 1 ago. With MIN_SEED_LOOKAHEAD set to one, the effect is that we can know the seed for the current epoch and the next epoch, but not beyond, since the next-but-one epoch depends on randomness from the current epoch that hasn't been accumulated yet.

This mechanism is designed to allow sufficient time for members of newly formed committees to find each other on the peer-to-peer network, while preventing committee makeup being known too far ahead limits the opportunity for coordinated collusion between validators.


The above notwithstanding, if an attacker has a large proportion of the stake, or is, for example, able to DoS block proposers for a while, then it might be possible for the attacker to predict the output of the RANDAO further ahead than MIN_SEED_LOOKAHEAD would normally allow. This might enable the attacker to manipulate committee memberships to their advantage by performing well-timed exits and activations of their validators.

To prevent this, we assume a maximum feasible lookahead that an attacker might achieve (MAX_SEED_LOOKAHEAD) and delay all activations and exits by this amount, which allows new randomness to come in via block proposals from honest validators. With MAX_SEED_LOOKAHEAD set to 4, if only 10% of validators are online and honest, then the chance that an attacker can succeed in forecasting the seed beyond (MAX_SEED_LOOKAHEAD - MIN_SEED_LOOKAHEAD) = 3 epochs is 0.93×320.9^{3\times 32}, which is about 1 in 25,000.


The inactivity penalty is discussed below. This parameter sets the length of time until it kicks in. If the last finalised epoch is longer ago than MIN_EPOCHS_TO_INACTIVITY_PENALTY, then the beacon chain starts operating in "leak" mode. In this mode, participating validators no longer get rewarded, and validators that are not participating get penalised.


In order to safely onboard new validators, the beacon chain needs to take a view on what the Eth1 chain looks like. This is done by collecting votes from beacon block proposers - they are expected to consult an available Eth1 client in order to construct their vote.

EPOCHS_PER_ETH1_VOTING_PERIOD * SLOTS_PER_EPOCH is the total number of votes for Eth1 blocks that are collected. As soon as half of this number of votes are for the same Eth1 block, that block is adopted by the beacon chain and deposit processing can continue. This processing is done in process_eth1_data().

Rules for how validators select the right block to vote for are set out in the validator guide. ETH1_FOLLOW_DISTANCE is the (approximate) minimum depth of block that can be considered.

This parameter was increased from 32 to 64 epochs for the beacon chain mainnet. This increase is intended to allow devs more time to respond if there is any trouble on the Eth1 chain, in addition to the eight hours grace provided by ETH1_FOLLOW_DISTANCE.

For a detailed analysis of these parameters, see this article.


There have been several redesigns of the way the beacon chain stores its past history. The current design is a double batched accumulator. The block root and state root for every slot are stored in the state for SLOTS_PER_HISTORICAL_ROOT slots. When those lists are full, each list is Merkleized separately, and their roots are added to the ever-growing state.historical_summaries list within an HistoricalSummary container.

State list lengths

The following parameters set the sizes of some lists in the beacon chain state. Some lists have natural sizes, others such as the validator registry need an explicit maximum size to guide SSZ serialisation.

Name Value Unit Duration
EPOCHS_PER_HISTORICAL_VECTOR uint64(2**16) (= 65,536) epochs ~0.8 years
EPOCHS_PER_SLASHINGS_VECTOR uint64(2**13) (= 8,192) epochs ~36 days
HISTORICAL_ROOTS_LIMIT uint64(2**24) (= 16,777,216) historical roots ~52,262 years
(= 1,099,511,627,776)
validators -

This is the number of epochs of previous RANDAO mixes that are stored (one per epoch). Having access to past randao mixes allows historical shufflings to be recalculated. Since Validator records keep track of the activation and exit epochs of all past validators, we can reconstitute past committees as far back as we have the RANDAO values. This information can be used for slashing long-past attestations, for example. It is not clear how the value of this parameter was decided.


In the epoch in which a misbehaving validator is slashed, its effective balance is added to an accumulator in the state. In this way, the state.slashings list tracks the total effective balance of all validators slashed during the last EPOCHS_PER_SLASHINGS_VECTOR epochs.

At a time EPOCHS_PER_SLASHINGS_VECTOR // 2 after being slashed, a further penalty is applied to the slashed validator, based on the total amount of value slashed during the 4096 epochs before and the 4096 epochs after it was originally slashed.

The idea of this is to disproportionately punish coordinated attacks, in which many validators break the slashing conditions around the same time, while only lightly penalising validators that get slashed by making a mistake. Early designs for Eth2 would always slash a validator's entire deposit.



Every SLOTS_PER_HISTORICAL_ROOT slots, the list of block roots and the list of state roots in the beacon state are Merkleized and added to state.historical_roots list. Although state.historical_roots is in principle unbounded, all SSZ lists must have maximum sizes specified. The size HISTORICAL_ROOTS_LIMIT will be fine for the next few millennia, after which it will be somebody else's problem. The list grows at less than 10 KB per year.

Storing past roots like this allows Merkle proofs to be constructed about anything in the beacon chain's history if required.


Every time the Eth1 deposit contract processes a deposit from a new validator (as identified by its public key), a new entry is appended to the state.validators list.

In the current design, validators are never removed from this list, even after exiting from being a validator. This is largely because there is nowhere yet to send a validator's remaining deposit and staking rewards, so they continue to need to be tracked in the beacon chain.

The maximum length of this list is VALIDATOR_REGISTRY_LIMIT, which is one trillion, so we ought to be OK for a while, especially given that the minimum deposit amount is 1 Ether.

Rewards and penalties

Name Value
BASE_REWARD_FACTOR uint64(2**6) (= 64)
INACTIVITY_PENALTY_QUOTIENT uint64(2**26) (= 67,108,864)
INACTIVITY_PENALTY_QUOTIENT_ALTAIR uint64(3 * 2**24) (= 50,331,648)

Note that there are similar constants with different values here.

  • The original beacon chain Phase 0 constants have no suffix.
  • Constants updated in the Altair upgrade have the suffix _ALTAIR.
  • Constants updated in the Bellatrix upgrade have the suffix _BELLATRIX.

This is explained in the specs repo as follows:

Variables are not replaced but extended with forks. This is to support syncing from one state to another over a fork boundary, without hot-swapping a config. Instead, for forks that introduce changes in a variable, the variable name is suffixed with the fork name.


This is the big knob to turn to change the issuance rate of Eth2. Almost all validator rewards are calculated in terms of a "base reward per increment" which is formulated as,

  EFFECTIVE_BALANCE_INCREMENT * BASE_REWARD_FACTOR // integer_squareroot(get_total_active_balance(state))

Thus, the total validator rewards per epoch (the Eth2 issuance rate) could be tuned by increasing or decreasing BASE_REWARD_FACTOR.

The exception is proposer rewards for including slashing reports in blocks. However, these are more than offset by the amount of stake burnt, so do not increase the overall issuance rate.


One reward that is not tied to the base reward is the whistleblower reward. This is an amount awarded to the proposer of a block containing one or more proofs that a proposer or attester has violated a slashing condition. The whistleblower reward is set at 1512\frac{1}{512} of the effective balance of the slashed validator.

The whistleblower reward comes from new issuance of Ether on the beacon chain, but is more than offset by the Ether burned due to slashing penalties.


PROPOSER_REWARD_QUOTIENT was removed in the Altair upgrade in favour of PROPOSER_WEIGHT. It was used to apportion rewards between attesters and proposers when including attestations in blocks.



If the beacon chain hasn't finalised a checkpoint 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 ).

Since the Altair upgrade, inactivity_score has become a per-validator quantity, whereas previously validators were penalised by a globally calculated amount when they missed a duty during a leak. See inactivity penalties for more on the rationale for this and how this score is calculated per validator.

During a leak, no validators receive rewards, and they continue to accrue the normal penalties when they fail to fulfil duties. In addition, for epochs in which validators do not make a correct, timely target vote, they receive a leak penalty.

To examine the effect of the leak on a single validator's balance, assume that during a period of inactivity leak (non-finalisation) the validator is completely offline. At each epoch, the offline validator will be penalised an extra amount nB/αnB / \alpha, where nn is the number of epochs since the leak started, BB is the validator's effective balance, and α\alpha is the prevailing inactivity penalty quotient (currently INACTIVITY_PENALTY_QUOTIENT_BELLATRIX).

The effective balance BB will remain constant for a while, by design, during which time the total amount of the penalty after nn epochs would be n(n+1)B/2αn(n+1)B / 2\alpha. This is sometimes called the "quadratic leak" since it grows as n2n^2 to first order. If BB were continuously variable, the penalty would satisfy dBdt=Btα\frac{dB}{dt}=-\frac{Bt}{\alpha}, which can be solved to give B(t)=B0et2/2αB(t)=B_0e^{-t^2/2\alpha}. The actual behaviour is somewhere between these two (piecewise quadratic) since the effective balance is neither constant nor continuously variable but decreases in a step-wise fashion.

In the continuous approximation, the inactivity penalty quotient, α\alpha, is the square of the time it takes to reduce the balance of a non-participating validator to 1/e1 / \sqrt{e}, or around 60.7% of its initial value. With the value of INACTIVITY_PENALTY_QUOTIENT_BELLATRIX at 2**24, this equates to 4096 epochs, or 18.2 days.

The idea for the inactivity leak (aka the quadratic leak) was proposed in the original Casper FFG paper. The problem it addresses is that, if a large fraction of the validator set were to go offline at the same time, it would not be possible to continue finalising checkpoints, since a majority vote from validators representing 2/3 of the total stake is required for finalisation.

In order to recover, the inactivity leak gradually reduces the stakes of validators who are not making attestations until, eventually, the participating validators control 2/3 of the remaining stake. They can then begin to finalise checkpoints once again.

This inactivity penalty mechanism is designed to protect the chain long-term in the face of catastrophic events (sometimes referred to as the ability to survive World War III). The result might be that the beacon chain could permanently split into two independent chains either side of a network partition, and this is assumed to be a reasonable outcome for any problem that can't be fixed in a few weeks. In this sense, the beacon chain formally prioritises availability over consistency. (You can't have both.)

The value of INACTIVITY_PENALTY_QUOTIENT was increased by a factor of four from 2**24 to 2**26 for the beacon chain launch, with the intention of penalising validators less severely in case of non-finalisation due to implementation problems in the early days. As it happens, there were no instances of non-finalisation during the eleven months of Phase 0 of the beacon chain.

The value was decreased by one quarter in the Altair upgrade from 2**26 (INACTIVITY_PENALTY_QUOTIENT) to 3 * 2**24 (INACTIVITY_PENALTY_QUOTIENT_ALTAIR), and to its final value of 2**24 (INACTIVITY_PENALTY_QUOTIENT_BELLATRIX) in the Bellatrix upgrade. Decreasing the inactivity penalty quotient speeds up recovery of finalisation in the event of an inactivity leak.


When a validator is first convicted of a slashable offence, an initial penalty is applied. This is calculated as, validator.effective_balance // MIN_SLASHING_PENALTY_QUOTIENT_BELLATRIX.

Thus, the initial slashing penalty is between 0.5 ETH and 1 ETH depending on the validator's effective balance (which is between 16 and 32 Ether; note that effective balance is denominated in Gwei).

A further slashing penalty is applied later based on the total amount of balance slashed during a period of EPOCHS_PER_SLASHINGS_VECTOR.

The value of MIN_SLASHING_PENALTY_QUOTIENT was increased by a factor of four from 2**5 to 2**7 for the beacon chain launch, anticipating that unfamiliarity with the rules of Ethereum 2.0 staking was likely to result in some unwary users getting slashed. In the event, a total of 157 validators were slashed during Phase 0, all as a result of user error or misconfiguration as far as can be determined.

The value of this parameter was halved in the Altair upgrade from 2**7 (MIN_SLASHING_PENALTY_QUOTIENT) to 2**6 (MIN_SLASHING_PENALTY_QUOTIENT_ALTAIR), and set to its final value of 2**5 (MIN_SLASHING_PENALTY_QUOTIENT_BELLATRIX) in the Bellatrix upgrade.


When a validator has been slashed, a further penalty is later applied to the validator based on how many other validators were slashed during a window of size EPOCHS_PER_SLASHINGS_VECTOR epochs centred on that slashing event (approximately 18 days before and after).

The proportion of the validator's remaining effective balance that will be subtracted is calculated as, PROPORTIONAL_SLASHING_MULTIPLIER_BELLATRIX multiplied by the sum of the effective balances of the slashed validators in the window, divided by the total effective balance of all validators. The idea of this mechanism is to punish accidents lightly (in which only a small number of validators were slashed) and attacks heavily (where many validators coordinated to double vote).

To finalise conflicting checkpoints, at least a third of the balance must have voted for both. That's why the "natural" setting of PROPORTIONAL_SLASHING_MULTIPLIER is three: in the event of an attack that finalises conflicting checkpoints, the attackers lose their entire stake. This provides "the maximal minimum accountable safety margin".

However, for the initial stage of the beacon chain, Phase 0, PROPORTIONAL_SLASHING_MULTIPLIER was set to one. It was increased to two at the Altair upgrade, and to its final value of three at the Bellatrix upgrade. The lower values provided some insurance against client bugs that might have caused mass slashings in the early days.

Max operations per block

Name Value
MAX_DEPOSITS 2**4 (= 16)

These parameters are used to size lists in the beacon block bodies for the purposes of SSZ serialisation, as well as constraining the maximum size of beacon blocks so that they can propagate efficiently, and avoid DoS attacks.

Some comments on the chosen values:

  • I have suggested elsewhere reducing MAX_DEPOSITS from sixteen to one to ensure that more validators must process deposits, which encourages them to run Eth1 clients.
  • At first sight, there looks to be a disparity between the number of proposer slashings and the number of attester slashings that may be included in a block. But note that an attester slashing (a) can be much larger than a proposer slashing, and (b) can result in many more validators getting slashed than a proposer slashing.
  • MAX_ATTESTATIONS is double the value of MAX_COMMITTEES_PER_SLOT. This allows there to be an empty slot (with no block proposal), yet still include all the attestations for the empty slot in the next slot. Since, ideally, each committee produces a single aggregate attestation, a block can hold two slots' worth of aggregate attestations.

Sync committee

Name Value Unit Duration
SYNC_COMMITTEE_SIZE uint64(2**9) (= 512) Validators
EPOCHS_PER_SYNC_COMMITTEE_PERIOD uint64(2**8) (= 256) epochs ~27 hours

Sync committees were introduced by the Altair upgrade to allow light clients to quickly and trustlessly determine the head of the beacon chain.

Why did we need a new committee type? Couldn't this be built on top of existing committees, say committees 0 to 3 at a slot? After all, voting for the head of the chain is already one of their duties. The reason is that it is important for reducing the load on light clients that sync committees do not change very often. Standard committees change every slot; we need something much longer lived here.

Only a single sync committee is active at any one time, and contains a randomly selected subset of size SYNC_COMMITTEE_SIZE of the total validator set.

A sync committee does its duties (and receives rewards for doing so) for only EPOCHS_PER_SYNC_COMMITTEE_PERIOD epochs until the next committee takes over.

With 500,000 validators, the expected time between being selected for sync committee duty is around 37 months. The probability of being in the current sync committee would be 512500,000\frac{512}{500{,}000} per validator.

SYNC_COMMITTEE_SIZE is a trade-off between security (ensuring that enough honest validators are always present) and efficiency for light clients (ensuring that they do not have to handle too much computation). The value 512 is conservative in terms of safety. It would be catastrophic for trustless bridges to other protocols, for example, if a sync committee voted in an invalid block.

EPOCHS_PER_SYNC_COMMITTEE_PERIOD is around a day, and again is a trade-off between security (short enough that it's hard for an attacker to find and corrupt committee members) and efficiency (reducing the data load on light clients).


Name Value
MAX_BYTES_PER_TRANSACTION uint64(2**30) (= 1,073,741,824)
MAX_TRANSACTIONS_PER_PAYLOAD uint64(2**20) (= 1,048,576)
BYTES_PER_LOGS_BLOOM uint64(2**8) (= 256)

These first four of these constants were introduced at the Bellatrix pre-Merge upgrade and are used only to size some fields within the ExecutionPayload class.

The execution payload (formerly known as an Eth1 block) contains a list of up to MAX_TRANSACTIONS_PER_PAYLOAD normal Ethereum transactions. Each of these has size up to MAX_BYTES_PER_TRANSACTION. These constants are needed only because SSZ list types require a maximum size to be specified. They are set ludicrously large, but that's not a problem in practice.

BYTES_PER_LOGS_BLOOM and MAX_EXTRA_DATA_BYTES are a direct carry-over from Eth1 blocks as specified in the Yellow Paper, being the size of a block's Bloom filter and the size of a block's extra data field respectively. The execution payload's extra data is analogous to a beacon block's graffiti - the block builder can set it to any value they choose.

MAX_WITHDRAWALS_PER_PAYLOAD was introduced at the Capella upgrade. It is the maximum number of withdrawals that the consensus client will ask the execution client to include in an execution payload. As a consequence, the rate of withdrawals is limited to MAX_WITHDRAWALS_PER_PAYLOAD per slot.

Withdrawals processing

Name Value

This preset constant was introduced in the Capella upgrade to bound the amount of work each node wold need to do when processing withdrawals.

The number of withdrawal transactions per block is bounded at MAX_WITHDRAWALS_PER_PAYLOAD. But not all validators will be eligible for a withdrawal transaction, meaning that nodes might have to search indefinitely through the validator set to find enough withdrawals to include. Searching the validator set can be an expensive operation, therefore we bound the search, considering only MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP validators per block. If we find fewer withdrawable validators than MAX_WITHDRAWALS_PER_PAYLOAD then we make fewer withdrawal transactions.

The primary reason that a validator might not be withdrawable is that it still has an old 0x00 BLS withdrawal credential. At least in the early days of Capella this could have led to long stretches of the validator set that were not withdrawable, before many validators had updated their credentials.

Another reason might be having an effective balance lower than MAX_EFFECTIVE_BALANCE. In the event of an inactivity leak this could also lead to long stretches of validators being non-withdrawable.

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