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  }