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 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.
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 . 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:
- if a validators' balance falls 0.25 Ether below its effective balance, then its effective balance is reduced by 1 Ether
- 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 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.
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 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 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.
SLOTS_PER_EPOCH
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.
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 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.
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 , 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. 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.
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 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 |
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 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_BELLATRIX
.
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) |
INACTIVITY_PENALTY_QUOTIENT_BELLATRIX |
uint64(2**24) (= 16,777,216) |
MIN_SLASHING_PENALTY_QUOTIENT_BELLATRIX |
uint64(2**5) (= 32) |
PROPORTIONAL_SLASHING_MULTIPLIER_BELLATRIX |
uint64(3) |
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.
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 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_BELLATRIX
This value supersedes INACTIVITY_PENALTY_QUOTIENT
and INACTIVITY_PENALTY_QUOTIENT_ALTAIR
.
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 , where is the number of epochs since the leak started, is the validator's effective balance, and is the prevailing inactivity penalty quotient (currently INACTIVITY_PENALTY_QUOTIENT_BELLATRIX
).
The effective balance will remain constant for a while, by design, during which time the total amount of the penalty after epochs would be . This is sometimes called the "quadratic leak" since it grows as to first order. If were continuously variable, the penalty would satisfy , which can be solved to give . 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, , is the square of the time it takes to reduce the balance of a non-participating validator to , 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.
MIN_SLASHING_PENALTY_QUOTIENT_BELLATRIX
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.
PROPORTIONAL_SLASHING_MULTIPLIER_BELLATRIX
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_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) |
MAX_BLS_TO_EXECUTION_CHANGES |
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 ofMAX_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 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).
Execution
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) |
MAX_EXTRA_DATA_BYTES |
2**5 (= 32) |
MAX_WITHDRAWALS_PER_PAYLOAD |
uint64(2**4) (= 16) |
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 |
---|---|
MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP |
16384 (= 2**14 ) |
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.