Part 2: Technical Overview
The Incentive Layer
- Each validator maintains an effective balance in addition to its actual balance.
- The validator's influence in the protocol is proportional to its effective balance, as are its rewards and penalties.
- The effective balance tracks the validator's actual balance, but is designed to change much more rarely. This is an optimisation.
- A validator's effective balance is capped at 32 ETH.
The beacon chain maintains two separate records of each validator's balance: its actual balance and its effective balance.
A validator's actual balance is straightforward. It is the sum of any deposits made for it via the deposit contract, plus accrued beacon chain rewards, minus accrued penalties. Withdrawals are not yet possible, but will be subtracted from this balance when available. The actual balance is rapidly changing, being updated at least once per epoch for all active validators, and every slot for sync committee participants. It is also fine-grained: units of the actual balance are Gwei, that is, ETH.
A validator's effective balance is derived from its actual balance in such a way that it changes much more slowly. To achieve this, the units of effective balance are whole Ether (see
EFFECTIVE_BALANCE_INCREMENT), and changes to the effective balance are subject to hysteresis.
Using the effective balance achieves two goals, one to do with economics, the other purely engineering.
Economic aspects of effective balance
The effective balance was first introduced to represent the "maximum balance at risk" for a validator, capped at 32 ETH. A validator's actual balance could be much higher, for example if a double deposit had been accidentally made a validator would have an actual balance of 64 ETH but an effective balance of only 32 ETH. We could envisage a protocol in which each validator has influence proportional to its uncapped actual balance, but that would complicate committee membership among other things. Instead we cap the effective balance and require stakers to deposit for more validators if they wish to stake more.
The scope of effective balance quickly grew, and now it completely represents the weight of a validator in the consensus protocol.
All of the following consensus-related matters are proportional to the effective balance of a validator:
- the probability of being selected as the beacon block proposer;
- the validator's weight in the LMD-GHOST fork choice rule;
- the validator's weight in the justification and finalisation calculations calculations; and
- the probability of being included in a sync committee.
Correspondingly, the following rewards, penalties, and punishments are also weighted by effective balance:
- the base reward for a validator, in terms of which the attestation rewards and penalties are calculated;
- the inactivity penalties applied to a validator as a consequence of an inactivity leak; and
- both the initial slashing penalty and the correlated slashing penalty.
However, the block proposer reward is not scaled in proportion to the proposer's effective balance. Since a validator's probability of being selected to propose is proportional to its effective balance, the reward scaling with effective balance is already taken care of. For the same reason sync committee rewards are not proportional to the participants' effective balances either.
Engineering aspects of effective balance
We could achieve all of the above simply by using validators' actual balances as their weights, capped at 32 ETH. However, we can gain significant performance benefits by basing everything on effective balances instead.
For one thing, effective balances are updated only once per epoch, which means that we need only calculate things like the base reward per increment once and we can cache the result for the whole epoch, irrespective of any changes in actual balances.
But the main feature of effective balances is that they are designed to change much more rarely than that. This is achieved by making them very granular, and by applying hysteresis to any updates.
One of the big performance challenges in calculating the beacon chain state transition is generating the hash tree root of the entire state. The Merkleization process allows parts of the state that have not been changed to be cached, providing a significant performance boost.
The list of validator records in the state is a large data structure. Were we to store the validators' actual balances within those records they would be frequently changing and the whole data structure would need to be re-hashed at least once per epoch.
The first approach to addressing this simply moved the validators' balances out of the validator records into a dedicated list in the state. This reduces the amount of re-hashing required as the whole validator list does not need to be re-hashed when only the validators' balances change.
However, that leads to a performance issue elsewhere. Light clients needing information on validators' balances would now need to acquire data from two different parts of the state – both the validator record and the validator balance list. This requires two Merkle proofs rather than one, significantly increasing their bandwidth costs.
A way round this is to store a slowly changing version of the balances in the validators' records – meaning that they need to be re-hashed infrequently – and to store the fast-changing actual balances in a separate list, a much smaller structure to re-hash.
From the notes for an early attempt at a kind of effective balance implementation:
[Effective balances are an] "approximate balance" that can be used by light clients in the
validator_registry, reducing the number of Merkle branches per validator they need to download from 3 to 2 (actually often from ~2.01 to ~1.01, because when fetching a committee the Merkle branches in active_index_roots are mostly shared), achieving a very significant decrease in light client bandwidth costs
The point is that light clients will not need to access the list of actual balances that is stored separately in state, only the validator records they were downloading anyway.
In summary, adding effective balances to validators' records allows us to achieve two performance goals simultaneously: avoiding the workload of frequently re-hashing the validator list in the state while not increasing the workload of light clients.
Although effective balances are denominated in Gwei they can only be whole multiples of
EFFECTIVE_BALANCE_INCREMENT, which is 1 ETH ( Gwei). Actual balances can be any number of Gwei.
This multiple is known in the spec as an "increment" and shows up in places like calculating the base reward, and other rewards and penalties calculations. Being a handy 1 ETH, it's easy to mentally substitute "Ether" for "increment" to gain some intuition.
It would probably be cleaner to store effective balance in terms of increments instead of Gwei. It would certainly reduce the amount of dividing and multiplying by
EFFECTIVE_BALANCE_INCREMENT that goes on, and the associated danger of arithmetic overflows. But the current version evolved over time, and it would be intrusive and risky to go back and change things now.
Effective balances are guaranteed to vary much more slowly than actual balances by adding hysteresis to their calculation.
In our context, hysteresis means that if the effective balance is 31 ETH, the actual balance must rise to 32.25 ETH to trigger an effective balance update to 32 ETH. Similarly, if the effective balance is 31 ETH, then the actual balance must fall to 30.75 ETH to trigger an effective balance update to 30 ETH.
The following chart illustrates the behaviour.
- The actual balance and the effective balance both start at 32 ETH.
- Initially the actual balance rises. Effective balance is capped at 32 ETH, so it does not get updated.
- Only when the actual balance falls below 31.75 ETH does the effective balance get reduced to 31 ETH.
- Although the actual balance rises and oscillates around 32 ETH, no effective balance update is triggered and it remains at 31 ETH.
- Eventually the actual balance rises above 32.25 ETH, and the effective balance is updated to 32 ETH.
- Despite the actual balance falling again, it does not fall below 31.75 ETH, so the effective balance remains at 32 ETH.
The hysteresis levels are controlled by the hysteresis parameters in the spec:
These are applied at the end of each epoch during effective balance updates. Every validator in the state (whether active or not) has its effective balance updated as follows:
- If actual balance is less than effective balance minus 0.25 (
HYSTERESIS_QUOTIENT) increments (ETH), then reduce the effective balance by an increment.
- If actual balance is more than effective balance plus 1.25 (
HYSTERESIS_QUOTIENT) increments (ETH), then increase the effective balance by an increment.
The effect of the hysteresis is that the effective balance cannot change more often than it takes for a validator's actual balance to change by 0.5 ETH, which would normally take several weeks or months.
Historical note: the initial implementation of hysteresis effectively had
QUOTIENT = 2,
DOWNWARD_MULTIPLIER = 0, and
UPWARD_MULTIPLIER = 3. This meant that a validator starting with 32 ETH actual balance but suffering a minor initial outage would immediately drop to 31 ETH effective balance. To get back to 32 ETH effective balance it would need to achieve a 32.5 ETH actual balance, and meanwhile the validator's rewards would be 3.1% lower due to the reduced effective balance. This seemed unfair, and incentivised stakers to "over-deposit" Ether to avoid the risk of an initial effective balance drop. Hence the change to the current parameters.
From the spec:
- The presets that constrain the effective balance,
- The parameters that control the hysteresis.
- The function
process_effective_balance_updates()for the actual calculation and application of hysteresis.
Validatorobjects store the effective balances. The registry in the beacon state contains the list of validators alongside a separate list of the actual balances.