github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/core/helpers/validators.go (about)

     1  package helpers
     2  
     3  import (
     4  	"bytes"
     5  
     6  	"github.com/pkg/errors"
     7  	types "github.com/prysmaticlabs/eth2-types"
     8  	iface "github.com/prysmaticlabs/prysm/beacon-chain/state/interface"
     9  	pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
    10  	ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
    11  	"github.com/prysmaticlabs/prysm/shared/bls"
    12  	"github.com/prysmaticlabs/prysm/shared/bytesutil"
    13  	"github.com/prysmaticlabs/prysm/shared/hashutil"
    14  	"github.com/prysmaticlabs/prysm/shared/params"
    15  )
    16  
    17  // IsActiveValidator returns the boolean value on whether the validator
    18  // is active or not.
    19  //
    20  // Spec pseudocode definition:
    21  //  def is_active_validator(validator: Validator, epoch: Epoch) -> bool:
    22  //    """
    23  //    Check if ``validator`` is active.
    24  //    """
    25  //    return validator.activation_epoch <= epoch < validator.exit_epoch
    26  func IsActiveValidator(validator *ethpb.Validator, epoch types.Epoch) bool {
    27  	return checkValidatorActiveStatus(validator.ActivationEpoch, validator.ExitEpoch, epoch)
    28  }
    29  
    30  // IsActiveValidatorUsingTrie checks if a read only validator is active.
    31  func IsActiveValidatorUsingTrie(validator iface.ReadOnlyValidator, epoch types.Epoch) bool {
    32  	return checkValidatorActiveStatus(validator.ActivationEpoch(), validator.ExitEpoch(), epoch)
    33  }
    34  
    35  func checkValidatorActiveStatus(activationEpoch, exitEpoch, epoch types.Epoch) bool {
    36  	return activationEpoch <= epoch && epoch < exitEpoch
    37  }
    38  
    39  // IsSlashableValidator returns the boolean value on whether the validator
    40  // is slashable or not.
    41  //
    42  // Spec pseudocode definition:
    43  //  def is_slashable_validator(validator: Validator, epoch: Epoch) -> bool:
    44  //  """
    45  //  Check if ``validator`` is slashable.
    46  //  """
    47  //  return (not validator.slashed) and (validator.activation_epoch <= epoch < validator.withdrawable_epoch)
    48  func IsSlashableValidator(activationEpoch, withdrawableEpoch types.Epoch, slashed bool, epoch types.Epoch) bool {
    49  	return checkValidatorSlashable(activationEpoch, withdrawableEpoch, slashed, epoch)
    50  }
    51  
    52  // IsSlashableValidatorUsingTrie checks if a read only validator is slashable.
    53  func IsSlashableValidatorUsingTrie(val iface.ReadOnlyValidator, epoch types.Epoch) bool {
    54  	return checkValidatorSlashable(val.ActivationEpoch(), val.WithdrawableEpoch(), val.Slashed(), epoch)
    55  }
    56  
    57  func checkValidatorSlashable(activationEpoch, withdrawableEpoch types.Epoch, slashed bool, epoch types.Epoch) bool {
    58  	active := activationEpoch <= epoch
    59  	beforeWithdrawable := epoch < withdrawableEpoch
    60  	return beforeWithdrawable && active && !slashed
    61  }
    62  
    63  // ActiveValidatorIndices filters out active validators based on validator status
    64  // and returns their indices in a list.
    65  //
    66  // WARNING: This method allocates a new copy of the validator index set and is
    67  // considered to be very memory expensive. Avoid using this unless you really
    68  // need the active validator indices for some specific reason.
    69  //
    70  // Spec pseudocode definition:
    71  //  def get_active_validator_indices(state: BeaconState, epoch: Epoch) -> Sequence[ValidatorIndex]:
    72  //    """
    73  //    Return the sequence of active validator indices at ``epoch``.
    74  //    """
    75  //    return [ValidatorIndex(i) for i, v in enumerate(state.validators) if is_active_validator(v, epoch)]
    76  func ActiveValidatorIndices(state iface.ReadOnlyBeaconState, epoch types.Epoch) ([]types.ValidatorIndex, error) {
    77  	seed, err := Seed(state, epoch, params.BeaconConfig().DomainBeaconAttester)
    78  	if err != nil {
    79  		return nil, errors.Wrap(err, "could not get seed")
    80  	}
    81  	activeIndices, err := committeeCache.ActiveIndices(seed)
    82  	if err != nil {
    83  		return nil, errors.Wrap(err, "could not interface with committee cache")
    84  	}
    85  	if activeIndices != nil {
    86  		return activeIndices, nil
    87  	}
    88  	var indices []types.ValidatorIndex
    89  	if err := state.ReadFromEveryValidator(func(idx int, val iface.ReadOnlyValidator) error {
    90  		if IsActiveValidatorUsingTrie(val, epoch) {
    91  			indices = append(indices, types.ValidatorIndex(idx))
    92  		}
    93  		return nil
    94  	}); err != nil {
    95  		return nil, err
    96  	}
    97  
    98  	if err := UpdateCommitteeCache(state, epoch); err != nil {
    99  		return nil, errors.Wrap(err, "could not update committee cache")
   100  	}
   101  
   102  	return indices, nil
   103  }
   104  
   105  // ActiveValidatorCount returns the number of active validators in the state
   106  // at the given epoch.
   107  func ActiveValidatorCount(state iface.ReadOnlyBeaconState, epoch types.Epoch) (uint64, error) {
   108  	seed, err := Seed(state, epoch, params.BeaconConfig().DomainBeaconAttester)
   109  	if err != nil {
   110  		return 0, errors.Wrap(err, "could not get seed")
   111  	}
   112  	activeCount, err := committeeCache.ActiveIndicesCount(seed)
   113  	if err != nil {
   114  		return 0, errors.Wrap(err, "could not interface with committee cache")
   115  	}
   116  	if activeCount != 0 && state.Slot() != 0 {
   117  		return uint64(activeCount), nil
   118  	}
   119  
   120  	count := uint64(0)
   121  	if err := state.ReadFromEveryValidator(func(idx int, val iface.ReadOnlyValidator) error {
   122  		if IsActiveValidatorUsingTrie(val, epoch) {
   123  			count++
   124  		}
   125  		return nil
   126  	}); err != nil {
   127  		return 0, err
   128  	}
   129  
   130  	if err := UpdateCommitteeCache(state, epoch); err != nil {
   131  		return 0, errors.Wrap(err, "could not update committee cache")
   132  	}
   133  
   134  	return count, nil
   135  }
   136  
   137  // ActivationExitEpoch takes in epoch number and returns when
   138  // the validator is eligible for activation and exit.
   139  //
   140  // Spec pseudocode definition:
   141  //  def compute_activation_exit_epoch(epoch: Epoch) -> Epoch:
   142  //    """
   143  //    Return the epoch during which validator activations and exits initiated in ``epoch`` take effect.
   144  //    """
   145  //    return Epoch(epoch + 1 + MAX_SEED_LOOKAHEAD)
   146  func ActivationExitEpoch(epoch types.Epoch) types.Epoch {
   147  	return epoch + 1 + params.BeaconConfig().MaxSeedLookahead
   148  }
   149  
   150  // ValidatorChurnLimit returns the number of validators that are allowed to
   151  // enter and exit validator pool for an epoch.
   152  //
   153  // Spec pseudocode definition:
   154  //   def get_validator_churn_limit(state: BeaconState) -> uint64:
   155  //    """
   156  //    Return the validator churn limit for the current epoch.
   157  //    """
   158  //    active_validator_indices = get_active_validator_indices(state, get_current_epoch(state))
   159  //    return max(MIN_PER_EPOCH_CHURN_LIMIT, uint64(len(active_validator_indices)) // CHURN_LIMIT_QUOTIENT)
   160  func ValidatorChurnLimit(activeValidatorCount uint64) (uint64, error) {
   161  	churnLimit := activeValidatorCount / params.BeaconConfig().ChurnLimitQuotient
   162  	if churnLimit < params.BeaconConfig().MinPerEpochChurnLimit {
   163  		churnLimit = params.BeaconConfig().MinPerEpochChurnLimit
   164  	}
   165  	return churnLimit, nil
   166  }
   167  
   168  // BeaconProposerIndex returns proposer index of a current slot.
   169  //
   170  // Spec pseudocode definition:
   171  //  def get_beacon_proposer_index(state: BeaconState) -> ValidatorIndex:
   172  //    """
   173  //    Return the beacon proposer index at the current slot.
   174  //    """
   175  //    epoch = get_current_epoch(state)
   176  //    seed = hash(get_seed(state, epoch, DOMAIN_BEACON_PROPOSER) + uint_to_bytes(state.slot))
   177  //    indices = get_active_validator_indices(state, epoch)
   178  //    return compute_proposer_index(state, indices, seed)
   179  func BeaconProposerIndex(state iface.ReadOnlyBeaconState) (types.ValidatorIndex, error) {
   180  	e := CurrentEpoch(state)
   181  	// The cache uses the state root of the previous epoch - minimum_seed_lookahead last slot as key. (e.g. Starting epoch 1, slot 32, the key would be block root at slot 31)
   182  	// For simplicity, the node will skip caching of genesis epoch.
   183  	if e > params.BeaconConfig().GenesisEpoch+params.BeaconConfig().MinSeedLookahead {
   184  		wantedEpoch := PrevEpoch(state)
   185  		if wantedEpoch >= params.BeaconConfig().MinSeedLookahead {
   186  			wantedEpoch -= params.BeaconConfig().MinSeedLookahead
   187  		}
   188  		s, err := EndSlot(wantedEpoch)
   189  		if err != nil {
   190  			return 0, err
   191  		}
   192  		r, err := StateRootAtSlot(state, s)
   193  		if err != nil {
   194  			return 0, err
   195  		}
   196  		if r != nil && !bytes.Equal(r, params.BeaconConfig().ZeroHash[:]) {
   197  			proposerIndices, err := proposerIndicesCache.ProposerIndices(bytesutil.ToBytes32(r))
   198  			if err != nil {
   199  				return 0, errors.Wrap(err, "could not interface with committee cache")
   200  			}
   201  			if proposerIndices != nil {
   202  				if len(proposerIndices) != int(params.BeaconConfig().SlotsPerEpoch) {
   203  					return 0, errors.Errorf("length of proposer indices is not equal %d to slots per epoch", len(proposerIndices))
   204  				}
   205  				return proposerIndices[state.Slot()%params.BeaconConfig().SlotsPerEpoch], nil
   206  			}
   207  			if err := UpdateProposerIndicesInCache(state); err != nil {
   208  				return 0, errors.Wrap(err, "could not update committee cache")
   209  			}
   210  		}
   211  	}
   212  
   213  	seed, err := Seed(state, e, params.BeaconConfig().DomainBeaconProposer)
   214  	if err != nil {
   215  		return 0, errors.Wrap(err, "could not generate seed")
   216  	}
   217  
   218  	seedWithSlot := append(seed[:], bytesutil.Bytes8(uint64(state.Slot()))...)
   219  	seedWithSlotHash := hashutil.Hash(seedWithSlot)
   220  
   221  	indices, err := ActiveValidatorIndices(state, e)
   222  	if err != nil {
   223  		return 0, errors.Wrap(err, "could not get active indices")
   224  	}
   225  
   226  	return ComputeProposerIndex(state, indices, seedWithSlotHash)
   227  }
   228  
   229  // ComputeProposerIndex returns the index sampled by effective balance, which is used to calculate proposer.
   230  //
   231  // Spec pseudocode definition:
   232  //  def compute_proposer_index(state: BeaconState, indices: Sequence[ValidatorIndex], seed: Bytes32) -> ValidatorIndex:
   233  //    """
   234  //    Return from ``indices`` a random index sampled by effective balance.
   235  //    """
   236  //    assert len(indices) > 0
   237  //    MAX_RANDOM_BYTE = 2**8 - 1
   238  //    i = uint64(0)
   239  //    total = uint64(len(indices))
   240  //    while True:
   241  //        candidate_index = indices[compute_shuffled_index(i % total, total, seed)]
   242  //        random_byte = hash(seed + uint_to_bytes(uint64(i // 32)))[i % 32]
   243  //        effective_balance = state.validators[candidate_index].effective_balance
   244  //        if effective_balance * MAX_RANDOM_BYTE >= MAX_EFFECTIVE_BALANCE * random_byte:
   245  //            return candidate_index
   246  //        i += 1
   247  func ComputeProposerIndex(bState iface.ReadOnlyValidators, activeIndices []types.ValidatorIndex, seed [32]byte) (types.ValidatorIndex, error) {
   248  	length := uint64(len(activeIndices))
   249  	if length == 0 {
   250  		return 0, errors.New("empty active indices list")
   251  	}
   252  	maxRandomByte := uint64(1<<8 - 1)
   253  	hashFunc := hashutil.CustomSHA256Hasher()
   254  
   255  	for i := uint64(0); ; i++ {
   256  		candidateIndex, err := ComputeShuffledIndex(types.ValidatorIndex(i%length), length, seed, true /* shuffle */)
   257  		if err != nil {
   258  			return 0, err
   259  		}
   260  		candidateIndex = activeIndices[candidateIndex]
   261  		if uint64(candidateIndex) >= uint64(bState.NumValidators()) {
   262  			return 0, errors.New("active index out of range")
   263  		}
   264  		b := append(seed[:], bytesutil.Bytes8(i/32)...)
   265  		randomByte := hashFunc(b)[i%32]
   266  		v, err := bState.ValidatorAtIndexReadOnly(candidateIndex)
   267  		if err != nil {
   268  			return 0, err
   269  		}
   270  		effectiveBal := v.EffectiveBalance()
   271  
   272  		if effectiveBal*maxRandomByte >= params.BeaconConfig().MaxEffectiveBalance*uint64(randomByte) {
   273  			return candidateIndex, nil
   274  		}
   275  	}
   276  }
   277  
   278  // Domain returns the domain version for BLS private key to sign and verify.
   279  //
   280  // Spec pseudocode definition:
   281  //  def get_domain(state: BeaconState, domain_type: DomainType, epoch: Epoch=None) -> Domain:
   282  //    """
   283  //    Return the signature domain (fork version concatenated with domain type) of a message.
   284  //    """
   285  //    epoch = get_current_epoch(state) if epoch is None else epoch
   286  //    fork_version = state.fork.previous_version if epoch < state.fork.epoch else state.fork.current_version
   287  //    return compute_domain(domain_type, fork_version, state.genesis_validators_root)
   288  func Domain(fork *pb.Fork, epoch types.Epoch, domainType [bls.DomainByteLength]byte, genesisRoot []byte) ([]byte, error) {
   289  	if fork == nil {
   290  		return []byte{}, errors.New("nil fork or domain type")
   291  	}
   292  	var forkVersion []byte
   293  	if epoch < fork.Epoch {
   294  		forkVersion = fork.PreviousVersion
   295  	} else {
   296  		forkVersion = fork.CurrentVersion
   297  	}
   298  	if len(forkVersion) != 4 {
   299  		return []byte{}, errors.New("fork version length is not 4 byte")
   300  	}
   301  	var forkVersionArray [4]byte
   302  	copy(forkVersionArray[:], forkVersion[:4])
   303  	return ComputeDomain(domainType, forkVersionArray[:], genesisRoot)
   304  }
   305  
   306  // IsEligibleForActivationQueue checks if the validator is eligible to
   307  // be placed into the activation queue.
   308  //
   309  // Spec pseudocode definition:
   310  //  def is_eligible_for_activation_queue(validator: Validator) -> bool:
   311  //    """
   312  //    Check if ``validator`` is eligible to be placed into the activation queue.
   313  //    """
   314  //    return (
   315  //        validator.activation_eligibility_epoch == FAR_FUTURE_EPOCH
   316  //        and validator.effective_balance == MAX_EFFECTIVE_BALANCE
   317  //    )
   318  func IsEligibleForActivationQueue(validator *ethpb.Validator) bool {
   319  	return isEligibileForActivationQueue(validator.ActivationEligibilityEpoch, validator.EffectiveBalance)
   320  }
   321  
   322  // IsEligibleForActivationQueueUsingTrie checks if the read-only validator is eligible to
   323  // be placed into the activation queue.
   324  func IsEligibleForActivationQueueUsingTrie(validator iface.ReadOnlyValidator) bool {
   325  	return isEligibileForActivationQueue(validator.ActivationEligibilityEpoch(), validator.EffectiveBalance())
   326  }
   327  
   328  // isEligibleForActivationQueue carries out the logic for IsEligibleForActivationQueue*
   329  func isEligibileForActivationQueue(activationEligibilityEpoch types.Epoch, effectiveBalance uint64) bool {
   330  	return activationEligibilityEpoch == params.BeaconConfig().FarFutureEpoch &&
   331  		effectiveBalance == params.BeaconConfig().MaxEffectiveBalance
   332  }
   333  
   334  // IsEligibleForActivation checks if the validator is eligible for activation.
   335  //
   336  // Spec pseudocode definition:
   337  //  def is_eligible_for_activation(state: BeaconState, validator: Validator) -> bool:
   338  //    """
   339  //    Check if ``validator`` is eligible for activation.
   340  //    """
   341  //    return (
   342  //        # Placement in queue is finalized
   343  //        validator.activation_eligibility_epoch <= state.finalized_checkpoint.epoch
   344  //        # Has not yet been activated
   345  //        and validator.activation_epoch == FAR_FUTURE_EPOCH
   346  //    )
   347  func IsEligibleForActivation(state iface.ReadOnlyCheckpoint, validator *ethpb.Validator) bool {
   348  	finalizedEpoch := state.FinalizedCheckpointEpoch()
   349  	return isEligibleForActivation(validator.ActivationEligibilityEpoch, validator.ActivationEpoch, finalizedEpoch)
   350  }
   351  
   352  // IsEligibleForActivationUsingTrie checks if the validator is eligible for activation.
   353  func IsEligibleForActivationUsingTrie(state iface.ReadOnlyCheckpoint, validator iface.ReadOnlyValidator) bool {
   354  	cpt := state.FinalizedCheckpoint()
   355  	if cpt == nil {
   356  		return false
   357  	}
   358  	return isEligibleForActivation(validator.ActivationEligibilityEpoch(), validator.ActivationEpoch(), cpt.Epoch)
   359  }
   360  
   361  // isEligibleForActivation carries out the logic for IsEligibleForActivation*
   362  func isEligibleForActivation(activationEligibilityEpoch, activationEpoch, finalizedEpoch types.Epoch) bool {
   363  	return activationEligibilityEpoch <= finalizedEpoch &&
   364  		activationEpoch == params.BeaconConfig().FarFutureEpoch
   365  }