---
tags: rewards
---
# RESEARCH: Rewards and Penalties
~~__Phase0__~~ и ~~__Belatrix__~~ - скипаем как неактуальные
делаем ресерч для __ALTAIR__
Начисление Наград и Штрафов выполняется в результате вычисления стейта.
Выполняется в 3-х основных случаях:
1. __computeStateRoot__: Расчет рута при создании блока Пропозером (предварительный расчет стейта). Есть еще апи валидаторов, но назначение не совсем понятно (см. `GetValidatorPerformance`(proto/prysm/v1alpha1/beacon_chain.proto:215) -> `ProcessRewardsAndPenaltiesPrecompute`)
2. __onBlock__: Обработка нового блока. Получен по сети (Пропагейт), либо создан локально (Пропозинг).
3. __ReplayProcessSlots__: Синхронизация (ИнитСинк)
## Aggregator: награда за агрегацию аттестаций
Функционал отсутствует
## Attestator: награда (и штраф) за аттестацию
Cрабатывает в конце эпохи.
`ProcessEpoch` -> `ProcessRewardsAndPenaltiesPrecompute`
````go
// ProcessEpoch describes the per epoch operations that are performed on the beacon state.
// It's optimized by pre computing validator attested info and epoch total/attested balances upfront.
func ProcessEpoch(ctx context.Context, state state.BeaconState) (state.BeaconStateAltair, error) {
...
// New in Altair.
state, err = ProcessRewardsAndPenaltiesPrecompute(state, bp, vp)
...
}
````
````go
// ProcessRewardsAndPenaltiesPrecompute processes the rewards and penalties of individual validator.
// This is an optimized version by passing in precomputed validator attesting records and and total epoch balances.
func ProcessRewardsAndPenaltiesPrecompute(...) ... {
...
attsRewards, attsPenalties, err := AttestationsDelta(beaconState, bal, vals)
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
...
balances := beaconState.Balances()
for i := 0; i < numOfVals; i++ {
// Compute the post balance of the validator after accounting for the
// attester and proposer rewards and penalties.
balances[i], err = helpers.IncreaseBalanceWithVal(balances[i], attsRewards[i])
...
balances[i] = helpers.DecreaseBalanceWithVal(balances[i], attsPenalties[i])
...
}
if err := beaconState.SetBalances(balances); err != nil {...}
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
...
}
````
````go
// AttestationsDelta computes and returns the rewards and penalties differences for individual validators based on the
// voting records.
func AttestationsDelta(beaconState state.BeaconState, bal *precompute.Balance, vals []*precompute.Validator) (rewards, penalties []uint64, err error) {
numOfVals := beaconState.NumValidators()
rewards = make([]uint64, numOfVals)
penalties = make([]uint64, numOfVals)
cfg := params.BeaconConfig()
prevEpoch := time.PrevEpoch(beaconState)
finalizedEpoch := beaconState.FinalizedCheckpointEpoch()
/*
EffectiveBalanceIncrement: 1 * 1e9,
*/
increment := cfg.EffectiveBalanceIncrement
/*
BaseRewardFactor: 64,
*/
factor := cfg.BaseRewardFactor
baseRewardMultiplier := increment * factor / math.IntegerSquareRoot(bal.ActiveCurrentEpoch)
/*
IsInInactivityLeak returns true if the state is experiencing inactivity leak.
leak := prevEpoch - finalizedEpoch > cfg.MinEpochsToInactivityPenalty
где
cfg.MinEpochsToInactivityPenalty = 4
Наверное фиксирует момент того, что чекпоинт Финалайзед перестал обновляться
*/
leak := helpers.IsInInactivityLeak(prevEpoch, finalizedEpoch)
// Modified in Altair and Bellatrix.
var inactivityDenominator uint64
bias := cfg.InactivityScoreBias
switch beaconState.Version() {
case version.Altair:
/*
InactivityPenaltyQuotientAltair: 3 * 1 << 24, //50331648
*/
inactivityDenominator = bias * cfg.InactivityPenaltyQuotientAltair
case version.Bellatrix:
inactivityDenominator = bias * cfg.InactivityPenaltyQuotientBellatrix
default:
return nil, nil, errors.Errorf("invalid state type version: %T", beaconState.Version())
}
for i, v := range vals {
rewards[i], penalties[i], err = attestationDelta(bal, v, baseRewardMultiplier, inactivityDenominator, leak)
if err != nil {
return nil, nil, err
}
}
return rewards, penalties, nil
}
func attestationDelta(
bal *precompute.Balance,
val *precompute.Validator,
baseRewardMultiplier, inactivityDenominator uint64,
inactivityLeak bool) (reward, penalty uint64, err error) {
eligible := val.IsActivePrevEpoch || (val.IsSlashed && !val.IsWithdrawableCurrentEpoch)
// Per spec `ActiveCurrentEpoch` can't be 0 to process attestation delta.
if !eligible || bal.ActiveCurrentEpoch == 0 {
return 0, 0, nil
}
cfg := params.BeaconConfig()
increment := cfg.EffectiveBalanceIncrement
effectiveBalance := val.CurrentEpochEffectiveBalance
baseReward := (effectiveBalance / increment) * baseRewardMultiplier
activeIncrement := bal.ActiveCurrentEpoch / increment
weightDenominator := cfg.WeightDenominator
srcWeight := cfg.TimelySourceWeight
tgtWeight := cfg.TimelyTargetWeight
headWeight := cfg.TimelyHeadWeight
reward, penalty = uint64(0), uint64(0)
/*
activeIncrement=8256
baseReward=712736
effectiveBalance=32000000000
headWeight=14
inactivityLeak=false
increment=1000000000
srcWeight=14
tgtWeight=26
val.IsPrevEpochHeadAttester=true
val.IsPrevEpochSourceAttester=true
val.IsPrevEpochTargetAttester=true
val.IsSlashed=false
weightDenominator=64
*/
// Process source reward / penalty
if val.IsPrevEpochSourceAttester && !val.IsSlashed {
if !inactivityLeak {
n := baseReward * srcWeight * (bal.PrevEpochAttested / increment)
reward += n / (activeIncrement * weightDenominator)
}
} else {
penalty += baseReward * srcWeight / weightDenominator
}
/*
penalty=0
reward=155911
*/
// Process target reward / penalty
if val.IsPrevEpochTargetAttester && !val.IsSlashed {
if !inactivityLeak {
n := baseReward * tgtWeight * (bal.PrevEpochTargetAttested / increment)
reward += n / (activeIncrement * weightDenominator)
}
} else {
penalty += baseReward * tgtWeight / weightDenominator
}
/*
penalty=0
reward=445460
*/
// Process head reward / penalty
if val.IsPrevEpochHeadAttester && !val.IsSlashed {
if !inactivityLeak {
n := baseReward * headWeight * (bal.PrevEpochHeadAttested / increment)
reward += n / (activeIncrement * weightDenominator)
}
}
/*
penalty=0
reward=601371
*/
// Process finality delay penalty
// Apply an additional penalty to validators that did not vote on the correct target or slashed
if !val.IsPrevEpochTargetAttester || val.IsSlashed {
n, err := math.Mul64(effectiveBalance, val.InactivityScore)
if err != nil {
return 0, 0, err
}
penalty += n / inactivityDenominator
}
/*
penalty=0
reward=601371
*/
return reward, penalty, nil
}
````
## Proposer: награда за создание блока
срабатывает на каждом слоте
Похоже, что это начисление выполняется за каждую аттестацию включенную в блок.
Причем, по каждой аттестации может быть три позиции по которым выполняется начисление:
1. голосование за таргет-эпоху
2. голосование за сорс-эпоху
3. голосование за хед
###### Обработка Блока (выполняется нодой при обработке блока)
```
Function
ProcessBlockForStateRoot
/home/mezin/go/src/tesseract/beacon-chain/core/transition
transition_no_verify_sig.go
CalculateStateRoot
state, err = ProcessBlockForStateRoot(ctx, state, signed)
ProcessBlockNoVerifyAnySig
state, err := ProcessBlockForStateRoot(ctx, state, signed)
/home/mezin/go/src/tesseract/beacon-chain/state/stategen
replay.go
executeStateTransitionStateGen
state, err = transition.ProcessBlockForStateRoot(ctx, state, signed)
```
````go
// ProcessBlockForStateRoot processes the state for state root computation. func ProcessBlockForStateRoot(...) ... {
...
state, err = ProcessOperationsNoVerifyAttsSigs(ctx, state, signed)
...
}
// ProcessOperationsNoVerifyAttsSigs processes the operations in the beacon block and updates beacon state
// with the operations in block. It does not verify attestation signatures.
//
// WARNING: This method does not verify attestation signatures.
// This is used to perform the block operations as fast as possible.
func ProcessOperationsNoVerifyAttsSigs(...) ... {
...
case version.Altair, version.Bellatrix:
state, err = altairOperations(ctx, state, signedBeaconBlock)
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
...
}
func altairOperations(...)...{
...
state, err = altair.ProcessAttestationsNoVerifySignature(ctx, state, signedBeaconBlock)
...
}
````
````go
// ProcessAttestationsNoVerifySignature applies processing operations to a block's inner attestation
// records. The only difference would be that the attestation signature would not be verified.
func ProcessAttestationsNoVerifySignature(
ctx context.Context,
beaconState state.BeaconState,
b block.SignedBeaconBlock,
) (state.BeaconState, error) {
...
for idx, attestation := range body.Attestations() {
beaconState, err = ProcessAttestationNoVerifySignature(ctx, beaconState, attestation, totalBalance)
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
if err != nil {
return nil, errors.Wrapf(err, "could not verify attestation at index %d in block", idx)
}
}
return beaconState, nil
}
````
###### RPC (выполняется пропозером при создании блока)
````go
// Build data required for creating a new beacon block, so this method can be shared across forks.
func (vs *Server) buildPhase0BlockData(...) ... {
...
deposits, atts, err := vs.packDepositsAndAttestations(ctx, head, eth1Data)
...
return &blockData{...}, nil
}
func (vs *Server) optimizedPackDepositsAndAttestations(...) ... {
// Pack aggregated attestations which have not been included in the beacon chain.
localAtts, err := vs.packAttestations(egctx, head)
}
func (vs *Server) packAttestations(...) ... {
...
uAtts, err = vs.validateAndDeleteAttsInPool(ctx, latestState, uAtts)
...
}
// This filters the input attestations to return a list of valid attestations to be packaged inside a beacon block.
func (vs *Server) validateAndDeleteAttsInPool(...) (...) {
...
validAtts, invalidAtts := proposerAtts(atts).filter(ctx, st)
...
}
// filter separates attestation list into two groups: valid and invalid attestations.
// The first group passes the all the required checks for attestation to be considered for proposing.
// And attestations from the second group should be deleted.
func (a proposerAtts) filter(ctx context.Context, st state.BeaconState) (proposerAtts, proposerAtts) {
validAtts := make([]*ethpb.Attestation, 0, len(a))
invalidAtts := make([]*ethpb.Attestation, 0, len(a))
var attestationProcessor func(context.Context, state.BeaconState, *ethpb.Attestation) (state.BeaconState, error)
switch st.Version() {
case version.Phase0:
attestationProcessor = blocks.ProcessAttestationNoVerifySignature
case version.Altair, version.Bellatrix:
// Use a wrapper here, as go needs strong typing for the function signature.
attestationProcessor = func(ctx context.Context, st state.BeaconState, attestation *ethpb.Attestation) (state.BeaconState, error) {
totalBalance, err := helpers.TotalActiveBalance(st)
...
return altair.ProcessAttestationNoVerifySignature(ctx, st, attestation, totalBalance)
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
}
default:
// Exit early if there is an unknown state type.
return validAtts, invalidAtts
}
for _, att := range a {
if _, err := attestationProcessor(ctx, st, att); err == nil {
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
validAtts = append(validAtts, att)
continue
}
invalidAtts = append(invalidAtts, att)
}
return validAtts, invalidAtts
}
````
````go
// ProcessAttestationNoVerifySignature processes the attestation without verifying the attestation signature. This
// method is used to validate attestations whose signatures have already been verified or will be verified later.
func ProcessAttestationNoVerifySignature(
ctx context.Context,
beaconState state.BeaconStateAltair,
att *ethpb.Attestation,
totalBalance uint64,
) (state.BeaconStateAltair, error) {
...
return SetParticipationAndRewardProposer(ctx, beaconState, att.Data.Target.Epoch, indices, participatedFlags, totalBalance)
}
````
````go
// SetParticipationAndRewardProposer retrieves and sets the epoch participation bits in state. Based on the epoch participation, it rewards
// the proposer in state.
//
// # Reward proposer
// proposer_reward_denominator = (WEIGHT_DENOMINATOR - PROPOSER_WEIGHT) * WEIGHT_DENOMINATOR // PROPOSER_WEIGHT
// proposer_reward = Gwei(proposer_reward_numerator // proposer_reward_denominator)
// increase_balance(state, get_beacon_proposer_index(state), proposer_reward)
func SetParticipationAndRewardProposer(
ctx context.Context,
beaconState state.BeaconState,
targetEpoch types.Epoch,
indices []uint64,
participatedFlags map[uint8]bool, totalBalance uint64) (state.BeaconState, error) {
var proposerRewardNumerator uint64
...
if targetEpoch == currentEpoch {
stateErr = beaconState.ModifyCurrentParticipationBits(func(val []byte) ([]byte, error) {
propRewardNum, epochParticipation, err := EpochParticipation(beaconState, indices, val, participatedFlags, totalBalance)
...
proposerRewardNumerator = propRewardNum
return epochParticipation, nil
})
} else {
stateErr = beaconState.ModifyPreviousParticipationBits(func(val []byte) ([]byte, error) {
propRewardNum, epochParticipation, err := EpochParticipation(beaconState, indices, val, participatedFlags, totalBalance)
...
proposerRewardNumerator = propRewardNum
return epochParticipation, nil
})
}
if err := RewardProposer(ctx, beaconState, proposerRewardNumerator); err != nil {
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^
return nil, err
}
return beaconState, nil
}
````
````go
// EpochParticipation sets and returns the proposer reward numerator and epoch participation.
//
// Spec code:
// proposer_reward_numerator = 0
// for index in get_attesting_indices(state, data, attestation.aggregation_bits):
// for flag_index, weight in enumerate(PARTICIPATION_FLAG_WEIGHTS):
// if flag_index in participation_flag_indices and not has_flag(epoch_participation[index], flag_index):
// epoch_participation[index] = add_flag(epoch_participation[index], flag_index)
// proposer_reward_numerator += get_base_reward(state, index) * weight
func EpochParticipation(beaconState state.BeaconState, indices []uint64, epochParticipation []byte, participatedFlags map[uint8]bool, totalBalance uint64) (uint64, []byte, error) {
cfg := params.BeaconConfig()
sourceFlagIndex := cfg.TimelySourceFlagIndex
targetFlagIndex := cfg.TimelyTargetFlagIndex
headFlagIndex := cfg.TimelyHeadFlagIndex
proposerRewardNumerator := uint64(0)
for _, index := range indices {
if index >= uint64(len(epochParticipation)) {
return 0, nil, fmt.Errorf("index %d exceeds participation length %d", index, len(epochParticipation))
}
br, err := BaseRewardWithTotalBalance(beaconState, types.ValidatorIndex(index), totalBalance)
if err != nil {
return 0, nil, err
}
has, err := HasValidatorFlag(epochParticipation[index], sourceFlagIndex)
if err != nil {
return 0, nil, err
}
if participatedFlags[sourceFlagIndex] && !has {
epochParticipation[index], err = AddValidatorFlag(epochParticipation[index], sourceFlagIndex)
if err != nil {
return 0, nil, err
}
proposerRewardNumerator += br * cfg.TimelySourceWeight
}
has, err = HasValidatorFlag(epochParticipation[index], targetFlagIndex)
if err != nil {
return 0, nil, err
}
if participatedFlags[targetFlagIndex] && !has {
epochParticipation[index], err = AddValidatorFlag(epochParticipation[index], targetFlagIndex)
if err != nil {
return 0, nil, err
}
proposerRewardNumerator += br * cfg.TimelyTargetWeight
}
has, err = HasValidatorFlag(epochParticipation[index], headFlagIndex)
if participatedFlags[headFlagIndex] && !has {
epochParticipation[index], err = AddValidatorFlag(epochParticipation[index], headFlagIndex)
...
proposerRewardNumerator += br * cfg.TimelyHeadWeight
}
}
return proposerRewardNumerator, epochParticipation, nil
}
````
````go
// RewardProposer rewards proposer by increasing proposer's balance with input reward numerator and calculated reward denominator.
//
// Spec code:
// proposer_reward_denominator = (WEIGHT_DENOMINATOR - PROPOSER_WEIGHT) * WEIGHT_DENOMINATOR // PROPOSER_WEIGHT
// proposer_reward = Gwei(proposer_reward_numerator // proposer_reward_denominator)
// increase_balance(state, get_beacon_proposer_index(state), proposer_reward)
func RewardProposer(ctx context.Context, beaconState state.BeaconState, proposerRewardNumerator uint64) error {
cfg := params.BeaconConfig()
d := (cfg.WeightDenominator - cfg.ProposerWeight) * cfg.WeightDenominator / cfg.ProposerWeight
proposerReward := proposerRewardNumerator / d
/*
cfg.ProposerWeight=8
cfg.WeightDenominator=64
d=448
proposerReward=687281
proposerRewardNumerator=307901952
*/
i, err := helpers.BeaconProposerIndex(ctx, beaconState)
if err != nil {
return err
}
return helpers.IncreaseBalance(beaconState, i, proposerReward)
}
````
## SyncCommitteeAggregator
ничего не получает
Аггрегирует SyncCommittee см. ниже.
## SyncCommittee: награда и штраф
срабатывает на каждом слоте
Служит для поддержки работы __Лайт-Клиента__.
Делает аттестацию рута стейта предшествующего слота __(currentSlot - 1)__
````go
func (vs *Server) buildAltairBeaconBlock(ctx context.Context, req *ethpb.BlockRequest) (*ethpb.BeaconBlockAltair, error) {
...
syncAggregate, err := vs.getSyncAggregate(ctx, req.Slot-1, bytesutil.ToBytes32(blkData.ParentRoot))
...
return ðpb.BeaconBlockAltair{
...
Body: ðpb.BeaconBlockBodyAltair{
...
SyncAggregate: syncAggregate,
//^^^^^^^^^^^^
},
}, nil
}
````
````go
Function
ProcessBlockForStateRoot
/home/mezin/go/src/tesseract/beacon-chain/core/transition
transition_no_verify_sig.go (Вызов в /beacon-chain/rpc/prysm/v1alpha1/validator/proposer.go:computeStateRoot)
CalculateStateRoot
state, err = ProcessBlockForStateRoot(ctx, state, signed)
ProcessBlockNoVerifyAnySig
state, err := ProcessBlockForStateRoot(ctx, state, signed)
/home/mezin/go/src/tesseract/beacon-chain/state/stategen
replay.go
executeStateTransitionStateGen
state, err = transition.ProcessBlockForStateRoot(ctx, state, signed)
// ProcessBlockForStateRoot processes the state for state root computation.
func ProcessBlockForStateRoot(...) ... {
...
state, err = altair.ProcessSyncAggregate(ctx, state, sa)
...
return state, nil
}
````
````go
Function
ProcessSyncAggregate
/home/mezin/go/src/tesseract/beacon-chain/core/transition
transition_no_verify_sig.go
ProcessBlockForStateRoot
state, err = altair.ProcessSyncAggregate(ctx, state, sa)
/*
Function
ProcessSyncAggregate
/home/mezin/go/src/tesseract/beacon-chain/core/transition
transition_no_verify_sig.go
ProcessBlockForStateRoot
state, err = altair.ProcessSyncAggregate(ctx, state, sa)
*/
// ProcessSyncAggregate verifies sync committee aggregate signature signing over the previous slot block root.
func ProcessSyncAggregate(ctx context.Context, s state.BeaconStateAltair, sync *ethpb.SyncAggregate) (state.BeaconStateAltair, error) {
votedKeys, votedIndices, didntVoteIndices, err := FilterSyncCommitteeVotes(s, sync)
...
if err := VerifySyncCommitteeSig(s, votedKeys, sync.SyncCommitteeSignature); err != nil {
return nil, err
}
return ApplySyncRewardsPenalties(ctx, s, votedIndices, didntVoteIndices)
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
}
````
````go
// ApplySyncRewardsPenalties applies rewards and penalties for proposer and sync committee participants.
func ApplySyncRewardsPenalties(ctx context.Context, s state.BeaconStateAltair, votedIndices, didntVoteIndices []types.ValidatorIndex) (state.BeaconStateAltair, error) {
activeBalance, err := helpers.TotalActiveBalance(s)
...
proposerReward, participantReward, err := SyncRewards(activeBalance)
...
// Apply sync committee rewards.
earnedProposerReward := uint64(0)
for _, index := range votedIndices {
/*
index=118
participantReward=350
*/
if err := helpers.IncreaseBalance(s, index, participantReward); err != nil {
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
return nil, err
}
earnedProposerReward += proposerReward
//^^^^^^^^^^^^^^^^^^^^
}
// Apply proposer rewards.
/*
Получить пропозера по стейту
*/
proposerIndex, err := helpers.BeaconProposerIndex(ctx, s)
...
/*
earnedProposerReward=25600
proposerIndex=7
*/
if err := helpers.IncreaseBalance(s, proposerIndex, earnedProposerReward); err != nil {
return nil, err
}
// Apply sync committee penalties.
for _, index := range didntVoteIndices {
if err := helpers.DecreaseBalance(s, index, participantReward); err != nil {
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
return nil, err
}
}
return s, nil
}
// SyncRewards returns the proposer reward and the sync participant reward given the total active balance in state.
func SyncRewards(activeBalance uint64) (proposerReward, participantReward uint64, err error) {
cfg := params.BeaconConfig()
totalActiveIncrements := activeBalance / cfg.EffectiveBalanceIncrement
baseRewardPerInc, err := BaseRewardPerIncrement(activeBalance)
if err != nil {
return 0, 0, err
}
totalBaseRewards := baseRewardPerInc * totalActiveIncrements
maxParticipantRewards := totalBaseRewards * cfg.SyncRewardWeight / cfg.WeightDenominator / uint64(cfg.SlotsPerEpoch)
participantReward = maxParticipantRewards / cfg.SyncCommitteeSize
proposerReward = participantReward * cfg.ProposerWeight / (cfg.WeightDenominator - cfg.ProposerWeight)
return
}
// BaseRewardPerIncrement of the beacon state
//
// Spec code:
// def get_base_reward_per_increment(state: BeaconState) -> Gwei:
// return Gwei(EFFECTIVE_BALANCE_INCREMENT * BASE_REWARD_FACTOR // integer_squareroot(get_total_active_balance(state)))
func BaseRewardPerIncrement(activeBalance uint64) (uint64, error) {
if activeBalance == 0 {
return 0, errors.New("active balance can't be 0")
}
cfg := params.BeaconConfig()
return cfg.EffectiveBalanceIncrement * cfg.BaseRewardFactor / math.IntegerSquareRoot(activeBalance), nil
}
````
## Slash
### SlashValidator
````go
// SlashValidator slashes the malicious validator's balance and awards
// the whistleblower's balance.
//
// Spec pseudocode definition:
// def slash_validator(state: BeaconState,
// slashed_index: ValidatorIndex,
// whistleblower_index: ValidatorIndex=None) -> None:
// """
// Slash the validator with index ``slashed_index``.
// """
// epoch = get_current_epoch(state)
// initiate_validator_exit(state, slashed_index)
// validator = state.validators[slashed_index]
// validator.slashed = True
// validator.withdrawable_epoch = max(validator.withdrawable_epoch, Epoch(epoch + EPOCHS_PER_SLASHINGS_VECTOR))
// state.slashings[epoch % EPOCHS_PER_SLASHINGS_VECTOR] += validator.effective_balance
// decrease_balance(state, slashed_index, validator.effective_balance // MIN_SLASHING_PENALTY_QUOTIENT)
//
// # Apply proposer and whistleblower rewards
// proposer_index = get_beacon_proposer_index(state)
// if whistleblower_index is None:
// whistleblower_index = proposer_index
// whistleblower_reward = Gwei(validator.effective_balance // WHISTLEBLOWER_REWARD_QUOTIENT)
// proposer_reward = Gwei(whistleblower_reward // PROPOSER_REWARD_QUOTIENT)
// increase_balance(state, proposer_index, proposer_reward)
// increase_balance(state, whistleblower_index, Gwei(whistleblower_reward - proposer_reward))
func SlashValidator(
ctx context.Context,
s state.BeaconState,
slashedIdx types.ValidatorIndex,
penaltyQuotient uint64,
proposerRewardQuotient uint64) (state.BeaconState, error) {
s, err := InitiateValidatorExit(ctx, s, slashedIdx)
if err != nil {
return nil, errors.Wrapf(err, "could not initiate validator %d exit", slashedIdx)
}
currentEpoch := slots.ToEpoch(s.Slot())
validator, err := s.ValidatorAtIndex(slashedIdx)
...
validator.Slashed = true
maxWithdrawableEpoch := types.MaxEpoch(validator.WithdrawableEpoch, currentEpoch+params.BeaconConfig().EpochsPerSlashingsVector)
validator.WithdrawableEpoch = maxWithdrawableEpoch
if err := s.UpdateValidatorAtIndex(slashedIdx, validator); err != nil {
return nil, err
}
// The slashing amount is represented by epochs per slashing vector. The validator's effective balance is then applied to that amount.
slashings := s.Slashings()
currentSlashing := slashings[currentEpoch%params.BeaconConfig().EpochsPerSlashingsVector]
if err := s.UpdateSlashingsAtIndex(
uint64(currentEpoch%params.BeaconConfig().EpochsPerSlashingsVector),
currentSlashing+validator.EffectiveBalance,
); err != nil {
return nil, err
}
if err := helpers.DecreaseBalance(s, slashedIdx, validator.EffectiveBalance/penaltyQuotient); err != nil {
return nil, err
}
proposerIdx, err := helpers.BeaconProposerIndex(ctx, s)
if err != nil {
return nil, errors.Wrap(err, "could not get proposer idx")
}
// In phase 0, the proposer is the whistleblower.
whistleBlowerIdx := proposerIdx
whistleblowerReward := validator.EffectiveBalance / params.BeaconConfig().WhistleBlowerRewardQuotient
proposerReward := whistleblowerReward / proposerRewardQuotient
err = helpers.IncreaseBalance(s, proposerIdx, proposerReward)
if err != nil {
return nil, err
}
err = helpers.IncreaseBalance(s, whistleBlowerIdx, whistleblowerReward-proposerReward)
if err != nil {
return nil, err
}
return s, nil
}
````
### ProcessSlashings
````go
// ProcessSlashings processes the slashed validators during epoch processing,
//
// def process_slashings(state: BeaconState) -> None:
// epoch = get_current_epoch(state)
// total_balance = get_total_active_balance(state)
// adjusted_total_slashing_balance = min(sum(state.slashings) * PROPORTIONAL_SLASHING_MULTIPLIER, total_balance)
// for index, validator in enumerate(state.validators):
// if validator.slashed and epoch + EPOCHS_PER_SLASHINGS_VECTOR // 2 == validator.withdrawable_epoch:
// increment = EFFECTIVE_BALANCE_INCREMENT # Factored out from penalty numerator to avoid uint64 overflow
// penalty_numerator = validator.effective_balance // increment * adjusted_total_slashing_balance
// penalty = penalty_numerator // total_balance * increment
// decrease_balance(state, ValidatorIndex(index), penalty)
func ProcessSlashings(state state.BeaconState, slashingMultiplier uint64) (state.BeaconState, error) {
currentEpoch := time.CurrentEpoch(state)
totalBalance, err := helpers.TotalActiveBalance(state)
if err != nil {
return nil, errors.Wrap(err, "could not get total active balance")
}
// Compute slashed balances in the current epoch
exitLength := params.BeaconConfig().EpochsPerSlashingsVector
// Compute the sum of state slashings
slashings := state.Slashings()
totalSlashing := uint64(0)
for _, slashing := range slashings {
totalSlashing, err = math.Add64(totalSlashing, slashing)
if err != nil {
return nil, err
}
}
// a callback is used here to apply the following actions to all validators
// below equally.
increment := params.BeaconConfig().EffectiveBalanceIncrement
minSlashing := math.Min(totalSlashing*slashingMultiplier, totalBalance)
err = state.ApplyToEveryValidator(func(idx int, val *ethpb.Validator) (bool, *ethpb.Validator, error) {
correctEpoch := (currentEpoch + exitLength/2) == val.WithdrawableEpoch
if val.Slashed && correctEpoch {
penaltyNumerator := val.EffectiveBalance / increment * minSlashing
penalty := penaltyNumerator / totalBalance * increment
if err := helpers.DecreaseBalance(state, types.ValidatorIndex(idx), penalty); err != nil {
return false, val, err
}
return true, val, nil
}
return false, val, nil
})
return state, err
}
````
### ProcessSlashingsPrecompute
````go
// ProcessSlashingsPrecompute processes the slashed validators during epoch processing.
// This is an optimized version by passing in precomputed total epoch balances.
func ProcessSlashingsPrecompute(s state.BeaconState, pBal *Balance) error {
currentEpoch := time.CurrentEpoch(s)
exitLength := params.BeaconConfig().EpochsPerSlashingsVector
// Compute the sum of state slashings
slashings := s.Slashings()
totalSlashing := uint64(0)
for _, slashing := range slashings {
totalSlashing += slashing
}
minSlashing := math.Min(totalSlashing*params.BeaconConfig().ProportionalSlashingMultiplier, pBal.ActiveCurrentEpoch)
epochToWithdraw := currentEpoch + exitLength/2
var hasSlashing bool
// Iterate through validator list in state, stop until a validator satisfies slashing condition of current epoch.
err := s.ReadFromEveryValidator(func(idx int, val state.ReadOnlyValidator) error {
correctEpoch := epochToWithdraw == val.WithdrawableEpoch()
if val.Slashed() && correctEpoch {
hasSlashing = true
}
return nil
})
if err != nil {
return err
}
// Exit early if there's no meaningful slashing to process.
if !hasSlashing {
return nil
}
increment := params.BeaconConfig().EffectiveBalanceIncrement
validatorFunc := func(idx int, val *ethpb.Validator) (bool, *ethpb.Validator, error) {
correctEpoch := epochToWithdraw == val.WithdrawableEpoch
if val.Slashed && correctEpoch {
penaltyNumerator := val.EffectiveBalance / increment * minSlashing
penalty := penaltyNumerator / pBal.ActiveCurrentEpoch * increment
if err := helpers.DecreaseBalance(s, types.ValidatorIndex(idx), penalty); err != nil {
return false, val, err
}
return true, val, nil
}
return false, val, nil
}
return s.ApplyToEveryValidator(validatorFunc)
}
````
## Методы обновления баланса валидатора
Используются только эти методы
### IncreaseBalance
````go
Function
IncreaseBalance
Usages in Project and Libraries
Unclassified
.workspace
/home/mezin/go/src/tesseract/beacon-chain/core/altair
attestation.go
RewardProposer
return helpers.IncreaseBalance(beaconState, i, proposerReward)
block.go
ApplySyncRewardsPenalties
if err := helpers.IncreaseBalance(s, index, participantReward); err != nil {
if err := helpers.IncreaseBalance(s, proposerIndex, earnedProposerReward); err != nil {
/home/mezin/go/src/tesseract/beacon-chain/core/blocks
deposit.go
ProcessDeposit
} else if err := helpers.IncreaseBalance(beaconState, index, amount); err != nil {
/home/mezin/go/src/tesseract/beacon-chain/core/helpers
rewards_penalties_test.go
TestIncreaseBadBalance_NotOK
require.ErrorContains(t, "addition overflows", IncreaseBalance(state, test.i, test.nb))
TestIncreaseBalance_OK
require.NoError(t, IncreaseBalance(state, test.i, test.nb))
/home/mezin/go/src/tesseract/beacon-chain/core/validators
validator.go
SlashValidator
err = helpers.IncreaseBalance(s, proposerIdx, proposerReward)
err = helpers.IncreaseBalance(s, whistleBlowerIdx, whistleblowerReward-proposerReward)
````
### DecreaseBalance
````go
Function
DecreaseBalance
Usages in Project and Libraries
Unclassified
.workspace
/home/mezin/go/src/tesseract/beacon-chain/core/altair
block.go
ApplySyncRewardsPenalties
if err := helpers.DecreaseBalance(s, index, participantReward); err != nil {
/home/mezin/go/src/tesseract/beacon-chain/core/epoch
epoch_processing.go
ProcessSlashings
if err := helpers.DecreaseBalance(state, types.ValidatorIndex(idx), penalty); err != nil {
/home/mezin/go/src/tesseract/beacon-chain/core/epoch/precompute
slashing.go
ProcessSlashingsPrecompute
if err := helpers.DecreaseBalance(s, types.ValidatorIndex(idx), penalty); err != nil {
/home/mezin/go/src/tesseract/beacon-chain/core/helpers
rewards_penalties_test.go
TestDecreaseBalance_OK
require.NoError(t, DecreaseBalance(state, test.i, test.nb))
/home/mezin/go/src/tesseract/beacon-chain/core/validators
validator.go
SlashValidator
if err := helpers.DecreaseBalance(s, slashedIdx, validator.EffectiveBalance/penaltyQuotient); err != nil {
````
### Balance struct
````go
// Balance stores the pre computation of the total participated balances for a given epoch
// Pre computing and storing such record is essential for process epoch optimizations.
type Balance struct {
// ActiveCurrentEpoch is the total effective balance of all active validators during current epoch.
ActiveCurrentEpoch uint64
// ActivePrevEpoch is the total effective balance of all active validators during prev epoch.
ActivePrevEpoch uint64
// CurrentEpochAttested is the total effective balance of all validators who attested during current epoch.
CurrentEpochAttested uint64
// CurrentEpochTargetAttested is the total effective balance of all validators who attested
// for epoch boundary block during current epoch.
CurrentEpochTargetAttested uint64
// PrevEpochAttested is the total effective balance of all validators who attested during prev epoch.
PrevEpochAttested uint64
// PrevEpochTargetAttested is the total effective balance of all validators who attested
// for epoch boundary block during prev epoch.
PrevEpochTargetAttested uint64
// PrevEpochHeadAttested is the total effective balance of all validators who attested
// correctly for head block during prev epoch.
PrevEpochHeadAttested uint64
}
````