github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/core/epoch/epoch_processing.go (about) 1 // Package epoch contains epoch processing libraries according to spec, able to 2 // process new balance for validators, justify and finalize new 3 // check points, and shuffle validators to different slots and 4 // shards. 5 package epoch 6 7 import ( 8 "fmt" 9 "sort" 10 11 "github.com/pkg/errors" 12 types "github.com/prysmaticlabs/eth2-types" 13 "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" 14 "github.com/prysmaticlabs/prysm/beacon-chain/core/validators" 15 iface "github.com/prysmaticlabs/prysm/beacon-chain/state/interface" 16 pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" 17 ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1" 18 "github.com/prysmaticlabs/prysm/shared/attestationutil" 19 "github.com/prysmaticlabs/prysm/shared/copyutil" 20 "github.com/prysmaticlabs/prysm/shared/featureconfig" 21 "github.com/prysmaticlabs/prysm/shared/mathutil" 22 "github.com/prysmaticlabs/prysm/shared/params" 23 ) 24 25 // sortableIndices implements the Sort interface to sort newly activated validator indices 26 // by activation epoch and by index number. 27 type sortableIndices struct { 28 indices []types.ValidatorIndex 29 validators []*ethpb.Validator 30 } 31 32 // Len is the number of elements in the collection. 33 func (s sortableIndices) Len() int { return len(s.indices) } 34 35 // Swap swaps the elements with indexes i and j. 36 func (s sortableIndices) Swap(i, j int) { s.indices[i], s.indices[j] = s.indices[j], s.indices[i] } 37 38 // Less reports whether the element with index i must sort before the element with index j. 39 func (s sortableIndices) Less(i, j int) bool { 40 if s.validators[s.indices[i]].ActivationEligibilityEpoch == s.validators[s.indices[j]].ActivationEligibilityEpoch { 41 return s.indices[i] < s.indices[j] 42 } 43 return s.validators[s.indices[i]].ActivationEligibilityEpoch < s.validators[s.indices[j]].ActivationEligibilityEpoch 44 } 45 46 // AttestingBalance returns the total balance from all the attesting indices. 47 // 48 // WARNING: This method allocates a new copy of the attesting validator indices set and is 49 // considered to be very memory expensive. Avoid using this unless you really 50 // need to get attesting balance from attestations. 51 // 52 // Spec pseudocode definition: 53 // def get_attesting_balance(state: BeaconState, attestations: Sequence[PendingAttestation]) -> Gwei: 54 // """ 55 // Return the combined effective balance of the set of unslashed validators participating in ``attestations``. 56 // Note: ``get_total_balance`` returns ``EFFECTIVE_BALANCE_INCREMENT`` Gwei minimum to avoid divisions by zero. 57 // """ 58 // return get_total_balance(state, get_unslashed_attesting_indices(state, attestations)) 59 func AttestingBalance(state iface.ReadOnlyBeaconState, atts []*pb.PendingAttestation) (uint64, error) { 60 indices, err := UnslashedAttestingIndices(state, atts) 61 if err != nil { 62 return 0, errors.Wrap(err, "could not get attesting indices") 63 } 64 return helpers.TotalBalance(state, indices), nil 65 } 66 67 // ProcessRegistryUpdates rotates validators in and out of active pool. 68 // the amount to rotate is determined churn limit. 69 // 70 // Spec pseudocode definition: 71 // def process_registry_updates(state: BeaconState) -> None: 72 // # Process activation eligibility and ejections 73 // for index, validator in enumerate(state.validators): 74 // if is_eligible_for_activation_queue(validator): 75 // validator.activation_eligibility_epoch = get_current_epoch(state) + 1 76 // 77 // if is_active_validator(validator, get_current_epoch(state)) and validator.effective_balance <= EJECTION_BALANCE: 78 // initiate_validator_exit(state, ValidatorIndex(index)) 79 // 80 // # Queue validators eligible for activation and not yet dequeued for activation 81 // activation_queue = sorted([ 82 // index for index, validator in enumerate(state.validators) 83 // if is_eligible_for_activation(state, validator) 84 // # Order by the sequence of activation_eligibility_epoch setting and then index 85 // ], key=lambda index: (state.validators[index].activation_eligibility_epoch, index)) 86 // # Dequeued validators for activation up to churn limit 87 // for index in activation_queue[:get_validator_churn_limit(state)]: 88 // validator = state.validators[index] 89 // validator.activation_epoch = compute_activation_exit_epoch(get_current_epoch(state)) 90 func ProcessRegistryUpdates(state iface.BeaconState) (iface.BeaconState, error) { 91 currentEpoch := helpers.CurrentEpoch(state) 92 vals := state.Validators() 93 var err error 94 ejectionBal := params.BeaconConfig().EjectionBalance 95 activationEligibilityEpoch := helpers.CurrentEpoch(state) + 1 96 for idx, validator := range vals { 97 // Process the validators for activation eligibility. 98 if helpers.IsEligibleForActivationQueue(validator) { 99 validator.ActivationEligibilityEpoch = activationEligibilityEpoch 100 if err := state.UpdateValidatorAtIndex(types.ValidatorIndex(idx), validator); err != nil { 101 return nil, err 102 } 103 } 104 105 // Process the validators for ejection. 106 isActive := helpers.IsActiveValidator(validator, currentEpoch) 107 belowEjectionBalance := validator.EffectiveBalance <= ejectionBal 108 if isActive && belowEjectionBalance { 109 state, err = validators.InitiateValidatorExit(state, types.ValidatorIndex(idx)) 110 if err != nil { 111 return nil, errors.Wrapf(err, "could not initiate exit for validator %d", idx) 112 } 113 } 114 } 115 116 // Queue validators eligible for activation and not yet dequeued for activation. 117 var activationQ []types.ValidatorIndex 118 for idx, validator := range vals { 119 if helpers.IsEligibleForActivation(state, validator) { 120 activationQ = append(activationQ, types.ValidatorIndex(idx)) 121 } 122 } 123 124 sort.Sort(sortableIndices{indices: activationQ, validators: vals}) 125 126 // Only activate just enough validators according to the activation churn limit. 127 limit := uint64(len(activationQ)) 128 activeValidatorCount, err := helpers.ActiveValidatorCount(state, currentEpoch) 129 if err != nil { 130 return nil, errors.Wrap(err, "could not get active validator count") 131 } 132 133 churnLimit, err := helpers.ValidatorChurnLimit(activeValidatorCount) 134 if err != nil { 135 return nil, errors.Wrap(err, "could not get churn limit") 136 } 137 138 // Prevent churn limit cause index out of bound. 139 if churnLimit < limit { 140 limit = churnLimit 141 } 142 143 activationExitEpoch := helpers.ActivationExitEpoch(currentEpoch) 144 for _, index := range activationQ[:limit] { 145 validator, err := state.ValidatorAtIndex(index) 146 if err != nil { 147 return nil, err 148 } 149 validator.ActivationEpoch = activationExitEpoch 150 if err := state.UpdateValidatorAtIndex(index, validator); err != nil { 151 return nil, err 152 } 153 } 154 return state, nil 155 } 156 157 // ProcessSlashings processes the slashed validators during epoch processing, 158 // 159 // def process_slashings(state: BeaconState) -> None: 160 // epoch = get_current_epoch(state) 161 // total_balance = get_total_active_balance(state) 162 // adjusted_total_slashing_balance = min(sum(state.slashings) * PROPORTIONAL_SLASHING_MULTIPLIER, total_balance) 163 // for index, validator in enumerate(state.validators): 164 // if validator.slashed and epoch + EPOCHS_PER_SLASHINGS_VECTOR // 2 == validator.withdrawable_epoch: 165 // increment = EFFECTIVE_BALANCE_INCREMENT # Factored out from penalty numerator to avoid uint64 overflow 166 // penalty_numerator = validator.effective_balance // increment * adjusted_total_slashing_balance 167 // penalty = penalty_numerator // total_balance * increment 168 // decrease_balance(state, ValidatorIndex(index), penalty) 169 func ProcessSlashings(state iface.BeaconState) (iface.BeaconState, error) { 170 currentEpoch := helpers.CurrentEpoch(state) 171 totalBalance, err := helpers.TotalActiveBalance(state) 172 if err != nil { 173 return nil, errors.Wrap(err, "could not get total active balance") 174 } 175 176 // Compute slashed balances in the current epoch 177 exitLength := params.BeaconConfig().EpochsPerSlashingsVector 178 179 // Compute the sum of state slashings 180 slashings := state.Slashings() 181 totalSlashing := uint64(0) 182 for _, slashing := range slashings { 183 totalSlashing += slashing 184 } 185 186 // a callback is used here to apply the following actions to all validators 187 // below equally. 188 increment := params.BeaconConfig().EffectiveBalanceIncrement 189 minSlashing := mathutil.Min(totalSlashing*params.BeaconConfig().ProportionalSlashingMultiplier, totalBalance) 190 err = state.ApplyToEveryValidator(func(idx int, val *ethpb.Validator) (bool, *ethpb.Validator, error) { 191 correctEpoch := (currentEpoch + exitLength/2) == val.WithdrawableEpoch 192 if val.Slashed && correctEpoch { 193 penaltyNumerator := val.EffectiveBalance / increment * minSlashing 194 penalty := penaltyNumerator / totalBalance * increment 195 if err := helpers.DecreaseBalance(state, types.ValidatorIndex(idx), penalty); err != nil { 196 return false, val, err 197 } 198 return true, val, nil 199 } 200 return false, val, nil 201 }) 202 return state, err 203 } 204 205 // ProcessEth1DataReset processes updates to ETH1 data votes during epoch processing. 206 // 207 // Spec pseudocode definition: 208 // def process_eth1_data_reset(state: BeaconState) -> None: 209 // next_epoch = Epoch(get_current_epoch(state) + 1) 210 // # Reset eth1 data votes 211 // if next_epoch % EPOCHS_PER_ETH1_VOTING_PERIOD == 0: 212 // state.eth1_data_votes = [] 213 func ProcessEth1DataReset(state iface.BeaconState) (iface.BeaconState, error) { 214 currentEpoch := helpers.CurrentEpoch(state) 215 nextEpoch := currentEpoch + 1 216 217 // Reset ETH1 data votes. 218 if nextEpoch%params.BeaconConfig().EpochsPerEth1VotingPeriod == 0 { 219 if err := state.SetEth1DataVotes([]*ethpb.Eth1Data{}); err != nil { 220 return nil, err 221 } 222 } 223 224 return state, nil 225 } 226 227 // ProcessEffectiveBalanceUpdates processes effective balance updates during epoch processing. 228 // 229 // Spec pseudocode definition: 230 // def process_effective_balance_updates(state: BeaconState) -> None: 231 // # Update effective balances with hysteresis 232 // for index, validator in enumerate(state.validators): 233 // balance = state.balances[index] 234 // HYSTERESIS_INCREMENT = uint64(EFFECTIVE_BALANCE_INCREMENT // HYSTERESIS_QUOTIENT) 235 // DOWNWARD_THRESHOLD = HYSTERESIS_INCREMENT * HYSTERESIS_DOWNWARD_MULTIPLIER 236 // UPWARD_THRESHOLD = HYSTERESIS_INCREMENT * HYSTERESIS_UPWARD_MULTIPLIER 237 // if ( 238 // balance + DOWNWARD_THRESHOLD < validator.effective_balance 239 // or validator.effective_balance + UPWARD_THRESHOLD < balance 240 // ): 241 // validator.effective_balance = min(balance - balance % EFFECTIVE_BALANCE_INCREMENT, MAX_EFFECTIVE_BALANCE) 242 func ProcessEffectiveBalanceUpdates(state iface.BeaconState) (iface.BeaconState, error) { 243 effBalanceInc := params.BeaconConfig().EffectiveBalanceIncrement 244 maxEffBalance := params.BeaconConfig().MaxEffectiveBalance 245 hysteresisInc := effBalanceInc / params.BeaconConfig().HysteresisQuotient 246 downwardThreshold := hysteresisInc * params.BeaconConfig().HysteresisDownwardMultiplier 247 upwardThreshold := hysteresisInc * params.BeaconConfig().HysteresisUpwardMultiplier 248 249 bals := state.Balances() 250 // Update effective balances with hysteresis. 251 validatorFunc := func(idx int, val *ethpb.Validator) (bool, *ethpb.Validator, error) { 252 if val == nil { 253 return false, nil, fmt.Errorf("validator %d is nil in state", idx) 254 } 255 if idx >= len(bals) { 256 return false, nil, fmt.Errorf("validator index exceeds validator length in state %d >= %d", idx, len(state.Balances())) 257 } 258 balance := bals[idx] 259 260 if balance+downwardThreshold < val.EffectiveBalance || val.EffectiveBalance+upwardThreshold < balance { 261 newVal := copyutil.CopyValidator(val) 262 newVal.EffectiveBalance = maxEffBalance 263 if newVal.EffectiveBalance > balance-balance%effBalanceInc { 264 newVal.EffectiveBalance = balance - balance%effBalanceInc 265 } 266 return true, newVal, nil 267 } 268 return false, val, nil 269 } 270 271 if featureconfig.Get().EnableOptimizedBalanceUpdate { 272 validatorFunc = func(idx int, val *ethpb.Validator) (bool, *ethpb.Validator, error) { 273 if val == nil { 274 return false, nil, fmt.Errorf("validator %d is nil in state", idx) 275 } 276 if idx >= len(bals) { 277 return false, nil, fmt.Errorf("validator index exceeds validator length in state %d >= %d", idx, len(state.Balances())) 278 } 279 balance := bals[idx] 280 281 if balance+downwardThreshold < val.EffectiveBalance || val.EffectiveBalance+upwardThreshold < balance { 282 effectiveBal := maxEffBalance 283 if effectiveBal > balance-balance%effBalanceInc { 284 effectiveBal = balance - balance%effBalanceInc 285 } 286 if effectiveBal != val.EffectiveBalance { 287 newVal := copyutil.CopyValidator(val) 288 newVal.EffectiveBalance = effectiveBal 289 return true, newVal, nil 290 } 291 return false, val, nil 292 } 293 return false, val, nil 294 } 295 } 296 297 if err := state.ApplyToEveryValidator(validatorFunc); err != nil { 298 return nil, err 299 } 300 301 return state, nil 302 } 303 304 // ProcessSlashingsReset processes the total slashing balances updates during epoch processing. 305 // 306 // Spec pseudocode definition: 307 // def process_slashings_reset(state: BeaconState) -> None: 308 // next_epoch = Epoch(get_current_epoch(state) + 1) 309 // # Reset slashings 310 // state.slashings[next_epoch % EPOCHS_PER_SLASHINGS_VECTOR] = Gwei(0) 311 func ProcessSlashingsReset(state iface.BeaconState) (iface.BeaconState, error) { 312 currentEpoch := helpers.CurrentEpoch(state) 313 nextEpoch := currentEpoch + 1 314 315 // Set total slashed balances. 316 slashedExitLength := params.BeaconConfig().EpochsPerSlashingsVector 317 slashedEpoch := nextEpoch % slashedExitLength 318 slashings := state.Slashings() 319 if uint64(len(slashings)) != uint64(slashedExitLength) { 320 return nil, fmt.Errorf( 321 "state slashing length %d different than EpochsPerHistoricalVector %d", 322 len(slashings), 323 slashedExitLength, 324 ) 325 } 326 if err := state.UpdateSlashingsAtIndex(uint64(slashedEpoch) /* index */, 0 /* value */); err != nil { 327 return nil, err 328 } 329 330 return state, nil 331 } 332 333 // ProcessRandaoMixesReset processes the final updates to RANDAO mix during epoch processing. 334 // 335 // Spec pseudocode definition: 336 // def process_randao_mixes_reset(state: BeaconState) -> None: 337 // current_epoch = get_current_epoch(state) 338 // next_epoch = Epoch(current_epoch + 1) 339 // # Set randao mix 340 // state.randao_mixes[next_epoch % EPOCHS_PER_HISTORICAL_VECTOR] = get_randao_mix(state, current_epoch) 341 func ProcessRandaoMixesReset(state iface.BeaconState) (iface.BeaconState, error) { 342 currentEpoch := helpers.CurrentEpoch(state) 343 nextEpoch := currentEpoch + 1 344 345 // Set RANDAO mix. 346 randaoMixLength := params.BeaconConfig().EpochsPerHistoricalVector 347 if uint64(state.RandaoMixesLength()) != uint64(randaoMixLength) { 348 return nil, fmt.Errorf( 349 "state randao length %d different than EpochsPerHistoricalVector %d", 350 state.RandaoMixesLength(), 351 randaoMixLength, 352 ) 353 } 354 mix, err := helpers.RandaoMix(state, currentEpoch) 355 if err != nil { 356 return nil, err 357 } 358 if err := state.UpdateRandaoMixesAtIndex(uint64(nextEpoch%randaoMixLength), mix); err != nil { 359 return nil, err 360 } 361 362 return state, nil 363 } 364 365 // ProcessHistoricalRootsUpdate processes the updates to historical root accumulator during epoch processing. 366 // 367 // Spec pseudocode definition: 368 // def process_historical_roots_update(state: BeaconState) -> None: 369 // # Set historical root accumulator 370 // next_epoch = Epoch(get_current_epoch(state) + 1) 371 // if next_epoch % (SLOTS_PER_HISTORICAL_ROOT // SLOTS_PER_EPOCH) == 0: 372 // historical_batch = HistoricalBatch(block_roots=state.block_roots, state_roots=state.state_roots) 373 // state.historical_roots.append(hash_tree_root(historical_batch)) 374 func ProcessHistoricalRootsUpdate(state iface.BeaconState) (iface.BeaconState, error) { 375 currentEpoch := helpers.CurrentEpoch(state) 376 nextEpoch := currentEpoch + 1 377 378 // Set historical root accumulator. 379 epochsPerHistoricalRoot := params.BeaconConfig().SlotsPerHistoricalRoot.DivSlot(params.BeaconConfig().SlotsPerEpoch) 380 if nextEpoch.Mod(uint64(epochsPerHistoricalRoot)) == 0 { 381 historicalBatch := &pb.HistoricalBatch{ 382 BlockRoots: state.BlockRoots(), 383 StateRoots: state.StateRoots(), 384 } 385 batchRoot, err := historicalBatch.HashTreeRoot() 386 if err != nil { 387 return nil, errors.Wrap(err, "could not hash historical batch") 388 } 389 if err := state.AppendHistoricalRoots(batchRoot); err != nil { 390 return nil, err 391 } 392 } 393 394 return state, nil 395 } 396 397 // ProcessParticipationRecordUpdates rotates current/previous epoch attestations during epoch processing. 398 // 399 // Spec pseudocode definition: 400 // def process_participation_record_updates(state: BeaconState) -> None: 401 // # Rotate current/previous epoch attestations 402 // state.previous_epoch_attestations = state.current_epoch_attestations 403 // state.current_epoch_attestations = [] 404 func ProcessParticipationRecordUpdates(state iface.BeaconState) (iface.BeaconState, error) { 405 if err := state.RotateAttestations(); err != nil { 406 return nil, err 407 } 408 return state, nil 409 } 410 411 // ProcessFinalUpdates processes the final updates during epoch processing. 412 func ProcessFinalUpdates(state iface.BeaconState) (iface.BeaconState, error) { 413 var err error 414 415 // Reset ETH1 data votes. 416 state, err = ProcessEth1DataReset(state) 417 if err != nil { 418 return nil, err 419 } 420 421 // Update effective balances with hysteresis. 422 state, err = ProcessEffectiveBalanceUpdates(state) 423 if err != nil { 424 return nil, err 425 } 426 427 // Set total slashed balances. 428 state, err = ProcessSlashingsReset(state) 429 if err != nil { 430 return nil, err 431 } 432 433 // Set RANDAO mix. 434 state, err = ProcessRandaoMixesReset(state) 435 if err != nil { 436 return nil, err 437 } 438 439 // Set historical root accumulator. 440 state, err = ProcessHistoricalRootsUpdate(state) 441 if err != nil { 442 return nil, err 443 } 444 445 // Rotate current and previous epoch attestations. 446 state, err = ProcessParticipationRecordUpdates(state) 447 if err != nil { 448 return nil, err 449 } 450 451 return state, nil 452 } 453 454 // UnslashedAttestingIndices returns all the attesting indices from a list of attestations, 455 // it sorts the indices and filters out the slashed ones. 456 // 457 // Spec pseudocode definition: 458 // def get_unslashed_attesting_indices(state: BeaconState, 459 // attestations: Sequence[PendingAttestation]) -> Set[ValidatorIndex]: 460 // output = set() # type: Set[ValidatorIndex] 461 // for a in attestations: 462 // output = output.union(get_attesting_indices(state, a.data, a.aggregation_bits)) 463 // return set(filter(lambda index: not state.validators[index].slashed, output)) 464 func UnslashedAttestingIndices(state iface.ReadOnlyBeaconState, atts []*pb.PendingAttestation) ([]types.ValidatorIndex, error) { 465 var setIndices []types.ValidatorIndex 466 seen := make(map[uint64]bool) 467 468 for _, att := range atts { 469 committee, err := helpers.BeaconCommitteeFromState(state, att.Data.Slot, att.Data.CommitteeIndex) 470 if err != nil { 471 return nil, err 472 } 473 attestingIndices, err := attestationutil.AttestingIndices(att.AggregationBits, committee) 474 if err != nil { 475 return nil, err 476 } 477 // Create a set for attesting indices 478 for _, index := range attestingIndices { 479 if !seen[index] { 480 setIndices = append(setIndices, types.ValidatorIndex(index)) 481 } 482 seen[index] = true 483 } 484 } 485 // Sort the attesting set indices by increasing order. 486 sort.Slice(setIndices, func(i, j int) bool { return setIndices[i] < setIndices[j] }) 487 // Remove the slashed validator indices. 488 for i := 0; i < len(setIndices); i++ { 489 v, err := state.ValidatorAtIndexReadOnly(setIndices[i]) 490 if err != nil { 491 return nil, errors.Wrap(err, "failed to look up validator") 492 } 493 if !v.IsNil() && v.Slashed() { 494 setIndices = append(setIndices[:i], setIndices[i+1:]...) 495 } 496 } 497 498 return setIndices, nil 499 } 500 501 // BaseReward takes state and validator index and calculate 502 // individual validator's base reward quotient. 503 // 504 // Note: Adjusted quotient is calculated of base reward because it's too inefficient 505 // to repeat the same calculation for every validator versus just doing it once. 506 // 507 // Spec pseudocode definition: 508 // def get_base_reward(state: BeaconState, index: ValidatorIndex) -> Gwei: 509 // total_balance = get_total_active_balance(state) 510 // effective_balance = state.validators[index].effective_balance 511 // return Gwei(effective_balance * BASE_REWARD_FACTOR // integer_squareroot(total_balance) // BASE_REWARDS_PER_EPOCH) 512 func BaseReward(state iface.ReadOnlyBeaconState, index types.ValidatorIndex) (uint64, error) { 513 totalBalance, err := helpers.TotalActiveBalance(state) 514 if err != nil { 515 return 0, errors.Wrap(err, "could not calculate active balance") 516 } 517 val, err := state.ValidatorAtIndexReadOnly(index) 518 if err != nil { 519 return 0, err 520 } 521 effectiveBalance := val.EffectiveBalance() 522 baseReward := effectiveBalance * params.BeaconConfig().BaseRewardFactor / 523 mathutil.IntegerSquareRoot(totalBalance) / params.BeaconConfig().BaseRewardsPerEpoch 524 return baseReward, nil 525 }