diff --git a/operator/duties/attester.go b/operator/duties/attester.go index 396ba70cd2..d9a6ec3d01 100644 --- a/operator/duties/attester.go +++ b/operator/duties/attester.go @@ -276,6 +276,23 @@ func (h *AttesterHandler) toGenesisSpecDuty(duty *eth2apiv1.AttesterDuty, role g func (h *AttesterHandler) shouldExecute(duty *eth2apiv1.AttesterDuty) bool { currentSlot := h.network.Beacon.EstimatedCurrentSlot() + currentEpoch := h.network.Beacon.EstimatedEpochAtSlot(currentSlot) + + v, exists := h.validatorProvider.Validator(duty.PubKey[:]) + if !exists { + h.logger.Warn("validator not found", fields.Validator(duty.PubKey[:])) + return false + } + + if v.MinParticipationEpoch() > currentEpoch { + h.logger.Debug("validator not yet participating", + fields.Validator(duty.PubKey[:]), + zap.Uint64("min_participation_epoch", uint64(v.MinParticipationEpoch())), + zap.Uint64("current_epoch", uint64(currentEpoch)), + ) + return false + } + // execute task if slot already began and not pass 1 epoch var attestationPropagationSlotRange = phase0.Slot(h.network.Beacon.SlotsPerEpoch()) if currentSlot >= duty.Slot && currentSlot-duty.Slot <= attestationPropagationSlotRange { diff --git a/operator/duties/scheduler.go b/operator/duties/scheduler.go index 2c42b4c268..7a77faa322 100644 --- a/operator/duties/scheduler.go +++ b/operator/duties/scheduler.go @@ -84,6 +84,7 @@ type ExecutionClient interface { type ValidatorProvider interface { ParticipatingValidators(epoch phase0.Epoch) []*types.SSVShare SelfParticipatingValidators(epoch phase0.Epoch) []*types.SSVShare + Validator(pubKey []byte) (*types.SSVShare, bool) } // ValidatorController represents the component that controls validators via the scheduler diff --git a/operator/duties/sync_committee.go b/operator/duties/sync_committee.go index 13f2776e16..d09fc43567 100644 --- a/operator/duties/sync_committee.go +++ b/operator/duties/sync_committee.go @@ -297,6 +297,23 @@ func (h *SyncCommitteeHandler) toSpecDuty(duty *eth2apiv1.SyncCommitteeDuty, slo func (h *SyncCommitteeHandler) shouldExecute(duty *eth2apiv1.SyncCommitteeDuty, slot phase0.Slot) bool { currentSlot := h.network.Beacon.EstimatedCurrentSlot() + currentEpoch := h.network.Beacon.EstimatedEpochAtSlot(currentSlot) + + v, exists := h.validatorProvider.Validator(duty.PubKey[:]) + if !exists { + h.logger.Warn("validator not found", fields.Validator(duty.PubKey[:])) + return false + } + + if v.MinParticipationEpoch() > currentEpoch { + h.logger.Debug("validator not yet participating", + fields.Validator(duty.PubKey[:]), + zap.Uint64("min_participation_epoch", uint64(v.MinParticipationEpoch())), + zap.Uint64("current_epoch", uint64(currentEpoch)), + ) + return false + } + // execute task if slot already began and not pass 1 slot if currentSlot == slot { return true diff --git a/protocol/v2/types/ssvshare.go b/protocol/v2/types/ssvshare.go index c0f1f4fd45..d79b2bd02d 100644 --- a/protocol/v2/types/ssvshare.go +++ b/protocol/v2/types/ssvshare.go @@ -59,8 +59,7 @@ func (s *SSVShare) IsAttesting(epoch phase0.Epoch) bool { } func (s *SSVShare) IsParticipating(epoch phase0.Epoch) bool { - participating := s.minParticipationEpoch == 0 || s.minParticipationEpoch <= epoch - return !s.Liquidated && s.IsAttesting(epoch) && participating + return !s.Liquidated && s.IsAttesting(epoch) } func (s *SSVShare) SetFeeRecipient(feeRecipient bellatrix.ExecutionAddress) { @@ -90,6 +89,10 @@ func (s *SSVShare) SetMinParticipationEpoch(epoch phase0.Epoch) { s.minParticipationEpoch = epoch } +func (s *SSVShare) MinParticipationEpoch() phase0.Epoch { + return s.minParticipationEpoch +} + func (s *SSVShare) OperatorIDs() []spectypes.OperatorID { ids := make([]spectypes.OperatorID, len(s.Committee)) for i, v := range s.Committee {