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 }