--- 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 &ethpb.BeaconBlockAltair{ ... Body: &ethpb.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 } ````