prysm-pulse/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_builder.go
terencechain b8a1bcdfe3
Circuit breaker: lower max builder epoch missed slots to 5 (#12076)
* Circuit breaker: lower max builder epoch missed slots to 5

* Change equality to >=

* Fix tests

---------

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2023-03-05 19:10:41 +00:00

93 lines
3.3 KiB
Go

package validator
import (
"context"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v3/beacon-chain/db/kv"
"github.com/prysmaticlabs/prysm/v3/config/params"
"github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
"github.com/sirupsen/logrus"
)
// Returns true if builder (ie outsourcing block construction) can be used. Both conditions have to meet:
// - Validator has registered to use builder (ie called registerBuilder API end point)
// - Circuit breaker has not been activated (ie the liveness of the chain is healthy)
func (vs *Server) canUseBuilder(ctx context.Context, slot primitives.Slot, idx primitives.ValidatorIndex) (bool, error) {
activated, err := vs.circuitBreakBuilder(slot)
if err != nil {
return false, err
}
if activated {
return false, nil
}
return vs.validatorRegistered(ctx, idx)
}
// validatorRegistered returns true if validator with index `id` was previously registered in the database.
func (vs *Server) validatorRegistered(ctx context.Context, id primitives.ValidatorIndex) (bool, error) {
if vs.BeaconDB == nil {
return false, errors.New("nil beacon db")
}
_, err := vs.BeaconDB.RegistrationByValidatorID(ctx, id)
switch {
case errors.Is(err, kv.ErrNotFoundFeeRecipient):
return false, nil
case err != nil:
return false, err
}
return true, nil
}
// circuitBreakBuilder returns true if the builder is not allowed to be used due to circuit breaker conditions.
func (vs *Server) circuitBreakBuilder(s primitives.Slot) (bool, error) {
if vs.ForkFetcher == nil || vs.ForkFetcher.ForkChoicer() == nil {
return true, errors.New("no fork choicer configured")
}
vs.ForkFetcher.ForkChoicer().RLock()
defer vs.ForkFetcher.ForkChoicer().RUnlock()
// Circuit breaker is active if the missing consecutive slots greater than `MaxBuilderConsecutiveMissedSlots`.
highestReceivedSlot := vs.ForkFetcher.ForkChoicer().HighestReceivedBlockSlot()
maxConsecutiveSkipSlotsAllowed := params.BeaconConfig().MaxBuilderConsecutiveMissedSlots
diff, err := s.SafeSubSlot(highestReceivedSlot)
if err != nil {
return true, err
}
if diff >= maxConsecutiveSkipSlotsAllowed {
log.WithFields(logrus.Fields{
"currentSlot": s,
"highestReceivedSlot": highestReceivedSlot,
"maxConsecutiveSkipSlotsAllowed": maxConsecutiveSkipSlotsAllowed,
}).Warn("Circuit breaker activated due to missing consecutive slot. Ignore if mev-boost is not used")
return true, nil
}
// Not much reason to check missed slots epoch rolling window if input slot is less than epoch.
if s < params.BeaconConfig().SlotsPerEpoch {
return false, nil
}
// Circuit breaker is active if the missing slots per epoch (rolling window) greater than `MaxBuilderEpochMissedSlots`.
receivedCount, err := vs.ForkFetcher.ForkChoicer().ReceivedBlocksLastEpoch()
if err != nil {
return true, err
}
maxEpochSkipSlotsAllowed := params.BeaconConfig().MaxBuilderEpochMissedSlots
diff, err = params.BeaconConfig().SlotsPerEpoch.SafeSub(receivedCount)
if err != nil {
return true, err
}
if diff >= maxEpochSkipSlotsAllowed {
log.WithFields(logrus.Fields{
"totalMissed": diff,
"maxEpochSkipSlotsAllowed": maxEpochSkipSlotsAllowed,
}).Warn("Circuit breaker activated due to missing enough slots last epoch. Ignore if mev-boost is not used")
return true, nil
}
return false, nil
}