mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-08 21:08:10 -05:00
#### This PR sets the foundation for the new logging features. --- The goal of this big PR is the following: 1. Adding a log.go file to every package: [_commit_](54f6396d4c) - Writing a bash script that adds the log.go file to every package that imports logrus, except the excluded packages, configured at the top of the bash script. - the log.go file creates a log variable and sets a field called `package` to the full path of that package. - I have tried to fix every error/problem that came from mass generation of this file. (duplicate declarations, different prefix names, etc...) - some packages had the log.go file from before, and had some helper functions in there as well. I've moved all of them to a `log_helpers.go` file within each package. 2. Create a CI rule which verifies that: [_commit_](b799c3a0ef) - every package which imports logrus, also has a log.go file, except the excluded packages. - the `package` field of each log.go variable, has the correct path. (to detect when we move a package or change it's name) - I pushed a commit with a manually changed log.go file to trigger the ci check failure and it worked. 3. Alter the logging system to read the prefix from this `package` field for every log while outputing: [_commit_](b0c7f1146c) - some packages have/want/need a different log prefix than their package name (like `kv`). This can be solved by keeping a map of package paths to prefix names somewhere. --- **Some notes:** - Please review everything carefully. - I created the `prefixReplacement` map and populated the data that I deemed necessary. Please check it and complain if something doesn't make sense or is missing. I attached at the bottom, the list of all the packages that used to use a different name than their package name as their prefix. - I have chosen to mark some packages to be excluded from this whole process. They will either not log anything, or log without a prefix, or log using their previously defined prefix. See the list of exclusions in the bottom. - I fixed all the tests that failed because of this change. These were failing because they were expecting the old prefix to be in the generated logs. I have changed those to expect the new `package` field instead. This might not be a great solution. Ideally we might want to remove this from the tests so they only test for relevant fields in the logs. but this is a problem for another day. - Please run the node with this config, and mention if you see something weird in the logs. (use different verbosities) - The CI workflow uses a script that basically runs the `hack/gen-logs.sh` and checks that the git diff is zero. that script is `hack/check-logs.sh`. This means that if one runs this script locally, it will not actually _check_ anything, rather than just regenerate the log.go files and fix any mistake. This might be confusing. Please suggest solutions if you think it's a problem. --- **A list of packages that used a different prefix than their package names for their logs:** - beacon-chain/cache/depositsnapshot/ package depositsnapshot, prefix "cache" - beacon-chain/core/transition/log.go — package transition, prefix "state" - beacon-chain/db/kv/log.go — package kv, prefix "db" - beacon-chain/db/slasherkv/log.go — package slasherkv, prefix "slasherdb" - beacon-chain/db/pruner/pruner.go — package pruner, prefix "db-pruner" - beacon-chain/light-client/log.go — package light_client, prefix "light-client" - beacon-chain/operations/attestations/log.go — package attestations, prefix "pool/attestations" - beacon-chain/operations/slashings/log.go — package slashings, prefix "pool/slashings" - beacon-chain/rpc/core/log.go — package core, prefix "rpc/core" - beacon-chain/rpc/eth/beacon/log.go — package beacon, prefix "rpc/beaconv1" - beacon-chain/rpc/eth/validator/log.go — package validator, prefix "beacon-api" - beacon-chain/rpc/prysm/v1alpha1/beacon/log.go — package beacon, prefix "rpc" - beacon-chain/rpc/prysm/v1alpha1/validator/log.go — package validator, prefix "rpc/validator" - beacon-chain/state/stategen/log.go — package stategen, prefix "state-gen" - beacon-chain/sync/checkpoint/log.go — package checkpoint, prefix "checkpoint-sync" - beacon-chain/sync/initial-sync/log.go — package initialsync, prefix "initial-sync" - cmd/prysmctl/p2p/log.go — package p2p, prefix "prysmctl-p2p" - config/features/log.go -- package features, prefix "flags" - io/file/log.go — package file, prefix "fileutil" - proto/prysm/v1alpha1/log.go — package eth, prefix "protobuf" - validator/client/beacon-api/log.go — package beacon_api, prefix "beacon-api" - validator/db/kv/log.go — package kv, prefix "db" - validator/db/filesystem/db.go — package filesystem, prefix "db" - validator/keymanager/derived/log.go — package derived, prefix "derived-keymanager" - validator/keymanager/local/log.go — package local, prefix "local-keymanager" - validator/keymanager/remote-web3signer/log.go — package remote_web3signer, prefix "remote-keymanager" - validator/keymanager/remote-web3signer/internal/log.go — package internal, prefix "remote-web3signer- internal" - beacon-chain/forkchoice/doubly... prefix is "forkchoice-doublylinkedtree" **List of excluded directories (their subdirectories are also excluded):** ``` EXCLUDED_PATH_PREFIXES=( "testing" "validator/client/testutil" "beacon-chain/p2p/testing" "beacon-chain/rpc/eth/config" "beacon-chain/rpc/prysm/v1alpha1/debug" "tools" "runtime" "monitoring" "io" "cmd" ".well-known" "changelog" "hack" "specrefs" "third_party" "bazel-out" "bazel-bin" "bazel-prysm" "bazel-testlogs" "build" ".github" ".jj" ".idea" ".vscode" ) ```
664 lines
26 KiB
Go
664 lines
26 KiB
Go
package helpers
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/binary"
|
|
|
|
"github.com/OffchainLabs/prysm/v7/beacon-chain/cache"
|
|
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/time"
|
|
forkchoicetypes "github.com/OffchainLabs/prysm/v7/beacon-chain/forkchoice/types"
|
|
"github.com/OffchainLabs/prysm/v7/beacon-chain/state"
|
|
fieldparams "github.com/OffchainLabs/prysm/v7/config/fieldparams"
|
|
"github.com/OffchainLabs/prysm/v7/config/params"
|
|
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
|
|
"github.com/OffchainLabs/prysm/v7/crypto/hash"
|
|
"github.com/OffchainLabs/prysm/v7/encoding/bytesutil"
|
|
"github.com/OffchainLabs/prysm/v7/monitoring/tracing/trace"
|
|
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
|
|
"github.com/OffchainLabs/prysm/v7/runtime/version"
|
|
"github.com/OffchainLabs/prysm/v7/time/slots"
|
|
"github.com/pkg/errors"
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
"github.com/prometheus/client_golang/prometheus/promauto"
|
|
)
|
|
|
|
var (
|
|
CommitteeCacheInProgressHit = promauto.NewCounter(prometheus.CounterOpts{
|
|
Name: "committee_cache_in_progress_hit",
|
|
Help: "The number of committee requests that are present in the cache.",
|
|
})
|
|
|
|
errProposerIndexMiss = errors.New("propoposer index not found in cache")
|
|
)
|
|
|
|
// IsActiveValidator returns the boolean value on whether the validator
|
|
// is active or not.
|
|
//
|
|
// Spec pseudocode definition:
|
|
//
|
|
// def is_active_validator(validator: Validator, epoch: Epoch) -> bool:
|
|
// """
|
|
// Check if ``validator`` is active.
|
|
// """
|
|
// return validator.activation_epoch <= epoch < validator.exit_epoch
|
|
func IsActiveValidator(validator *ethpb.Validator, epoch primitives.Epoch) bool {
|
|
return checkValidatorActiveStatus(validator.ActivationEpoch, validator.ExitEpoch, epoch)
|
|
}
|
|
|
|
// IsActiveValidatorUsingTrie checks if a read only validator is active.
|
|
func IsActiveValidatorUsingTrie(validator state.ReadOnlyValidator, epoch primitives.Epoch) bool {
|
|
return checkValidatorActiveStatus(validator.ActivationEpoch(), validator.ExitEpoch(), epoch)
|
|
}
|
|
|
|
// IsActiveNonSlashedValidatorUsingTrie checks if a read only validator is active and not slashed
|
|
func IsActiveNonSlashedValidatorUsingTrie(validator state.ReadOnlyValidator, epoch primitives.Epoch) bool {
|
|
active := checkValidatorActiveStatus(validator.ActivationEpoch(), validator.ExitEpoch(), epoch)
|
|
return active && !validator.Slashed()
|
|
}
|
|
|
|
func checkValidatorActiveStatus(activationEpoch, exitEpoch, epoch primitives.Epoch) bool {
|
|
return activationEpoch <= epoch && epoch < exitEpoch
|
|
}
|
|
|
|
// IsSlashableValidator returns the boolean value on whether the validator
|
|
// is slashable or not.
|
|
//
|
|
// Spec pseudocode definition:
|
|
//
|
|
// def is_slashable_validator(validator: Validator, epoch: Epoch) -> bool:
|
|
// """
|
|
// Check if ``validator`` is slashable.
|
|
// """
|
|
// return (not validator.slashed) and (validator.activation_epoch <= epoch < validator.withdrawable_epoch)
|
|
func IsSlashableValidator(activationEpoch, withdrawableEpoch primitives.Epoch, slashed bool, epoch primitives.Epoch) bool {
|
|
return checkValidatorSlashable(activationEpoch, withdrawableEpoch, slashed, epoch)
|
|
}
|
|
|
|
// IsSlashableValidatorUsingTrie checks if a read only validator is slashable.
|
|
func IsSlashableValidatorUsingTrie(val state.ReadOnlyValidator, epoch primitives.Epoch) bool {
|
|
return checkValidatorSlashable(val.ActivationEpoch(), val.WithdrawableEpoch(), val.Slashed(), epoch)
|
|
}
|
|
|
|
func checkValidatorSlashable(activationEpoch, withdrawableEpoch primitives.Epoch, slashed bool, epoch primitives.Epoch) bool {
|
|
active := activationEpoch <= epoch
|
|
beforeWithdrawable := epoch < withdrawableEpoch
|
|
return beforeWithdrawable && active && !slashed
|
|
}
|
|
|
|
// ActiveValidatorIndices filters out active validators based on validator status
|
|
// and returns their indices in a list.
|
|
//
|
|
// WARNING: This method allocates a new copy of the validator index set and is
|
|
// considered to be very memory expensive. Avoid using this unless you really
|
|
// need the active validator indices for some specific reason.
|
|
//
|
|
// Spec pseudocode definition:
|
|
//
|
|
// def get_active_validator_indices(state: BeaconState, epoch: Epoch) -> Sequence[ValidatorIndex]:
|
|
// """
|
|
// Return the sequence of active validator indices at ``epoch``.
|
|
// """
|
|
// return [ValidatorIndex(i) for i, v in enumerate(state.validators) if is_active_validator(v, epoch)]
|
|
func ActiveValidatorIndices(ctx context.Context, s state.ReadOnlyBeaconState, epoch primitives.Epoch) ([]primitives.ValidatorIndex, error) {
|
|
ctx, span := trace.StartSpan(ctx, "helpers.ActiveValidatorIndices")
|
|
defer span.End()
|
|
|
|
seed, err := Seed(s, epoch, params.BeaconConfig().DomainBeaconAttester)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "could not get seed")
|
|
}
|
|
activeIndices, err := committeeCache.ActiveIndices(ctx, seed)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "could not interface with committee cache")
|
|
}
|
|
if activeIndices != nil {
|
|
return activeIndices, nil
|
|
}
|
|
|
|
if err := committeeCache.MarkInProgress(seed); err != nil {
|
|
if errors.Is(err, cache.ErrAlreadyInProgress) {
|
|
activeIndices, err := committeeCache.ActiveIndices(ctx, seed)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if activeIndices == nil {
|
|
return nil, errors.New("nil active indices")
|
|
}
|
|
CommitteeCacheInProgressHit.Inc()
|
|
return activeIndices, nil
|
|
}
|
|
return nil, errors.Wrap(err, "could not mark committee cache as in progress")
|
|
}
|
|
defer func() {
|
|
if err := committeeCache.MarkNotInProgress(seed); err != nil {
|
|
log.WithError(err).Error("Could not mark cache not in progress")
|
|
}
|
|
}()
|
|
|
|
var indices []primitives.ValidatorIndex
|
|
if err := s.ReadFromEveryValidator(func(idx int, val state.ReadOnlyValidator) error {
|
|
if IsActiveValidatorUsingTrie(val, epoch) {
|
|
indices = append(indices, primitives.ValidatorIndex(idx))
|
|
}
|
|
return nil
|
|
}); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(indices) == 0 {
|
|
return nil, errors.New("no active validator indices")
|
|
}
|
|
|
|
if err := UpdateCommitteeCache(ctx, s, epoch); err != nil {
|
|
log.WithError(err).Error("Could not update committee cache")
|
|
}
|
|
|
|
return indices, nil
|
|
}
|
|
|
|
// ActiveValidatorCount returns the number of active validators in the state
|
|
// at the given epoch.
|
|
func ActiveValidatorCount(ctx context.Context, s state.ReadOnlyBeaconState, epoch primitives.Epoch) (uint64, error) {
|
|
seed, err := Seed(s, epoch, params.BeaconConfig().DomainBeaconAttester)
|
|
if err != nil {
|
|
return 0, errors.Wrap(err, "could not get seed")
|
|
}
|
|
activeCount, err := committeeCache.ActiveIndicesCount(ctx, seed)
|
|
if err != nil {
|
|
return 0, errors.Wrap(err, "could not interface with committee cache")
|
|
}
|
|
if activeCount != 0 && s.Slot() != 0 {
|
|
return uint64(activeCount), nil
|
|
}
|
|
|
|
if err := committeeCache.MarkInProgress(seed); err != nil {
|
|
if errors.Is(err, cache.ErrAlreadyInProgress) {
|
|
activeCount, err := committeeCache.ActiveIndicesCount(ctx, seed)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
CommitteeCacheInProgressHit.Inc()
|
|
return uint64(activeCount), nil
|
|
}
|
|
return 0, errors.Wrap(err, "could not mark committee cache as in progress")
|
|
}
|
|
defer func() {
|
|
if err := committeeCache.MarkNotInProgress(seed); err != nil {
|
|
log.WithError(err).Error("Could not mark cache not in progress")
|
|
}
|
|
}()
|
|
|
|
count := uint64(0)
|
|
if err := s.ReadFromEveryValidator(func(idx int, val state.ReadOnlyValidator) error {
|
|
if IsActiveValidatorUsingTrie(val, epoch) {
|
|
count++
|
|
}
|
|
return nil
|
|
}); err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
if err := UpdateCommitteeCache(ctx, s, epoch); err != nil {
|
|
return 0, errors.Wrap(err, "could not update committee cache")
|
|
}
|
|
|
|
return count, nil
|
|
}
|
|
|
|
// ActivationExitEpoch takes in epoch number and returns when
|
|
// the validator is eligible for activation and exit.
|
|
//
|
|
// Spec pseudocode definition:
|
|
//
|
|
// def compute_activation_exit_epoch(epoch: Epoch) -> Epoch:
|
|
// """
|
|
// Return the epoch during which validator activations and exits initiated in ``epoch`` take effect.
|
|
// """
|
|
// return Epoch(epoch + 1 + MAX_SEED_LOOKAHEAD)
|
|
func ActivationExitEpoch(epoch primitives.Epoch) primitives.Epoch {
|
|
return epoch + 1 + params.BeaconConfig().MaxSeedLookahead
|
|
}
|
|
|
|
// calculateChurnLimit based on the formula in the spec.
|
|
//
|
|
// def get_validator_churn_limit(state: BeaconState) -> uint64:
|
|
// """
|
|
// Return the validator churn limit for the current epoch.
|
|
// """
|
|
// active_validator_indices = get_active_validator_indices(state, get_current_epoch(state))
|
|
// return max(MIN_PER_EPOCH_CHURN_LIMIT, uint64(len(active_validator_indices)) // CHURN_LIMIT_QUOTIENT)
|
|
func calculateChurnLimit(activeValidatorCount uint64) uint64 {
|
|
churnLimit := activeValidatorCount / params.BeaconConfig().ChurnLimitQuotient
|
|
if churnLimit < params.BeaconConfig().MinPerEpochChurnLimit {
|
|
return params.BeaconConfig().MinPerEpochChurnLimit
|
|
}
|
|
return churnLimit
|
|
}
|
|
|
|
// ValidatorActivationChurnLimit returns the maximum number of validators that can be activated in a slot.
|
|
func ValidatorActivationChurnLimit(activeValidatorCount uint64) uint64 {
|
|
return calculateChurnLimit(activeValidatorCount)
|
|
}
|
|
|
|
// ValidatorExitChurnLimit returns the maximum number of validators that can be exited in a slot.
|
|
func ValidatorExitChurnLimit(activeValidatorCount uint64) uint64 {
|
|
return calculateChurnLimit(activeValidatorCount)
|
|
}
|
|
|
|
// ValidatorActivationChurnLimitDeneb returns the maximum number of validators that can be activated in a slot post Deneb.
|
|
func ValidatorActivationChurnLimitDeneb(activeValidatorCount uint64) uint64 {
|
|
limit := calculateChurnLimit(activeValidatorCount)
|
|
// New in Deneb.
|
|
if limit > params.BeaconConfig().MaxPerEpochActivationChurnLimit {
|
|
return params.BeaconConfig().MaxPerEpochActivationChurnLimit
|
|
}
|
|
return limit
|
|
}
|
|
|
|
// BeaconProposerIndex returns proposer index of a current slot.
|
|
//
|
|
// Spec pseudocode definition:
|
|
//
|
|
// def get_beacon_proposer_index(state: BeaconState) -> ValidatorIndex:
|
|
// """
|
|
// Return the beacon proposer index at the current slot.
|
|
// """
|
|
// epoch = get_current_epoch(state)
|
|
// seed = hash(get_seed(state, epoch, DOMAIN_BEACON_PROPOSER) + uint_to_bytes(state.slot))
|
|
// indices = get_active_validator_indices(state, epoch)
|
|
// return compute_proposer_index(state, indices, seed)
|
|
func BeaconProposerIndex(ctx context.Context, state state.ReadOnlyBeaconState) (primitives.ValidatorIndex, error) {
|
|
return BeaconProposerIndexAtSlot(ctx, state, state.Slot())
|
|
}
|
|
|
|
// cachedProposerIndexAtSlot returns the proposer index at the given slot from
|
|
// the cache at the given root key.
|
|
func cachedProposerIndexAtSlot(slot primitives.Slot, root [32]byte) (primitives.ValidatorIndex, error) {
|
|
proposerIndices, has := proposerIndicesCache.ProposerIndices(slots.ToEpoch(slot), root)
|
|
if !has {
|
|
return 0, errProposerIndexMiss
|
|
}
|
|
if len(proposerIndices) != int(params.BeaconConfig().SlotsPerEpoch) {
|
|
return 0, errProposerIndexMiss
|
|
}
|
|
return proposerIndices[slot%params.BeaconConfig().SlotsPerEpoch], nil
|
|
}
|
|
|
|
// ProposerIndexAtSlotFromCheckpoint returns the proposer index at the given
|
|
// slot from the cache at the given checkpoint
|
|
func ProposerIndexAtSlotFromCheckpoint(c *forkchoicetypes.Checkpoint, slot primitives.Slot) (primitives.ValidatorIndex, error) {
|
|
proposerIndices, has := proposerIndicesCache.IndicesFromCheckpoint(*c)
|
|
if !has {
|
|
return 0, errProposerIndexMiss
|
|
}
|
|
if len(proposerIndices) != int(params.BeaconConfig().SlotsPerEpoch) {
|
|
return 0, errProposerIndexMiss
|
|
}
|
|
return proposerIndices[slot%params.BeaconConfig().SlotsPerEpoch], nil
|
|
}
|
|
|
|
func beaconProposerIndexAtSlotFulu(state state.ReadOnlyBeaconState, slot primitives.Slot) (primitives.ValidatorIndex, error) {
|
|
e := slots.ToEpoch(slot)
|
|
stateEpoch := slots.ToEpoch(state.Slot())
|
|
if e < stateEpoch || e > stateEpoch+1 {
|
|
return 0, errors.Errorf("slot %d is not in the current epoch %d or the next epoch", slot, stateEpoch)
|
|
}
|
|
lookAhead, err := state.ProposerLookahead()
|
|
if err != nil {
|
|
return 0, errors.Wrap(err, "could not get proposer lookahead")
|
|
}
|
|
spe := params.BeaconConfig().SlotsPerEpoch
|
|
if e == stateEpoch {
|
|
return lookAhead[slot%spe], nil
|
|
}
|
|
// The caller is requesting the proposer for the next epoch
|
|
return lookAhead[spe+slot%spe], nil
|
|
}
|
|
|
|
// BeaconProposerIndexAtSlot returns proposer index at the given slot from the
|
|
// point of view of the given state as head state
|
|
func BeaconProposerIndexAtSlot(ctx context.Context, state state.ReadOnlyBeaconState, slot primitives.Slot) (primitives.ValidatorIndex, error) {
|
|
e := slots.ToEpoch(slot)
|
|
stateEpoch := slots.ToEpoch(state.Slot())
|
|
// Even if the state is post Fulu, we may request a past proposer index.
|
|
if state.Version() >= version.Fulu && e >= params.BeaconConfig().FuluForkEpoch {
|
|
// We can use the cached lookahead only for the current and the next epoch.
|
|
if e == stateEpoch || e == stateEpoch+1 {
|
|
return beaconProposerIndexAtSlotFulu(state, slot)
|
|
}
|
|
}
|
|
// 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)
|
|
// For simplicity, the node will skip caching of genesis epoch. If the passed state has not yet reached this slot then we do not check the cache.
|
|
if e <= stateEpoch && e > params.BeaconConfig().GenesisEpoch+params.BeaconConfig().MinSeedLookahead {
|
|
s, err := slots.EpochEnd(e - 1)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
r, err := StateRootAtSlot(state, s)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
if r != nil && !bytes.Equal(r, params.BeaconConfig().ZeroHash[:]) {
|
|
pid, err := cachedProposerIndexAtSlot(slot, [32]byte(r))
|
|
if err == nil {
|
|
return pid, nil
|
|
}
|
|
if err := UpdateProposerIndicesInCache(ctx, state, e); err != nil {
|
|
return 0, errors.Wrap(err, "could not update proposer index cache")
|
|
}
|
|
pid, err = cachedProposerIndexAtSlot(slot, [32]byte(r))
|
|
if err == nil {
|
|
return pid, nil
|
|
}
|
|
}
|
|
}
|
|
|
|
seed, err := Seed(state, e, params.BeaconConfig().DomainBeaconProposer)
|
|
if err != nil {
|
|
return 0, errors.Wrap(err, "could not generate seed")
|
|
}
|
|
|
|
seedWithSlot := append(seed[:], bytesutil.Bytes8(uint64(slot))...)
|
|
seedWithSlotHash := hash.Hash(seedWithSlot)
|
|
|
|
indices, err := ActiveValidatorIndices(ctx, state, e)
|
|
if err != nil {
|
|
return 0, errors.Wrap(err, "could not get active indices")
|
|
}
|
|
|
|
return ComputeProposerIndex(state, indices, seedWithSlotHash)
|
|
}
|
|
|
|
// ComputeProposerIndex returns the index sampled by effective balance, which is used to calculate proposer.
|
|
//
|
|
// nolint:dupword
|
|
// Spec pseudocode definition:
|
|
//
|
|
// def compute_proposer_index(state: BeaconState, indices: Sequence[ValidatorIndex], seed: Bytes32) -> ValidatorIndex:
|
|
// """
|
|
// Return from ``indices`` a random index sampled by effective balance.
|
|
// """
|
|
// assert len(indices) > 0
|
|
// MAX_RANDOM_VALUE = 2**16 - 1 # [Modified in Electra]
|
|
// i = uint64(0)
|
|
// total = uint64(len(indices))
|
|
// while True:
|
|
// candidate_index = indices[compute_shuffled_index(i % total, total, seed)]
|
|
// # [Modified in Electra]
|
|
// random_bytes = hash(seed + uint_to_bytes(i // 16))
|
|
// offset = i % 16 * 2
|
|
// random_value = bytes_to_uint64(random_bytes[offset:offset + 2])
|
|
// effective_balance = state.validators[candidate_index].effective_balance
|
|
// # [Modified in Electra:EIP7251]
|
|
// if effective_balance * MAX_RANDOM_VALUE >= MAX_EFFECTIVE_BALANCE_ELECTRA * random_value:
|
|
// return candidate_index
|
|
// i += 1
|
|
func ComputeProposerIndex(bState state.ReadOnlyBeaconState, activeIndices []primitives.ValidatorIndex, seed [32]byte) (primitives.ValidatorIndex, error) {
|
|
length := uint64(len(activeIndices))
|
|
if length == 0 {
|
|
return 0, errors.New("empty active indices list")
|
|
}
|
|
hashFunc := hash.CustomSHA256Hasher()
|
|
cfg := params.BeaconConfig()
|
|
seedBuffer := make([]byte, len(seed)+8)
|
|
copy(seedBuffer, seed[:])
|
|
|
|
for i := uint64(0); ; i++ {
|
|
candidateIndex, err := ComputeShuffledIndex(primitives.ValidatorIndex(i%length), length, seed, true /* shuffle */)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
candidateIndex = activeIndices[candidateIndex]
|
|
if uint64(candidateIndex) >= uint64(bState.NumValidators()) {
|
|
return 0, errors.New("active index out of range")
|
|
}
|
|
|
|
v, err := bState.ValidatorAtIndexReadOnly(candidateIndex)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
effectiveBal := v.EffectiveBalance()
|
|
if bState.Version() >= version.Electra {
|
|
binary.LittleEndian.PutUint64(seedBuffer[len(seed):], i/16)
|
|
randomBytes := hashFunc(seedBuffer)
|
|
offset := (i % 16) * 2
|
|
randomValue := uint64(randomBytes[offset]) | uint64(randomBytes[offset+1])<<8
|
|
|
|
if effectiveBal*fieldparams.MaxRandomValueElectra >= cfg.MaxEffectiveBalanceElectra*randomValue {
|
|
return candidateIndex, nil
|
|
}
|
|
} else {
|
|
binary.LittleEndian.PutUint64(seedBuffer[len(seed):], i/32)
|
|
randomByte := hashFunc(seedBuffer)[i%32]
|
|
|
|
if effectiveBal*fieldparams.MaxRandomByte >= cfg.MaxEffectiveBalance*uint64(randomByte) {
|
|
return candidateIndex, nil
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// IsEligibleForActivationQueue checks if the validator is eligible to
|
|
// be placed into the activation queue.
|
|
//
|
|
// Spec definition:
|
|
//
|
|
// def is_eligible_for_activation_queue(validator: Validator) -> bool:
|
|
// """
|
|
// Check if ``validator`` is eligible to be placed into the activation queue.
|
|
// """
|
|
// return (
|
|
// validator.activation_eligibility_epoch == FAR_FUTURE_EPOCH
|
|
// and validator.effective_balance >= MIN_ACTIVATION_BALANCE # [Modified in Electra:EIP7251]
|
|
// )
|
|
func IsEligibleForActivationQueue(validator state.ReadOnlyValidator, currentEpoch primitives.Epoch) bool {
|
|
if currentEpoch >= params.BeaconConfig().ElectraForkEpoch {
|
|
return isEligibleForActivationQueueElectra(validator.ActivationEligibilityEpoch(), validator.EffectiveBalance())
|
|
}
|
|
return isEligibleForActivationQueue(validator.ActivationEligibilityEpoch(), validator.EffectiveBalance())
|
|
}
|
|
|
|
// isEligibleForActivationQueue carries out the logic for IsEligibleForActivationQueue
|
|
// Spec pseudocode definition:
|
|
//
|
|
// def is_eligible_for_activation_queue(validator: Validator) -> bool:
|
|
// """
|
|
// Check if ``validator`` is eligible to be placed into the activation queue.
|
|
// """
|
|
// return (
|
|
// validator.activation_eligibility_epoch == FAR_FUTURE_EPOCH
|
|
// and validator.effective_balance == MAX_EFFECTIVE_BALANCE
|
|
// )
|
|
func isEligibleForActivationQueue(activationEligibilityEpoch primitives.Epoch, effectiveBalance uint64) bool {
|
|
return activationEligibilityEpoch == params.BeaconConfig().FarFutureEpoch &&
|
|
effectiveBalance == params.BeaconConfig().MaxEffectiveBalance
|
|
}
|
|
|
|
// IsEligibleForActivationQueue checks if the validator is eligible to
|
|
// be placed into the activation queue.
|
|
//
|
|
// Spec definition:
|
|
//
|
|
// def is_eligible_for_activation_queue(validator: Validator) -> bool:
|
|
// """
|
|
// Check if ``validator`` is eligible to be placed into the activation queue.
|
|
// """
|
|
// return (
|
|
// validator.activation_eligibility_epoch == FAR_FUTURE_EPOCH
|
|
// and validator.effective_balance >= MIN_ACTIVATION_BALANCE # [Modified in Electra:EIP7251]
|
|
// )
|
|
func isEligibleForActivationQueueElectra(activationEligibilityEpoch primitives.Epoch, effectiveBalance uint64) bool {
|
|
return activationEligibilityEpoch == params.BeaconConfig().FarFutureEpoch &&
|
|
effectiveBalance >= params.BeaconConfig().MinActivationBalance
|
|
}
|
|
|
|
// IsEligibleForActivation checks if the validator is eligible for activation.
|
|
//
|
|
// Spec pseudocode definition:
|
|
//
|
|
// def is_eligible_for_activation(state: BeaconState, validator: Validator) -> bool:
|
|
// """
|
|
// Check if ``validator`` is eligible for activation.
|
|
// """
|
|
// return (
|
|
// # Placement in queue is finalized
|
|
// validator.activation_eligibility_epoch <= state.finalized_checkpoint.epoch
|
|
// # Has not yet been activated
|
|
// and validator.activation_epoch == FAR_FUTURE_EPOCH
|
|
// )
|
|
func IsEligibleForActivation(state state.ReadOnlyCheckpoint, validator *ethpb.Validator) bool {
|
|
finalizedEpoch := state.FinalizedCheckpointEpoch()
|
|
return isEligibleForActivation(validator.ActivationEligibilityEpoch, validator.ActivationEpoch, finalizedEpoch)
|
|
}
|
|
|
|
// IsEligibleForActivationUsingROVal checks if the validator is eligible for activation using the provided read only validator.
|
|
func IsEligibleForActivationUsingROVal(state state.ReadOnlyCheckpoint, validator state.ReadOnlyValidator) bool {
|
|
return isEligibleForActivation(validator.ActivationEligibilityEpoch(), validator.ActivationEpoch(), state.FinalizedCheckpointEpoch())
|
|
}
|
|
|
|
// isEligibleForActivation carries out the logic for IsEligibleForActivation*
|
|
func isEligibleForActivation(activationEligibilityEpoch, activationEpoch, finalizedEpoch primitives.Epoch) bool {
|
|
return activationEligibilityEpoch <= finalizedEpoch &&
|
|
activationEpoch == params.BeaconConfig().FarFutureEpoch
|
|
}
|
|
|
|
// LastActivatedValidatorIndex provides the last activated validator given a state
|
|
func LastActivatedValidatorIndex(ctx context.Context, st state.ReadOnlyBeaconState) (primitives.ValidatorIndex, error) {
|
|
_, span := trace.StartSpan(ctx, "helpers.LastActivatedValidatorIndex")
|
|
defer span.End()
|
|
var lastActivatedvalidatorIndex primitives.ValidatorIndex
|
|
// linear search because status are not sorted
|
|
for j := st.NumValidators() - 1; j >= 0; j-- {
|
|
val, err := st.ValidatorAtIndexReadOnly(primitives.ValidatorIndex(j))
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
if IsActiveValidatorUsingTrie(val, time.CurrentEpoch(st)) {
|
|
lastActivatedvalidatorIndex = primitives.ValidatorIndex(j)
|
|
break
|
|
}
|
|
}
|
|
return lastActivatedvalidatorIndex, nil
|
|
}
|
|
|
|
// IsSameWithdrawalCredentials returns true if both validators have the same withdrawal credentials.
|
|
//
|
|
// return a.withdrawal_credentials[12:] == b.withdrawal_credentials[12:]
|
|
func IsSameWithdrawalCredentials(a, b *ethpb.Validator) bool {
|
|
if a == nil || b == nil {
|
|
return false
|
|
}
|
|
if len(a.WithdrawalCredentials) <= 12 || len(b.WithdrawalCredentials) <= 12 {
|
|
return false
|
|
}
|
|
return bytes.Equal(a.WithdrawalCredentials[12:], b.WithdrawalCredentials[12:])
|
|
}
|
|
|
|
// IsFullyWithdrawableValidator returns whether the validator is able to perform a full
|
|
// withdrawal. This function assumes that the caller holds a lock on the state.
|
|
//
|
|
// Spec definition:
|
|
//
|
|
// def is_fully_withdrawable_validator(validator: Validator, balance: Gwei, epoch: Epoch) -> bool:
|
|
// """
|
|
// Check if ``validator`` is fully withdrawable.
|
|
// """
|
|
// return (
|
|
// has_execution_withdrawal_credential(validator) # [Modified in Electra:EIP7251]
|
|
// and validator.withdrawable_epoch <= epoch
|
|
// and balance > 0
|
|
// )
|
|
func IsFullyWithdrawableValidator(val state.ReadOnlyValidator, balance uint64, epoch primitives.Epoch, fork int) bool {
|
|
if val == nil || balance <= 0 {
|
|
return false
|
|
}
|
|
|
|
// Electra / EIP-7251 logic
|
|
if fork >= version.Electra {
|
|
return val.HasExecutionWithdrawalCredentials() && val.WithdrawableEpoch() <= epoch
|
|
}
|
|
|
|
return val.HasETH1WithdrawalCredentials() && val.WithdrawableEpoch() <= epoch
|
|
}
|
|
|
|
// IsPartiallyWithdrawableValidator returns whether the validator is able to perform a
|
|
// partial withdrawal. This function assumes that the caller has a lock on the state.
|
|
// This method conditionally calls the fork appropriate implementation based on the epoch argument.
|
|
func IsPartiallyWithdrawableValidator(val state.ReadOnlyValidator, balance uint64, epoch primitives.Epoch, fork int) bool {
|
|
if val == nil {
|
|
return false
|
|
}
|
|
|
|
if fork < version.Electra {
|
|
return isPartiallyWithdrawableValidatorCapella(val, balance, epoch)
|
|
}
|
|
|
|
return isPartiallyWithdrawableValidatorElectra(val, balance, epoch)
|
|
}
|
|
|
|
// isPartiallyWithdrawableValidatorElectra implements is_partially_withdrawable_validator in the
|
|
// electra fork.
|
|
//
|
|
// Spec definition:
|
|
//
|
|
// def is_partially_withdrawable_validator(validator: Validator, balance: Gwei) -> bool:
|
|
//
|
|
// """
|
|
// Check if ``validator`` is partially withdrawable.
|
|
// """
|
|
// max_effective_balance = get_max_effective_balance(validator)
|
|
// has_max_effective_balance = validator.effective_balance == max_effective_balance # [Modified in Electra:EIP7251]
|
|
// has_excess_balance = balance > max_effective_balance # [Modified in Electra:EIP7251]
|
|
// return (
|
|
// has_execution_withdrawal_credential(validator) # [Modified in Electra:EIP7251]
|
|
// and has_max_effective_balance
|
|
// and has_excess_balance
|
|
// )
|
|
func isPartiallyWithdrawableValidatorElectra(val state.ReadOnlyValidator, balance uint64, epoch primitives.Epoch) bool {
|
|
maxEB := ValidatorMaxEffectiveBalance(val)
|
|
hasMaxBalance := val.EffectiveBalance() == maxEB
|
|
hasExcessBalance := balance > maxEB
|
|
|
|
return val.HasExecutionWithdrawalCredentials() &&
|
|
hasMaxBalance &&
|
|
hasExcessBalance
|
|
}
|
|
|
|
// isPartiallyWithdrawableValidatorCapella implements is_partially_withdrawable_validator in the
|
|
// capella fork.
|
|
//
|
|
// Spec definition:
|
|
//
|
|
// def is_partially_withdrawable_validator(validator: Validator, balance: Gwei) -> bool:
|
|
// """
|
|
// Check if ``validator`` is partially withdrawable.
|
|
// """
|
|
// has_max_effective_balance = validator.effective_balance == MAX_EFFECTIVE_BALANCE
|
|
// has_excess_balance = balance > MAX_EFFECTIVE_BALANCE
|
|
// return has_eth1_withdrawal_credential(validator) and has_max_effective_balance and has_excess_balance
|
|
func isPartiallyWithdrawableValidatorCapella(val state.ReadOnlyValidator, balance uint64, epoch primitives.Epoch) bool {
|
|
hasMaxBalance := val.EffectiveBalance() == params.BeaconConfig().MaxEffectiveBalance
|
|
hasExcessBalance := balance > params.BeaconConfig().MaxEffectiveBalance
|
|
return val.HasETH1WithdrawalCredentials() && hasExcessBalance && hasMaxBalance
|
|
}
|
|
|
|
// ValidatorMaxEffectiveBalance returns the maximum effective balance for a validator.
|
|
//
|
|
// Spec definition:
|
|
//
|
|
// def get_max_effective_balance(validator: Validator) -> Gwei:
|
|
// """
|
|
// Get max effective balance for ``validator``.
|
|
// """
|
|
// if has_compounding_withdrawal_credential(validator):
|
|
// return MAX_EFFECTIVE_BALANCE_ELECTRA
|
|
// else:
|
|
// return MIN_ACTIVATION_BALANCE
|
|
func ValidatorMaxEffectiveBalance(val state.ReadOnlyValidator) uint64 {
|
|
if val.HasCompoundingWithdrawalCredentials() {
|
|
return params.BeaconConfig().MaxEffectiveBalanceElectra
|
|
}
|
|
return params.BeaconConfig().MinActivationBalance
|
|
}
|