Part 3: Annotated Specification

Types, Constants, Presets, and Configuration

Preset

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.

Misc

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)
SHUFFLE_ROUND_COUNT uint64(90)
MAX_COMMITTEES_PER_SLOT

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 is intended to map to one committee per shard once data shards are deployed, since these committees will also vote on shard crosslinks.

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

TARGET_COMMITTEE_SIZE

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.

MAX_VALIDATORS_PER_COMMITTEE

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.

SHUFFLE_ROUND_COUNT

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
HYSTERESIS_QUOTIENT uint64(4)
HYSTERESIS_DOWNWARD_MULTIPLIER uint64(1)
HYSTERESIS_UPWARD_MULTIPLIER uint64(5)

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

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, so as 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.

MAX_EFFECTIVE_BALANCE

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.

EFFECTIVE_BALANCE_INCREMENT

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 of 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
MIN_ATTESTATION_INCLUSION_DELAY

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, now that we plan to crosslink shards every slot, this is the only value that makes sense. So MIN_ATTESTATION_INCLUSION_DELAY exists today as a kind of 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.

SLOTS_PER_EPOCH

We currently have 12 second slots and 32 slot epochs. In earlier designs slots were six 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.

MIN_SEED_LOOKAHEAD

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 committee members to find each other on the peer-to-peer network, and in future to sync up any shard data they need. But preventing committee makeup being known too far ahead limits the opportunity for coordinated collusion between validators.

MAX_SEED_LOOKAHEAD

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.

MIN_EPOCHS_TO_INACTIVITY_PENALTY

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.

EPOCHS_PER_ETH1_VOTING_PERIOD

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.

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.

SLOTS_PER_HISTORICAL_ROOT

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 that list is full, both lists are Merkleized into a single Merkle root, which is added to the ever-growing state.historical_roots list.

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
VALIDATOR_REGISTRY_LIMIT uint64(2**40) (= 1,099,511,627,776) validators
EPOCHS_PER_HISTORICAL_VECTOR

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 thus 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.

EPOCHS_PER_SLASHINGS_VECTOR

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.

See also PROPORTIONAL_SLASHING_MULTIPLIER_ALTAIR.

HISTORICAL_ROOTS_LIMIT

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.

VALIDATOR_REGISTRY_LIMIT

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)
WHISTLEBLOWER_REWARD_QUOTIENT uint64(2**9) (= 512)
PROPOSER_REWARD_QUOTIENT uint64(2**3) (= 8)
INACTIVITY_PENALTY_QUOTIENT uint64(2**26) (= 67,108,864)
MIN_SLASHING_PENALTY_QUOTIENT uint64(2**7) (= 128)
PROPORTIONAL_SLASHING_MULTIPLIER uint64(1)
INACTIVITY_PENALTY_QUOTIENT_ALTAIR uint64(3 * 2**24) (= 50,331,648)
MIN_SLASHING_PENALTY_QUOTIENT_ALTAIR uint64(2**6) (= 64)
PROPORTIONAL_SLASHING_MULTIPLIER_ALTAIR uint64(2)

Note that there are similar constants with different values here, one version with an _ALTAIR suffix. 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.

So, the unsuffixed versions are the Phase 0 values, and the _ALTAIR suffixed versions are the values that apply to the current Altair fork.

BASE_REWARD_FACTOR

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.

WHISTLEBLOWER_REWARD_QUOTIENT

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

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.

INACTIVITY_PENALTY_QUOTIENT_ALTAIR

This value supersedes INACTIVITY_PENALTY_QUOTIENT.

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_ALTAIR ).

In Altair, inactivity_score is 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 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.

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: the famous "quadratic leak". 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 since the effective balance decreases in a step-wise fashion.

In the continuous case, 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_ALTAIR at 3 * 2**24, this equates to around seven thousand epochs, or 31.5 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 to 3 * 2**24 as a step towards eventually setting it to its final value. Decreasing the inactivity penalty quotient speeds up recovery of finalisation in the event of an inactivity leak.

MIN_SLASHING_PENALTY_QUOTIENT_ALTAIR

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_ALTAIR.

Thus, the initial slashing penalty is between 0.25 Ether and 0.5 Ether 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 was halved in the Altair upgrade from 2**7 to 2**6 as a step towards eventually setting it to its final value of 2**5.

PROPORTIONAL_SLASHING_MULTIPLIER_ALTAIR

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 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, and increased to two at the Altair upgrade. These lower values provide some insurance against client bugs that might cause mass slashings in the early days. It will eventually be increased to its final value of three in a later upgrade.

Max operations per block

Name Value
MAX_PROPOSER_SLASHINGS 2**4 (= 16)
MAX_ATTESTER_SLASHINGS 2**1 (= 2)
MAX_ATTESTATIONS 2**7 (= 128)
MAX_DEPOSITS 2**4 (= 16)
MAX_VOLUNTARY_EXITS 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 262,144 validators (2182^{18}), the expected time between being selected for sync committee duty is over 19 months. The probability of being in the current sync committee would be 1/512 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).

Created by Ben Edgington. Licensed under CC BY-SA 4.0. Published 2023-07-01 13:17 UTC. Commit 8fa708b.