From adece068819ac78c292212c17dae2d1ccbc43980 Mon Sep 17 00:00:00 2001 From: olegshmuelov <45327364+olegshmuelov@users.noreply.github.com> Date: Wed, 11 Sep 2024 13:32:30 +0300 Subject: [PATCH 01/14] draft --- logging/fields/fields.go | 15 +- operator/duties/attester.go | 163 ++--- operator/duties/attester_test.go | 967 ------------------------- operator/duties/committee.go | 140 ++-- operator/duties/committee_test.go | 46 +- operator/duties/dutystore/duties.go | 2 +- operator/duties/proposer.go | 51 +- operator/duties/scheduler.go | 13 +- operator/duties/sync_committee.go | 121 ++-- operator/duties/sync_committee_test.go | 674 ----------------- 10 files changed, 244 insertions(+), 1948 deletions(-) delete mode 100644 operator/duties/attester_test.go delete mode 100644 operator/duties/sync_committee_test.go diff --git a/logging/fields/fields.go b/logging/fields/fields.go index 3592e565b0..d72f1325c1 100644 --- a/logging/fields/fields.go +++ b/logging/fields/fields.go @@ -81,9 +81,9 @@ const ( FieldRole = "role" FieldRound = "round" FieldSlot = "slot" + FieldSlotTickerID = "slot_ticker_id" FieldStartTimeUnixMilli = "start_time_unix_milli" FieldSubmissionTime = "submission_time" - FieldTotalConsensusTime = "total_consensus_time" FieldSubnets = "subnets" FieldSyncOffset = "sync_offset" FieldSyncResults = "sync_results" @@ -91,6 +91,7 @@ const ( FieldToBlock = "to_block" FieldTook = "took" FieldTopic = "topic" + FieldTotalConsensusTime = "total_consensus_time" FieldTxHash = "tx_hash" FieldType = "type" FieldUpdatedENRLocalNode = "updated_enr" @@ -408,3 +409,15 @@ func Type(v any) zapcore.Field { func FormatDuration(val time.Duration) string { return strconv.FormatFloat(val.Seconds(), 'f', 5, 64) } + +func FormatSlotTickerID(epoch phase0.Epoch, slot phase0.Slot) string { + return fmt.Sprintf("e%v-s%v-#%v", epoch, slot, slot%32+1) +} + +func FormatSlotTickerCommitteeID(period uint64, epoch phase0.Epoch, slot phase0.Slot) string { + return fmt.Sprintf("p%v-%s", period, FormatSlotTickerID(epoch, slot)) +} + +func SlotTickerID(val string) zap.Field { + return zap.String(FieldSlotTickerID, val) +} diff --git a/operator/duties/attester.go b/operator/duties/attester.go index cc3971f9ea..746f61d572 100644 --- a/operator/duties/attester.go +++ b/operator/duties/attester.go @@ -35,31 +35,7 @@ func (h *AttesterHandler) Name() string { return spectypes.BNRoleAttester.String() } -// HandleDuties manages the duty lifecycle, handling different cases: -// -// On First Run: -// 1. Fetch duties for the current epoch. -// 2. If necessary, fetch duties for the next epoch. -// 3. Execute duties. -// -// On Re-org: -// -// If the previous dependent root changed: -// 1. Fetch duties for the current epoch. -// 2. Execute duties. -// If the current dependent root changed: -// 1. Execute duties. -// 2. If necessary, fetch duties for the next epoch. -// -// On Indices Change: -// 1. Execute duties. -// 2. ResetEpoch duties for the current epoch. -// 3. Fetch duties for the current epoch. -// 4. If necessary, fetch duties for the next epoch. -// -// On Ticker event: -// 1. Execute duties. -// 2. If necessary, fetch duties for the next epoch. +// HandleDuties manages the duty lifecycle func (h *AttesterHandler) HandleDuties(ctx context.Context) { h.logger.Info("starting duty handler") defer h.logger.Info("duty handler exited") @@ -75,67 +51,38 @@ func (h *AttesterHandler) HandleDuties(ctx context.Context) { case <-next: slot := h.ticker.Slot() next = h.ticker.Next() - currentEpoch := h.network.Beacon.EstimatedEpochAtSlot(slot) - buildStr := fmt.Sprintf("e%v-s%v-#%v", currentEpoch, slot, slot%32+1) - h.logger.Debug("🛠 ticker event", zap.String("epoch_slot_pos", buildStr)) - - h.processExecution(currentEpoch, slot) - if h.indicesChanged { - h.duties.ResetEpoch(currentEpoch) - h.indicesChanged = false - } - h.processFetching(ctx, currentEpoch, slot) - - slotsPerEpoch := h.network.Beacon.SlotsPerEpoch() - - // If we have reached the mid-point of the epoch, fetch the duties for the next epoch in the next slot. - // This allows us to set them up at a time when the beacon node should be less busy. - if uint64(slot)%slotsPerEpoch == slotsPerEpoch/2-1 { - h.fetchNextEpoch = true - } - - // last slot of epoch - if uint64(slot)%slotsPerEpoch == slotsPerEpoch-1 { - h.duties.ResetEpoch(currentEpoch - 1) + epoch := h.network.Beacon.EstimatedEpochAtSlot(slot) + tickerID := fields.FormatSlotTickerID(epoch, slot) + h.logger.Debug("🛠 ticker event", fields.SlotTickerID(tickerID)) + + if !h.network.PastAlanForkAtEpoch(epoch) { + h.processExecution(epoch, slot) + if h.indicesChanged { + h.duties.Reset(epoch) + h.indicesChanged = false + } + h.processFetching(ctx, epoch, slot) + h.processSlotTransition(epoch, slot) } case reorgEvent := <-h.reorg: - currentEpoch := h.network.Beacon.EstimatedEpochAtSlot(reorgEvent.Slot) - buildStr := fmt.Sprintf("e%v-s%v-#%v", currentEpoch, reorgEvent.Slot, reorgEvent.Slot%32+1) - h.logger.Info("🔀 reorg event received", zap.String("epoch_slot_pos", buildStr), zap.Any("event", reorgEvent)) - - // reset current epoch duties - if reorgEvent.Previous { - h.duties.ResetEpoch(currentEpoch) - h.fetchCurrentEpoch = true - if h.shouldFetchNexEpoch(reorgEvent.Slot) { - h.duties.ResetEpoch(currentEpoch + 1) - h.fetchNextEpoch = true - } + epoch := h.network.Beacon.EstimatedEpochAtSlot(reorgEvent.Slot) + tickerID := fields.FormatSlotTickerID(epoch, reorgEvent.Slot) + h.logger.Info("🔀 reorg event received", fields.SlotTickerID(tickerID), zap.Any("event", reorgEvent)) - h.processFetching(ctx, currentEpoch, reorgEvent.Slot) - } else if reorgEvent.Current { - // reset & re-fetch next epoch duties if in appropriate slot range, - // otherwise they will be fetched by the appropriate slot tick. - if h.shouldFetchNexEpoch(reorgEvent.Slot) { - h.duties.ResetEpoch(currentEpoch + 1) - h.fetchNextEpoch = true - } + if !h.network.PastAlanForkAtEpoch(epoch) { + h.processReorg(ctx, epoch, reorgEvent) } case <-h.indicesChange: slot := h.network.Beacon.EstimatedCurrentSlot() - currentEpoch := h.network.Beacon.EstimatedEpochAtSlot(slot) - buildStr := fmt.Sprintf("e%v-s%v-#%v", currentEpoch, slot, slot%32+1) - h.logger.Info("🔁 indices change received", zap.String("epoch_slot_pos", buildStr)) - - h.indicesChanged = true - h.fetchCurrentEpoch = true + epoch := h.network.Beacon.EstimatedEpochAtSlot(slot) + tickerID := fields.FormatSlotTickerID(epoch, slot) + h.logger.Info("🔁 indices change received", fields.SlotTickerID(tickerID)) - // reset next epoch duties if in appropriate slot range - if h.shouldFetchNexEpoch(slot) { - h.duties.ResetEpoch(currentEpoch + 1) - h.fetchNextEpoch = true + if !h.network.PastAlanForkAtEpoch(epoch) { + h.indicesChanged = true + h.processIndicesChange(epoch, slot) } } } @@ -177,27 +124,61 @@ func (h *AttesterHandler) processExecution(epoch phase0.Epoch, slot phase0.Slot) return } - if !h.network.PastAlanForkAtEpoch(h.network.Beacon.EstimatedEpochAtSlot(slot)) { - toExecute := make([]*genesisspectypes.Duty, 0, len(duties)*2) - for _, d := range duties { - if h.shouldExecute(d) { - toExecute = append(toExecute, h.toGenesisSpecDuty(d, genesisspectypes.BNRoleAttester)) - toExecute = append(toExecute, h.toGenesisSpecDuty(d, genesisspectypes.BNRoleAggregator)) - } + toExecute := make([]*genesisspectypes.Duty, 0, len(duties)*2) + for _, d := range duties { + if h.shouldExecute(d) { + toExecute = append(toExecute, h.toGenesisSpecDuty(d, genesisspectypes.BNRoleAttester)) + toExecute = append(toExecute, h.toGenesisSpecDuty(d, genesisspectypes.BNRoleAggregator)) } + } - h.dutiesExecutor.ExecuteGenesisDuties(h.logger, toExecute) - return + h.dutiesExecutor.ExecuteGenesisDuties(h.logger, toExecute) +} + +func (h *AttesterHandler) processIndicesChange(epoch phase0.Epoch, slot phase0.Slot) { + h.fetchCurrentEpoch = true + + // reset next epoch duties if in appropriate slot range + if h.shouldFetchNexEpoch(slot) { + h.duties.Reset(epoch + 1) + h.fetchNextEpoch = true } +} - toExecute := make([]*spectypes.ValidatorDuty, 0, len(duties)) - for _, d := range duties { - if h.shouldExecute(d) { - toExecute = append(toExecute, h.toSpecDuty(d, spectypes.BNRoleAggregator)) +func (h *AttesterHandler) processReorg(ctx context.Context, epoch phase0.Epoch, reorgEvent ReorgEvent) { + // reset current epoch duties + if reorgEvent.Previous { + h.duties.Reset(epoch) + h.fetchCurrentEpoch = true + if h.shouldFetchNexEpoch(reorgEvent.Slot) { + h.duties.Reset(epoch + 1) + h.fetchNextEpoch = true + } + + h.processFetching(ctx, epoch, reorgEvent.Slot) + } else if reorgEvent.Current { + // reset & re-fetch next epoch duties if in appropriate slot range, + // otherwise they will be fetched by the appropriate slot tick. + if h.shouldFetchNexEpoch(reorgEvent.Slot) { + h.duties.Reset(epoch + 1) + h.fetchNextEpoch = true } } +} + +func (h *AttesterHandler) processSlotTransition(epoch phase0.Epoch, slot phase0.Slot) { + slotsPerEpoch := h.network.Beacon.SlotsPerEpoch() - h.dutiesExecutor.ExecuteDuties(h.logger, toExecute) + // If we have reached the mid-point of the epoch, fetch the duties for the next epoch in the next slot. + // This allows us to set them up at a time when the beacon node should be less busy. + if uint64(slot)%slotsPerEpoch == slotsPerEpoch/2-1 { + h.fetchNextEpoch = true + } + + // last slot of epoch + if uint64(slot)%slotsPerEpoch == slotsPerEpoch-1 { + h.duties.Reset(epoch - 1) + } } func (h *AttesterHandler) fetchAndProcessDuties(ctx context.Context, epoch phase0.Epoch) error { diff --git a/operator/duties/attester_test.go b/operator/duties/attester_test.go deleted file mode 100644 index cd89fcdce6..0000000000 --- a/operator/duties/attester_test.go +++ /dev/null @@ -1,967 +0,0 @@ -package duties - -import ( - "context" - "testing" - "time" - - eth2apiv1 "github.com/attestantio/go-eth2-client/api/v1" - "github.com/attestantio/go-eth2-client/spec/phase0" - spectypes "github.com/ssvlabs/ssv-spec/types" - "github.com/stretchr/testify/require" - "go.uber.org/mock/gomock" - - "github.com/ssvlabs/ssv/utils/hashmap" - - "github.com/ssvlabs/ssv/operator/duties/dutystore" - "github.com/ssvlabs/ssv/protocol/v2/types" -) - -func setupAttesterDutiesMock( - s *Scheduler, - dutiesMap *hashmap.Map[phase0.Epoch, []*eth2apiv1.AttesterDuty], - waitForDuties *SafeValue[bool], -) (chan struct{}, chan []*spectypes.ValidatorDuty) { - fetchDutiesCall := make(chan struct{}) - executeDutiesCall := make(chan []*spectypes.ValidatorDuty) - - s.beaconNode.(*MockBeaconNode).EXPECT().AttesterDuties(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn( - func(ctx context.Context, epoch phase0.Epoch, indices []phase0.ValidatorIndex) ([]*eth2apiv1.AttesterDuty, error) { - if waitForDuties.Get() { - fetchDutiesCall <- struct{}{} - } - duties, _ := dutiesMap.Get(epoch) - return duties, nil - }).AnyTimes() - - getShares := func(epoch phase0.Epoch) []*types.SSVShare { - uniqueIndices := make(map[phase0.ValidatorIndex]bool) - - duties, _ := dutiesMap.Get(epoch) - for _, d := range duties { - uniqueIndices[d.ValidatorIndex] = true - } - - shares := make([]*types.SSVShare, 0, len(uniqueIndices)) - for index := range uniqueIndices { - share := &types.SSVShare{ - Share: spectypes.Share{ - ValidatorIndex: index, - }, - } - shares = append(shares, share) - } - - return shares - } - s.validatorProvider.(*MockValidatorProvider).EXPECT().SelfParticipatingValidators(gomock.Any()).DoAndReturn(getShares).AnyTimes() - s.validatorProvider.(*MockValidatorProvider).EXPECT().ParticipatingValidators(gomock.Any()).DoAndReturn(getShares).AnyTimes() - - s.beaconNode.(*MockBeaconNode).EXPECT().SubmitBeaconCommitteeSubscriptions(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() - - return fetchDutiesCall, executeDutiesCall -} - -func expectedExecutedAttesterDuties(handler *AttesterHandler, duties []*eth2apiv1.AttesterDuty) []*spectypes.ValidatorDuty { - expectedDuties := make([]*spectypes.ValidatorDuty, 0) - for _, d := range duties { - expectedDuties = append(expectedDuties, handler.toSpecDuty(d, spectypes.BNRoleAggregator)) - } - return expectedDuties -} - -func TestScheduler_Attester_Same_Slot(t *testing.T) { - var ( - handler = NewAttesterHandler(dutystore.NewDuties[eth2apiv1.AttesterDuty]()) - currentSlot = &SafeValue[phase0.Slot]{} - dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() - waitForDuties = &SafeValue[bool]{} - forkEpoch = phase0.Epoch(0) - ) - dutiesMap.Set(phase0.Epoch(0), []*eth2apiv1.AttesterDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - Slot: phase0.Slot(1), - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - currentSlot.Set(phase0.Slot(1)) - - scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) - fetchDutiesCall, executeDutiesCall := setupAttesterDutiesMock(scheduler, dutiesMap, waitForDuties) - startFn() - - duties, _ := dutiesMap.Get(phase0.Epoch(0)) - expected := expectedExecutedAttesterDuties(handler, duties) - setExecuteDutyFunc(scheduler, executeDutiesCall, len(expected)) - - ticker.Send(currentSlot.Get()) - waitForDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - - // Stop scheduler & wait for graceful exit. - cancel() - require.NoError(t, schedulerPool.Wait()) -} - -func TestScheduler_Attester_Diff_Slots(t *testing.T) { - var ( - handler = NewAttesterHandler(dutystore.NewDuties[eth2apiv1.AttesterDuty]()) - currentSlot = &SafeValue[phase0.Slot]{} - dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() - waitForDuties = &SafeValue[bool]{} - forkEpoch = phase0.Epoch(0) - ) - dutiesMap.Set(phase0.Epoch(0), []*eth2apiv1.AttesterDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - Slot: phase0.Slot(2), - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - currentSlot.Set(phase0.Slot(0)) - - scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) - fetchDutiesCall, executeDutiesCall := setupAttesterDutiesMock(scheduler, dutiesMap, waitForDuties) - startFn() - - ticker.Send(currentSlot.Get()) - waitForNoAction(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - currentSlot.Set(phase0.Slot(1)) - ticker.Send(currentSlot.Get()) - waitForNoAction(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - currentSlot.Set(phase0.Slot(2)) - duties, _ := dutiesMap.Get(phase0.Epoch(0)) - expected := expectedExecutedAttesterDuties(handler, duties) - setExecuteDutyFunc(scheduler, executeDutiesCall, len(expected)) - - ticker.Send(currentSlot.Get()) - waitForDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - - // Stop scheduler & wait for graceful exit. - cancel() - require.NoError(t, schedulerPool.Wait()) -} - -func TestScheduler_Attester_Indices_Changed(t *testing.T) { - var ( - handler = NewAttesterHandler(dutystore.NewDuties[eth2apiv1.AttesterDuty]()) - currentSlot = &SafeValue[phase0.Slot]{} - dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() - waitForDuties = &SafeValue[bool]{} - forkEpoch = phase0.Epoch(0) - ) - currentSlot.Set(phase0.Slot(0)) - scheduler, logger, mockTicker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) - fetchDutiesCall, executeDutiesCall := setupAttesterDutiesMock(scheduler, dutiesMap, waitForDuties) - startFn() - - // STEP 1: wait for no action to be taken - mockTicker.Send(currentSlot.Get()) - waitForNoAction(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 2: trigger a change in active indices - scheduler.indicesChg <- struct{}{} - // no execution should happen in slot 0 - waitForNoAction(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - dutiesMap.Set(phase0.Epoch(0), []*eth2apiv1.AttesterDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - Slot: phase0.Slot(0), - ValidatorIndex: phase0.ValidatorIndex(1), - }, - { - PubKey: phase0.BLSPubKey{1, 2, 4}, - Slot: phase0.Slot(1), - ValidatorIndex: phase0.ValidatorIndex(2), - }, - { - PubKey: phase0.BLSPubKey{1, 2, 5}, - Slot: phase0.Slot(2), - ValidatorIndex: phase0.ValidatorIndex(3), - }, - }) - - // STEP 3: wait for attester duties to be fetched again - currentSlot.Set(phase0.Slot(1)) - waitForDuties.Set(true) - mockTicker.Send(currentSlot.Get()) - waitForDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - // no execution should happen in slot 1 - waitForNoAction(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 4: wait for attester duties to be executed - currentSlot.Set(phase0.Slot(2)) - duties, _ := dutiesMap.Get(phase0.Epoch(0)) - expected := expectedExecutedAttesterDuties(handler, []*eth2apiv1.AttesterDuty{duties[2]}) - setExecuteDutyFunc(scheduler, executeDutiesCall, len(expected)) - - mockTicker.Send(currentSlot.Get()) - waitForDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - - // Stop scheduler & wait for graceful exit. - cancel() - require.NoError(t, schedulerPool.Wait()) -} - -func TestScheduler_Attester_Multiple_Indices_Changed_Same_Slot(t *testing.T) { - var ( - handler = NewAttesterHandler(dutystore.NewDuties[eth2apiv1.AttesterDuty]()) - currentSlot = &SafeValue[phase0.Slot]{} - dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() - waitForDuties = &SafeValue[bool]{} - forkEpoch = phase0.Epoch(0) - ) - currentSlot.Set(phase0.Slot(0)) - scheduler, logger, mockTicker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) - fetchDutiesCall, executeDutiesCall := setupAttesterDutiesMock(scheduler, dutiesMap, waitForDuties) - startFn() - - // STEP 1: wait for no action to be taken - mockTicker.Send(currentSlot.Get()) - waitForNoAction(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 2: wait for no action to be taken - currentSlot.Set(phase0.Slot(1)) - mockTicker.Send(currentSlot.Get()) - waitForNoAction(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 3: trigger a change in active indices - scheduler.indicesChg <- struct{}{} - duties, _ := dutiesMap.Get(phase0.Epoch(0)) - dutiesMap.Set(phase0.Epoch(0), append(duties, ð2apiv1.AttesterDuty{ - PubKey: phase0.BLSPubKey{1, 2, 3}, - Slot: phase0.Slot(3), - ValidatorIndex: phase0.ValidatorIndex(1), - })) - waitForNoAction(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 4: trigger a change in active indices in the same slot - scheduler.indicesChg <- struct{}{} - duties, _ = dutiesMap.Get(phase0.Epoch(0)) - dutiesMap.Set(phase0.Epoch(0), append(duties, ð2apiv1.AttesterDuty{ - PubKey: phase0.BLSPubKey{1, 2, 4}, - Slot: phase0.Slot(4), - ValidatorIndex: phase0.ValidatorIndex(2), - })) - waitForNoAction(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 5: wait for attester duties to be fetched - currentSlot.Set(phase0.Slot(2)) - waitForDuties.Set(true) - mockTicker.Send(currentSlot.Get()) - waitForDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 6: wait for attester duties to be executed - currentSlot.Set(phase0.Slot(3)) - duties, _ = dutiesMap.Get(phase0.Epoch(0)) - expected := expectedExecutedAttesterDuties(handler, []*eth2apiv1.AttesterDuty{duties[0]}) - setExecuteDutyFunc(scheduler, executeDutiesCall, len(expected)) - - mockTicker.Send(currentSlot.Get()) - waitForDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - - // STEP 7: wait for attester duties to be executed - currentSlot.Set(phase0.Slot(4)) - duties, _ = dutiesMap.Get(phase0.Epoch(0)) - expected = expectedExecutedAttesterDuties(handler, []*eth2apiv1.AttesterDuty{duties[1]}) - setExecuteDutyFunc(scheduler, executeDutiesCall, len(expected)) - - mockTicker.Send(currentSlot.Get()) - waitForDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - - // Stop scheduler & wait for graceful exit. - cancel() - require.NoError(t, schedulerPool.Wait()) -} - -// reorg previous dependent root changed -func TestScheduler_Attester_Reorg_Previous_Epoch_Transition(t *testing.T) { - var ( - handler = NewAttesterHandler(dutystore.NewDuties[eth2apiv1.AttesterDuty]()) - currentSlot = &SafeValue[phase0.Slot]{} - dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() - waitForDuties = &SafeValue[bool]{} - forkEpoch = phase0.Epoch(0) - ) - currentSlot.Set(phase0.Slot(63)) - scheduler, logger, mockTicker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) - fetchDutiesCall, executeDutiesCall := setupAttesterDutiesMock(scheduler, dutiesMap, waitForDuties) - startFn() - - dutiesMap.Set(phase0.Epoch(2), []*eth2apiv1.AttesterDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - Slot: phase0.Slot(66), - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - - // STEP 1: wait for attester duties to be fetched for next epoch - waitForDuties.Set(true) - mockTicker.Send(currentSlot.Get()) - waitForDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 2: trigger head event - e := ð2apiv1.Event{ - Data: ð2apiv1.HeadEvent{ - Slot: currentSlot.Get(), - CurrentDutyDependentRoot: phase0.Root{0x01}, - PreviousDutyDependentRoot: phase0.Root{0x01}, - }, - } - scheduler.HandleHeadEvent(logger)(e) - waitForNoAction(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 3: Ticker with no action - currentSlot.Set(phase0.Slot(64)) - mockTicker.Send(currentSlot.Get()) - waitForNoAction(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 4: trigger reorg on epoch transition - e = ð2apiv1.Event{ - Data: ð2apiv1.HeadEvent{ - Slot: currentSlot.Get(), - PreviousDutyDependentRoot: phase0.Root{0x02}, - }, - } - dutiesMap.Set(phase0.Epoch(2), []*eth2apiv1.AttesterDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - Slot: phase0.Slot(67), - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - scheduler.HandleHeadEvent(logger)(e) - waitForDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 5: wait for attester duties to be fetched again for the current epoch - currentSlot.Set(phase0.Slot(65)) - mockTicker.Send(currentSlot.Get()) - waitForNoAction(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 6: The first assigned duty should not be executed - currentSlot.Set(phase0.Slot(66)) - mockTicker.Send(currentSlot.Get()) - waitForNoAction(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 7: The second assigned duty should be executed - currentSlot.Set(phase0.Slot(67)) - duties, _ := dutiesMap.Get(phase0.Epoch(2)) - expected := expectedExecutedAttesterDuties(handler, duties) - setExecuteDutyFunc(scheduler, executeDutiesCall, len(expected)) - - mockTicker.Send(currentSlot.Get()) - waitForDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - - // Stop scheduler & wait for graceful exit. - cancel() - require.NoError(t, schedulerPool.Wait()) -} - -// reorg previous dependent root changed and the indices changed as well -func TestScheduler_Attester_Reorg_Previous_Epoch_Transition_Indices_Changed(t *testing.T) { - var ( - handler = NewAttesterHandler(dutystore.NewDuties[eth2apiv1.AttesterDuty]()) - currentSlot = &SafeValue[phase0.Slot]{} - dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() - waitForDuties = &SafeValue[bool]{} - forkEpoch = phase0.Epoch(0) - ) - currentSlot.Set(phase0.Slot(63)) - scheduler, logger, mockTicker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) - fetchDutiesCall, executeDutiesCall := setupAttesterDutiesMock(scheduler, dutiesMap, waitForDuties) - startFn() - - dutiesMap.Set(phase0.Epoch(2), []*eth2apiv1.AttesterDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - Slot: phase0.Slot(66), - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - - // STEP 1: wait for attester duties to be fetched for next epoch - mockTicker.Send(currentSlot.Get()) - waitForDuties.Set(true) - waitForDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - waitForNoAction(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 2: trigger head event - e := ð2apiv1.Event{ - Data: ð2apiv1.HeadEvent{ - Slot: currentSlot.Get(), - CurrentDutyDependentRoot: phase0.Root{0x01}, - PreviousDutyDependentRoot: phase0.Root{0x01}, - }, - } - scheduler.HandleHeadEvent(logger)(e) - waitForNoAction(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 3: Ticker with no action - currentSlot.Set(phase0.Slot(64)) - mockTicker.Send(currentSlot.Get()) - waitForNoAction(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 4: trigger reorg on epoch transition - e = ð2apiv1.Event{ - Data: ð2apiv1.HeadEvent{ - Slot: currentSlot.Get(), - PreviousDutyDependentRoot: phase0.Root{0x02}, - }, - } - dutiesMap.Set(phase0.Epoch(2), []*eth2apiv1.AttesterDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - Slot: phase0.Slot(67), - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - scheduler.HandleHeadEvent(logger)(e) - waitForDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 5: trigger indices change - scheduler.indicesChg <- struct{}{} - duties, _ := dutiesMap.Get(phase0.Epoch(2)) - dutiesMap.Set(phase0.Epoch(2), append(duties, ð2apiv1.AttesterDuty{ - PubKey: phase0.BLSPubKey{1, 2, 4}, - Slot: phase0.Slot(67), - ValidatorIndex: phase0.ValidatorIndex(2), - })) - waitForNoAction(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 6: wait for attester duties to be fetched again for the current epoch - currentSlot.Set(phase0.Slot(65)) - mockTicker.Send(currentSlot.Get()) - waitForDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 7: The first assigned duty should not be executed - currentSlot.Set(phase0.Slot(66)) - mockTicker.Send(currentSlot.Get()) - waitForNoAction(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 8: The second assigned duty should be executed - currentSlot.Set(phase0.Slot(67)) - duties, _ = dutiesMap.Get(phase0.Epoch(2)) - expected := expectedExecutedAttesterDuties(handler, duties) - setExecuteDutyFunc(scheduler, executeDutiesCall, len(expected)) - - mockTicker.Send(currentSlot.Get()) - waitForDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - - // Stop scheduler & wait for graceful exit. - cancel() - require.NoError(t, schedulerPool.Wait()) -} - -// reorg previous dependent root changed -func TestScheduler_Attester_Reorg_Previous(t *testing.T) { - var ( - handler = NewAttesterHandler(dutystore.NewDuties[eth2apiv1.AttesterDuty]()) - currentSlot = &SafeValue[phase0.Slot]{} - dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() - waitForDuties = &SafeValue[bool]{} - forkEpoch = phase0.Epoch(0) - ) - dutiesMap.Set(phase0.Epoch(1), []*eth2apiv1.AttesterDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - Slot: phase0.Slot(35), - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - currentSlot.Set(phase0.Slot(32)) - - // STEP 1: wait for attester duties to be fetched (handle initial duties) - scheduler, logger, mockTicker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) - fetchDutiesCall, executeDutiesCall := setupAttesterDutiesMock(scheduler, dutiesMap, waitForDuties) - startFn() - - mockTicker.Send(currentSlot.Get()) - waitForNoAction(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 2: trigger head event - e := ð2apiv1.Event{ - Data: ð2apiv1.HeadEvent{ - Slot: currentSlot.Get(), - PreviousDutyDependentRoot: phase0.Root{0x01}, - }, - } - scheduler.HandleHeadEvent(logger)(e) - waitForNoAction(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 3: Ticker with no action - currentSlot.Set(phase0.Slot(33)) - waitForDuties.Set(true) - mockTicker.Send(currentSlot.Get()) - waitForNoAction(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 4: trigger reorg - e = ð2apiv1.Event{ - Data: ð2apiv1.HeadEvent{ - Slot: currentSlot.Get(), - PreviousDutyDependentRoot: phase0.Root{0x02}, - }, - } - dutiesMap.Set(phase0.Epoch(1), []*eth2apiv1.AttesterDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - Slot: phase0.Slot(36), - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - scheduler.HandleHeadEvent(logger)(e) - waitForDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 5: wait for no action to be taken - currentSlot.Set(phase0.Slot(34)) - mockTicker.Send(currentSlot.Get()) - waitForNoAction(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 6: The first assigned duty should not be executed - currentSlot.Set(phase0.Slot(35)) - mockTicker.Send(currentSlot.Get()) - waitForNoAction(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 7: The second assigned duty should be executed - currentSlot.Set(phase0.Slot(36)) - duties, _ := dutiesMap.Get(phase0.Epoch(1)) - expected := expectedExecutedAttesterDuties(handler, duties) - setExecuteDutyFunc(scheduler, executeDutiesCall, len(expected)) - - mockTicker.Send(currentSlot.Get()) - waitForDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - - // Stop scheduler & wait for graceful exit. - cancel() - require.NoError(t, schedulerPool.Wait()) -} - -// reorg previous dependent root changed and the indices changed the same slot -func TestScheduler_Attester_Reorg_Previous_Indices_Change_Same_Slot(t *testing.T) { - var ( - handler = NewAttesterHandler(dutystore.NewDuties[eth2apiv1.AttesterDuty]()) - currentSlot = &SafeValue[phase0.Slot]{} - dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() - waitForDuties = &SafeValue[bool]{} - forkEpoch = phase0.Epoch(0) - ) - dutiesMap.Set(phase0.Epoch(1), []*eth2apiv1.AttesterDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - Slot: phase0.Slot(35), - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - currentSlot.Set(phase0.Slot(32)) - - // STEP 1: wait for attester duties to be fetched (handle initial duties) - scheduler, logger, mockTicker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) - fetchDutiesCall, executeDutiesCall := setupAttesterDutiesMock(scheduler, dutiesMap, waitForDuties) - startFn() - - mockTicker.Send(currentSlot.Get()) - waitForNoAction(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 2: trigger head event - e := ð2apiv1.Event{ - Data: ð2apiv1.HeadEvent{ - Slot: currentSlot.Get(), - PreviousDutyDependentRoot: phase0.Root{0x01}, - }, - } - scheduler.HandleHeadEvent(logger)(e) - waitForNoAction(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 3: Ticker with no action - currentSlot.Set(phase0.Slot(33)) - waitForDuties.Set(true) - mockTicker.Send(currentSlot.Get()) - waitForNoAction(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 4: trigger reorg - e = ð2apiv1.Event{ - Data: ð2apiv1.HeadEvent{ - Slot: currentSlot.Get(), - PreviousDutyDependentRoot: phase0.Root{0x02}, - }, - } - dutiesMap.Set(phase0.Epoch(1), []*eth2apiv1.AttesterDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - Slot: phase0.Slot(36), - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - scheduler.HandleHeadEvent(logger)(e) - waitForDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 5: trigger indices change - scheduler.indicesChg <- struct{}{} - duties, _ := dutiesMap.Get(phase0.Epoch(1)) - dutiesMap.Set(phase0.Epoch(1), append(duties, ð2apiv1.AttesterDuty{ - PubKey: phase0.BLSPubKey{1, 2, 4}, - Slot: phase0.Slot(36), - ValidatorIndex: phase0.ValidatorIndex(2), - })) - waitForNoAction(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 6: wait for attester duties to be fetched again for the current epoch - currentSlot.Set(phase0.Slot(34)) - mockTicker.Send(currentSlot.Get()) - waitForDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 7: The first assigned duty should not be executed - currentSlot.Set(phase0.Slot(35)) - mockTicker.Send(currentSlot.Get()) - waitForNoAction(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 8: The second and new from indices change assigned duties should be executed - currentSlot.Set(phase0.Slot(36)) - duties, _ = dutiesMap.Get(phase0.Epoch(1)) - expected := expectedExecutedAttesterDuties(handler, duties) - setExecuteDutyFunc(scheduler, executeDutiesCall, len(expected)) - - mockTicker.Send(currentSlot.Get()) - waitForDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - - // Stop scheduler & wait for graceful exit. - cancel() - require.NoError(t, schedulerPool.Wait()) -} - -// reorg current dependent root changed -func TestScheduler_Attester_Reorg_Current(t *testing.T) { - var ( - handler = NewAttesterHandler(dutystore.NewDuties[eth2apiv1.AttesterDuty]()) - currentSlot = &SafeValue[phase0.Slot]{} - dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() - waitForDuties = &SafeValue[bool]{} - forkEpoch = phase0.Epoch(0) - ) - currentSlot.Set(phase0.Slot(48)) - scheduler, logger, mockTicker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) - fetchDutiesCall, executeDutiesCall := setupAttesterDutiesMock(scheduler, dutiesMap, waitForDuties) - startFn() - - dutiesMap.Set(phase0.Epoch(2), []*eth2apiv1.AttesterDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - Slot: phase0.Slot(64), - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - - // STEP 1: wait for attester duties to be fetched for next epoch - waitForDuties.Set(true) - mockTicker.Send(currentSlot.Get()) - waitForDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 2: trigger head event - e := ð2apiv1.Event{ - Data: ð2apiv1.HeadEvent{ - Slot: currentSlot.Get(), - CurrentDutyDependentRoot: phase0.Root{0x01}, - }, - } - scheduler.HandleHeadEvent(logger)(e) - waitForNoAction(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 3: Ticker with no action - currentSlot.Set(phase0.Slot(49)) - mockTicker.Send(currentSlot.Get()) - waitForNoAction(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 4: trigger reorg - e = ð2apiv1.Event{ - Data: ð2apiv1.HeadEvent{ - Slot: currentSlot.Get(), - CurrentDutyDependentRoot: phase0.Root{0x02}, - }, - } - dutiesMap.Set(phase0.Epoch(2), []*eth2apiv1.AttesterDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - Slot: phase0.Slot(65), - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - scheduler.HandleHeadEvent(logger)(e) - waitForNoAction(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 5: wait for attester duties to be fetched again for the current epoch - currentSlot.Set(phase0.Slot(50)) - mockTicker.Send(currentSlot.Get()) - waitForDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 6: skip to the next epoch - currentSlot.Set(phase0.Slot(51)) - for slot := currentSlot.Get(); slot < 64; slot++ { - mockTicker.Send(slot) - waitForNoAction(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - currentSlot.Set(slot + 1) - } - - // STEP 7: The first assigned duty should not be executed - // slot = 64 - mockTicker.Send(currentSlot.Get()) - waitForNoAction(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 8: The second assigned duty should be executed - currentSlot.Set(phase0.Slot(65)) - duties, _ := dutiesMap.Get(phase0.Epoch(2)) - expected := expectedExecutedAttesterDuties(handler, duties) - setExecuteDutyFunc(scheduler, executeDutiesCall, len(expected)) - - mockTicker.Send(currentSlot.Get()) - waitForDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - - // Stop scheduler & wait for graceful exit. - cancel() - require.NoError(t, schedulerPool.Wait()) -} - -// reorg current dependent root changed including indices change in the same slot -func TestScheduler_Attester_Reorg_Current_Indices_Changed(t *testing.T) { - var ( - handler = NewAttesterHandler(dutystore.NewDuties[eth2apiv1.AttesterDuty]()) - currentSlot = &SafeValue[phase0.Slot]{} - dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() - waitForDuties = &SafeValue[bool]{} - forkEpoch = phase0.Epoch(0) - ) - currentSlot.Set(phase0.Slot(48)) - scheduler, logger, mockTicker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) - fetchDutiesCall, executeDutiesCall := setupAttesterDutiesMock(scheduler, dutiesMap, waitForDuties) - startFn() - - dutiesMap.Set(phase0.Epoch(2), []*eth2apiv1.AttesterDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - Slot: phase0.Slot(64), - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - - // STEP 1: wait for attester duties to be fetched for next epoch - waitForDuties.Set(true) - mockTicker.Send(currentSlot.Get()) - waitForDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 2: trigger head event - e := ð2apiv1.Event{ - Data: ð2apiv1.HeadEvent{ - Slot: currentSlot.Get(), - CurrentDutyDependentRoot: phase0.Root{0x01}, - }, - } - scheduler.HandleHeadEvent(logger)(e) - waitForNoAction(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 3: Ticker with no action - currentSlot.Set(phase0.Slot(49)) - mockTicker.Send(currentSlot.Get()) - waitForNoAction(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 4: trigger reorg - e = ð2apiv1.Event{ - Data: ð2apiv1.HeadEvent{ - Slot: currentSlot.Get(), - CurrentDutyDependentRoot: phase0.Root{0x02}, - }, - } - dutiesMap.Set(phase0.Epoch(2), []*eth2apiv1.AttesterDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - Slot: phase0.Slot(65), - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - scheduler.HandleHeadEvent(logger)(e) - waitForNoAction(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 5: trigger indices change - scheduler.indicesChg <- struct{}{} - duties, _ := dutiesMap.Get(phase0.Epoch(2)) - dutiesMap.Set(phase0.Epoch(2), append(duties, ð2apiv1.AttesterDuty{ - PubKey: phase0.BLSPubKey{1, 2, 4}, - Slot: phase0.Slot(65), - ValidatorIndex: phase0.ValidatorIndex(2), - })) - waitForNoAction(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 6: wait for attester duties to be fetched again for the next epoch due to indices change - currentSlot.Set(phase0.Slot(50)) - mockTicker.Send(currentSlot.Get()) - waitForDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 7: skip to the next epoch - currentSlot.Set(phase0.Slot(51)) - for slot := currentSlot.Get(); slot < 64; slot++ { - mockTicker.Send(slot) - waitForNoAction(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - currentSlot.Set(slot + 1) - } - - // STEP 8: The first assigned duty should not be executed - // slot = 64 - mockTicker.Send(currentSlot.Get()) - waitForNoAction(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 9: The second assigned duty should be executed - currentSlot.Set(phase0.Slot(65)) - duties, _ = dutiesMap.Get(phase0.Epoch(2)) - expected := expectedExecutedAttesterDuties(handler, duties) - setExecuteDutyFunc(scheduler, executeDutiesCall, len(expected)) - - mockTicker.Send(currentSlot.Get()) - waitForDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - - // Stop scheduler & wait for graceful exit. - cancel() - require.NoError(t, schedulerPool.Wait()) -} - -func TestScheduler_Attester_Early_Block(t *testing.T) { - var ( - handler = NewAttesterHandler(dutystore.NewDuties[eth2apiv1.AttesterDuty]()) - currentSlot = &SafeValue[phase0.Slot]{} - dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() - waitForDuties = &SafeValue[bool]{} - forkEpoch = phase0.Epoch(0) - ) - dutiesMap.Set(phase0.Epoch(0), []*eth2apiv1.AttesterDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - Slot: phase0.Slot(2), - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - currentSlot.Set(phase0.Slot(0)) - - // STEP 1: wait for attester duties to be fetched (handle initial duties) - scheduler, logger, mockTicker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) - fetchDutiesCall, executeDutiesCall := setupAttesterDutiesMock(scheduler, dutiesMap, waitForDuties) - startFn() - - mockTicker.Send(currentSlot.Get()) - waitForNoAction(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 2: wait for no action to be taken - currentSlot.Set(phase0.Slot(1)) - mockTicker.Send(currentSlot.Get()) - waitForNoAction(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 3: wait for attester duties to be executed faster than 1/3 of the slot duration - startTime := time.Now() - currentSlot.Set(phase0.Slot(2)) - mockTicker.Send(currentSlot.Get()) - duties, _ := dutiesMap.Get(phase0.Epoch(0)) - expected := expectedExecutedAttesterDuties(handler, duties) - setExecuteDutyFunc(scheduler, executeDutiesCall, len(expected)) - - // STEP 4: trigger head event (block arrival) - e := ð2apiv1.Event{ - Data: ð2apiv1.HeadEvent{ - Slot: currentSlot.Get(), - }, - } - scheduler.HandleHeadEvent(logger)(e) - waitForDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - require.Less(t, time.Since(startTime), scheduler.network.Beacon.SlotDurationSec()/3) - - // Stop scheduler & wait for graceful exit. - cancel() - require.NoError(t, schedulerPool.Wait()) -} - -func TestScheduler_Attester_Start_In_The_End_Of_The_Epoch(t *testing.T) { - var ( - handler = NewAttesterHandler(dutystore.NewDuties[eth2apiv1.AttesterDuty]()) - currentSlot = &SafeValue[phase0.Slot]{} - dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() - waitForDuties = &SafeValue[bool]{} - forkEpoch = phase0.Epoch(0) - ) - currentSlot.Set(phase0.Slot(31)) - scheduler, logger, mockTicker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) - fetchDutiesCall, executeDutiesCall := setupAttesterDutiesMock(scheduler, dutiesMap, waitForDuties) - startFn() - - dutiesMap.Set(phase0.Epoch(1), []*eth2apiv1.AttesterDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - Slot: phase0.Slot(32), - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - - // STEP 1: wait for attester duties to be fetched for the next epoch - waitForDuties.Set(true) - mockTicker.Send(currentSlot.Get()) - waitForDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 2: wait for attester duties to be executed - currentSlot.Set(phase0.Slot(32)) - duties, _ := dutiesMap.Get(phase0.Epoch(1)) - expected := expectedExecutedAttesterDuties(handler, duties) - setExecuteDutyFunc(scheduler, executeDutiesCall, len(expected)) - - mockTicker.Send(currentSlot.Get()) - waitForDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - - // Stop scheduler & wait for graceful exit. - cancel() - require.NoError(t, schedulerPool.Wait()) -} - -func TestScheduler_Attester_Fetch_Execute_Next_Epoch_Duty(t *testing.T) { - var ( - handler = NewAttesterHandler(dutystore.NewDuties[eth2apiv1.AttesterDuty]()) - currentSlot = &SafeValue[phase0.Slot]{} - dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() - waitForDuties = &SafeValue[bool]{} - forkEpoch = phase0.Epoch(0) - ) - currentSlot.Set(phase0.Slot(13)) - scheduler, logger, mockTicker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) - fetchDutiesCall, executeDutiesCall := setupAttesterDutiesMock(scheduler, dutiesMap, waitForDuties) - startFn() - - dutiesMap.Set(phase0.Epoch(1), []*eth2apiv1.AttesterDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - Slot: phase0.Slot(32), - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - - // STEP 1: wait for no action to be taken - mockTicker.Send(currentSlot.Get()) - waitForNoAction(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 2: wait for no action to be taken - currentSlot.Set(phase0.Slot(14)) - mockTicker.Send(currentSlot.Get()) - waitForNoAction(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 2: wait for duties to be fetched for the next epoch - currentSlot.Set(phase0.Slot(15)) - waitForDuties.Set(true) - mockTicker.Send(currentSlot.Get()) - waitForDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 3: wait for attester duties to be executed - currentSlot.Set(phase0.Slot(32)) - duties, _ := dutiesMap.Get(phase0.Epoch(1)) - expected := expectedExecutedAttesterDuties(handler, duties) - setExecuteDutyFunc(scheduler, executeDutiesCall, len(expected)) - - mockTicker.Send(currentSlot.Get()) - waitForDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - - // Stop scheduler & wait for graceful exit. - cancel() - require.NoError(t, schedulerPool.Wait()) -} diff --git a/operator/duties/committee.go b/operator/duties/committee.go index 1ada1e7a29..1c708bff53 100644 --- a/operator/duties/committee.go +++ b/operator/duties/committee.go @@ -2,14 +2,13 @@ package duties import ( "context" - "fmt" eth2apiv1 "github.com/attestantio/go-eth2-client/api/v1" "github.com/attestantio/go-eth2-client/spec/phase0" spectypes "github.com/ssvlabs/ssv-spec/types" "go.uber.org/zap" - "github.com/ssvlabs/ssv/operator/duties/dutystore" + "github.com/ssvlabs/ssv/logging/fields" ) type validatorCommitteeDutyMap map[phase0.ValidatorIndex]*committeeDuty @@ -18,8 +17,8 @@ type committeeDutiesMap map[spectypes.CommitteeID]*committeeDuty type CommitteeHandler struct { baseHandler - attDuties *dutystore.Duties[eth2apiv1.AttesterDuty] - syncDuties *dutystore.SyncCommitteeDuties + attHandler *AttesterHandler + syncHandler *SyncCommitteeHandler } type committeeDuty struct { @@ -28,17 +27,17 @@ type committeeDuty struct { operatorIDs []spectypes.OperatorID } -func NewCommitteeHandler(attDuties *dutystore.Duties[eth2apiv1.AttesterDuty], syncDuties *dutystore.SyncCommitteeDuties) *CommitteeHandler { +func NewCommitteeHandler(attHandler *AttesterHandler, syncHandler *SyncCommitteeHandler) *CommitteeHandler { h := &CommitteeHandler{ - attDuties: attDuties, - syncDuties: syncDuties, + attHandler: attHandler, + syncHandler: syncHandler, } return h } func (h *CommitteeHandler) Name() string { - return "CLUSTER" + return "COMMITTEE" } func (h *CommitteeHandler) HandleDuties(ctx context.Context) { @@ -56,37 +55,84 @@ func (h *CommitteeHandler) HandleDuties(ctx context.Context) { next = h.ticker.Next() epoch := h.network.Beacon.EstimatedEpochAtSlot(slot) period := h.network.Beacon.EstimatedSyncCommitteePeriodAtEpoch(epoch) - buildStr := fmt.Sprintf("p%v-e%v-s%v-#%v", period, epoch, slot, slot%32+1) + tickerID := fields.FormatSlotTickerCommitteeID(period, epoch, slot) if !h.network.PastAlanForkAtEpoch(epoch) { h.logger.Debug("🛠 ticker event", - zap.String("period_epoch_slot_pos", buildStr), + fields.SlotTickerID(tickerID), zap.String("status", "alan not forked yet"), ) continue } + h.logger.Debug("🛠 ticker event", fields.SlotTickerID(tickerID)) - h.logger.Debug("🛠 ticker event", zap.String("period_epoch_slot_pos", buildStr)) h.processExecution(period, epoch, slot) + if h.indicesChanged { + h.attHandler.duties.Reset(epoch) + // we should not reset sync committee duties here as it is necessary for message validation + // but this can lead to executing duties for deleted/liquidated validators + h.indicesChanged = false + } + h.processFetching(ctx, period, epoch, slot) + h.processSlotTransition(period, epoch, slot) + + case reorgEvent := <-h.reorg: + epoch := h.network.Beacon.EstimatedEpochAtSlot(reorgEvent.Slot) + period := h.network.Beacon.EstimatedSyncCommitteePeriodAtEpoch(epoch) + tickerID := fields.FormatSlotTickerCommitteeID(period, epoch, reorgEvent.Slot) + h.logger.Info("🔀 reorg event received", fields.SlotTickerID(tickerID), zap.Any("event", reorgEvent)) - case <-h.reorg: - // do nothing + h.attHandler.processReorg(ctx, epoch, reorgEvent) + h.syncHandler.processReorg(period, reorgEvent) case <-h.indicesChange: - // do nothing + slot := h.network.Beacon.EstimatedCurrentSlot() + epoch := h.network.Beacon.EstimatedEpochAtSlot(slot) + period := h.network.Beacon.EstimatedSyncCommitteePeriodAtEpoch(epoch) + tickerID := fields.FormatSlotTickerCommitteeID(period, epoch, slot) + h.logger.Info("🔁 indices change received", fields.SlotTickerID(tickerID)) + + h.indicesChanged = true + h.attHandler.processIndicesChange(epoch, slot) + h.syncHandler.processIndicesChange(period, slot) } } } func (h *CommitteeHandler) processExecution(period uint64, epoch phase0.Epoch, slot phase0.Slot) { - attDuties := h.attDuties.CommitteeSlotDuties(epoch, slot) - syncDuties := h.syncDuties.CommitteePeriodDuties(period) + attDuties := h.attHandler.duties.CommitteeSlotDuties(epoch, slot) + syncDuties := h.syncHandler.duties.CommitteePeriodDuties(period) if attDuties == nil && syncDuties == nil { return } committeeMap := h.buildCommitteeDuties(attDuties, syncDuties, epoch, slot) h.dutiesExecutor.ExecuteCommitteeDuties(h.logger, committeeMap) + + // aggregator and contribution duties + toExecute := make([]*spectypes.ValidatorDuty, 0, len(attDuties)+len(syncDuties)) + for _, d := range attDuties { + if h.attHandler.shouldExecute(d) { + toExecute = append(toExecute, h.attHandler.toSpecDuty(d, spectypes.BNRoleAggregator)) + } + } + for _, d := range syncDuties { + if h.syncHandler.shouldExecute(d, slot) { + toExecute = append(toExecute, h.syncHandler.toSpecDuty(d, slot, spectypes.BNRoleSyncCommitteeContribution)) + } + } + + h.dutiesExecutor.ExecuteDuties(h.logger, toExecute) +} + +func (h *CommitteeHandler) processFetching(ctx context.Context, period uint64, epoch phase0.Epoch, slot phase0.Slot) { + h.attHandler.processFetching(ctx, epoch, slot) + h.syncHandler.processFetching(ctx, period, slot, true) +} + +func (h *CommitteeHandler) processSlotTransition(period uint64, epoch phase0.Epoch, slot phase0.Slot) { + h.attHandler.processSlotTransition(epoch, slot) + h.syncHandler.processSlotTransition(period, slot) } func (h *CommitteeHandler) buildCommitteeDuties(attDuties []*eth2apiv1.AttesterDuty, syncDuties []*eth2apiv1.SyncCommitteeDuty, epoch phase0.Epoch, slot phase0.Slot) committeeDutiesMap { @@ -103,15 +149,15 @@ func (h *CommitteeHandler) buildCommitteeDuties(attDuties []*eth2apiv1.AttesterD } for _, d := range attDuties { - if h.shouldExecuteAtt(d) { - specDuty := h.toSpecAttDuty(d, spectypes.BNRoleAttester) + if h.attHandler.shouldExecute(d) { + specDuty := h.attHandler.toSpecDuty(d, spectypes.BNRoleAttester) h.appendBeaconDuty(validatorCommitteeMap, committeeMap, specDuty) } } for _, d := range syncDuties { - if h.shouldExecuteSync(d, slot) { - specDuty := h.toSpecSyncDuty(d, slot, spectypes.BNRoleSyncCommittee) + if h.syncHandler.shouldExecute(d, slot) { + specDuty := h.syncHandler.toSpecDuty(d, slot, spectypes.BNRoleSyncCommittee) h.appendBeaconDuty(validatorCommitteeMap, committeeMap, specDuty) } } @@ -146,57 +192,3 @@ func (h *CommitteeHandler) appendBeaconDuty(vc validatorCommitteeDutyMap, c comm cd.duty.ValidatorDuties = append(c[committee.id].duty.ValidatorDuties, beaconDuty) } - -func (h *CommitteeHandler) toSpecAttDuty(duty *eth2apiv1.AttesterDuty, role spectypes.BeaconRole) *spectypes.ValidatorDuty { - return &spectypes.ValidatorDuty{ - Type: role, - PubKey: duty.PubKey, - Slot: duty.Slot, - ValidatorIndex: duty.ValidatorIndex, - CommitteeIndex: duty.CommitteeIndex, - CommitteeLength: duty.CommitteeLength, - CommitteesAtSlot: duty.CommitteesAtSlot, - ValidatorCommitteeIndex: duty.ValidatorCommitteeIndex, - } -} - -func (h *CommitteeHandler) toSpecSyncDuty(duty *eth2apiv1.SyncCommitteeDuty, slot phase0.Slot, role spectypes.BeaconRole) *spectypes.ValidatorDuty { - indices := make([]uint64, len(duty.ValidatorSyncCommitteeIndices)) - for i, index := range duty.ValidatorSyncCommitteeIndices { - indices[i] = uint64(index) - } - return &spectypes.ValidatorDuty{ - Type: role, - PubKey: duty.PubKey, - Slot: slot, // in order for the duty scheduler to execute - ValidatorIndex: duty.ValidatorIndex, - ValidatorSyncCommitteeIndices: indices, - } -} - -func (h *CommitteeHandler) shouldExecuteAtt(duty *eth2apiv1.AttesterDuty) bool { - currentSlot := h.network.Beacon.EstimatedCurrentSlot() - // 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 { - return true - } - if currentSlot+1 == duty.Slot { - h.warnMisalignedSlotAndDuty(duty.String()) - return true - } - return false -} - -func (h *CommitteeHandler) shouldExecuteSync(duty *eth2apiv1.SyncCommitteeDuty, slot phase0.Slot) bool { - currentSlot := h.network.Beacon.EstimatedCurrentSlot() - // execute task if slot already began and not pass 1 slot - if currentSlot == slot { - return true - } - if currentSlot+1 == slot { - h.warnMisalignedSlotAndDuty(duty.String()) - return true - } - return false -} diff --git a/operator/duties/committee_test.go b/operator/duties/committee_test.go index 5c9154c6a7..8225173530 100644 --- a/operator/duties/committee_test.go +++ b/operator/duties/committee_test.go @@ -93,7 +93,7 @@ func TestScheduler_Committee_Same_Slot_Attester_Only(t *testing.T) { dutyStore = dutystore.New() attHandler = NewAttesterHandler(dutyStore.Attester) syncHandler = NewSyncCommitteeHandler(dutyStore.SyncCommittee) - commHandler = NewCommitteeHandler(dutyStore.Attester, dutyStore.SyncCommittee) + commHandler = NewCommitteeHandler(attHandler, syncHandler) alanForkEpoch = phase0.Epoch(0) currentSlot = &SafeValue[phase0.Slot]{} waitForDuties = &SafeValue[bool]{} @@ -145,7 +145,7 @@ func TestScheduler_Committee_Same_Slot_SyncCommittee_Only(t *testing.T) { dutyStore = dutystore.New() attHandler = NewAttesterHandler(dutyStore.Attester) syncHandler = NewSyncCommitteeHandler(dutyStore.SyncCommittee) - commHandler = NewCommitteeHandler(dutyStore.Attester, dutyStore.SyncCommittee) + commHandler = NewCommitteeHandler(attHandler, syncHandler) alanForkEpoch = phase0.Epoch(0) currentSlot = &SafeValue[phase0.Slot]{} waitForDuties = &SafeValue[bool]{} @@ -196,7 +196,7 @@ func TestScheduler_Committee_Same_Slot(t *testing.T) { dutyStore = dutystore.New() attHandler = NewAttesterHandler(dutyStore.Attester) syncHandler = NewSyncCommitteeHandler(dutyStore.SyncCommittee) - commHandler = NewCommitteeHandler(dutyStore.Attester, dutyStore.SyncCommittee) + commHandler = NewCommitteeHandler(attHandler, syncHandler) alanForkEpoch = phase0.Epoch(0) currentSlot = &SafeValue[phase0.Slot]{} waitForDuties = &SafeValue[bool]{} @@ -255,7 +255,7 @@ func TestScheduler_Committee_Diff_Slot_Attester_Only(t *testing.T) { dutyStore = dutystore.New() attHandler = NewAttesterHandler(dutyStore.Attester) syncHandler = NewSyncCommitteeHandler(dutyStore.SyncCommittee) - commHandler = NewCommitteeHandler(dutyStore.Attester, dutyStore.SyncCommittee) + commHandler = NewCommitteeHandler(attHandler, syncHandler) alanForkEpoch = phase0.Epoch(0) currentSlot = &SafeValue[phase0.Slot]{} waitForDuties = &SafeValue[bool]{} @@ -312,7 +312,7 @@ func TestScheduler_Committee_Indices_Changed_Attester_Only(t *testing.T) { dutyStore = dutystore.New() attHandler = NewAttesterHandler(dutyStore.Attester) syncHandler = NewSyncCommitteeHandler(dutyStore.SyncCommittee) - commHandler = NewCommitteeHandler(dutyStore.Attester, dutyStore.SyncCommittee) + commHandler = NewCommitteeHandler(attHandler, syncHandler) alanForkEpoch = phase0.Epoch(0) currentSlot = &SafeValue[phase0.Slot]{} waitForDuties = &SafeValue[bool]{} @@ -380,10 +380,6 @@ func TestScheduler_Committee_Indices_Changed_Attester_Only(t *testing.T) { currentSlot.Set(phase0.Slot(1)) waitForDuties.Set(true) ticker.Send(currentSlot.Get()) - // Wait for the slot ticker to be triggered in the attester, sync committee, and cluster handlers. - // This ensures that no attester duties are fetched before the cluster ticker is triggered, - // preventing a scenario where the cluster handler executes duties in the same slot as the attester fetching them. - time.Sleep(10 * time.Millisecond) // wait for attester duties to be fetched waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) @@ -415,7 +411,7 @@ func TestScheduler_Committee_Indices_Changed_Attester_Only_2(t *testing.T) { dutyStore = dutystore.New() attHandler = NewAttesterHandler(dutyStore.Attester) syncHandler = NewSyncCommitteeHandler(dutyStore.SyncCommittee) - commHandler = NewCommitteeHandler(dutyStore.Attester, dutyStore.SyncCommittee) + commHandler = NewCommitteeHandler(attHandler, syncHandler) alanForkEpoch = phase0.Epoch(0) currentSlot = &SafeValue[phase0.Slot]{} waitForDuties = &SafeValue[bool]{} @@ -483,10 +479,6 @@ func TestScheduler_Committee_Indices_Changed_Attester_Only_2(t *testing.T) { currentSlot.Set(phase0.Slot(1)) waitForDuties.Set(true) ticker.Send(currentSlot.Get()) - // Wait for the slot ticker to be triggered in the attester, sync committee, and cluster handlers. - // This ensures that no attester duties are fetched before the cluster ticker is triggered, - // preventing a scenario where the cluster handler executes duties in the same slot as the attester fetching them. - time.Sleep(10 * time.Millisecond) // wait for attester duties to be fetched waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) @@ -518,7 +510,7 @@ func TestScheduler_Committee_Indices_Changed_Attester_Only_3(t *testing.T) { dutyStore = dutystore.New() attHandler = NewAttesterHandler(dutyStore.Attester) syncHandler = NewSyncCommitteeHandler(dutyStore.SyncCommittee) - commHandler = NewCommitteeHandler(dutyStore.Attester, dutyStore.SyncCommittee) + commHandler = NewCommitteeHandler(attHandler, syncHandler) alanForkEpoch = phase0.Epoch(0) currentSlot = &SafeValue[phase0.Slot]{} waitForDuties = &SafeValue[bool]{} @@ -576,10 +568,6 @@ func TestScheduler_Committee_Indices_Changed_Attester_Only_3(t *testing.T) { currentSlot.Set(phase0.Slot(1)) waitForDuties.Set(true) ticker.Send(currentSlot.Get()) - // Wait for the slot ticker to be triggered in the attester, sync committee, and cluster handlers. - // This ensures that no attester duties are fetched before the cluster ticker is triggered, - // preventing a scenario where the cluster handler executes duties in the same slot as the attester fetching them. - time.Sleep(10 * time.Millisecond) // wait for attester duties to be fetched waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) @@ -612,7 +600,7 @@ func TestScheduler_Committee_Reorg_Previous_Epoch_Transition_Attester_only(t *te dutyStore = dutystore.New() attHandler = NewAttesterHandler(dutyStore.Attester) syncHandler = NewSyncCommitteeHandler(dutyStore.SyncCommittee) - commHandler = NewCommitteeHandler(dutyStore.Attester, dutyStore.SyncCommittee) + commHandler = NewCommitteeHandler(attHandler, syncHandler) alanForkEpoch = phase0.Epoch(0) currentSlot = &SafeValue[phase0.Slot]{} waitForDuties = &SafeValue[bool]{} @@ -710,7 +698,7 @@ func TestScheduler_Committee_Reorg_Previous_Epoch_Transition_Indices_Changed_Att dutyStore = dutystore.New() attHandler = NewAttesterHandler(dutyStore.Attester) syncHandler = NewSyncCommitteeHandler(dutyStore.SyncCommittee) - commHandler = NewCommitteeHandler(dutyStore.Attester, dutyStore.SyncCommittee) + commHandler = NewCommitteeHandler(attHandler, syncHandler) alanForkEpoch = phase0.Epoch(0) currentSlot = &SafeValue[phase0.Slot]{} waitForDuties = &SafeValue[bool]{} @@ -799,10 +787,6 @@ func TestScheduler_Committee_Reorg_Previous_Epoch_Transition_Indices_Changed_Att // STEP 6: wait for attester duties to be fetched again for the current epoch currentSlot.Set(phase0.Slot(65)) ticker.Send(currentSlot.Get()) - // Wait for the slot ticker to be triggered in the attester, sync committee, and cluster handlers. - // This ensures that no attester duties are fetched before the cluster ticker is triggered, - // preventing a scenario where the cluster handler executes duties in the same slot as the attester fetching them. - time.Sleep(10 * time.Millisecond) // wait for attester duties to be fetched waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) @@ -830,7 +814,7 @@ func TestScheduler_Committee_Reorg_Previous_Attester_only(t *testing.T) { dutyStore = dutystore.New() attHandler = NewAttesterHandler(dutyStore.Attester) syncHandler = NewSyncCommitteeHandler(dutyStore.SyncCommittee) - commHandler = NewCommitteeHandler(dutyStore.Attester, dutyStore.SyncCommittee) + commHandler = NewCommitteeHandler(attHandler, syncHandler) alanForkEpoch = phase0.Epoch(0) currentSlot = &SafeValue[phase0.Slot]{} waitForDuties = &SafeValue[bool]{} @@ -927,7 +911,7 @@ func TestScheduler_Committee_Early_Block_Attester_Only(t *testing.T) { dutyStore = dutystore.New() attHandler = NewAttesterHandler(dutyStore.Attester) syncHandler = NewSyncCommitteeHandler(dutyStore.SyncCommittee) - commHandler = NewCommitteeHandler(dutyStore.Attester, dutyStore.SyncCommittee) + commHandler = NewCommitteeHandler(attHandler, syncHandler) alanForkEpoch = phase0.Epoch(0) currentSlot = &SafeValue[phase0.Slot]{} waitForDuties = &SafeValue[bool]{} @@ -992,7 +976,7 @@ func TestScheduler_Committee_Early_Block(t *testing.T) { dutyStore = dutystore.New() attHandler = NewAttesterHandler(dutyStore.Attester) syncHandler = NewSyncCommitteeHandler(dutyStore.SyncCommittee) - commHandler = NewCommitteeHandler(dutyStore.Attester, dutyStore.SyncCommittee) + commHandler = NewCommitteeHandler(attHandler, syncHandler) alanForkEpoch = phase0.Epoch(0) currentSlot = &SafeValue[phase0.Slot]{} waitForDuties = &SafeValue[bool]{} @@ -1067,7 +1051,7 @@ func TestScheduler_Committee_On_Fork_Attester_only(t *testing.T) { dutyStore = dutystore.New() attHandler = NewAttesterHandler(dutyStore.Attester) syncHandler = NewSyncCommitteeHandler(dutyStore.SyncCommittee) - commHandler = NewCommitteeHandler(dutyStore.Attester, dutyStore.SyncCommittee) + commHandler = NewCommitteeHandler(attHandler, syncHandler) alanForkEpoch = phase0.Epoch(2) currentSlot = &SafeValue[phase0.Slot]{} waitForDuties = &SafeValue[bool]{} @@ -1100,7 +1084,6 @@ func TestScheduler_Committee_On_Fork_Attester_only(t *testing.T) { currentSlot.Set(phase0.Slot(1)) scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot, alanForkEpoch) fetchAttesterDutiesCall, executeAttesterDutiesCall := setupAttesterGenesisDutiesMock(scheduler, attDuties, waitForDuties) - _, _ = setupSyncCommitteeDutiesMock(scheduler, activeShares, syncDuties, waitForDuties) fetchDutiesCall, executeDutiesCall := setupCommitteeDutiesMock(scheduler, activeShares, attDuties, syncDuties, waitForDuties) startFn() @@ -1154,7 +1137,7 @@ func TestScheduler_Committee_On_Fork(t *testing.T) { dutyStore = dutystore.New() attHandler = NewAttesterHandler(dutyStore.Attester) syncHandler = NewSyncCommitteeHandler(dutyStore.SyncCommittee) - commHandler = NewCommitteeHandler(dutyStore.Attester, dutyStore.SyncCommittee) + commHandler = NewCommitteeHandler(attHandler, syncHandler) alanForkEpoch = phase0.Epoch(256) currentSlot = &SafeValue[phase0.Slot]{} waitForDuties = &SafeValue[bool]{} @@ -1195,7 +1178,6 @@ func TestScheduler_Committee_On_Fork(t *testing.T) { currentSlot.Set(phase0.Slot(lastPeriodEpoch * 32)) scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot, alanForkEpoch) fetchAttesterDutiesCall, executeAttesterDutiesCall := setupAttesterGenesisDutiesMock(scheduler, attDuties, waitForDuties) - _, _ = setupSyncCommitteeDutiesMock(scheduler, activeShares, syncDuties, waitForDuties) fetchDutiesCall, executeDutiesCall := setupCommitteeDutiesMock(scheduler, activeShares, attDuties, syncDuties, waitForDuties) startFn() diff --git a/operator/duties/dutystore/duties.go b/operator/duties/dutystore/duties.go index 50fd0d7e22..aa19ee8d97 100644 --- a/operator/duties/dutystore/duties.go +++ b/operator/duties/dutystore/duties.go @@ -89,7 +89,7 @@ func (d *Duties[D]) Add(epoch phase0.Epoch, slot phase0.Slot, validatorIndex pha } } -func (d *Duties[D]) ResetEpoch(epoch phase0.Epoch) { +func (d *Duties[D]) Reset(epoch phase0.Epoch) { d.mu.Lock() defer d.mu.Unlock() diff --git a/operator/duties/proposer.go b/operator/duties/proposer.go index 8056737a3c..000d70fe47 100644 --- a/operator/duties/proposer.go +++ b/operator/duties/proposer.go @@ -34,24 +34,7 @@ func (h *ProposerHandler) Name() string { return spectypes.BNRoleProposer.String() } -// HandleDuties manages the duty lifecycle, handling different cases: -// -// On First Run: -// 1. Fetch duties for the current epoch. -// 2. Execute duties. -// -// On Re-org (current dependent root changed): -// 1. Fetch duties for the current epoch. -// 2. Execute duties. -// -// On Indices Change: -// 1. Execute duties. -// 2. ResetEpoch duties for the current epoch. -// 3. Fetch duties for the current epoch. -// -// On Ticker event: -// 1. Execute duties. -// 2. If necessary, fetch duties for the current epoch. +// HandleDuties manages the duty lifecycle func (h *ProposerHandler) HandleDuties(ctx context.Context) { h.logger.Info("starting duty handler") defer h.logger.Info("duty handler exited") @@ -65,47 +48,47 @@ func (h *ProposerHandler) HandleDuties(ctx context.Context) { case <-next: slot := h.ticker.Slot() next = h.ticker.Next() - currentEpoch := h.network.Beacon.EstimatedEpochAtSlot(slot) - buildStr := fmt.Sprintf("e%v-s%v-#%v", currentEpoch, slot, slot%32+1) - h.logger.Debug("🛠 ticker event", zap.String("epoch_slot_pos", buildStr)) + epoch := h.network.Beacon.EstimatedEpochAtSlot(slot) + tickerID := fields.FormatSlotTickerID(epoch, slot) + h.logger.Debug("🛠 ticker event", fields.SlotTickerID(tickerID)) ctx, cancel := context.WithDeadline(ctx, h.network.Beacon.GetSlotStartTime(slot+1).Add(100*time.Millisecond)) if h.fetchFirst { h.fetchFirst = false h.indicesChanged = false - h.processFetching(ctx, currentEpoch) - h.processExecution(currentEpoch, slot) + h.processFetching(ctx, epoch) + h.processExecution(epoch, slot) } else { - h.processExecution(currentEpoch, slot) + h.processExecution(epoch, slot) if h.indicesChanged { h.indicesChanged = false - h.processFetching(ctx, currentEpoch) + h.processFetching(ctx, epoch) } } cancel() // last slot of epoch if uint64(slot)%h.network.Beacon.SlotsPerEpoch() == h.network.Beacon.SlotsPerEpoch()-1 { - h.duties.ResetEpoch(currentEpoch - 1) + h.duties.Reset(epoch - 1) h.fetchFirst = true } case reorgEvent := <-h.reorg: - currentEpoch := h.network.Beacon.EstimatedEpochAtSlot(reorgEvent.Slot) - buildStr := fmt.Sprintf("e%v-s%v-#%v", currentEpoch, reorgEvent.Slot, reorgEvent.Slot%32+1) - h.logger.Info("🔀 reorg event received", zap.String("epoch_slot_pos", buildStr), zap.Any("event", reorgEvent)) + epoch := h.network.Beacon.EstimatedEpochAtSlot(reorgEvent.Slot) + tickerID := fields.FormatSlotTickerID(epoch, reorgEvent.Slot) + h.logger.Info("🔀 reorg event received", fields.SlotTickerID(tickerID), zap.Any("event", reorgEvent)) // reset current epoch duties if reorgEvent.Current { - h.duties.ResetEpoch(currentEpoch) + h.duties.Reset(epoch) h.fetchFirst = true } case <-h.indicesChange: slot := h.network.Beacon.EstimatedCurrentSlot() - currentEpoch := h.network.Beacon.EstimatedEpochAtSlot(slot) - buildStr := fmt.Sprintf("e%v-s%v-#%v", currentEpoch, slot, slot%32+1) - h.logger.Info("🔁 indices change received", zap.String("epoch_slot_pos", buildStr)) + epoch := h.network.Beacon.EstimatedEpochAtSlot(slot) + tickerID := fields.FormatSlotTickerID(epoch, slot) + h.logger.Info("🔁 indices change received", fields.SlotTickerID(tickerID)) h.indicesChanged = true } @@ -178,7 +161,7 @@ func (h *ProposerHandler) fetchAndProcessDuties(ctx context.Context, epoch phase return fmt.Errorf("failed to fetch proposer duties: %w", err) } - h.duties.ResetEpoch(epoch) + h.duties.Reset(epoch) specDuties := make([]*spectypes.ValidatorDuty, 0, len(duties)) for _, d := range duties { diff --git a/operator/duties/scheduler.go b/operator/duties/scheduler.go index 0cdc2db1ae..357ee31f09 100644 --- a/operator/duties/scheduler.go +++ b/operator/duties/scheduler.go @@ -135,6 +135,9 @@ func NewScheduler(opts *SchedulerOptions) *Scheduler { dutyStore = dutystore.New() } + attHandler := NewAttesterHandler(dutyStore.Attester) + syncCommHandler := NewSyncCommitteeHandler(dutyStore.SyncCommittee) + s := &Scheduler{ beaconNode: opts.BeaconNode, executionClient: opts.ExecutionClient, @@ -147,11 +150,11 @@ func NewScheduler(opts *SchedulerOptions) *Scheduler { blockPropagateDelay: blockPropagationDelay, handlers: []dutyHandler{ - NewAttesterHandler(dutyStore.Attester), + attHandler, NewProposerHandler(dutyStore.Proposer), - NewSyncCommitteeHandler(dutyStore.SyncCommittee), + syncCommHandler, NewVoluntaryExitHandler(dutyStore.VoluntaryExit, opts.ValidatorExitCh), - NewCommitteeHandler(dutyStore.Attester, dutyStore.SyncCommittee), + NewCommitteeHandler(attHandler, syncCommHandler), NewValidatorRegistrationHandler(), }, @@ -305,8 +308,8 @@ func (s *Scheduler) HandleHeadEvent(logger *zap.Logger) func(event *eth2apiv1.Ev // check for reorg epoch := s.network.Beacon.EstimatedEpochAtSlot(data.Slot) - buildStr := fmt.Sprintf("e%v-s%v-#%v", epoch, data.Slot, data.Slot%32+1) - logger := logger.With(zap.String("epoch_slot_pos", buildStr)) + tickerID := fields.FormatSlotTickerID(epoch, data.Slot) + logger := logger.With(fields.SlotTickerID(tickerID)) if s.lastBlockEpoch != 0 { if epoch > s.lastBlockEpoch { // Change of epoch. diff --git a/operator/duties/sync_committee.go b/operator/duties/sync_committee.go index a596106fb6..f3fa693e3d 100644 --- a/operator/duties/sync_committee.go +++ b/operator/duties/sync_committee.go @@ -40,26 +40,7 @@ func (h *SyncCommitteeHandler) Name() string { return spectypes.BNRoleSyncCommittee.String() } -// HandleDuties manages the duty lifecycle, handling different cases: -// -// On First Run: -// 1. Fetch duties for the current period. -// 2. If necessary, fetch duties for the next period. -// 3. Execute duties. -// -// On Re-org: -// 1. Execute duties. -// 2. If necessary, fetch duties for the next period. -// -// On Indices Change: -// 1. Execute duties. -// 2. ResetEpoch duties for the current period. -// 3. Fetch duties for the current period. -// 4. If necessary, fetch duties for the next period. -// -// On Ticker event: -// 1. Execute duties. -// 2. If necessary, fetch duties for the next period. +// HandleDuties manages the duty lifecycle func (h *SyncCommitteeHandler) HandleDuties(ctx context.Context) { h.logger.Info("starting duty handler") defer h.logger.Info("duty handler exited") @@ -83,50 +64,34 @@ func (h *SyncCommitteeHandler) HandleDuties(ctx context.Context) { next = h.ticker.Next() epoch := h.network.Beacon.EstimatedEpochAtSlot(slot) period := h.network.Beacon.EstimatedSyncCommitteePeriodAtEpoch(epoch) - buildStr := fmt.Sprintf("p%v-e%v-s%v-#%v", period, epoch, slot, slot%32+1) - h.logger.Debug("🛠 ticker event", zap.String("period_epoch_slot_pos", buildStr)) - - ctx, cancel := context.WithDeadline(ctx, h.network.Beacon.GetSlotStartTime(slot+1).Add(100*time.Millisecond)) - h.processExecution(period, slot) - h.processFetching(ctx, period, true) - cancel() - - // if we have reached the preparation slots -1, prepare the next period duties in the next slot. - periodSlots := h.slotsPerPeriod() - if uint64(slot)%periodSlots == periodSlots-h.preparationSlots-1 { - h.fetchNextPeriod = true - } + tickerID := fields.FormatSlotTickerCommitteeID(period, epoch, slot) + h.logger.Debug("🛠 ticker event", fields.SlotTickerID(tickerID)) - // last slot of period - if slot == h.network.Beacon.LastSlotOfSyncPeriod(period) { - h.duties.Reset(period - 1) + if !h.network.PastAlanForkAtEpoch(epoch) { + h.processExecution(period, slot) + h.processFetching(ctx, period, slot, true) + h.processSlotTransition(period, slot) } case reorgEvent := <-h.reorg: epoch := h.network.Beacon.EstimatedEpochAtSlot(reorgEvent.Slot) period := h.network.Beacon.EstimatedSyncCommitteePeriodAtEpoch(epoch) + tickerID := fields.FormatSlotTickerCommitteeID(period, epoch, reorgEvent.Slot) + h.logger.Info("🔀 reorg event received", fields.SlotTickerID(tickerID), zap.Any("event", reorgEvent)) - buildStr := fmt.Sprintf("p%v-e%v-s%v-#%v", period, epoch, reorgEvent.Slot, reorgEvent.Slot%32+1) - h.logger.Info("🔀 reorg event received", zap.String("period_epoch_slot_pos", buildStr), zap.Any("event", reorgEvent)) - - // reset current epoch duties - if reorgEvent.Current && h.shouldFetchNextPeriod(reorgEvent.Slot) { - h.duties.Reset(period + 1) - h.fetchNextPeriod = true + if !h.network.PastAlanForkAtEpoch(epoch) { + h.processReorg(period, reorgEvent) } case <-h.indicesChange: slot := h.network.Beacon.EstimatedCurrentSlot() epoch := h.network.Beacon.EstimatedEpochAtSlot(slot) period := h.network.Beacon.EstimatedSyncCommitteePeriodAtEpoch(epoch) - buildStr := fmt.Sprintf("p%v-e%v-s%v-#%v", period, epoch, slot, slot%32+1) - h.logger.Info("🔁 indices change received", zap.String("period_epoch_slot_pos", buildStr)) - - h.fetchCurrentPeriod = true + tickerID := fields.FormatSlotTickerCommitteeID(period, epoch, slot) + h.logger.Info("🔁 indices change received", fields.SlotTickerID(tickerID)) - // reset next period duties if in appropriate slot range - if h.shouldFetchNextPeriod(slot) { - h.fetchNextPeriod = true + if !h.network.PastAlanForkAtEpoch(epoch) { + h.processIndicesChange(period, slot) } } } @@ -136,13 +101,14 @@ func (h *SyncCommitteeHandler) HandleInitialDuties(ctx context.Context) { ctx, cancel := context.WithTimeout(ctx, h.network.Beacon.SlotDurationSec()/2) defer cancel() + slot := h.network.Beacon.EstimatedCurrentSlot() epoch := h.network.Beacon.EstimatedCurrentEpoch() period := h.network.Beacon.EstimatedSyncCommitteePeriodAtEpoch(epoch) - h.processFetching(ctx, period, false) + h.processFetching(ctx, period, slot, false) } -func (h *SyncCommitteeHandler) processFetching(ctx context.Context, period uint64, waitForInitial bool) { - ctx, cancel := context.WithCancel(ctx) +func (h *SyncCommitteeHandler) processFetching(ctx context.Context, period uint64, slot phase0.Slot, waitForInitial bool) { + ctx, cancel := context.WithDeadline(ctx, h.network.Beacon.GetSlotStartTime(slot+1).Add(100*time.Millisecond)) defer cancel() if h.fetchCurrentPeriod { @@ -163,33 +129,50 @@ func (h *SyncCommitteeHandler) processFetching(ctx context.Context, period uint6 } func (h *SyncCommitteeHandler) processExecution(period uint64, slot phase0.Slot) { - // range over duties and execute duties := h.duties.CommitteePeriodDuties(period) if duties == nil { return } - if !h.network.PastAlanForkAtEpoch(h.network.Beacon.EstimatedEpochAtSlot(slot)) { - toExecute := make([]*genesisspectypes.Duty, 0, len(duties)*2) - for _, d := range duties { - if h.shouldExecute(d, slot) { - toExecute = append(toExecute, h.toGenesisSpecDuty(d, slot, genesisspectypes.BNRoleSyncCommittee)) - toExecute = append(toExecute, h.toGenesisSpecDuty(d, slot, genesisspectypes.BNRoleSyncCommitteeContribution)) - } + toExecute := make([]*genesisspectypes.Duty, 0, len(duties)*2) + for _, d := range duties { + if h.shouldExecute(d, slot) { + toExecute = append(toExecute, h.toGenesisSpecDuty(d, slot, genesisspectypes.BNRoleSyncCommittee)) + toExecute = append(toExecute, h.toGenesisSpecDuty(d, slot, genesisspectypes.BNRoleSyncCommitteeContribution)) } + } - h.dutiesExecutor.ExecuteGenesisDuties(h.logger, toExecute) - return + h.dutiesExecutor.ExecuteGenesisDuties(h.logger, toExecute) +} + +func (h *SyncCommitteeHandler) processIndicesChange(period uint64, slot phase0.Slot) { + h.fetchCurrentPeriod = true + + // reset next period duties if in appropriate slot range + if h.shouldFetchNextPeriod(slot) { + h.duties.Reset(period + 1) + h.fetchNextPeriod = true } +} - toExecute := make([]*spectypes.ValidatorDuty, 0, len(duties)) - for _, d := range duties { - if h.shouldExecute(d, slot) { - toExecute = append(toExecute, h.toSpecDuty(d, slot, spectypes.BNRoleSyncCommitteeContribution)) - } +func (h *SyncCommitteeHandler) processReorg(period uint64, reorgEvent ReorgEvent) { + if reorgEvent.Current && h.shouldFetchNextPeriod(reorgEvent.Slot) { + h.duties.Reset(period + 1) + h.fetchNextPeriod = true + } +} + +func (h *SyncCommitteeHandler) processSlotTransition(period uint64, slot phase0.Slot) { + // if we have reached the preparation slots -1, prepare the next period duties in the next slot. + periodSlots := h.slotsPerPeriod() + if uint64(slot)%periodSlots == periodSlots-h.preparationSlots-1 { + h.fetchNextPeriod = true } - h.dutiesExecutor.ExecuteDuties(h.logger, toExecute) + // last slot of period + if slot == h.network.Beacon.LastSlotOfSyncPeriod(period) { + h.duties.Reset(period - 1) + } } func (h *SyncCommitteeHandler) fetchAndProcessDuties(ctx context.Context, period uint64, waitForInitial bool) error { diff --git a/operator/duties/sync_committee_test.go b/operator/duties/sync_committee_test.go deleted file mode 100644 index aa3e65270a..0000000000 --- a/operator/duties/sync_committee_test.go +++ /dev/null @@ -1,674 +0,0 @@ -package duties - -import ( - "context" - "testing" - "time" - - v1 "github.com/attestantio/go-eth2-client/api/v1" - "github.com/attestantio/go-eth2-client/spec/phase0" - "github.com/stretchr/testify/require" - "go.uber.org/mock/gomock" - - "github.com/ssvlabs/ssv/utils/hashmap" - - spectypes "github.com/ssvlabs/ssv-spec/types" - - "github.com/ssvlabs/ssv/operator/duties/dutystore" - mocknetwork "github.com/ssvlabs/ssv/protocol/v2/blockchain/beacon/mocks" - ssvtypes "github.com/ssvlabs/ssv/protocol/v2/types" -) - -func setupSyncCommitteeDutiesMock( - s *Scheduler, - activeShares []*ssvtypes.SSVShare, - dutiesMap *hashmap.Map[uint64, []*v1.SyncCommitteeDuty], - waitForDuties *SafeValue[bool], -) (chan struct{}, chan []*spectypes.ValidatorDuty) { - fetchDutiesCall := make(chan struct{}) - executeDutiesCall := make(chan []*spectypes.ValidatorDuty) - - s.network.Beacon.(*mocknetwork.MockBeaconNetwork).EXPECT().EstimatedSyncCommitteePeriodAtEpoch(gomock.Any()).DoAndReturn( - func(epoch phase0.Epoch) uint64 { - return uint64(epoch) / s.network.Beacon.EpochsPerSyncCommitteePeriod() - }, - ).AnyTimes() - - s.network.Beacon.(*mocknetwork.MockBeaconNetwork).EXPECT().FirstEpochOfSyncPeriod(gomock.Any()).DoAndReturn( - func(period uint64) phase0.Epoch { - return phase0.Epoch(period * s.network.Beacon.EpochsPerSyncCommitteePeriod()) - }, - ).AnyTimes() - - s.network.Beacon.(*mocknetwork.MockBeaconNetwork).EXPECT().LastSlotOfSyncPeriod(gomock.Any()).DoAndReturn( - func(period uint64) phase0.Slot { - lastEpoch := s.network.Beacon.FirstEpochOfSyncPeriod(period+1) - 1 - // If we are in the sync committee that ends at slot x we do not generate a message during slot x-1 - // as it will never be included, hence -1. - return s.network.Beacon.GetEpochFirstSlot(lastEpoch+1) - 2 - }, - ).AnyTimes() - - s.network.Beacon.(*mocknetwork.MockBeaconNetwork).EXPECT().GetEpochFirstSlot(gomock.Any()).DoAndReturn( - func(epoch phase0.Epoch) phase0.Slot { - return phase0.Slot(uint64(epoch) * s.network.Beacon.SlotsPerEpoch()) - }, - ).AnyTimes() - - s.beaconNode.(*MockBeaconNode).EXPECT().SyncCommitteeDuties(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn( - func(ctx context.Context, epoch phase0.Epoch, indices []phase0.ValidatorIndex) ([]*v1.SyncCommitteeDuty, error) { - if waitForDuties.Get() { - fetchDutiesCall <- struct{}{} - } - period := s.network.Beacon.EstimatedSyncCommitteePeriodAtEpoch(epoch) - duties, _ := dutiesMap.Get(period) - return duties, nil - }).AnyTimes() - - s.validatorProvider.(*MockValidatorProvider).EXPECT().SelfParticipatingValidators(gomock.Any()).Return(activeShares).AnyTimes() - s.validatorProvider.(*MockValidatorProvider).EXPECT().ParticipatingValidators(gomock.Any()).Return(activeShares).AnyTimes() - - s.validatorController.(*MockValidatorController).EXPECT().AllActiveIndices(gomock.Any(), gomock.Any()).DoAndReturn( - func(epoch phase0.Epoch, afterInit bool) []phase0.ValidatorIndex { - return indicesFromShares(activeShares) - }).AnyTimes() - - s.beaconNode.(*MockBeaconNode).EXPECT().SubmitSyncCommitteeSubscriptions(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() - - return fetchDutiesCall, executeDutiesCall -} - -func expectedExecutedSyncCommitteeDuties(handler *SyncCommitteeHandler, duties []*v1.SyncCommitteeDuty, slot phase0.Slot) []*spectypes.ValidatorDuty { - expectedDuties := make([]*spectypes.ValidatorDuty, 0) - for _, d := range duties { - if !handler.network.PastAlanForkAtEpoch(handler.network.Beacon.EstimatedEpochAtSlot(slot)) { - expectedDuties = append(expectedDuties, handler.toSpecDuty(d, slot, spectypes.BNRoleSyncCommittee)) - } - expectedDuties = append(expectedDuties, handler.toSpecDuty(d, slot, spectypes.BNRoleSyncCommitteeContribution)) - } - return expectedDuties -} - -func TestScheduler_SyncCommittee_Same_Period(t *testing.T) { - var ( - handler = NewSyncCommitteeHandler(dutystore.NewSyncCommitteeDuties()) - currentSlot = &SafeValue[phase0.Slot]{} - waitForDuties = &SafeValue[bool]{} - forkEpoch = phase0.Epoch(0) - dutiesMap = hashmap.New[uint64, []*v1.SyncCommitteeDuty]() - activeShares = []*ssvtypes.SSVShare{{ - Share: spectypes.Share{ - Committee: []*spectypes.ShareMember{ - {Signer: 1}, {Signer: 2}, {Signer: 3}, {Signer: 4}, - }, - ValidatorIndex: 1, - }, - }} - ) - dutiesMap.Set(0, []*v1.SyncCommitteeDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - - // STEP 1: wait for sync committee duties to be fetched (handle initial duties) - currentSlot.Set(phase0.Slot(1)) - scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) - fetchDutiesCall, executeDutiesCall := setupSyncCommitteeDutiesMock(scheduler, activeShares, dutiesMap, waitForDuties) - startFn() - - // STEP 1: wait for sync committee duties to be fetched and executed at the same slot - duties, _ := dutiesMap.Get(0) - expected := expectedExecutedSyncCommitteeDuties(handler, duties, currentSlot.Get()) - setExecuteDutyFunc(scheduler, executeDutiesCall, len(expected)) - - ticker.Send(currentSlot.Get()) - waitForDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - - // STEP 2: expect sync committee duties to be executed at the same period - currentSlot.Set(phase0.Slot(2)) - duties, _ = dutiesMap.Get(0) - expected = expectedExecutedSyncCommitteeDuties(handler, duties, currentSlot.Get()) - setExecuteDutyFunc(scheduler, executeDutiesCall, len(expected)) - - ticker.Send(currentSlot.Get()) - waitForDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - - // STEP 3: expect sync committee duties to be executed at the last slot of the period - currentSlot.Set(scheduler.network.Beacon.LastSlotOfSyncPeriod(0)) - duties, _ = dutiesMap.Get(0) - expected = expectedExecutedSyncCommitteeDuties(handler, duties, currentSlot.Get()) - setExecuteDutyFunc(scheduler, executeDutiesCall, len(expected)) - - ticker.Send(currentSlot.Get()) - waitForDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - - // STEP 4: expect no action to be taken as we are in the next period - firstSlotOfNextPeriod := scheduler.network.Beacon.GetEpochFirstSlot(scheduler.network.Beacon.FirstEpochOfSyncPeriod(1)) - currentSlot.Set(firstSlotOfNextPeriod) - ticker.Send(currentSlot.Get()) - waitForNoAction(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // Stop scheduler & wait for graceful exit. - cancel() - require.NoError(t, schedulerPool.Wait()) -} - -func TestScheduler_SyncCommittee_Current_Next_Periods(t *testing.T) { - var ( - handler = NewSyncCommitteeHandler(dutystore.NewSyncCommitteeDuties()) - currentSlot = &SafeValue[phase0.Slot]{} - waitForDuties = &SafeValue[bool]{} - forkEpoch = phase0.Epoch(0) - dutiesMap = hashmap.New[uint64, []*v1.SyncCommitteeDuty]() - activeShares = []*ssvtypes.SSVShare{ - { - Share: spectypes.Share{ - Committee: []*spectypes.ShareMember{ - {Signer: 1}, {Signer: 2}, {Signer: 3}, {Signer: 4}, - }, - ValidatorIndex: 1, - }, - }, - { - Share: spectypes.Share{ - Committee: []*spectypes.ShareMember{ - {Signer: 1}, {Signer: 2}, {Signer: 3}, {Signer: 4}, - }, - ValidatorIndex: 2, - }, - }, - } - ) - dutiesMap.Set(0, []*v1.SyncCommitteeDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - dutiesMap.Set(1, []*v1.SyncCommitteeDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 4}, - ValidatorIndex: phase0.ValidatorIndex(2), - }, - }) - - // STEP 1: wait for sync committee duties to be fetched (handle initial duties) - currentSlot.Set(phase0.Slot(256*32 - 49)) - scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) - fetchDutiesCall, executeDutiesCall := setupSyncCommitteeDutiesMock(scheduler, activeShares, dutiesMap, waitForDuties) - startFn() - - duties, _ := dutiesMap.Get(0) - expected := expectedExecutedSyncCommitteeDuties(handler, duties, currentSlot.Get()) - setExecuteDutyFunc(scheduler, executeDutiesCall, len(expected)) - - ticker.Send(currentSlot.Get()) - waitForDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - - // STEP 2: wait for sync committee duties to be executed - currentSlot.Set(phase0.Slot(256*32 - 48)) - duties, _ = dutiesMap.Get(0) - expected = expectedExecutedSyncCommitteeDuties(handler, duties, currentSlot.Get()) - setExecuteDutyFunc(scheduler, executeDutiesCall, len(expected)) - - ticker.Send(currentSlot.Get()) - waitForDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - - // STEP 3: wait for sync committee duties to be executed - currentSlot.Set(phase0.Slot(256*32 - 47)) - duties, _ = dutiesMap.Get(0) - expected = expectedExecutedSyncCommitteeDuties(handler, duties, currentSlot.Get()) - setExecuteDutyFunc(scheduler, executeDutiesCall, len(expected)) - - ticker.Send(currentSlot.Get()) - waitForDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - - // ... - - // STEP 4: new period, wait for sync committee duties to be executed - currentSlot.Set(phase0.Slot(256 * 32)) - duties, _ = dutiesMap.Get(1) - expected = expectedExecutedSyncCommitteeDuties(handler, duties, currentSlot.Get()) - setExecuteDutyFunc(scheduler, executeDutiesCall, len(expected)) - - ticker.Send(currentSlot.Get()) - waitForDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - - // Stop scheduler & wait for graceful exit. - cancel() - require.NoError(t, schedulerPool.Wait()) -} - -func TestScheduler_SyncCommittee_Indices_Changed(t *testing.T) { - var ( - handler = NewSyncCommitteeHandler(dutystore.NewSyncCommitteeDuties()) - currentSlot = &SafeValue[phase0.Slot]{} - waitForDuties = &SafeValue[bool]{} - forkEpoch = phase0.Epoch(0) - dutiesMap = hashmap.New[uint64, []*v1.SyncCommitteeDuty]() - activeShares = []*ssvtypes.SSVShare{ - { - Share: spectypes.Share{ - Committee: []*spectypes.ShareMember{ - {Signer: 1}, {Signer: 2}, {Signer: 3}, {Signer: 4}, - }, - ValidatorIndex: 1, - }, - }, - { - Share: spectypes.Share{ - Committee: []*spectypes.ShareMember{ - {Signer: 1}, {Signer: 2}, {Signer: 3}, {Signer: 4}, - }, - ValidatorIndex: 2, - }, - }, - } - ) - currentSlot.Set(phase0.Slot(256*32 - 3)) - scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) - fetchDutiesCall, executeDutiesCall := setupSyncCommitteeDutiesMock(scheduler, activeShares, dutiesMap, waitForDuties) - startFn() - - dutiesMap.Set(1, []*v1.SyncCommitteeDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - - // STEP 1: wait for sync committee duties to be fetched for next period - waitForDuties.Set(true) - ticker.Send(currentSlot.Get()) - waitForDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 2: trigger a change in active indices - scheduler.indicesChg <- struct{}{} - duties, _ := dutiesMap.Get(1) - dutiesMap.Set(1, append(duties, &v1.SyncCommitteeDuty{ - PubKey: phase0.BLSPubKey{1, 2, 4}, - ValidatorIndex: phase0.ValidatorIndex(2), - })) - waitForNoAction(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 3: wait for sync committee duties to be fetched again - currentSlot.Set(phase0.Slot(256*32 - 2)) - ticker.Send(currentSlot.Get()) - waitForDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - waitForDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 4: no action should be taken - currentSlot.Set(phase0.Slot(256*32 - 1)) - ticker.Send(currentSlot.Get()) - waitForNoAction(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 5: execute duties - currentSlot.Set(phase0.Slot(256 * 32)) - duties, _ = dutiesMap.Get(1) - expected := expectedExecutedSyncCommitteeDuties(handler, duties, currentSlot.Get()) - setExecuteDutyFunc(scheduler, executeDutiesCall, len(expected)) - - ticker.Send(currentSlot.Get()) - waitForDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - - // Stop scheduler & wait for graceful exit. - cancel() - require.NoError(t, schedulerPool.Wait()) -} - -func TestScheduler_SyncCommittee_Multiple_Indices_Changed_Same_Slot(t *testing.T) { - var ( - handler = NewSyncCommitteeHandler(dutystore.NewSyncCommitteeDuties()) - currentSlot = &SafeValue[phase0.Slot]{} - waitForDuties = &SafeValue[bool]{} - forkEpoch = phase0.Epoch(0) - dutiesMap = hashmap.New[uint64, []*v1.SyncCommitteeDuty]() - activeShares = []*ssvtypes.SSVShare{ - { - Share: spectypes.Share{ - Committee: []*spectypes.ShareMember{ - {Signer: 1}, {Signer: 2}, {Signer: 3}, {Signer: 4}, - }, - ValidatorIndex: 1, - }, - }, - { - Share: spectypes.Share{ - Committee: []*spectypes.ShareMember{ - {Signer: 1}, {Signer: 2}, {Signer: 3}, {Signer: 4}, - }, - ValidatorIndex: 2, - }, - }, - } - ) - currentSlot.Set(phase0.Slot(256*32 - 3)) - scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) - fetchDutiesCall, executeDutiesCall := setupSyncCommitteeDutiesMock(scheduler, activeShares, dutiesMap, waitForDuties) - startFn() - - // STEP 1: wait for no action to be taken - ticker.Send(currentSlot.Get()) - waitForNoAction(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 2: trigger a change in active indices - scheduler.indicesChg <- struct{}{} - dutiesMap.Set(1, []*v1.SyncCommitteeDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - waitForNoAction(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 3: trigger a change in active indices - scheduler.indicesChg <- struct{}{} - duties, _ := dutiesMap.Get(1) - dutiesMap.Set(1, append(duties, &v1.SyncCommitteeDuty{ - PubKey: phase0.BLSPubKey{1, 2, 4}, - ValidatorIndex: phase0.ValidatorIndex(2), - })) - waitForNoAction(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 4: wait for sync committee duties to be fetched again - currentSlot.Set(phase0.Slot(256*32 - 2)) - waitForDuties.Set(true) - ticker.Send(currentSlot.Get()) - waitForDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - waitForDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 5: no action should be taken - currentSlot.Set(phase0.Slot(256*32 - 1)) - ticker.Send(currentSlot.Get()) - waitForNoAction(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 6: The first assigned duty should not be executed, but the second one should - currentSlot.Set(phase0.Slot(256 * 32)) - duties, _ = dutiesMap.Get(1) - expected := expectedExecutedSyncCommitteeDuties(handler, duties, currentSlot.Get()) - setExecuteDutyFunc(scheduler, executeDutiesCall, len(expected)) - - ticker.Send(currentSlot.Get()) - waitForDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - - // Stop scheduler & wait for graceful exit. - cancel() - require.NoError(t, schedulerPool.Wait()) -} - -// reorg current dependent root changed -func TestScheduler_SyncCommittee_Reorg_Current(t *testing.T) { - var ( - handler = NewSyncCommitteeHandler(dutystore.NewSyncCommitteeDuties()) - currentSlot = &SafeValue[phase0.Slot]{} - waitForDuties = &SafeValue[bool]{} - forkEpoch = phase0.Epoch(0) - dutiesMap = hashmap.New[uint64, []*v1.SyncCommitteeDuty]() - activeShares = []*ssvtypes.SSVShare{ - { - Share: spectypes.Share{ - Committee: []*spectypes.ShareMember{ - {Signer: 1}, {Signer: 2}, {Signer: 3}, {Signer: 4}, - }, - ValidatorIndex: 1, - }, - }, - { - Share: spectypes.Share{ - Committee: []*spectypes.ShareMember{ - {Signer: 1}, {Signer: 2}, {Signer: 3}, {Signer: 4}, - }, - ValidatorIndex: 2, - }, - }, - } - ) - currentSlot.Set(phase0.Slot(256*32 - 3)) - scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) - fetchDutiesCall, executeDutiesCall := setupSyncCommitteeDutiesMock(scheduler, activeShares, dutiesMap, waitForDuties) - startFn() - - dutiesMap.Set(1, []*v1.SyncCommitteeDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - - // STEP 1: wait for sync committee duties to be fetched and executed at the same slot - waitForDuties.Set(true) - ticker.Send(currentSlot.Get()) - waitForDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 2: trigger head event - e := &v1.Event{ - Data: &v1.HeadEvent{ - Slot: currentSlot.Get(), - CurrentDutyDependentRoot: phase0.Root{0x01}, - }, - } - scheduler.HandleHeadEvent(logger)(e) - waitForNoAction(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 3: Ticker with no action - currentSlot.Set(phase0.Slot(256*32 - 2)) - ticker.Send(currentSlot.Get()) - waitForNoAction(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 4: trigger reorg - e = &v1.Event{ - Data: &v1.HeadEvent{ - Slot: currentSlot.Get(), - CurrentDutyDependentRoot: phase0.Root{0x02}, - }, - } - dutiesMap.Set(1, []*v1.SyncCommitteeDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 4}, - ValidatorIndex: phase0.ValidatorIndex(2), - }, - }) - scheduler.HandleHeadEvent(logger)(e) - waitForNoAction(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 5: wait for sync committee duties to be fetched again for the current epoch - currentSlot.Set(phase0.Slot(256*32 - 1)) - ticker.Send(currentSlot.Get()) - waitForDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 6: The first assigned duty should not be executed, but the second one should - currentSlot.Set(phase0.Slot(256 * 32)) - duties, _ := dutiesMap.Get(1) - expected := expectedExecutedSyncCommitteeDuties(handler, duties, currentSlot.Get()) - setExecuteDutyFunc(scheduler, executeDutiesCall, len(expected)) - - ticker.Send(currentSlot.Get()) - waitForDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - - // Stop scheduler & wait for graceful exit. - cancel() - require.NoError(t, schedulerPool.Wait()) -} - -// reorg current dependent root changed including indices change in the same slot -func TestScheduler_SyncCommittee_Reorg_Current_Indices_Changed(t *testing.T) { - var ( - handler = NewSyncCommitteeHandler(dutystore.NewSyncCommitteeDuties()) - currentSlot = &SafeValue[phase0.Slot]{} - waitForDuties = &SafeValue[bool]{} - forkEpoch = phase0.Epoch(0) - dutiesMap = hashmap.New[uint64, []*v1.SyncCommitteeDuty]() - activeShares = []*ssvtypes.SSVShare{ - { - Share: spectypes.Share{ - Committee: []*spectypes.ShareMember{ - {Signer: 1}, {Signer: 2}, {Signer: 3}, {Signer: 4}, - }, - ValidatorIndex: 1, - }, - }, - { - Share: spectypes.Share{ - Committee: []*spectypes.ShareMember{ - {Signer: 1}, {Signer: 2}, {Signer: 3}, {Signer: 4}, - }, - ValidatorIndex: 2, - }, - }, - { - Share: spectypes.Share{ - Committee: []*spectypes.ShareMember{ - {Signer: 1}, {Signer: 2}, {Signer: 3}, {Signer: 4}, - }, - ValidatorIndex: 3, - }, - }, - } - ) - currentSlot.Set(phase0.Slot(256*32 - 3)) - scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) - fetchDutiesCall, executeDutiesCall := setupSyncCommitteeDutiesMock(scheduler, activeShares, dutiesMap, waitForDuties) - startFn() - - dutiesMap.Set(1, []*v1.SyncCommitteeDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - - // STEP 1: wait for sync committee duties to be fetched and executed at the same slot - waitForDuties.Set(true) - ticker.Send(currentSlot.Get()) - waitForDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 2: trigger head event - e := &v1.Event{ - Data: &v1.HeadEvent{ - Slot: currentSlot.Get(), - CurrentDutyDependentRoot: phase0.Root{0x01}, - }, - } - scheduler.HandleHeadEvent(logger)(e) - waitForNoAction(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 3: Ticker with no action - currentSlot.Set(phase0.Slot(256*32 - 2)) - ticker.Send(currentSlot.Get()) - waitForNoAction(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 4: trigger reorg - e = &v1.Event{ - Data: &v1.HeadEvent{ - Slot: currentSlot.Get(), - CurrentDutyDependentRoot: phase0.Root{0x02}, - }, - } - dutiesMap.Set(1, []*v1.SyncCommitteeDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 4}, - ValidatorIndex: phase0.ValidatorIndex(2), - }, - }) - scheduler.HandleHeadEvent(logger)(e) - waitForNoAction(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 3: trigger a change in active indices - scheduler.indicesChg <- struct{}{} - duties, _ := dutiesMap.Get(1) - dutiesMap.Set(1, append(duties, &v1.SyncCommitteeDuty{ - PubKey: phase0.BLSPubKey{1, 2, 5}, - ValidatorIndex: phase0.ValidatorIndex(3), - })) - waitForNoAction(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 5: wait for sync committee duties to be fetched again for the current epoch - currentSlot.Set(phase0.Slot(256*32 - 1)) - ticker.Send(currentSlot.Get()) - waitForDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - waitForDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 6: The first assigned duty should not be executed, but the second and the new from indices change should - currentSlot.Set(phase0.Slot(256 * 32)) - duties, _ = dutiesMap.Get(1) - expected := expectedExecutedSyncCommitteeDuties(handler, duties, currentSlot.Get()) - setExecuteDutyFunc(scheduler, executeDutiesCall, len(expected)) - - ticker.Send(currentSlot.Get()) - waitForDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - - // Stop scheduler & wait for graceful exit. - cancel() - require.NoError(t, schedulerPool.Wait()) -} - -func TestScheduler_SyncCommittee_Early_Block(t *testing.T) { - var ( - handler = NewSyncCommitteeHandler(dutystore.NewSyncCommitteeDuties()) - currentSlot = &SafeValue[phase0.Slot]{} - waitForDuties = &SafeValue[bool]{} - forkEpoch = phase0.Epoch(0) - dutiesMap = hashmap.New[uint64, []*v1.SyncCommitteeDuty]() - activeShares = []*ssvtypes.SSVShare{{ - Share: spectypes.Share{ - Committee: []*spectypes.ShareMember{ - {Signer: 1}, {Signer: 2}, {Signer: 3}, {Signer: 4}, - }, - ValidatorIndex: 1, - }, - }} - ) - dutiesMap.Set(0, []*v1.SyncCommitteeDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - - currentSlot.Set(phase0.Slot(0)) - scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) - fetchDutiesCall, executeDutiesCall := setupSyncCommitteeDutiesMock(scheduler, activeShares, dutiesMap, waitForDuties) - startFn() - - duties, _ := dutiesMap.Get(0) - expected := expectedExecutedSyncCommitteeDuties(handler, duties, currentSlot.Get()) - setExecuteDutyFunc(scheduler, executeDutiesCall, len(expected)) - - // STEP 1: wait for sync committee duties to be fetched and executed at the same slot - ticker.Send(currentSlot.Get()) - waitForDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - - // STEP 2: expect sync committee duties to be executed at the same period - currentSlot.Set(phase0.Slot(1)) - duties, _ = dutiesMap.Get(0) - expected = expectedExecutedSyncCommitteeDuties(handler, duties, currentSlot.Get()) - setExecuteDutyFunc(scheduler, executeDutiesCall, len(expected)) - - ticker.Send(currentSlot.Get()) - waitForDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - - // STEP 3: wait for sync committee duties to be executed faster than 1/3 of the slot duration - startTime := time.Now() - currentSlot.Set(phase0.Slot(2)) - duties, _ = dutiesMap.Get(0) - expected = expectedExecutedSyncCommitteeDuties(handler, duties, currentSlot.Get()) - setExecuteDutyFunc(scheduler, executeDutiesCall, len(expected)) - - ticker.Send(currentSlot.Get()) - - // STEP 4: trigger head event (block arrival) - e := &v1.Event{ - Data: &v1.HeadEvent{ - Slot: currentSlot.Get(), - }, - } - scheduler.HandleHeadEvent(logger)(e) - waitForDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - require.Greater(t, time.Since(startTime), scheduler.network.Beacon.SlotDurationSec()/3) - - // Stop scheduler & wait for graceful exit. - cancel() - require.NoError(t, schedulerPool.Wait()) -} From a2650228a7e7f228787d54e2f717d80879737c8b Mon Sep 17 00:00:00 2001 From: olegshmuelov <45327364+olegshmuelov@users.noreply.github.com> Date: Wed, 11 Sep 2024 15:00:08 +0300 Subject: [PATCH 02/14] tmp postfork --- operator/duties/committee_test.go | 360 +++++++++++++++++++++++++++++- 1 file changed, 352 insertions(+), 8 deletions(-) diff --git a/operator/duties/committee_test.go b/operator/duties/committee_test.go index 8225173530..6dbe19bb17 100644 --- a/operator/duties/committee_test.go +++ b/operator/duties/committee_test.go @@ -595,7 +595,7 @@ func TestScheduler_Committee_Indices_Changed_Attester_Only_3(t *testing.T) { } // reorg previous dependent root changed -func TestScheduler_Committee_Reorg_Previous_Epoch_Transition_Attester_only(t *testing.T) { +func TestScheduler_Committee_Reorg_Previous_Epoch_Transition_Attester_Only(t *testing.T) { var ( dutyStore = dutystore.New() attHandler = NewAttesterHandler(dutyStore.Attester) @@ -693,7 +693,7 @@ func TestScheduler_Committee_Reorg_Previous_Epoch_Transition_Attester_only(t *te } // reorg previous dependent root changed and the indices changed as well -func TestScheduler_Committee_Reorg_Previous_Epoch_Transition_Indices_Changed_Attester_only(t *testing.T) { +func TestScheduler_Committee_Reorg_Previous_Epoch_Transition_Indices_Changed_Attester_Only(t *testing.T) { var ( dutyStore = dutystore.New() attHandler = NewAttesterHandler(dutyStore.Attester) @@ -809,7 +809,7 @@ func TestScheduler_Committee_Reorg_Previous_Epoch_Transition_Indices_Changed_Att } // reorg previous dependent root changed -func TestScheduler_Committee_Reorg_Previous_Attester_only(t *testing.T) { +func TestScheduler_Committee_Reorg_Previous_Attester_Only(t *testing.T) { var ( dutyStore = dutystore.New() attHandler = NewAttesterHandler(dutyStore.Attester) @@ -831,7 +831,6 @@ func TestScheduler_Committee_Reorg_Previous_Attester_only(t *testing.T) { }, } ) - attDuties.Set(phase0.Epoch(1), []*eth2apiv1.AttesterDuty{ { PubKey: phase0.BLSPubKey{1, 2, 3}, @@ -840,13 +839,15 @@ func TestScheduler_Committee_Reorg_Previous_Attester_only(t *testing.T) { }, }) - // STEP 1: wait for attester duties to be fetched using handle initial duties + // STEP 1: wait for attester duties to be fetched (handle initial duties) currentSlot.Set(phase0.Slot(32)) scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot, alanForkEpoch) fetchDutiesCall, executeDutiesCall := setupCommitteeDutiesMock(scheduler, activeShares, attDuties, syncDuties, waitForDuties) startFn() - waitForDuties.Set(true) + ticker.Send(currentSlot.Get()) + waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + // STEP 2: trigger head event e := ð2apiv1.Event{ Data: ð2apiv1.HeadEvent{ @@ -858,6 +859,7 @@ func TestScheduler_Committee_Reorg_Previous_Attester_only(t *testing.T) { waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 3: Ticker with no action + waitForDuties.Set(true) currentSlot.Set(phase0.Slot(33)) ticker.Send(currentSlot.Get()) waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) @@ -879,8 +881,6 @@ func TestScheduler_Committee_Reorg_Previous_Attester_only(t *testing.T) { scheduler.HandleHeadEvent(logger)(e) // wait for attester duties to be fetched again for the current epoch waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - // no execution should happen in slot 33 - waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 5: Ticker with no action currentSlot.Set(phase0.Slot(34)) @@ -906,6 +906,350 @@ func TestScheduler_Committee_Reorg_Previous_Attester_only(t *testing.T) { require.NoError(t, schedulerPool.Wait()) } +// reorg previous dependent root changed and the indices changed the same slot +func TestScheduler_Committee_Reorg_Previous_Indices_Changed_Attester_Only(t *testing.T) { + var ( + dutyStore = dutystore.New() + attHandler = NewAttesterHandler(dutyStore.Attester) + syncHandler = NewSyncCommitteeHandler(dutyStore.SyncCommittee) + commHandler = NewCommitteeHandler(attHandler, syncHandler) + alanForkEpoch = phase0.Epoch(0) + currentSlot = &SafeValue[phase0.Slot]{} + waitForDuties = &SafeValue[bool]{} + attDuties = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() + syncDuties = hashmap.New[uint64, []*eth2apiv1.SyncCommitteeDuty]() + activeShares = []*ssvtypes.SSVShare{ + { + Share: spectypes.Share{ + Committee: []*spectypes.ShareMember{ + {Signer: 1}, {Signer: 2}, {Signer: 3}, {Signer: 4}, + }, + ValidatorIndex: 1, + }, + }, + { + Share: spectypes.Share{ + Committee: []*spectypes.ShareMember{ + {Signer: 1}, {Signer: 2}, {Signer: 3}, {Signer: 4}, + }, + ValidatorIndex: 2, + }, + }, + } + ) + attDuties.Set(phase0.Epoch(1), []*eth2apiv1.AttesterDuty{ + { + PubKey: phase0.BLSPubKey{1, 2, 3}, + Slot: phase0.Slot(35), + ValidatorIndex: phase0.ValidatorIndex(1), + }, + }) + + // STEP 1: wait for attester duties to be fetched (handle initial duties) + currentSlot.Set(phase0.Slot(32)) + scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot, alanForkEpoch) + fetchDutiesCall, executeDutiesCall := setupCommitteeDutiesMock(scheduler, activeShares, attDuties, syncDuties, waitForDuties) + startFn() + + ticker.Send(currentSlot.Get()) + waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + + // STEP 2: trigger head event + e := ð2apiv1.Event{ + Data: ð2apiv1.HeadEvent{ + Slot: currentSlot.Get(), + PreviousDutyDependentRoot: phase0.Root{0x01}, + }, + } + scheduler.HandleHeadEvent(logger)(e) + waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + + // STEP 3: Ticker with no action + currentSlot.Set(phase0.Slot(33)) + waitForDuties.Set(true) + ticker.Send(currentSlot.Get()) + waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + + // STEP 4: trigger reorg + e = ð2apiv1.Event{ + Data: ð2apiv1.HeadEvent{ + Slot: currentSlot.Get(), + PreviousDutyDependentRoot: phase0.Root{0x02}, + }, + } + attDuties.Set(phase0.Epoch(1), []*eth2apiv1.AttesterDuty{ + { + PubKey: phase0.BLSPubKey{1, 2, 3}, + Slot: phase0.Slot(36), + ValidatorIndex: phase0.ValidatorIndex(1), + }, + }) + scheduler.HandleHeadEvent(logger)(e) + // wait for attester duties to be fetched + waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + + // STEP 5: trigger indices change + scheduler.indicesChg <- struct{}{} + waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + aDuties, _ := attDuties.Get(phase0.Epoch(1)) + attDuties.Set(phase0.Epoch(1), append(aDuties, ð2apiv1.AttesterDuty{ + PubKey: phase0.BLSPubKey{1, 2, 4}, + Slot: phase0.Slot(36), + ValidatorIndex: phase0.ValidatorIndex(2), + })) + waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + + // STEP 6: wait for attester duties to be fetched again for the current epoch + currentSlot.Set(phase0.Slot(34)) + ticker.Send(currentSlot.Get()) + // wait for attester duties to be fetched + waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + // wait for sync committee duties to be fetched + waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + + // STEP 7: The first assigned duty should not be executed + currentSlot.Set(phase0.Slot(35)) + ticker.Send(currentSlot.Get()) + waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + + // STEP 8: The second and new from indices change assigned duties should be executed + currentSlot.Set(phase0.Slot(36)) + aDuties, _ = attDuties.Get(phase0.Epoch(1)) + committeeMap := commHandler.buildCommitteeDuties(aDuties, nil, 0, currentSlot.Get()) + setExecuteDutyFuncs(scheduler, executeDutiesCall, len(committeeMap)) + + ticker.Send(currentSlot.Get()) + waitForDutiesExecutionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout, committeeMap) + + // Stop scheduler & wait for graceful exit. + cancel() + require.NoError(t, schedulerPool.Wait()) +} + +// reorg current dependent root changed +func TestScheduler_Committee_Reorg_Current_Attester_Only(t *testing.T) { + var ( + dutyStore = dutystore.New() + attHandler = NewAttesterHandler(dutyStore.Attester) + syncHandler = NewSyncCommitteeHandler(dutyStore.SyncCommittee) + commHandler = NewCommitteeHandler(attHandler, syncHandler) + alanForkEpoch = phase0.Epoch(0) + currentSlot = &SafeValue[phase0.Slot]{} + waitForDuties = &SafeValue[bool]{} + attDuties = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() + syncDuties = hashmap.New[uint64, []*eth2apiv1.SyncCommitteeDuty]() + activeShares = []*ssvtypes.SSVShare{ + { + Share: spectypes.Share{ + Committee: []*spectypes.ShareMember{ + {Signer: 1}, {Signer: 2}, {Signer: 3}, {Signer: 4}, + }, + ValidatorIndex: 1, + }, + }, + } + ) + currentSlot.Set(phase0.Slot(48)) + scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot, alanForkEpoch) + fetchDutiesCall, executeDutiesCall := setupCommitteeDutiesMock(scheduler, activeShares, attDuties, syncDuties, waitForDuties) + startFn() + + attDuties.Set(phase0.Epoch(2), []*eth2apiv1.AttesterDuty{ + { + PubKey: phase0.BLSPubKey{1, 2, 3}, + Slot: phase0.Slot(64), + ValidatorIndex: phase0.ValidatorIndex(1), + }, + }) + + // STEP 1: wait for attester duties to be fetched for next epoch + waitForDuties.Set(true) + ticker.Send(currentSlot.Get()) + waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + + // STEP 2: trigger head event + e := ð2apiv1.Event{ + Data: ð2apiv1.HeadEvent{ + Slot: currentSlot.Get(), + CurrentDutyDependentRoot: phase0.Root{0x01}, + }, + } + scheduler.HandleHeadEvent(logger)(e) + waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + + // STEP 3: Ticker with no action + currentSlot.Set(phase0.Slot(49)) + ticker.Send(currentSlot.Get()) + waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + + // STEP 4: trigger reorg + e = ð2apiv1.Event{ + Data: ð2apiv1.HeadEvent{ + Slot: currentSlot.Get(), + CurrentDutyDependentRoot: phase0.Root{0x02}, + }, + } + attDuties.Set(phase0.Epoch(2), []*eth2apiv1.AttesterDuty{ + { + PubKey: phase0.BLSPubKey{1, 2, 3}, + Slot: phase0.Slot(65), + ValidatorIndex: phase0.ValidatorIndex(1), + }, + }) + scheduler.HandleHeadEvent(logger)(e) + waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + + // STEP 5: wait for attester duties to be fetched again for the current epoch + currentSlot.Set(phase0.Slot(50)) + ticker.Send(currentSlot.Get()) + waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + + // STEP 6: skip to the next epoch + currentSlot.Set(phase0.Slot(51)) + for slot := currentSlot.Get(); slot < 64; slot++ { + ticker.Send(slot) + waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + currentSlot.Set(slot + 1) + } + + // STEP 7: The first assigned duty should not be executed + // slot = 64 + ticker.Send(currentSlot.Get()) + waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + + // STEP 8: The second assigned duty should be executed + currentSlot.Set(phase0.Slot(65)) + aDuties, _ := attDuties.Get(phase0.Epoch(2)) + committeeMap := commHandler.buildCommitteeDuties(aDuties, nil, 0, currentSlot.Get()) + setExecuteDutyFuncs(scheduler, executeDutiesCall, len(committeeMap)) + + ticker.Send(currentSlot.Get()) + waitForDutiesExecutionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout, committeeMap) + + // Stop scheduler & wait for graceful exit. + cancel() + require.NoError(t, schedulerPool.Wait()) +} + +// reorg current dependent root changed including indices change in the same slot +func TestScheduler_Committee_Reorg_Current_Indices_Changed_Attester_Only(t *testing.T) { + var ( + dutyStore = dutystore.New() + attHandler = NewAttesterHandler(dutyStore.Attester) + syncHandler = NewSyncCommitteeHandler(dutyStore.SyncCommittee) + commHandler = NewCommitteeHandler(attHandler, syncHandler) + alanForkEpoch = phase0.Epoch(0) + currentSlot = &SafeValue[phase0.Slot]{} + waitForDuties = &SafeValue[bool]{} + attDuties = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() + syncDuties = hashmap.New[uint64, []*eth2apiv1.SyncCommitteeDuty]() + activeShares = []*ssvtypes.SSVShare{ + { + Share: spectypes.Share{ + Committee: []*spectypes.ShareMember{ + {Signer: 1}, {Signer: 2}, {Signer: 3}, {Signer: 4}, + }, + ValidatorIndex: 1, + }, + }, + { + Share: spectypes.Share{ + Committee: []*spectypes.ShareMember{ + {Signer: 1}, {Signer: 2}, {Signer: 3}, {Signer: 4}, + }, + ValidatorIndex: 2, + }, + }, + } + ) + currentSlot.Set(phase0.Slot(48)) + scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot, alanForkEpoch) + fetchDutiesCall, executeDutiesCall := setupCommitteeDutiesMock(scheduler, activeShares, attDuties, syncDuties, waitForDuties) + startFn() + + attDuties.Set(phase0.Epoch(2), []*eth2apiv1.AttesterDuty{ + { + PubKey: phase0.BLSPubKey{1, 2, 3}, + Slot: phase0.Slot(48), + ValidatorIndex: phase0.ValidatorIndex(1), + }, + }) + + // STEP 1: wait for attester duties to be fetched for next epoch + waitForDuties.Set(true) + ticker.Send(currentSlot.Get()) + waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + + // STEP 2: trigger head event + e := ð2apiv1.Event{ + Data: ð2apiv1.HeadEvent{ + Slot: currentSlot.Get(), + CurrentDutyDependentRoot: phase0.Root{0x01}, + }, + } + scheduler.HandleHeadEvent(logger)(e) + waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + + // STEP 3: Ticker with no action + currentSlot.Set(phase0.Slot(49)) + ticker.Send(currentSlot.Get()) + waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + + // STEP 4: trigger reorg + e = ð2apiv1.Event{ + Data: ð2apiv1.HeadEvent{ + Slot: currentSlot.Get(), + CurrentDutyDependentRoot: phase0.Root{0x02}, + }, + } + attDuties.Set(phase0.Epoch(2), []*eth2apiv1.AttesterDuty{ + { + PubKey: phase0.BLSPubKey{1, 2, 3}, + Slot: phase0.Slot(65), + ValidatorIndex: phase0.ValidatorIndex(1), + }, + }) + scheduler.HandleHeadEvent(logger)(e) + waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + + // STEP 5: trigger indices change + scheduler.indicesChg <- struct{}{} + waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + aDuties, _ := attDuties.Get(phase0.Epoch(2)) + attDuties.Set(phase0.Epoch(2), append(aDuties, ð2apiv1.AttesterDuty{ + PubKey: phase0.BLSPubKey{1, 2, 4}, + Slot: phase0.Slot(65), + ValidatorIndex: phase0.ValidatorIndex(2), + })) + waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + + // STEP 6: wait for attester duties to be fetched again for the next epoch due to indices change + currentSlot.Set(phase0.Slot(50)) + ticker.Send(currentSlot.Get()) + // wait for attester duties to be fetched + waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + // wait for sync committee duties to be fetched + waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + + // STEP 7: The first assigned duty should not be executed + currentSlot.Set(phase0.Slot(35)) + ticker.Send(currentSlot.Get()) + waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + + // STEP 8: The second and new from indices change assigned duties should be executed + currentSlot.Set(phase0.Slot(36)) + aDuties, _ = attDuties.Get(phase0.Epoch(1)) + committeeMap := commHandler.buildCommitteeDuties(aDuties, nil, 0, currentSlot.Get()) + setExecuteDutyFuncs(scheduler, executeDutiesCall, len(committeeMap)) + + ticker.Send(currentSlot.Get()) + waitForDutiesExecutionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout, committeeMap) + + // Stop scheduler & wait for graceful exit. + cancel() + require.NoError(t, schedulerPool.Wait()) +} + func TestScheduler_Committee_Early_Block_Attester_Only(t *testing.T) { var ( dutyStore = dutystore.New() From 33e76c8badb284b85ad987d4162133092423194d Mon Sep 17 00:00:00 2001 From: olegshmuelov <45327364+olegshmuelov@users.noreply.github.com> Date: Wed, 11 Sep 2024 15:01:02 +0300 Subject: [PATCH 03/14] Revert "tmp postfork" This reverts commit a2650228a7e7f228787d54e2f717d80879737c8b. --- operator/duties/committee_test.go | 360 +----------------------------- 1 file changed, 8 insertions(+), 352 deletions(-) diff --git a/operator/duties/committee_test.go b/operator/duties/committee_test.go index 6dbe19bb17..8225173530 100644 --- a/operator/duties/committee_test.go +++ b/operator/duties/committee_test.go @@ -595,7 +595,7 @@ func TestScheduler_Committee_Indices_Changed_Attester_Only_3(t *testing.T) { } // reorg previous dependent root changed -func TestScheduler_Committee_Reorg_Previous_Epoch_Transition_Attester_Only(t *testing.T) { +func TestScheduler_Committee_Reorg_Previous_Epoch_Transition_Attester_only(t *testing.T) { var ( dutyStore = dutystore.New() attHandler = NewAttesterHandler(dutyStore.Attester) @@ -693,7 +693,7 @@ func TestScheduler_Committee_Reorg_Previous_Epoch_Transition_Attester_Only(t *te } // reorg previous dependent root changed and the indices changed as well -func TestScheduler_Committee_Reorg_Previous_Epoch_Transition_Indices_Changed_Attester_Only(t *testing.T) { +func TestScheduler_Committee_Reorg_Previous_Epoch_Transition_Indices_Changed_Attester_only(t *testing.T) { var ( dutyStore = dutystore.New() attHandler = NewAttesterHandler(dutyStore.Attester) @@ -809,7 +809,7 @@ func TestScheduler_Committee_Reorg_Previous_Epoch_Transition_Indices_Changed_Att } // reorg previous dependent root changed -func TestScheduler_Committee_Reorg_Previous_Attester_Only(t *testing.T) { +func TestScheduler_Committee_Reorg_Previous_Attester_only(t *testing.T) { var ( dutyStore = dutystore.New() attHandler = NewAttesterHandler(dutyStore.Attester) @@ -831,6 +831,7 @@ func TestScheduler_Committee_Reorg_Previous_Attester_Only(t *testing.T) { }, } ) + attDuties.Set(phase0.Epoch(1), []*eth2apiv1.AttesterDuty{ { PubKey: phase0.BLSPubKey{1, 2, 3}, @@ -839,15 +840,13 @@ func TestScheduler_Committee_Reorg_Previous_Attester_Only(t *testing.T) { }, }) - // STEP 1: wait for attester duties to be fetched (handle initial duties) + // STEP 1: wait for attester duties to be fetched using handle initial duties currentSlot.Set(phase0.Slot(32)) scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot, alanForkEpoch) fetchDutiesCall, executeDutiesCall := setupCommitteeDutiesMock(scheduler, activeShares, attDuties, syncDuties, waitForDuties) startFn() - ticker.Send(currentSlot.Get()) - waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - + waitForDuties.Set(true) // STEP 2: trigger head event e := ð2apiv1.Event{ Data: ð2apiv1.HeadEvent{ @@ -859,7 +858,6 @@ func TestScheduler_Committee_Reorg_Previous_Attester_Only(t *testing.T) { waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 3: Ticker with no action - waitForDuties.Set(true) currentSlot.Set(phase0.Slot(33)) ticker.Send(currentSlot.Get()) waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) @@ -881,6 +879,8 @@ func TestScheduler_Committee_Reorg_Previous_Attester_Only(t *testing.T) { scheduler.HandleHeadEvent(logger)(e) // wait for attester duties to be fetched again for the current epoch waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + // no execution should happen in slot 33 + waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 5: Ticker with no action currentSlot.Set(phase0.Slot(34)) @@ -906,350 +906,6 @@ func TestScheduler_Committee_Reorg_Previous_Attester_Only(t *testing.T) { require.NoError(t, schedulerPool.Wait()) } -// reorg previous dependent root changed and the indices changed the same slot -func TestScheduler_Committee_Reorg_Previous_Indices_Changed_Attester_Only(t *testing.T) { - var ( - dutyStore = dutystore.New() - attHandler = NewAttesterHandler(dutyStore.Attester) - syncHandler = NewSyncCommitteeHandler(dutyStore.SyncCommittee) - commHandler = NewCommitteeHandler(attHandler, syncHandler) - alanForkEpoch = phase0.Epoch(0) - currentSlot = &SafeValue[phase0.Slot]{} - waitForDuties = &SafeValue[bool]{} - attDuties = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() - syncDuties = hashmap.New[uint64, []*eth2apiv1.SyncCommitteeDuty]() - activeShares = []*ssvtypes.SSVShare{ - { - Share: spectypes.Share{ - Committee: []*spectypes.ShareMember{ - {Signer: 1}, {Signer: 2}, {Signer: 3}, {Signer: 4}, - }, - ValidatorIndex: 1, - }, - }, - { - Share: spectypes.Share{ - Committee: []*spectypes.ShareMember{ - {Signer: 1}, {Signer: 2}, {Signer: 3}, {Signer: 4}, - }, - ValidatorIndex: 2, - }, - }, - } - ) - attDuties.Set(phase0.Epoch(1), []*eth2apiv1.AttesterDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - Slot: phase0.Slot(35), - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - - // STEP 1: wait for attester duties to be fetched (handle initial duties) - currentSlot.Set(phase0.Slot(32)) - scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot, alanForkEpoch) - fetchDutiesCall, executeDutiesCall := setupCommitteeDutiesMock(scheduler, activeShares, attDuties, syncDuties, waitForDuties) - startFn() - - ticker.Send(currentSlot.Get()) - waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 2: trigger head event - e := ð2apiv1.Event{ - Data: ð2apiv1.HeadEvent{ - Slot: currentSlot.Get(), - PreviousDutyDependentRoot: phase0.Root{0x01}, - }, - } - scheduler.HandleHeadEvent(logger)(e) - waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 3: Ticker with no action - currentSlot.Set(phase0.Slot(33)) - waitForDuties.Set(true) - ticker.Send(currentSlot.Get()) - waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 4: trigger reorg - e = ð2apiv1.Event{ - Data: ð2apiv1.HeadEvent{ - Slot: currentSlot.Get(), - PreviousDutyDependentRoot: phase0.Root{0x02}, - }, - } - attDuties.Set(phase0.Epoch(1), []*eth2apiv1.AttesterDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - Slot: phase0.Slot(36), - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - scheduler.HandleHeadEvent(logger)(e) - // wait for attester duties to be fetched - waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 5: trigger indices change - scheduler.indicesChg <- struct{}{} - waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - aDuties, _ := attDuties.Get(phase0.Epoch(1)) - attDuties.Set(phase0.Epoch(1), append(aDuties, ð2apiv1.AttesterDuty{ - PubKey: phase0.BLSPubKey{1, 2, 4}, - Slot: phase0.Slot(36), - ValidatorIndex: phase0.ValidatorIndex(2), - })) - waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 6: wait for attester duties to be fetched again for the current epoch - currentSlot.Set(phase0.Slot(34)) - ticker.Send(currentSlot.Get()) - // wait for attester duties to be fetched - waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - // wait for sync committee duties to be fetched - waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 7: The first assigned duty should not be executed - currentSlot.Set(phase0.Slot(35)) - ticker.Send(currentSlot.Get()) - waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 8: The second and new from indices change assigned duties should be executed - currentSlot.Set(phase0.Slot(36)) - aDuties, _ = attDuties.Get(phase0.Epoch(1)) - committeeMap := commHandler.buildCommitteeDuties(aDuties, nil, 0, currentSlot.Get()) - setExecuteDutyFuncs(scheduler, executeDutiesCall, len(committeeMap)) - - ticker.Send(currentSlot.Get()) - waitForDutiesExecutionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout, committeeMap) - - // Stop scheduler & wait for graceful exit. - cancel() - require.NoError(t, schedulerPool.Wait()) -} - -// reorg current dependent root changed -func TestScheduler_Committee_Reorg_Current_Attester_Only(t *testing.T) { - var ( - dutyStore = dutystore.New() - attHandler = NewAttesterHandler(dutyStore.Attester) - syncHandler = NewSyncCommitteeHandler(dutyStore.SyncCommittee) - commHandler = NewCommitteeHandler(attHandler, syncHandler) - alanForkEpoch = phase0.Epoch(0) - currentSlot = &SafeValue[phase0.Slot]{} - waitForDuties = &SafeValue[bool]{} - attDuties = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() - syncDuties = hashmap.New[uint64, []*eth2apiv1.SyncCommitteeDuty]() - activeShares = []*ssvtypes.SSVShare{ - { - Share: spectypes.Share{ - Committee: []*spectypes.ShareMember{ - {Signer: 1}, {Signer: 2}, {Signer: 3}, {Signer: 4}, - }, - ValidatorIndex: 1, - }, - }, - } - ) - currentSlot.Set(phase0.Slot(48)) - scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot, alanForkEpoch) - fetchDutiesCall, executeDutiesCall := setupCommitteeDutiesMock(scheduler, activeShares, attDuties, syncDuties, waitForDuties) - startFn() - - attDuties.Set(phase0.Epoch(2), []*eth2apiv1.AttesterDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - Slot: phase0.Slot(64), - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - - // STEP 1: wait for attester duties to be fetched for next epoch - waitForDuties.Set(true) - ticker.Send(currentSlot.Get()) - waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 2: trigger head event - e := ð2apiv1.Event{ - Data: ð2apiv1.HeadEvent{ - Slot: currentSlot.Get(), - CurrentDutyDependentRoot: phase0.Root{0x01}, - }, - } - scheduler.HandleHeadEvent(logger)(e) - waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 3: Ticker with no action - currentSlot.Set(phase0.Slot(49)) - ticker.Send(currentSlot.Get()) - waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 4: trigger reorg - e = ð2apiv1.Event{ - Data: ð2apiv1.HeadEvent{ - Slot: currentSlot.Get(), - CurrentDutyDependentRoot: phase0.Root{0x02}, - }, - } - attDuties.Set(phase0.Epoch(2), []*eth2apiv1.AttesterDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - Slot: phase0.Slot(65), - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - scheduler.HandleHeadEvent(logger)(e) - waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 5: wait for attester duties to be fetched again for the current epoch - currentSlot.Set(phase0.Slot(50)) - ticker.Send(currentSlot.Get()) - waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 6: skip to the next epoch - currentSlot.Set(phase0.Slot(51)) - for slot := currentSlot.Get(); slot < 64; slot++ { - ticker.Send(slot) - waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - currentSlot.Set(slot + 1) - } - - // STEP 7: The first assigned duty should not be executed - // slot = 64 - ticker.Send(currentSlot.Get()) - waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 8: The second assigned duty should be executed - currentSlot.Set(phase0.Slot(65)) - aDuties, _ := attDuties.Get(phase0.Epoch(2)) - committeeMap := commHandler.buildCommitteeDuties(aDuties, nil, 0, currentSlot.Get()) - setExecuteDutyFuncs(scheduler, executeDutiesCall, len(committeeMap)) - - ticker.Send(currentSlot.Get()) - waitForDutiesExecutionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout, committeeMap) - - // Stop scheduler & wait for graceful exit. - cancel() - require.NoError(t, schedulerPool.Wait()) -} - -// reorg current dependent root changed including indices change in the same slot -func TestScheduler_Committee_Reorg_Current_Indices_Changed_Attester_Only(t *testing.T) { - var ( - dutyStore = dutystore.New() - attHandler = NewAttesterHandler(dutyStore.Attester) - syncHandler = NewSyncCommitteeHandler(dutyStore.SyncCommittee) - commHandler = NewCommitteeHandler(attHandler, syncHandler) - alanForkEpoch = phase0.Epoch(0) - currentSlot = &SafeValue[phase0.Slot]{} - waitForDuties = &SafeValue[bool]{} - attDuties = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() - syncDuties = hashmap.New[uint64, []*eth2apiv1.SyncCommitteeDuty]() - activeShares = []*ssvtypes.SSVShare{ - { - Share: spectypes.Share{ - Committee: []*spectypes.ShareMember{ - {Signer: 1}, {Signer: 2}, {Signer: 3}, {Signer: 4}, - }, - ValidatorIndex: 1, - }, - }, - { - Share: spectypes.Share{ - Committee: []*spectypes.ShareMember{ - {Signer: 1}, {Signer: 2}, {Signer: 3}, {Signer: 4}, - }, - ValidatorIndex: 2, - }, - }, - } - ) - currentSlot.Set(phase0.Slot(48)) - scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot, alanForkEpoch) - fetchDutiesCall, executeDutiesCall := setupCommitteeDutiesMock(scheduler, activeShares, attDuties, syncDuties, waitForDuties) - startFn() - - attDuties.Set(phase0.Epoch(2), []*eth2apiv1.AttesterDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - Slot: phase0.Slot(48), - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - - // STEP 1: wait for attester duties to be fetched for next epoch - waitForDuties.Set(true) - ticker.Send(currentSlot.Get()) - waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 2: trigger head event - e := ð2apiv1.Event{ - Data: ð2apiv1.HeadEvent{ - Slot: currentSlot.Get(), - CurrentDutyDependentRoot: phase0.Root{0x01}, - }, - } - scheduler.HandleHeadEvent(logger)(e) - waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 3: Ticker with no action - currentSlot.Set(phase0.Slot(49)) - ticker.Send(currentSlot.Get()) - waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 4: trigger reorg - e = ð2apiv1.Event{ - Data: ð2apiv1.HeadEvent{ - Slot: currentSlot.Get(), - CurrentDutyDependentRoot: phase0.Root{0x02}, - }, - } - attDuties.Set(phase0.Epoch(2), []*eth2apiv1.AttesterDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - Slot: phase0.Slot(65), - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - scheduler.HandleHeadEvent(logger)(e) - waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 5: trigger indices change - scheduler.indicesChg <- struct{}{} - waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - aDuties, _ := attDuties.Get(phase0.Epoch(2)) - attDuties.Set(phase0.Epoch(2), append(aDuties, ð2apiv1.AttesterDuty{ - PubKey: phase0.BLSPubKey{1, 2, 4}, - Slot: phase0.Slot(65), - ValidatorIndex: phase0.ValidatorIndex(2), - })) - waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 6: wait for attester duties to be fetched again for the next epoch due to indices change - currentSlot.Set(phase0.Slot(50)) - ticker.Send(currentSlot.Get()) - // wait for attester duties to be fetched - waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - // wait for sync committee duties to be fetched - waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 7: The first assigned duty should not be executed - currentSlot.Set(phase0.Slot(35)) - ticker.Send(currentSlot.Get()) - waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 8: The second and new from indices change assigned duties should be executed - currentSlot.Set(phase0.Slot(36)) - aDuties, _ = attDuties.Get(phase0.Epoch(1)) - committeeMap := commHandler.buildCommitteeDuties(aDuties, nil, 0, currentSlot.Get()) - setExecuteDutyFuncs(scheduler, executeDutiesCall, len(committeeMap)) - - ticker.Send(currentSlot.Get()) - waitForDutiesExecutionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout, committeeMap) - - // Stop scheduler & wait for graceful exit. - cancel() - require.NoError(t, schedulerPool.Wait()) -} - func TestScheduler_Committee_Early_Block_Attester_Only(t *testing.T) { var ( dutyStore = dutystore.New() From caeeb87cbfd3dfd36d6168ddbc8e6dabec08d8f8 Mon Sep 17 00:00:00 2001 From: olegshmuelov <45327364+olegshmuelov@users.noreply.github.com> Date: Wed, 11 Sep 2024 15:01:17 +0300 Subject: [PATCH 04/14] tmp postfork --- networkconfig/holesky-stage.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/networkconfig/holesky-stage.go b/networkconfig/holesky-stage.go index 07d8747794..a545cb493f 100644 --- a/networkconfig/holesky-stage.go +++ b/networkconfig/holesky-stage.go @@ -16,7 +16,7 @@ var HoleskyStage = NetworkConfig{ GenesisEpoch: 1, RegistrySyncOffset: new(big.Int).SetInt64(84599), RegistryContractAddr: "0x0d33801785340072C452b994496B19f196b7eE15", - AlanForkEpoch: 99999999, + //AlanForkEpoch: 99999999, Bootnodes: []string{ // Public bootnode: // "enr:-Ja4QDYHVgUs9NvlMqq93ot6VNqbmrIlMrwKnq4X3DPRgyUNB4ospDp8ubMvsf-KsgqY8rzpZKy4GbE1DLphabpRBc-GAY_diLjngmlkgnY0gmlwhDQrLYqJc2VjcDI1NmsxoQKnAiuSlgSR8asjCH0aYoVKM8uPbi4noFuFHZHaAHqknYNzc3YBg3RjcIITiYN1ZHCCD6E", From 276ab6d0e6d2fa38eb85ab4f597f7b75eb1fa467 Mon Sep 17 00:00:00 2001 From: olegshmuelov <45327364+olegshmuelov@users.noreply.github.com> Date: Wed, 11 Sep 2024 15:39:45 +0300 Subject: [PATCH 05/14] renaming --- operator/duties/attester_genesis_test.go | 156 +++++++++--------- operator/duties/proposer_genesis_test.go | 18 +- operator/duties/proposer_test.go | 2 +- operator/duties/scheduler_test.go | 2 +- .../duties/sync_committee_genesis_test.go | 20 +-- 5 files changed, 99 insertions(+), 99 deletions(-) diff --git a/operator/duties/attester_genesis_test.go b/operator/duties/attester_genesis_test.go index e43e0ca544..385dff5f8a 100644 --- a/operator/duties/attester_genesis_test.go +++ b/operator/duties/attester_genesis_test.go @@ -159,12 +159,12 @@ func TestScheduler_Attester_Genesis_Indices_Changed(t *testing.T) { forkEpoch = goclient.FarFutureEpoch ) currentSlot.Set(phase0.Slot(0)) - scheduler, logger, mockTicker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) + scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) fetchDutiesCall, executeDutiesCall := setupAttesterGenesisDutiesMock(scheduler, dutiesMap, waitForDuties) startFn() // STEP 1: wait for no action to be taken - mockTicker.Send(currentSlot.Get()) + ticker.Send(currentSlot.Get()) waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 2: trigger a change in active indices @@ -192,8 +192,8 @@ func TestScheduler_Attester_Genesis_Indices_Changed(t *testing.T) { // STEP 3: wait for attester duties to be fetched again currentSlot.Set(phase0.Slot(1)) waitForDuties.Set(true) - mockTicker.Send(currentSlot.Get()) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + ticker.Send(currentSlot.Get()) + waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // no execution should happen in slot 1 waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) @@ -203,7 +203,7 @@ func TestScheduler_Attester_Genesis_Indices_Changed(t *testing.T) { expected := expectedExecutedGenesisAttesterDuties(handler, []*eth2apiv1.AttesterDuty{duties[2]}) setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) - mockTicker.Send(currentSlot.Get()) + ticker.Send(currentSlot.Get()) waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) // Stop scheduler & wait for graceful exit. @@ -220,17 +220,17 @@ func TestScheduler_Attester_Genesis_Multiple_Indices_Changed_Same_Slot(t *testin forkEpoch = goclient.FarFutureEpoch ) currentSlot.Set(phase0.Slot(0)) - scheduler, logger, mockTicker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) + scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) fetchDutiesCall, executeDutiesCall := setupAttesterGenesisDutiesMock(scheduler, dutiesMap, waitForDuties) startFn() // STEP 1: wait for no action to be taken - mockTicker.Send(currentSlot.Get()) + ticker.Send(currentSlot.Get()) waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 2: wait for no action to be taken currentSlot.Set(phase0.Slot(1)) - mockTicker.Send(currentSlot.Get()) + ticker.Send(currentSlot.Get()) waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 3: trigger a change in active indices @@ -256,8 +256,8 @@ func TestScheduler_Attester_Genesis_Multiple_Indices_Changed_Same_Slot(t *testin // STEP 5: wait for attester duties to be fetched currentSlot.Set(phase0.Slot(2)) waitForDuties.Set(true) - mockTicker.Send(currentSlot.Get()) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + ticker.Send(currentSlot.Get()) + waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 6: wait for attester duties to be executed currentSlot.Set(phase0.Slot(3)) @@ -265,7 +265,7 @@ func TestScheduler_Attester_Genesis_Multiple_Indices_Changed_Same_Slot(t *testin expected := expectedExecutedGenesisAttesterDuties(handler, []*eth2apiv1.AttesterDuty{duties[0]}) setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) - mockTicker.Send(currentSlot.Get()) + ticker.Send(currentSlot.Get()) waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) // STEP 7: wait for attester duties to be executed @@ -274,7 +274,7 @@ func TestScheduler_Attester_Genesis_Multiple_Indices_Changed_Same_Slot(t *testin expected = expectedExecutedGenesisAttesterDuties(handler, []*eth2apiv1.AttesterDuty{duties[1]}) setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) - mockTicker.Send(currentSlot.Get()) + ticker.Send(currentSlot.Get()) waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) // Stop scheduler & wait for graceful exit. @@ -292,7 +292,7 @@ func TestScheduler_Attester_Genesis_Reorg_Previous_Epoch_Transition(t *testing.T forkEpoch = goclient.FarFutureEpoch ) currentSlot.Set(phase0.Slot(63)) - scheduler, logger, mockTicker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) + scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) fetchDutiesCall, executeDutiesCall := setupAttesterGenesisDutiesMock(scheduler, dutiesMap, waitForDuties) startFn() @@ -306,8 +306,8 @@ func TestScheduler_Attester_Genesis_Reorg_Previous_Epoch_Transition(t *testing.T // STEP 1: wait for attester duties to be fetched for next epoch waitForDuties.Set(true) - mockTicker.Send(currentSlot.Get()) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + ticker.Send(currentSlot.Get()) + waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 2: trigger head event e := ð2apiv1.Event{ @@ -322,7 +322,7 @@ func TestScheduler_Attester_Genesis_Reorg_Previous_Epoch_Transition(t *testing.T // STEP 3: Ticker with no action currentSlot.Set(phase0.Slot(64)) - mockTicker.Send(currentSlot.Get()) + ticker.Send(currentSlot.Get()) waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 4: trigger reorg on epoch transition @@ -340,16 +340,16 @@ func TestScheduler_Attester_Genesis_Reorg_Previous_Epoch_Transition(t *testing.T }, }) scheduler.HandleHeadEvent(logger)(e) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 5: wait for attester duties to be fetched again for the current epoch currentSlot.Set(phase0.Slot(65)) - mockTicker.Send(currentSlot.Get()) + ticker.Send(currentSlot.Get()) waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 6: The first assigned duty should not be executed currentSlot.Set(phase0.Slot(66)) - mockTicker.Send(currentSlot.Get()) + ticker.Send(currentSlot.Get()) waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 7: The second assigned duty should be executed @@ -358,7 +358,7 @@ func TestScheduler_Attester_Genesis_Reorg_Previous_Epoch_Transition(t *testing.T expected := expectedExecutedGenesisAttesterDuties(handler, duties) setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) - mockTicker.Send(currentSlot.Get()) + ticker.Send(currentSlot.Get()) waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) // Stop scheduler & wait for graceful exit. @@ -376,7 +376,7 @@ func TestScheduler_Attester_Genesis_Reorg_Previous_Epoch_Transition_Indices_Chan forkEpoch = goclient.FarFutureEpoch ) currentSlot.Set(phase0.Slot(63)) - scheduler, logger, mockTicker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) + scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) fetchDutiesCall, executeDutiesCall := setupAttesterGenesisDutiesMock(scheduler, dutiesMap, waitForDuties) startFn() @@ -390,8 +390,8 @@ func TestScheduler_Attester_Genesis_Reorg_Previous_Epoch_Transition_Indices_Chan // STEP 1: wait for attester duties to be fetched for next epoch waitForDuties.Set(true) - mockTicker.Send(currentSlot.Get()) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + ticker.Send(currentSlot.Get()) + waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 2: trigger head event @@ -407,7 +407,7 @@ func TestScheduler_Attester_Genesis_Reorg_Previous_Epoch_Transition_Indices_Chan // STEP 3: Ticker with no action currentSlot.Set(phase0.Slot(64)) - mockTicker.Send(currentSlot.Get()) + ticker.Send(currentSlot.Get()) waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 4: trigger reorg on epoch transition @@ -425,7 +425,7 @@ func TestScheduler_Attester_Genesis_Reorg_Previous_Epoch_Transition_Indices_Chan }, }) scheduler.HandleHeadEvent(logger)(e) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 5: trigger indices change scheduler.indicesChg <- struct{}{} @@ -439,12 +439,12 @@ func TestScheduler_Attester_Genesis_Reorg_Previous_Epoch_Transition_Indices_Chan // STEP 6: wait for attester duties to be fetched again for the current epoch currentSlot.Set(phase0.Slot(65)) - mockTicker.Send(currentSlot.Get()) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + ticker.Send(currentSlot.Get()) + waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 7: The first assigned duty should not be executed currentSlot.Set(phase0.Slot(66)) - mockTicker.Send(currentSlot.Get()) + ticker.Send(currentSlot.Get()) waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 8: The second assigned duty should be executed @@ -453,7 +453,7 @@ func TestScheduler_Attester_Genesis_Reorg_Previous_Epoch_Transition_Indices_Chan expected := expectedExecutedGenesisAttesterDuties(handler, duties) setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) - mockTicker.Send(currentSlot.Get()) + ticker.Send(currentSlot.Get()) waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) // Stop scheduler & wait for graceful exit. @@ -480,11 +480,11 @@ func TestScheduler_Attester_Genesis_Reorg_Previous(t *testing.T) { currentSlot.Set(phase0.Slot(32)) // STEP 1: wait for attester duties to be fetched (handle initial duties) - scheduler, logger, mockTicker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) + scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) fetchDutiesCall, executeDutiesCall := setupAttesterGenesisDutiesMock(scheduler, dutiesMap, waitForDuties) startFn() - mockTicker.Send(currentSlot.Get()) + ticker.Send(currentSlot.Get()) waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 2: trigger head event @@ -500,7 +500,7 @@ func TestScheduler_Attester_Genesis_Reorg_Previous(t *testing.T) { // STEP 3: Ticker with no action waitForDuties.Set(true) currentSlot.Set(phase0.Slot(33)) - mockTicker.Send(currentSlot.Get()) + ticker.Send(currentSlot.Get()) waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 4: trigger reorg @@ -518,16 +518,16 @@ func TestScheduler_Attester_Genesis_Reorg_Previous(t *testing.T) { }, }) scheduler.HandleHeadEvent(logger)(e) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 5: wait for no action to be taken currentSlot.Set(phase0.Slot(34)) - mockTicker.Send(currentSlot.Get()) + ticker.Send(currentSlot.Get()) waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 6: The first assigned duty should not be executed currentSlot.Set(phase0.Slot(35)) - mockTicker.Send(currentSlot.Get()) + ticker.Send(currentSlot.Get()) waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 7: The second assigned duty should be executed @@ -536,7 +536,7 @@ func TestScheduler_Attester_Genesis_Reorg_Previous(t *testing.T) { expected := expectedExecutedGenesisAttesterDuties(handler, duties) setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) - mockTicker.Send(currentSlot.Get()) + ticker.Send(currentSlot.Get()) waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) // Stop scheduler & wait for graceful exit. @@ -563,11 +563,11 @@ func TestScheduler_Attester_Genesis_Reorg_Previous_Indices_Change_Same_Slot(t *t currentSlot.Set(phase0.Slot(32)) // STEP 1: wait for attester duties to be fetched (handle initial duties) - scheduler, logger, mockTicker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) + scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) fetchDutiesCall, executeDutiesCall := setupAttesterGenesisDutiesMock(scheduler, dutiesMap, waitForDuties) startFn() - mockTicker.Send(currentSlot.Get()) + ticker.Send(currentSlot.Get()) waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 2: trigger head event @@ -583,7 +583,7 @@ func TestScheduler_Attester_Genesis_Reorg_Previous_Indices_Change_Same_Slot(t *t // STEP 3: Ticker with no action currentSlot.Set(phase0.Slot(33)) waitForDuties.Set(true) - mockTicker.Send(currentSlot.Get()) + ticker.Send(currentSlot.Get()) waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 4: trigger reorg @@ -601,7 +601,7 @@ func TestScheduler_Attester_Genesis_Reorg_Previous_Indices_Change_Same_Slot(t *t }, }) scheduler.HandleHeadEvent(logger)(e) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 5: trigger indices change scheduler.indicesChg <- struct{}{} @@ -615,12 +615,12 @@ func TestScheduler_Attester_Genesis_Reorg_Previous_Indices_Change_Same_Slot(t *t // STEP 6: wait for attester duties to be fetched again for the current epoch currentSlot.Set(phase0.Slot(34)) - mockTicker.Send(currentSlot.Get()) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + ticker.Send(currentSlot.Get()) + waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 7: The first assigned duty should not be executed currentSlot.Set(phase0.Slot(35)) - mockTicker.Send(currentSlot.Get()) + ticker.Send(currentSlot.Get()) waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 8: The second and new from indices change assigned duties should be executed @@ -629,7 +629,7 @@ func TestScheduler_Attester_Genesis_Reorg_Previous_Indices_Change_Same_Slot(t *t expected := expectedExecutedGenesisAttesterDuties(handler, duties) setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) - mockTicker.Send(currentSlot.Get()) + ticker.Send(currentSlot.Get()) waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) // Stop scheduler & wait for graceful exit. @@ -647,7 +647,7 @@ func TestScheduler_Attester_Genesis_Reorg_Current(t *testing.T) { forkEpoch = goclient.FarFutureEpoch ) currentSlot.Set(phase0.Slot(48)) - scheduler, logger, mockTicker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) + scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) fetchDutiesCall, executeDutiesCall := setupAttesterGenesisDutiesMock(scheduler, dutiesMap, waitForDuties) startFn() @@ -661,8 +661,8 @@ func TestScheduler_Attester_Genesis_Reorg_Current(t *testing.T) { // STEP 1: wait for attester duties to be fetched for next epoch waitForDuties.Set(true) - mockTicker.Send(currentSlot.Get()) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + ticker.Send(currentSlot.Get()) + waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 2: trigger head event e := ð2apiv1.Event{ @@ -676,7 +676,7 @@ func TestScheduler_Attester_Genesis_Reorg_Current(t *testing.T) { // STEP 3: Ticker with no action currentSlot.Set(phase0.Slot(49)) - mockTicker.Send(currentSlot.Get()) + ticker.Send(currentSlot.Get()) waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 4: trigger reorg @@ -698,20 +698,20 @@ func TestScheduler_Attester_Genesis_Reorg_Current(t *testing.T) { // STEP 5: wait for attester duties to be fetched again for the current epoch currentSlot.Set(phase0.Slot(50)) - mockTicker.Send(currentSlot.Get()) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + ticker.Send(currentSlot.Get()) + waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 6: skip to the next epoch currentSlot.Set(phase0.Slot(51)) for slot := currentSlot.Get(); slot < 64; slot++ { - mockTicker.Send(slot) + ticker.Send(slot) waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) currentSlot.Set(slot + 1) } // STEP 7: The first assigned duty should not be executed // slot = 64 - mockTicker.Send(currentSlot.Get()) + ticker.Send(currentSlot.Get()) waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 8: The second assigned duty should be executed @@ -720,7 +720,7 @@ func TestScheduler_Attester_Genesis_Reorg_Current(t *testing.T) { expected := expectedExecutedGenesisAttesterDuties(handler, duties) setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) - mockTicker.Send(currentSlot.Get()) + ticker.Send(currentSlot.Get()) waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) // Stop scheduler & wait for graceful exit. @@ -738,7 +738,7 @@ func TestScheduler_Attester_Genesis_Reorg_Current_Indices_Changed(t *testing.T) forkEpoch = goclient.FarFutureEpoch ) currentSlot.Set(phase0.Slot(48)) - scheduler, logger, mockTicker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) + scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) fetchDutiesCall, executeDutiesCall := setupAttesterGenesisDutiesMock(scheduler, dutiesMap, waitForDuties) startFn() @@ -752,8 +752,8 @@ func TestScheduler_Attester_Genesis_Reorg_Current_Indices_Changed(t *testing.T) // STEP 1: wait for attester duties to be fetched for next epoch waitForDuties.Set(true) - mockTicker.Send(currentSlot.Get()) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + ticker.Send(currentSlot.Get()) + waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 2: trigger head event e := ð2apiv1.Event{ @@ -767,7 +767,7 @@ func TestScheduler_Attester_Genesis_Reorg_Current_Indices_Changed(t *testing.T) // STEP 3: Ticker with no action currentSlot.Set(phase0.Slot(49)) - mockTicker.Send(currentSlot.Get()) + ticker.Send(currentSlot.Get()) waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 4: trigger reorg @@ -799,20 +799,20 @@ func TestScheduler_Attester_Genesis_Reorg_Current_Indices_Changed(t *testing.T) // STEP 6: wait for attester duties to be fetched again for the next epoch due to indices change currentSlot.Set(phase0.Slot(50)) - mockTicker.Send(currentSlot.Get()) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + ticker.Send(currentSlot.Get()) + waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 7: skip to the next epoch currentSlot.Set(phase0.Slot(51)) for slot := currentSlot.Get(); slot < 64; slot++ { - mockTicker.Send(slot) + ticker.Send(slot) waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) currentSlot.Set(slot + 1) } // STEP 8: The first assigned duty should not be executed // slot = 64 - mockTicker.Send(currentSlot.Get()) + ticker.Send(currentSlot.Get()) waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 9: The second assigned duty should be executed @@ -821,7 +821,7 @@ func TestScheduler_Attester_Genesis_Reorg_Current_Indices_Changed(t *testing.T) expected := expectedExecutedGenesisAttesterDuties(handler, duties) setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) - mockTicker.Send(currentSlot.Get()) + ticker.Send(currentSlot.Get()) waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) // Stop scheduler & wait for graceful exit. @@ -847,22 +847,22 @@ func TestScheduler_Attester_Genesis_Early_Block(t *testing.T) { currentSlot.Set(phase0.Slot(0)) // STEP 1: wait for attester duties to be fetched (handle initial duties) - scheduler, logger, mockTicker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) + scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) fetchDutiesCall, executeDutiesCall := setupAttesterGenesisDutiesMock(scheduler, dutiesMap, waitForDuties) startFn() - mockTicker.Send(currentSlot.Get()) + ticker.Send(currentSlot.Get()) waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 2: wait for no action to be taken currentSlot.Set(phase0.Slot(1)) - mockTicker.Send(currentSlot.Get()) + ticker.Send(currentSlot.Get()) waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 3: wait for attester duties to be executed faster than 1/3 of the slot duration startTime := time.Now() currentSlot.Set(phase0.Slot(2)) - mockTicker.Send(currentSlot.Get()) + ticker.Send(currentSlot.Get()) duties, _ := dutiesMap.Get(phase0.Epoch(0)) expected := expectedExecutedGenesisAttesterDuties(handler, duties) setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) @@ -891,7 +891,7 @@ func TestScheduler_Attester_Genesis_Start_In_The_End_Of_The_Epoch(t *testing.T) forkEpoch = goclient.FarFutureEpoch ) currentSlot.Set(phase0.Slot(31)) - scheduler, logger, mockTicker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) + scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) fetchDutiesCall, executeDutiesCall := setupAttesterGenesisDutiesMock(scheduler, dutiesMap, waitForDuties) startFn() @@ -905,8 +905,8 @@ func TestScheduler_Attester_Genesis_Start_In_The_End_Of_The_Epoch(t *testing.T) // STEP 1: wait for attester duties to be fetched for the next epoch waitForDuties.Set(true) - mockTicker.Send(currentSlot.Get()) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + ticker.Send(currentSlot.Get()) + waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 2: wait for attester duties to be executed currentSlot.Set(phase0.Slot(32)) @@ -914,7 +914,7 @@ func TestScheduler_Attester_Genesis_Start_In_The_End_Of_The_Epoch(t *testing.T) expected := expectedExecutedGenesisAttesterDuties(handler, duties) setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) - mockTicker.Send(currentSlot.Get()) + ticker.Send(currentSlot.Get()) waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) // Stop scheduler & wait for graceful exit. @@ -931,7 +931,7 @@ func TestScheduler_Attester_Genesis_Fetch_Execute_Next_Epoch_Duty(t *testing.T) forkEpoch = goclient.FarFutureEpoch ) currentSlot.Set(phase0.Slot(13)) - scheduler, logger, mockTicker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) + scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) fetchDutiesCall, executeDutiesCall := setupAttesterGenesisDutiesMock(scheduler, dutiesMap, waitForDuties) startFn() @@ -944,27 +944,27 @@ func TestScheduler_Attester_Genesis_Fetch_Execute_Next_Epoch_Duty(t *testing.T) }) // STEP 1: wait for no action to be taken - mockTicker.Send(currentSlot.Get()) + ticker.Send(currentSlot.Get()) waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 2: wait for no action to be taken currentSlot.Set(phase0.Slot(14)) - mockTicker.Send(currentSlot.Get()) + ticker.Send(currentSlot.Get()) waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - // STEP 2: wait for duties to be fetched for the next epoch + // STEP 3: wait for duties to be fetched for the next epoch currentSlot.Set(phase0.Slot(15)) waitForDuties.Set(true) - mockTicker.Send(currentSlot.Get()) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + ticker.Send(currentSlot.Get()) + waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - // STEP 3: wait for attester duties to be executed + // STEP 4: wait for attester duties to be executed currentSlot.Set(phase0.Slot(32)) duties, _ := dutiesMap.Get(phase0.Epoch(1)) expected := expectedExecutedGenesisAttesterDuties(handler, duties) setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) - mockTicker.Send(currentSlot.Get()) + ticker.Send(currentSlot.Get()) waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) // Stop scheduler & wait for graceful exit. diff --git a/operator/duties/proposer_genesis_test.go b/operator/duties/proposer_genesis_test.go index c9125fafec..c0827d363a 100644 --- a/operator/duties/proposer_genesis_test.go +++ b/operator/duties/proposer_genesis_test.go @@ -90,7 +90,7 @@ func TestScheduler_Proposer_Genesis_Same_Slot(t *testing.T) { setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) ticker.Send(currentSlot.Get()) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) // Stop scheduler & wait for graceful exit. @@ -119,7 +119,7 @@ func TestScheduler_Proposer_Genesis_Diff_Slots(t *testing.T) { // STEP 1: wait for proposer duties to be fetched ticker.Send(currentSlot.Get()) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 2: wait for no action to be taken currentSlot.Set(phase0.Slot(1)) @@ -186,7 +186,7 @@ func TestScheduler_Proposer_Genesis_Indices_Changed(t *testing.T) { // STEP 4: wait for proposer duties to be fetched again currentSlot.Set(phase0.Slot(2)) ticker.Send(currentSlot.Get()) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // no execution should happen in slot 2 waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) @@ -225,7 +225,7 @@ func TestScheduler_Proposer_Genesis_Multiple_Indices_Changed_Same_Slot(t *testin // STEP 1: wait for proposer duties to be fetched ticker.Send(currentSlot.Get()) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 2: trigger a change in active indices scheduler.indicesChg <- struct{}{} @@ -250,7 +250,7 @@ func TestScheduler_Proposer_Genesis_Multiple_Indices_Changed_Same_Slot(t *testin // STEP 4: wait for proposer duties to be fetched again currentSlot.Set(phase0.Slot(1)) ticker.Send(currentSlot.Get()) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 5: wait for proposer duties to be executed currentSlot.Set(phase0.Slot(2)) @@ -306,7 +306,7 @@ func TestScheduler_Proposer_Genesis_Reorg_Current(t *testing.T) { // STEP 1: wait for proposer duties to be fetched ticker.Send(currentSlot.Get()) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 2: trigger head event e := ð2apiv1.Event{ @@ -344,7 +344,7 @@ func TestScheduler_Proposer_Genesis_Reorg_Current(t *testing.T) { // The first assigned duty should not be executed currentSlot.Set(phase0.Slot(36)) ticker.Send(currentSlot.Get()) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 7: The second assigned duty should be executed currentSlot.Set(phase0.Slot(37)) @@ -382,7 +382,7 @@ func TestScheduler_Proposer_Genesis_Reorg_Current_Indices_Changed(t *testing.T) // STEP 1: wait for proposer duties to be fetched ticker.Send(currentSlot.Get()) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 2: trigger head event e := ð2apiv1.Event{ @@ -430,7 +430,7 @@ func TestScheduler_Proposer_Genesis_Reorg_Current_Indices_Changed(t *testing.T) // The first assigned duty should not be executed currentSlot.Set(phase0.Slot(36)) ticker.Send(currentSlot.Get()) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 7: The second assigned duty should be executed currentSlot.Set(phase0.Slot(37)) diff --git a/operator/duties/proposer_test.go b/operator/duties/proposer_test.go index 8a13a3ce77..6fefb61b98 100644 --- a/operator/duties/proposer_test.go +++ b/operator/duties/proposer_test.go @@ -483,7 +483,7 @@ func TestScheduler_Proposer_On_Fork(t *testing.T) { // STEP 1: wait for proposer genesis duties to be fetched ticker.Send(currentSlot.Get()) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCallGenesis, executeDutiesCallGenesis, timeout) + waitForDutiesFetchGenesis(t, logger, fetchDutiesCallGenesis, executeDutiesCallGenesis, timeout) // STEP 2: wait for no action to be taken currentSlot.Set(phase0.Slot(1)) diff --git a/operator/duties/scheduler_test.go b/operator/duties/scheduler_test.go index 29034820d9..32eef3491b 100644 --- a/operator/duties/scheduler_test.go +++ b/operator/duties/scheduler_test.go @@ -260,7 +260,7 @@ func waitForDutiesFetch(t *testing.T, logger *zap.Logger, fetchDutiesCall chan s } } -func waitForGenesisDutiesFetch(t *testing.T, logger *zap.Logger, fetchDutiesCall chan struct{}, executeDutiesCall chan []*genesisspectypes.Duty, timeout time.Duration) { +func waitForDutiesFetchGenesis(t *testing.T, logger *zap.Logger, fetchDutiesCall chan struct{}, executeDutiesCall chan []*genesisspectypes.Duty, timeout time.Duration) { select { case <-fetchDutiesCall: logger.Debug("duties fetched") diff --git a/operator/duties/sync_committee_genesis_test.go b/operator/duties/sync_committee_genesis_test.go index 1c1cfc4765..ed5e3c1ec0 100644 --- a/operator/duties/sync_committee_genesis_test.go +++ b/operator/duties/sync_committee_genesis_test.go @@ -282,7 +282,7 @@ func TestScheduler_SyncCommittee_Genesis_Indices_Changed(t *testing.T) { // STEP 1: wait for sync committee duties to be fetched for next period waitForDuties.Set(true) ticker.Send(currentSlot.Get()) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 2: trigger a change in active indices scheduler.indicesChg <- struct{}{} @@ -296,8 +296,8 @@ func TestScheduler_SyncCommittee_Genesis_Indices_Changed(t *testing.T) { // STEP 3: wait for sync committee duties to be fetched again currentSlot.Set(phase0.Slot(256*32 - 2)) ticker.Send(currentSlot.Get()) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 4: no action should be taken currentSlot.Set(phase0.Slot(256*32 - 1)) @@ -376,8 +376,8 @@ func TestScheduler_SyncCommittee_Genesis_Multiple_Indices_Changed_Same_Slot(t *t currentSlot.Set(phase0.Slot(256*32 - 2)) waitForDuties.Set(true) ticker.Send(currentSlot.Get()) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 5: no action should be taken currentSlot.Set(phase0.Slot(256*32 - 1)) @@ -440,7 +440,7 @@ func TestScheduler_SyncCommittee_Genesis_Reorg_Current(t *testing.T) { // STEP 1: wait for sync committee duties to be fetched and executed at the same slot waitForDuties.Set(true) ticker.Send(currentSlot.Get()) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 2: trigger head event e := &v1.Event{ @@ -476,7 +476,7 @@ func TestScheduler_SyncCommittee_Genesis_Reorg_Current(t *testing.T) { // STEP 5: wait for sync committee duties to be fetched again for the current epoch currentSlot.Set(phase0.Slot(256*32 - 1)) ticker.Send(currentSlot.Get()) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 6: The first assigned duty should not be executed, but the second one should currentSlot.Set(phase0.Slot(256 * 32)) @@ -542,7 +542,7 @@ func TestScheduler_SyncCommittee_Genesis_Reorg_Current_Indices_Changed(t *testin // STEP 1: wait for sync committee duties to be fetched and executed at the same slot waitForDuties.Set(true) ticker.Send(currentSlot.Get()) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 2: trigger head event e := &v1.Event{ @@ -587,8 +587,8 @@ func TestScheduler_SyncCommittee_Genesis_Reorg_Current_Indices_Changed(t *testin // STEP 5: wait for sync committee duties to be fetched again for the current epoch currentSlot.Set(phase0.Slot(256*32 - 1)) ticker.Send(currentSlot.Get()) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 6: The first assigned duty should not be executed, but the second and the new from indices change should currentSlot.Set(phase0.Slot(256 * 32)) From 2c15442ff5cdbb7aa59c710ccc9726c94104cd49 Mon Sep 17 00:00:00 2001 From: olegshmuelov <45327364+olegshmuelov@users.noreply.github.com> Date: Wed, 11 Sep 2024 15:39:56 +0300 Subject: [PATCH 06/14] add more tests --- operator/duties/committee_test.go | 490 +++++++++++++++++++++++++++++- 1 file changed, 480 insertions(+), 10 deletions(-) diff --git a/operator/duties/committee_test.go b/operator/duties/committee_test.go index 8225173530..e46b1927b4 100644 --- a/operator/duties/committee_test.go +++ b/operator/duties/committee_test.go @@ -595,7 +595,7 @@ func TestScheduler_Committee_Indices_Changed_Attester_Only_3(t *testing.T) { } // reorg previous dependent root changed -func TestScheduler_Committee_Reorg_Previous_Epoch_Transition_Attester_only(t *testing.T) { +func TestScheduler_Committee_Reorg_Previous_Epoch_Transition_Attester_Only(t *testing.T) { var ( dutyStore = dutystore.New() attHandler = NewAttesterHandler(dutyStore.Attester) @@ -693,7 +693,7 @@ func TestScheduler_Committee_Reorg_Previous_Epoch_Transition_Attester_only(t *te } // reorg previous dependent root changed and the indices changed as well -func TestScheduler_Committee_Reorg_Previous_Epoch_Transition_Indices_Changed_Attester_only(t *testing.T) { +func TestScheduler_Committee_Reorg_Previous_Epoch_Transition_Indices_Changed_Attester_Only(t *testing.T) { var ( dutyStore = dutystore.New() attHandler = NewAttesterHandler(dutyStore.Attester) @@ -809,7 +809,7 @@ func TestScheduler_Committee_Reorg_Previous_Epoch_Transition_Indices_Changed_Att } // reorg previous dependent root changed -func TestScheduler_Committee_Reorg_Previous_Attester_only(t *testing.T) { +func TestScheduler_Committee_Reorg_Previous_Attester_Only(t *testing.T) { var ( dutyStore = dutystore.New() attHandler = NewAttesterHandler(dutyStore.Attester) @@ -831,7 +831,6 @@ func TestScheduler_Committee_Reorg_Previous_Attester_only(t *testing.T) { }, } ) - attDuties.Set(phase0.Epoch(1), []*eth2apiv1.AttesterDuty{ { PubKey: phase0.BLSPubKey{1, 2, 3}, @@ -840,13 +839,15 @@ func TestScheduler_Committee_Reorg_Previous_Attester_only(t *testing.T) { }, }) - // STEP 1: wait for attester duties to be fetched using handle initial duties + // STEP 1: wait for attester duties to be fetched (handle initial duties) currentSlot.Set(phase0.Slot(32)) scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot, alanForkEpoch) fetchDutiesCall, executeDutiesCall := setupCommitteeDutiesMock(scheduler, activeShares, attDuties, syncDuties, waitForDuties) startFn() - waitForDuties.Set(true) + ticker.Send(currentSlot.Get()) + waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + // STEP 2: trigger head event e := ð2apiv1.Event{ Data: ð2apiv1.HeadEvent{ @@ -858,6 +859,7 @@ func TestScheduler_Committee_Reorg_Previous_Attester_only(t *testing.T) { waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 3: Ticker with no action + waitForDuties.Set(true) currentSlot.Set(phase0.Slot(33)) ticker.Send(currentSlot.Get()) waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) @@ -879,8 +881,6 @@ func TestScheduler_Committee_Reorg_Previous_Attester_only(t *testing.T) { scheduler.HandleHeadEvent(logger)(e) // wait for attester duties to be fetched again for the current epoch waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - // no execution should happen in slot 33 - waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 5: Ticker with no action currentSlot.Set(phase0.Slot(34)) @@ -906,6 +906,360 @@ func TestScheduler_Committee_Reorg_Previous_Attester_only(t *testing.T) { require.NoError(t, schedulerPool.Wait()) } +// reorg previous dependent root changed and the indices changed the same slot +func TestScheduler_Committee_Reorg_Previous_Indices_Changed_Attester_Only(t *testing.T) { + var ( + dutyStore = dutystore.New() + attHandler = NewAttesterHandler(dutyStore.Attester) + syncHandler = NewSyncCommitteeHandler(dutyStore.SyncCommittee) + commHandler = NewCommitteeHandler(attHandler, syncHandler) + alanForkEpoch = phase0.Epoch(0) + currentSlot = &SafeValue[phase0.Slot]{} + waitForDuties = &SafeValue[bool]{} + attDuties = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() + syncDuties = hashmap.New[uint64, []*eth2apiv1.SyncCommitteeDuty]() + activeShares = []*ssvtypes.SSVShare{ + { + Share: spectypes.Share{ + Committee: []*spectypes.ShareMember{ + {Signer: 1}, {Signer: 2}, {Signer: 3}, {Signer: 4}, + }, + ValidatorIndex: 1, + }, + }, + { + Share: spectypes.Share{ + Committee: []*spectypes.ShareMember{ + {Signer: 1}, {Signer: 2}, {Signer: 3}, {Signer: 4}, + }, + ValidatorIndex: 2, + }, + }, + } + ) + attDuties.Set(phase0.Epoch(1), []*eth2apiv1.AttesterDuty{ + { + PubKey: phase0.BLSPubKey{1, 2, 3}, + Slot: phase0.Slot(35), + ValidatorIndex: phase0.ValidatorIndex(1), + }, + }) + + // STEP 1: wait for attester duties to be fetched (handle initial duties) + currentSlot.Set(phase0.Slot(32)) + scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot, alanForkEpoch) + fetchDutiesCall, executeDutiesCall := setupCommitteeDutiesMock(scheduler, activeShares, attDuties, syncDuties, waitForDuties) + startFn() + + ticker.Send(currentSlot.Get()) + waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + + // STEP 2: trigger head event + e := ð2apiv1.Event{ + Data: ð2apiv1.HeadEvent{ + Slot: currentSlot.Get(), + PreviousDutyDependentRoot: phase0.Root{0x01}, + }, + } + scheduler.HandleHeadEvent(logger)(e) + waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + + // STEP 3: Ticker with no action + currentSlot.Set(phase0.Slot(33)) + waitForDuties.Set(true) + ticker.Send(currentSlot.Get()) + waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + + // STEP 4: trigger reorg + e = ð2apiv1.Event{ + Data: ð2apiv1.HeadEvent{ + Slot: currentSlot.Get(), + PreviousDutyDependentRoot: phase0.Root{0x02}, + }, + } + attDuties.Set(phase0.Epoch(1), []*eth2apiv1.AttesterDuty{ + { + PubKey: phase0.BLSPubKey{1, 2, 3}, + Slot: phase0.Slot(36), + ValidatorIndex: phase0.ValidatorIndex(1), + }, + }) + scheduler.HandleHeadEvent(logger)(e) + // wait for attester duties to be fetched + waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + + // STEP 5: trigger indices change + scheduler.indicesChg <- struct{}{} + waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + aDuties, _ := attDuties.Get(phase0.Epoch(1)) + attDuties.Set(phase0.Epoch(1), append(aDuties, ð2apiv1.AttesterDuty{ + PubKey: phase0.BLSPubKey{1, 2, 4}, + Slot: phase0.Slot(36), + ValidatorIndex: phase0.ValidatorIndex(2), + })) + waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + + // STEP 6: wait for attester duties to be fetched again for the current epoch + currentSlot.Set(phase0.Slot(34)) + ticker.Send(currentSlot.Get()) + // wait for attester duties to be fetched + waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + // wait for sync committee duties to be fetched + waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + + // STEP 7: The first assigned duty should not be executed + currentSlot.Set(phase0.Slot(35)) + ticker.Send(currentSlot.Get()) + waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + + // STEP 8: The second and new from indices change assigned duties should be executed + currentSlot.Set(phase0.Slot(36)) + aDuties, _ = attDuties.Get(phase0.Epoch(1)) + committeeMap := commHandler.buildCommitteeDuties(aDuties, nil, 0, currentSlot.Get()) + setExecuteDutyFuncs(scheduler, executeDutiesCall, len(committeeMap)) + + ticker.Send(currentSlot.Get()) + waitForDutiesExecutionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout, committeeMap) + + // Stop scheduler & wait for graceful exit. + cancel() + require.NoError(t, schedulerPool.Wait()) +} + +// reorg current dependent root changed +func TestScheduler_Committee_Reorg_Current_Attester_Only(t *testing.T) { + var ( + dutyStore = dutystore.New() + attHandler = NewAttesterHandler(dutyStore.Attester) + syncHandler = NewSyncCommitteeHandler(dutyStore.SyncCommittee) + commHandler = NewCommitteeHandler(attHandler, syncHandler) + alanForkEpoch = phase0.Epoch(0) + currentSlot = &SafeValue[phase0.Slot]{} + waitForDuties = &SafeValue[bool]{} + attDuties = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() + syncDuties = hashmap.New[uint64, []*eth2apiv1.SyncCommitteeDuty]() + activeShares = []*ssvtypes.SSVShare{ + { + Share: spectypes.Share{ + Committee: []*spectypes.ShareMember{ + {Signer: 1}, {Signer: 2}, {Signer: 3}, {Signer: 4}, + }, + ValidatorIndex: 1, + }, + }, + } + ) + currentSlot.Set(phase0.Slot(48)) + scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot, alanForkEpoch) + fetchDutiesCall, executeDutiesCall := setupCommitteeDutiesMock(scheduler, activeShares, attDuties, syncDuties, waitForDuties) + startFn() + + attDuties.Set(phase0.Epoch(2), []*eth2apiv1.AttesterDuty{ + { + PubKey: phase0.BLSPubKey{1, 2, 3}, + Slot: phase0.Slot(64), + ValidatorIndex: phase0.ValidatorIndex(1), + }, + }) + + // STEP 1: wait for attester duties to be fetched for next epoch + waitForDuties.Set(true) + ticker.Send(currentSlot.Get()) + waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + + // STEP 2: trigger head event + e := ð2apiv1.Event{ + Data: ð2apiv1.HeadEvent{ + Slot: currentSlot.Get(), + CurrentDutyDependentRoot: phase0.Root{0x01}, + }, + } + scheduler.HandleHeadEvent(logger)(e) + waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + + // STEP 3: Ticker with no action + currentSlot.Set(phase0.Slot(49)) + ticker.Send(currentSlot.Get()) + waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + + // STEP 4: trigger reorg + e = ð2apiv1.Event{ + Data: ð2apiv1.HeadEvent{ + Slot: currentSlot.Get(), + CurrentDutyDependentRoot: phase0.Root{0x02}, + }, + } + attDuties.Set(phase0.Epoch(2), []*eth2apiv1.AttesterDuty{ + { + PubKey: phase0.BLSPubKey{1, 2, 3}, + Slot: phase0.Slot(65), + ValidatorIndex: phase0.ValidatorIndex(1), + }, + }) + scheduler.HandleHeadEvent(logger)(e) + waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + + // STEP 5: wait for attester duties to be fetched again for the current epoch + currentSlot.Set(phase0.Slot(50)) + ticker.Send(currentSlot.Get()) + waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + + // STEP 6: skip to the next epoch + currentSlot.Set(phase0.Slot(51)) + for slot := currentSlot.Get(); slot < 64; slot++ { + ticker.Send(slot) + waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + currentSlot.Set(slot + 1) + } + + // STEP 7: The first assigned duty should not be executed + // slot = 64 + ticker.Send(currentSlot.Get()) + waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + + // STEP 8: The second assigned duty should be executed + currentSlot.Set(phase0.Slot(65)) + aDuties, _ := attDuties.Get(phase0.Epoch(2)) + committeeMap := commHandler.buildCommitteeDuties(aDuties, nil, 0, currentSlot.Get()) + setExecuteDutyFuncs(scheduler, executeDutiesCall, len(committeeMap)) + + ticker.Send(currentSlot.Get()) + waitForDutiesExecutionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout, committeeMap) + + // Stop scheduler & wait for graceful exit. + cancel() + require.NoError(t, schedulerPool.Wait()) +} + +// reorg current dependent root changed including indices change in the same slot +func TestScheduler_Committee_Reorg_Current_Indices_Changed_Attester_Only(t *testing.T) { + var ( + dutyStore = dutystore.New() + attHandler = NewAttesterHandler(dutyStore.Attester) + syncHandler = NewSyncCommitteeHandler(dutyStore.SyncCommittee) + commHandler = NewCommitteeHandler(attHandler, syncHandler) + alanForkEpoch = phase0.Epoch(0) + currentSlot = &SafeValue[phase0.Slot]{} + waitForDuties = &SafeValue[bool]{} + attDuties = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() + syncDuties = hashmap.New[uint64, []*eth2apiv1.SyncCommitteeDuty]() + activeShares = []*ssvtypes.SSVShare{ + { + Share: spectypes.Share{ + Committee: []*spectypes.ShareMember{ + {Signer: 1}, {Signer: 2}, {Signer: 3}, {Signer: 4}, + }, + ValidatorIndex: 1, + }, + }, + { + Share: spectypes.Share{ + Committee: []*spectypes.ShareMember{ + {Signer: 1}, {Signer: 2}, {Signer: 3}, {Signer: 4}, + }, + ValidatorIndex: 2, + }, + }, + } + ) + currentSlot.Set(phase0.Slot(48)) + scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot, alanForkEpoch) + fetchDutiesCall, executeDutiesCall := setupCommitteeDutiesMock(scheduler, activeShares, attDuties, syncDuties, waitForDuties) + startFn() + + attDuties.Set(phase0.Epoch(2), []*eth2apiv1.AttesterDuty{ + { + PubKey: phase0.BLSPubKey{1, 2, 3}, + Slot: phase0.Slot(48), + ValidatorIndex: phase0.ValidatorIndex(1), + }, + }) + + // STEP 1: wait for attester duties to be fetched for next epoch + waitForDuties.Set(true) + ticker.Send(currentSlot.Get()) + waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + + // STEP 2: trigger head event + e := ð2apiv1.Event{ + Data: ð2apiv1.HeadEvent{ + Slot: currentSlot.Get(), + CurrentDutyDependentRoot: phase0.Root{0x01}, + }, + } + scheduler.HandleHeadEvent(logger)(e) + waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + + // STEP 3: Ticker with no action + currentSlot.Set(phase0.Slot(49)) + ticker.Send(currentSlot.Get()) + waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + + // STEP 4: trigger reorg + e = ð2apiv1.Event{ + Data: ð2apiv1.HeadEvent{ + Slot: currentSlot.Get(), + CurrentDutyDependentRoot: phase0.Root{0x02}, + }, + } + attDuties.Set(phase0.Epoch(2), []*eth2apiv1.AttesterDuty{ + { + PubKey: phase0.BLSPubKey{1, 2, 3}, + Slot: phase0.Slot(65), + ValidatorIndex: phase0.ValidatorIndex(1), + }, + }) + scheduler.HandleHeadEvent(logger)(e) + waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + + // STEP 5: trigger indices change + scheduler.indicesChg <- struct{}{} + waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + aDuties, _ := attDuties.Get(phase0.Epoch(2)) + attDuties.Set(phase0.Epoch(2), append(aDuties, ð2apiv1.AttesterDuty{ + PubKey: phase0.BLSPubKey{1, 2, 4}, + Slot: phase0.Slot(65), + ValidatorIndex: phase0.ValidatorIndex(2), + })) + waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + + // STEP 6: wait for attester duties to be fetched again for the next epoch due to indices change + currentSlot.Set(phase0.Slot(50)) + ticker.Send(currentSlot.Get()) + // wait for attester duties to be fetched for current epoch + waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + // wait for attester duties to be fetched for next epoch + waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + // wait for sync committee duties to be fetched for current period + waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + + // STEP 7: skip to the next epoch + currentSlot.Set(phase0.Slot(51)) + for slot := currentSlot.Get(); slot < 64; slot++ { + ticker.Send(slot) + waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + currentSlot.Set(slot + 1) + } + + // STEP 8: The first assigned duty should not be executed + // slot = 64 + ticker.Send(currentSlot.Get()) + waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + + // STEP 9: The second assigned duty should be executed + currentSlot.Set(phase0.Slot(65)) + aDuties, _ = attDuties.Get(phase0.Epoch(2)) + committeeMap := commHandler.buildCommitteeDuties(aDuties, nil, 0, currentSlot.Get()) + setExecuteDutyFuncs(scheduler, executeDutiesCall, len(committeeMap)) + + ticker.Send(currentSlot.Get()) + waitForDutiesExecutionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout, committeeMap) + + // Stop scheduler & wait for graceful exit. + cancel() + require.NoError(t, schedulerPool.Wait()) +} + func TestScheduler_Committee_Early_Block_Attester_Only(t *testing.T) { var ( dutyStore = dutystore.New() @@ -1046,7 +1400,123 @@ func TestScheduler_Committee_Early_Block(t *testing.T) { require.NoError(t, schedulerPool.Wait()) } -func TestScheduler_Committee_On_Fork_Attester_only(t *testing.T) { +func TestScheduler_Committee_Start_In_The_End_Of_The_Epoch_Attester_Only(t *testing.T) { + var ( + dutyStore = dutystore.New() + attHandler = NewAttesterHandler(dutyStore.Attester) + syncHandler = NewSyncCommitteeHandler(dutyStore.SyncCommittee) + commHandler = NewCommitteeHandler(attHandler, syncHandler) + alanForkEpoch = phase0.Epoch(0) + currentSlot = &SafeValue[phase0.Slot]{} + waitForDuties = &SafeValue[bool]{} + attDuties = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() + syncDuties = hashmap.New[uint64, []*eth2apiv1.SyncCommitteeDuty]() + activeShares = []*ssvtypes.SSVShare{{ + Share: spectypes.Share{ + Committee: []*spectypes.ShareMember{ + {Signer: 1}, {Signer: 2}, {Signer: 3}, {Signer: 4}, + }, + ValidatorIndex: 1, + }, + }} + ) + currentSlot.Set(phase0.Slot(31)) + scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot, alanForkEpoch) + fetchDutiesCall, executeDutiesCall := setupCommitteeDutiesMock(scheduler, activeShares, attDuties, syncDuties, waitForDuties) + startFn() + + attDuties.Set(phase0.Epoch(1), []*eth2apiv1.AttesterDuty{ + { + PubKey: phase0.BLSPubKey{1, 2, 3}, + Slot: phase0.Slot(32), + ValidatorIndex: phase0.ValidatorIndex(1), + }, + }) + + // STEP 1: wait for attester duties to be fetched for the next epoch + waitForDuties.Set(true) + ticker.Send(currentSlot.Get()) + waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + + // STEP 2: wait for attester duties to be executed + currentSlot.Set(phase0.Slot(32)) + aDuties, _ := attDuties.Get(1) + sDuties, _ := syncDuties.Get(1) + committeeMap := commHandler.buildCommitteeDuties(aDuties, sDuties, 0, currentSlot.Get()) + setExecuteDutyFuncs(scheduler, executeDutiesCall, len(committeeMap)) + + ticker.Send(currentSlot.Get()) + waitForDutiesExecutionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout, committeeMap) + + // Stop scheduler & wait for graceful exit. + cancel() + require.NoError(t, schedulerPool.Wait()) +} + +func TestScheduler_Committee_Fetch_Execute_Next_Epoch_Duty(t *testing.T) { + var ( + dutyStore = dutystore.New() + attHandler = NewAttesterHandler(dutyStore.Attester) + syncHandler = NewSyncCommitteeHandler(dutyStore.SyncCommittee) + commHandler = NewCommitteeHandler(attHandler, syncHandler) + alanForkEpoch = phase0.Epoch(0) + currentSlot = &SafeValue[phase0.Slot]{} + waitForDuties = &SafeValue[bool]{} + attDuties = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() + syncDuties = hashmap.New[uint64, []*eth2apiv1.SyncCommitteeDuty]() + activeShares = []*ssvtypes.SSVShare{{ + Share: spectypes.Share{ + Committee: []*spectypes.ShareMember{ + {Signer: 1}, {Signer: 2}, {Signer: 3}, {Signer: 4}, + }, + ValidatorIndex: 1, + }, + }} + ) + currentSlot.Set(phase0.Slot(13)) + scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot, alanForkEpoch) + fetchDutiesCall, executeDutiesCall := setupCommitteeDutiesMock(scheduler, activeShares, attDuties, syncDuties, waitForDuties) + startFn() + + attDuties.Set(phase0.Epoch(1), []*eth2apiv1.AttesterDuty{ + { + PubKey: phase0.BLSPubKey{1, 2, 3}, + Slot: phase0.Slot(32), + ValidatorIndex: phase0.ValidatorIndex(1), + }, + }) + + // STEP 1: wait for no action to be taken + ticker.Send(currentSlot.Get()) + waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + + // STEP 2: wait for no action to be taken + currentSlot.Set(phase0.Slot(14)) + ticker.Send(currentSlot.Get()) + waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + + // STEP 3: wait for duties to be fetched for the next epoch + currentSlot.Set(phase0.Slot(15)) + waitForDuties.Set(true) + ticker.Send(currentSlot.Get()) + waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + + // STEP 4: wait for attester duties to be executed + currentSlot.Set(phase0.Slot(32)) + aDuties, _ := attDuties.Get(1) + sDuties, _ := syncDuties.Get(1) + committeeMap := commHandler.buildCommitteeDuties(aDuties, sDuties, 0, currentSlot.Get()) + setExecuteDutyFuncs(scheduler, executeDutiesCall, len(committeeMap)) + + ticker.Send(currentSlot.Get()) + waitForDutiesExecutionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout, committeeMap) + + // Stop scheduler & wait for graceful exit. + cancel() + require.NoError(t, schedulerPool.Wait()) +} + +func TestScheduler_Committee_On_Fork_Attester_Only(t *testing.T) { var ( dutyStore = dutystore.New() attHandler = NewAttesterHandler(dutyStore.Attester) @@ -1112,7 +1582,7 @@ func TestScheduler_Committee_On_Fork_Attester_only(t *testing.T) { currentSlot.Set(phase0.Slot(48)) waitForDuties.Set(true) ticker.Send(currentSlot.Get()) - waitForGenesisDutiesFetch(t, logger, fetchAttesterDutiesCall, executeAttesterDutiesCall, timeout) + waitForDutiesFetchGenesis(t, logger, fetchAttesterDutiesCall, executeAttesterDutiesCall, timeout) currentSlot.Set(phase0.Slot(64)) aDuties, _ = attDuties.Get(2) From f52c444ea046cbbca0a68450a67d8debaf51d468 Mon Sep 17 00:00:00 2001 From: olegshmuelov <45327364+olegshmuelov@users.noreply.github.com> Date: Wed, 11 Sep 2024 15:41:56 +0300 Subject: [PATCH 07/14] Revert "tmp postfork" This reverts commit caeeb87cbfd3dfd36d6168ddbc8e6dabec08d8f8. --- networkconfig/holesky-stage.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/networkconfig/holesky-stage.go b/networkconfig/holesky-stage.go index a545cb493f..07d8747794 100644 --- a/networkconfig/holesky-stage.go +++ b/networkconfig/holesky-stage.go @@ -16,7 +16,7 @@ var HoleskyStage = NetworkConfig{ GenesisEpoch: 1, RegistrySyncOffset: new(big.Int).SetInt64(84599), RegistryContractAddr: "0x0d33801785340072C452b994496B19f196b7eE15", - //AlanForkEpoch: 99999999, + AlanForkEpoch: 99999999, Bootnodes: []string{ // Public bootnode: // "enr:-Ja4QDYHVgUs9NvlMqq93ot6VNqbmrIlMrwKnq4X3DPRgyUNB4ospDp8ubMvsf-KsgqY8rzpZKy4GbE1DLphabpRBc-GAY_diLjngmlkgnY0gmlwhDQrLYqJc2VjcDI1NmsxoQKnAiuSlgSR8asjCH0aYoVKM8uPbi4noFuFHZHaAHqknYNzc3YBg3RjcIITiYN1ZHCCD6E", From c4694e898bd81083654b351937637483f3b168a4 Mon Sep 17 00:00:00 2001 From: olegshmuelov <45327364+olegshmuelov@users.noreply.github.com> Date: Wed, 11 Sep 2024 15:51:30 +0300 Subject: [PATCH 08/14] optimization --- operator/duties/committee.go | 61 +++++++++++++++++++++++------------- 1 file changed, 40 insertions(+), 21 deletions(-) diff --git a/operator/duties/committee.go b/operator/duties/committee.go index 1c708bff53..8cf248aab2 100644 --- a/operator/duties/committee.go +++ b/operator/duties/committee.go @@ -106,33 +106,35 @@ func (h *CommitteeHandler) processExecution(period uint64, epoch phase0.Epoch, s return } - committeeMap := h.buildCommitteeDuties(attDuties, syncDuties, epoch, slot) - h.dutiesExecutor.ExecuteCommitteeDuties(h.logger, committeeMap) - - // aggregator and contribution duties - toExecute := make([]*spectypes.ValidatorDuty, 0, len(attDuties)+len(syncDuties)) - for _, d := range attDuties { - if h.attHandler.shouldExecute(d) { - toExecute = append(toExecute, h.attHandler.toSpecDuty(d, spectypes.BNRoleAggregator)) - } - } - for _, d := range syncDuties { - if h.syncHandler.shouldExecute(d, slot) { - toExecute = append(toExecute, h.syncHandler.toSpecDuty(d, slot, spectypes.BNRoleSyncCommitteeContribution)) - } - } - - h.dutiesExecutor.ExecuteDuties(h.logger, toExecute) + go func() { + committeeDuties := h.buildCommitteeDuties(attDuties, syncDuties, epoch, slot) + h.dutiesExecutor.ExecuteCommitteeDuties(h.logger, committeeDuties) + }() + + go func() { + aggregationDuties := h.buildAggregationDuties(attDuties, syncDuties, slot) + h.dutiesExecutor.ExecuteDuties(h.logger, aggregationDuties) + }() } func (h *CommitteeHandler) processFetching(ctx context.Context, period uint64, epoch phase0.Epoch, slot phase0.Slot) { - h.attHandler.processFetching(ctx, epoch, slot) - h.syncHandler.processFetching(ctx, period, slot, true) + go func() { + h.attHandler.processFetching(ctx, epoch, slot) + }() + + go func() { + h.syncHandler.processFetching(ctx, period, slot, true) + }() } func (h *CommitteeHandler) processSlotTransition(period uint64, epoch phase0.Epoch, slot phase0.Slot) { - h.attHandler.processSlotTransition(epoch, slot) - h.syncHandler.processSlotTransition(period, slot) + go func() { + h.attHandler.processSlotTransition(epoch, slot) + }() + + go func() { + h.syncHandler.processSlotTransition(period, slot) + }() } func (h *CommitteeHandler) buildCommitteeDuties(attDuties []*eth2apiv1.AttesterDuty, syncDuties []*eth2apiv1.SyncCommitteeDuty, epoch phase0.Epoch, slot phase0.Slot) committeeDutiesMap { @@ -165,6 +167,23 @@ func (h *CommitteeHandler) buildCommitteeDuties(attDuties []*eth2apiv1.AttesterD return committeeMap } +func (h *CommitteeHandler) buildAggregationDuties(attDuties []*eth2apiv1.AttesterDuty, syncDuties []*eth2apiv1.SyncCommitteeDuty, slot phase0.Slot) []*spectypes.ValidatorDuty { + // aggregator and contribution duties + toExecute := make([]*spectypes.ValidatorDuty, 0, len(attDuties)+len(syncDuties)) + for _, d := range attDuties { + if h.attHandler.shouldExecute(d) { + toExecute = append(toExecute, h.attHandler.toSpecDuty(d, spectypes.BNRoleAggregator)) + } + } + for _, d := range syncDuties { + if h.syncHandler.shouldExecute(d, slot) { + toExecute = append(toExecute, h.syncHandler.toSpecDuty(d, slot, spectypes.BNRoleSyncCommitteeContribution)) + } + } + + return toExecute +} + func (h *CommitteeHandler) appendBeaconDuty(vc validatorCommitteeDutyMap, c committeeDutiesMap, beaconDuty *spectypes.ValidatorDuty) { if beaconDuty == nil { h.logger.Error("received nil beaconDuty") From 00b3aa82618b99ee114eebe3f4c0ac46d6e55a24 Mon Sep 17 00:00:00 2001 From: olegshmuelov <45327364+olegshmuelov@users.noreply.github.com> Date: Wed, 11 Sep 2024 16:08:28 +0300 Subject: [PATCH 09/14] dont run in parallel --- operator/duties/committee.go | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/operator/duties/committee.go b/operator/duties/committee.go index 8cf248aab2..7c9e2c0a30 100644 --- a/operator/duties/committee.go +++ b/operator/duties/committee.go @@ -106,15 +106,11 @@ func (h *CommitteeHandler) processExecution(period uint64, epoch phase0.Epoch, s return } - go func() { - committeeDuties := h.buildCommitteeDuties(attDuties, syncDuties, epoch, slot) - h.dutiesExecutor.ExecuteCommitteeDuties(h.logger, committeeDuties) - }() + committeeDuties := h.buildCommitteeDuties(attDuties, syncDuties, epoch, slot) + h.dutiesExecutor.ExecuteCommitteeDuties(h.logger, committeeDuties) - go func() { - aggregationDuties := h.buildAggregationDuties(attDuties, syncDuties, slot) - h.dutiesExecutor.ExecuteDuties(h.logger, aggregationDuties) - }() + aggregationDuties := h.buildAggregationDuties(attDuties, syncDuties, slot) + h.dutiesExecutor.ExecuteDuties(h.logger, aggregationDuties) } func (h *CommitteeHandler) processFetching(ctx context.Context, period uint64, epoch phase0.Epoch, slot phase0.Slot) { @@ -128,13 +124,8 @@ func (h *CommitteeHandler) processFetching(ctx context.Context, period uint64, e } func (h *CommitteeHandler) processSlotTransition(period uint64, epoch phase0.Epoch, slot phase0.Slot) { - go func() { - h.attHandler.processSlotTransition(epoch, slot) - }() - - go func() { - h.syncHandler.processSlotTransition(period, slot) - }() + h.attHandler.processSlotTransition(epoch, slot) + h.syncHandler.processSlotTransition(period, slot) } func (h *CommitteeHandler) buildCommitteeDuties(attDuties []*eth2apiv1.AttesterDuty, syncDuties []*eth2apiv1.SyncCommitteeDuty, epoch phase0.Epoch, slot phase0.Slot) committeeDutiesMap { From c7d6778df45f3de2a8a67ca2757660699e484686 Mon Sep 17 00:00:00 2001 From: olegshmuelov <45327364+olegshmuelov@users.noreply.github.com> Date: Wed, 11 Sep 2024 16:37:35 +0300 Subject: [PATCH 10/14] optimization --- operator/duties/committee.go | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/operator/duties/committee.go b/operator/duties/committee.go index 7c9e2c0a30..f3a6c1fbf7 100644 --- a/operator/duties/committee.go +++ b/operator/duties/committee.go @@ -2,6 +2,7 @@ package duties import ( "context" + "sync" eth2apiv1 "github.com/attestantio/go-eth2-client/api/v1" "github.com/attestantio/go-eth2-client/spec/phase0" @@ -106,26 +107,42 @@ func (h *CommitteeHandler) processExecution(period uint64, epoch phase0.Epoch, s return } - committeeDuties := h.buildCommitteeDuties(attDuties, syncDuties, epoch, slot) - h.dutiesExecutor.ExecuteCommitteeDuties(h.logger, committeeDuties) + go func() { + committeeDuties := h.buildCommitteeDuties(attDuties, syncDuties, epoch, slot) + h.dutiesExecutor.ExecuteCommitteeDuties(h.logger, committeeDuties) + }() - aggregationDuties := h.buildAggregationDuties(attDuties, syncDuties, slot) - h.dutiesExecutor.ExecuteDuties(h.logger, aggregationDuties) + go func() { + aggregationDuties := h.buildAggregationDuties(attDuties, syncDuties, slot) + h.dutiesExecutor.ExecuteDuties(h.logger, aggregationDuties) + }() } func (h *CommitteeHandler) processFetching(ctx context.Context, period uint64, epoch phase0.Epoch, slot phase0.Slot) { + var wg sync.WaitGroup + wg.Add(2) + go func() { + defer wg.Done() // Mark this goroutine as done once it completes h.attHandler.processFetching(ctx, epoch, slot) }() go func() { + defer wg.Done() // Mark this goroutine as done once it completes h.syncHandler.processFetching(ctx, period, slot, true) }() + + wg.Wait() } func (h *CommitteeHandler) processSlotTransition(period uint64, epoch phase0.Epoch, slot phase0.Slot) { - h.attHandler.processSlotTransition(epoch, slot) - h.syncHandler.processSlotTransition(period, slot) + go func() { + h.attHandler.processSlotTransition(epoch, slot) + }() + + go func() { + h.syncHandler.processSlotTransition(period, slot) + }() } func (h *CommitteeHandler) buildCommitteeDuties(attDuties []*eth2apiv1.AttesterDuty, syncDuties []*eth2apiv1.SyncCommitteeDuty, epoch phase0.Epoch, slot phase0.Slot) committeeDutiesMap { From 7edf712a578bbe91befdc007efbd9803df697b5b Mon Sep 17 00:00:00 2001 From: olegshmuelov <45327364+olegshmuelov@users.noreply.github.com> Date: Wed, 11 Sep 2024 16:51:03 +0300 Subject: [PATCH 11/14] fix data race --- operator/duties/committee.go | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/operator/duties/committee.go b/operator/duties/committee.go index f3a6c1fbf7..76495843aa 100644 --- a/operator/duties/committee.go +++ b/operator/duties/committee.go @@ -107,15 +107,12 @@ func (h *CommitteeHandler) processExecution(period uint64, epoch phase0.Epoch, s return } - go func() { - committeeDuties := h.buildCommitteeDuties(attDuties, syncDuties, epoch, slot) - h.dutiesExecutor.ExecuteCommitteeDuties(h.logger, committeeDuties) - }() + committeeDuties := h.buildCommitteeDuties(attDuties, syncDuties, epoch, slot) + h.dutiesExecutor.ExecuteCommitteeDuties(h.logger, committeeDuties) + + aggregationDuties := h.buildAggregationDuties(attDuties, syncDuties, slot) + h.dutiesExecutor.ExecuteDuties(h.logger, aggregationDuties) - go func() { - aggregationDuties := h.buildAggregationDuties(attDuties, syncDuties, slot) - h.dutiesExecutor.ExecuteDuties(h.logger, aggregationDuties) - }() } func (h *CommitteeHandler) processFetching(ctx context.Context, period uint64, epoch phase0.Epoch, slot phase0.Slot) { @@ -136,13 +133,8 @@ func (h *CommitteeHandler) processFetching(ctx context.Context, period uint64, e } func (h *CommitteeHandler) processSlotTransition(period uint64, epoch phase0.Epoch, slot phase0.Slot) { - go func() { - h.attHandler.processSlotTransition(epoch, slot) - }() - - go func() { - h.syncHandler.processSlotTransition(period, slot) - }() + h.attHandler.processSlotTransition(epoch, slot) + h.syncHandler.processSlotTransition(period, slot) } func (h *CommitteeHandler) buildCommitteeDuties(attDuties []*eth2apiv1.AttesterDuty, syncDuties []*eth2apiv1.SyncCommitteeDuty, epoch phase0.Epoch, slot phase0.Slot) committeeDutiesMap { From 6cee156aa87bbd96c0ad0fa625fc9296a9b31f91 Mon Sep 17 00:00:00 2001 From: olegshmuelov <45327364+olegshmuelov@users.noreply.github.com> Date: Sun, 15 Sep 2024 16:24:27 +0300 Subject: [PATCH 12/14] fix #1714 --- operator/duties/attester.go | 35 +- operator/duties/attester_genesis_test.go | 235 ++++++------ operator/duties/committee.go | 44 ++- operator/duties/committee_test.go | 334 ++++++++++++------ operator/duties/proposer_genesis_test.go | 52 +-- operator/duties/proposer_test.go | 44 +-- operator/duties/scheduler_test.go | 2 +- operator/duties/sync_committee.go | 39 +- .../duties/sync_committee_genesis_test.go | 174 +++++---- .../duties/voluntary_exit_genesis_test.go | 8 +- 10 files changed, 554 insertions(+), 413 deletions(-) diff --git a/operator/duties/attester.go b/operator/duties/attester.go index 746f61d572..402739013c 100644 --- a/operator/duties/attester.go +++ b/operator/duties/attester.go @@ -21,13 +21,15 @@ type AttesterHandler struct { duties *dutystore.Duties[eth2apiv1.AttesterDuty] fetchCurrentEpoch bool fetchNextEpoch bool + + firstRun bool } func NewAttesterHandler(duties *dutystore.Duties[eth2apiv1.AttesterDuty]) *AttesterHandler { h := &AttesterHandler{ - duties: duties, + duties: duties, + firstRun: true, } - h.fetchCurrentEpoch = true return h } @@ -40,8 +42,6 @@ func (h *AttesterHandler) HandleDuties(ctx context.Context) { h.logger.Info("starting duty handler") defer h.logger.Info("duty handler exited") - h.fetchNextEpoch = true - next := h.ticker.Next() for { select { @@ -56,10 +56,12 @@ func (h *AttesterHandler) HandleDuties(ctx context.Context) { h.logger.Debug("🛠 ticker event", fields.SlotTickerID(tickerID)) if !h.network.PastAlanForkAtEpoch(epoch) { + if h.firstRun { + h.processFirstRun(ctx, epoch, slot) + } h.processExecution(epoch, slot) if h.indicesChanged { - h.duties.Reset(epoch) - h.indicesChanged = false + h.processIndicesChange(epoch, slot) } h.processFetching(ctx, epoch, slot) h.processSlotTransition(epoch, slot) @@ -82,19 +84,19 @@ func (h *AttesterHandler) HandleDuties(ctx context.Context) { if !h.network.PastAlanForkAtEpoch(epoch) { h.indicesChanged = true - h.processIndicesChange(epoch, slot) } } } } -func (h *AttesterHandler) HandleInitialDuties(ctx context.Context) { - ctx, cancel := context.WithTimeout(ctx, h.network.Beacon.SlotDurationSec()/2) - defer cancel() - - slot := h.network.Beacon.EstimatedCurrentSlot() - epoch := h.network.Beacon.EstimatedEpochAtSlot(slot) +func (h *AttesterHandler) processFirstRun(ctx context.Context, epoch phase0.Epoch, slot phase0.Slot) { + h.fetchCurrentEpoch = true h.processFetching(ctx, epoch, slot) + + if uint64(slot)%h.network.Beacon.SlotsPerEpoch() > h.network.Beacon.SlotsPerEpoch()/2-1 { + h.fetchNextEpoch = true + } + h.firstRun = false } func (h *AttesterHandler) processFetching(ctx context.Context, epoch phase0.Epoch, slot phase0.Slot) { @@ -109,7 +111,7 @@ func (h *AttesterHandler) processFetching(ctx context.Context, epoch phase0.Epoc h.fetchCurrentEpoch = false } - if h.fetchNextEpoch && h.shouldFetchNexEpoch(slot) { + if h.fetchNextEpoch { if err := h.fetchAndProcessDuties(ctx, epoch+1); err != nil { h.logger.Error("failed to fetch duties for next epoch", zap.Error(err)) return @@ -136,6 +138,7 @@ func (h *AttesterHandler) processExecution(epoch phase0.Epoch, slot phase0.Slot) } func (h *AttesterHandler) processIndicesChange(epoch phase0.Epoch, slot phase0.Slot) { + h.duties.Reset(epoch) h.fetchCurrentEpoch = true // reset next epoch duties if in appropriate slot range @@ -143,6 +146,8 @@ func (h *AttesterHandler) processIndicesChange(epoch phase0.Epoch, slot phase0.S h.duties.Reset(epoch + 1) h.fetchNextEpoch = true } + + h.indicesChanged = false } func (h *AttesterHandler) processReorg(ctx context.Context, epoch phase0.Epoch, reorgEvent ReorgEvent) { @@ -290,5 +295,5 @@ func toBeaconCommitteeSubscription(duty *eth2apiv1.AttesterDuty, role spectypes. } func (h *AttesterHandler) shouldFetchNexEpoch(slot phase0.Slot) bool { - return uint64(slot)%h.network.Beacon.SlotsPerEpoch() > h.network.Beacon.SlotsPerEpoch()/2-2 + return uint64(slot)%h.network.Beacon.SlotsPerEpoch() >= h.network.Beacon.SlotsPerEpoch()/2-1 } diff --git a/operator/duties/attester_genesis_test.go b/operator/duties/attester_genesis_test.go index 385dff5f8a..662c7c5553 100644 --- a/operator/duties/attester_genesis_test.go +++ b/operator/duties/attester_genesis_test.go @@ -19,19 +19,16 @@ import ( "github.com/ssvlabs/ssv/protocol/v2/types" ) -func setupAttesterGenesisDutiesMock( +func setupDutiesMockAttesterGenesis( s *Scheduler, dutiesMap *hashmap.Map[phase0.Epoch, []*eth2apiv1.AttesterDuty], - waitForDuties *SafeValue[bool], ) (chan struct{}, chan []*genesisspectypes.Duty) { fetchDutiesCall := make(chan struct{}) executeDutiesCall := make(chan []*genesisspectypes.Duty) s.beaconNode.(*MockBeaconNode).EXPECT().AttesterDuties(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn( func(ctx context.Context, epoch phase0.Epoch, indices []phase0.ValidatorIndex) ([]*eth2apiv1.AttesterDuty, error) { - if waitForDuties.Get() { - fetchDutiesCall <- struct{}{} - } + fetchDutiesCall <- struct{}{} duties, _ := dutiesMap.Get(epoch) return duties, nil }).AnyTimes() @@ -64,7 +61,7 @@ func setupAttesterGenesisDutiesMock( return fetchDutiesCall, executeDutiesCall } -func expectedExecutedGenesisAttesterDuties(handler *AttesterHandler, duties []*eth2apiv1.AttesterDuty) []*genesisspectypes.Duty { +func expectedExecutedDutiesAttesterGenesis(handler *AttesterHandler, duties []*eth2apiv1.AttesterDuty) []*genesisspectypes.Duty { expectedDuties := make([]*genesisspectypes.Duty, 0) for _, d := range duties { expectedDuties = append(expectedDuties, handler.toGenesisSpecDuty(d, genesisspectypes.BNRoleAttester)) @@ -75,11 +72,10 @@ func expectedExecutedGenesisAttesterDuties(handler *AttesterHandler, duties []*e func TestScheduler_Attester_Genesis_Same_Slot(t *testing.T) { var ( - handler = NewAttesterHandler(dutystore.NewDuties[eth2apiv1.AttesterDuty]()) - currentSlot = &SafeValue[phase0.Slot]{} - dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() - waitForDuties = &SafeValue[bool]{} - forkEpoch = goclient.FarFutureEpoch + handler = NewAttesterHandler(dutystore.NewDuties[eth2apiv1.AttesterDuty]()) + currentSlot = &SafeValue[phase0.Slot]{} + dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() + forkEpoch = goclient.FarFutureEpoch ) dutiesMap.Set(phase0.Epoch(0), []*eth2apiv1.AttesterDuty{ { @@ -91,16 +87,17 @@ func TestScheduler_Attester_Genesis_Same_Slot(t *testing.T) { currentSlot.Set(phase0.Slot(1)) scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) - fetchDutiesCall, executeDutiesCall := setupAttesterGenesisDutiesMock(scheduler, dutiesMap, waitForDuties) + fetchDutiesCall, executeDutiesCall := setupDutiesMockAttesterGenesis(scheduler, dutiesMap) startFn() duties, _ := dutiesMap.Get(phase0.Epoch(0)) - expected := expectedExecutedGenesisAttesterDuties(handler, duties) + expected := expectedExecutedDutiesAttesterGenesis(handler, duties) setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) startTime := time.Now() ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) + waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + waitForDutiesExecutionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) require.Less(t, scheduler.network.Beacon.SlotDurationSec()/3, time.Since(startTime)) @@ -111,11 +108,10 @@ func TestScheduler_Attester_Genesis_Same_Slot(t *testing.T) { func TestScheduler_Attester_Genesis_Diff_Slots(t *testing.T) { var ( - handler = NewAttesterHandler(dutystore.NewDuties[eth2apiv1.AttesterDuty]()) - currentSlot = &SafeValue[phase0.Slot]{} - dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() - waitForDuties = &SafeValue[bool]{} - forkEpoch = goclient.FarFutureEpoch + handler = NewAttesterHandler(dutystore.NewDuties[eth2apiv1.AttesterDuty]()) + currentSlot = &SafeValue[phase0.Slot]{} + dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() + forkEpoch = goclient.FarFutureEpoch ) dutiesMap.Set(phase0.Epoch(0), []*eth2apiv1.AttesterDuty{ { @@ -127,11 +123,11 @@ func TestScheduler_Attester_Genesis_Diff_Slots(t *testing.T) { currentSlot.Set(phase0.Slot(0)) scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) - fetchDutiesCall, executeDutiesCall := setupAttesterGenesisDutiesMock(scheduler, dutiesMap, waitForDuties) + fetchDutiesCall, executeDutiesCall := setupDutiesMockAttesterGenesis(scheduler, dutiesMap) startFn() ticker.Send(currentSlot.Get()) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) currentSlot.Set(phase0.Slot(1)) ticker.Send(currentSlot.Get()) @@ -139,11 +135,11 @@ func TestScheduler_Attester_Genesis_Diff_Slots(t *testing.T) { currentSlot.Set(phase0.Slot(2)) duties, _ := dutiesMap.Get(phase0.Epoch(0)) - expected := expectedExecutedGenesisAttesterDuties(handler, duties) + expected := expectedExecutedDutiesAttesterGenesis(handler, duties) setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) + waitForDutiesExecutionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) // Stop scheduler & wait for graceful exit. cancel() @@ -152,15 +148,14 @@ func TestScheduler_Attester_Genesis_Diff_Slots(t *testing.T) { func TestScheduler_Attester_Genesis_Indices_Changed(t *testing.T) { var ( - handler = NewAttesterHandler(dutystore.NewDuties[eth2apiv1.AttesterDuty]()) - currentSlot = &SafeValue[phase0.Slot]{} - dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() - waitForDuties = &SafeValue[bool]{} - forkEpoch = goclient.FarFutureEpoch + handler = NewAttesterHandler(dutystore.NewDuties[eth2apiv1.AttesterDuty]()) + currentSlot = &SafeValue[phase0.Slot]{} + dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() + forkEpoch = goclient.FarFutureEpoch ) currentSlot.Set(phase0.Slot(0)) scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) - fetchDutiesCall, executeDutiesCall := setupAttesterGenesisDutiesMock(scheduler, dutiesMap, waitForDuties) + fetchDutiesCall, executeDutiesCall := setupDutiesMockAttesterGenesis(scheduler, dutiesMap) startFn() // STEP 1: wait for no action to be taken @@ -191,7 +186,6 @@ func TestScheduler_Attester_Genesis_Indices_Changed(t *testing.T) { // STEP 3: wait for attester duties to be fetched again currentSlot.Set(phase0.Slot(1)) - waitForDuties.Set(true) ticker.Send(currentSlot.Get()) waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // no execution should happen in slot 1 @@ -200,11 +194,11 @@ func TestScheduler_Attester_Genesis_Indices_Changed(t *testing.T) { // STEP 4: wait for attester duties to be executed currentSlot.Set(phase0.Slot(2)) duties, _ := dutiesMap.Get(phase0.Epoch(0)) - expected := expectedExecutedGenesisAttesterDuties(handler, []*eth2apiv1.AttesterDuty{duties[2]}) + expected := expectedExecutedDutiesAttesterGenesis(handler, []*eth2apiv1.AttesterDuty{duties[2]}) setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) + waitForDutiesExecutionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) // Stop scheduler & wait for graceful exit. cancel() @@ -213,15 +207,14 @@ func TestScheduler_Attester_Genesis_Indices_Changed(t *testing.T) { func TestScheduler_Attester_Genesis_Multiple_Indices_Changed_Same_Slot(t *testing.T) { var ( - handler = NewAttesterHandler(dutystore.NewDuties[eth2apiv1.AttesterDuty]()) - currentSlot = &SafeValue[phase0.Slot]{} - dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() - waitForDuties = &SafeValue[bool]{} - forkEpoch = goclient.FarFutureEpoch + handler = NewAttesterHandler(dutystore.NewDuties[eth2apiv1.AttesterDuty]()) + currentSlot = &SafeValue[phase0.Slot]{} + dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() + forkEpoch = goclient.FarFutureEpoch ) currentSlot.Set(phase0.Slot(0)) scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) - fetchDutiesCall, executeDutiesCall := setupAttesterGenesisDutiesMock(scheduler, dutiesMap, waitForDuties) + fetchDutiesCall, executeDutiesCall := setupDutiesMockAttesterGenesis(scheduler, dutiesMap) startFn() // STEP 1: wait for no action to be taken @@ -255,27 +248,26 @@ func TestScheduler_Attester_Genesis_Multiple_Indices_Changed_Same_Slot(t *testin // STEP 5: wait for attester duties to be fetched currentSlot.Set(phase0.Slot(2)) - waitForDuties.Set(true) ticker.Send(currentSlot.Get()) waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 6: wait for attester duties to be executed currentSlot.Set(phase0.Slot(3)) duties, _ = dutiesMap.Get(phase0.Epoch(0)) - expected := expectedExecutedGenesisAttesterDuties(handler, []*eth2apiv1.AttesterDuty{duties[0]}) + expected := expectedExecutedDutiesAttesterGenesis(handler, []*eth2apiv1.AttesterDuty{duties[0]}) setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) + waitForDutiesExecutionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) // STEP 7: wait for attester duties to be executed currentSlot.Set(phase0.Slot(4)) duties, _ = dutiesMap.Get(phase0.Epoch(0)) - expected = expectedExecutedGenesisAttesterDuties(handler, []*eth2apiv1.AttesterDuty{duties[1]}) + expected = expectedExecutedDutiesAttesterGenesis(handler, []*eth2apiv1.AttesterDuty{duties[1]}) setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) + waitForDutiesExecutionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) // Stop scheduler & wait for graceful exit. cancel() @@ -285,15 +277,14 @@ func TestScheduler_Attester_Genesis_Multiple_Indices_Changed_Same_Slot(t *testin // reorg previous dependent root changed func TestScheduler_Attester_Genesis_Reorg_Previous_Epoch_Transition(t *testing.T) { var ( - handler = NewAttesterHandler(dutystore.NewDuties[eth2apiv1.AttesterDuty]()) - currentSlot = &SafeValue[phase0.Slot]{} - dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() - waitForDuties = &SafeValue[bool]{} - forkEpoch = goclient.FarFutureEpoch + handler = NewAttesterHandler(dutystore.NewDuties[eth2apiv1.AttesterDuty]()) + currentSlot = &SafeValue[phase0.Slot]{} + dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() + forkEpoch = goclient.FarFutureEpoch ) currentSlot.Set(phase0.Slot(63)) scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) - fetchDutiesCall, executeDutiesCall := setupAttesterGenesisDutiesMock(scheduler, dutiesMap, waitForDuties) + fetchDutiesCall, executeDutiesCall := setupDutiesMockAttesterGenesis(scheduler, dutiesMap) startFn() dutiesMap.Set(phase0.Epoch(2), []*eth2apiv1.AttesterDuty{ @@ -305,7 +296,6 @@ func TestScheduler_Attester_Genesis_Reorg_Previous_Epoch_Transition(t *testing.T }) // STEP 1: wait for attester duties to be fetched for next epoch - waitForDuties.Set(true) ticker.Send(currentSlot.Get()) waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) @@ -355,11 +345,11 @@ func TestScheduler_Attester_Genesis_Reorg_Previous_Epoch_Transition(t *testing.T // STEP 7: The second assigned duty should be executed currentSlot.Set(phase0.Slot(67)) duties, _ := dutiesMap.Get(phase0.Epoch(2)) - expected := expectedExecutedGenesisAttesterDuties(handler, duties) + expected := expectedExecutedDutiesAttesterGenesis(handler, duties) setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) + waitForDutiesExecutionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) // Stop scheduler & wait for graceful exit. cancel() @@ -369,15 +359,14 @@ func TestScheduler_Attester_Genesis_Reorg_Previous_Epoch_Transition(t *testing.T // reorg previous dependent root changed and the indices changed as well func TestScheduler_Attester_Genesis_Reorg_Previous_Epoch_Transition_Indices_Changed(t *testing.T) { var ( - handler = NewAttesterHandler(dutystore.NewDuties[eth2apiv1.AttesterDuty]()) - currentSlot = &SafeValue[phase0.Slot]{} - dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() - waitForDuties = &SafeValue[bool]{} - forkEpoch = goclient.FarFutureEpoch + handler = NewAttesterHandler(dutystore.NewDuties[eth2apiv1.AttesterDuty]()) + currentSlot = &SafeValue[phase0.Slot]{} + dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() + forkEpoch = goclient.FarFutureEpoch ) currentSlot.Set(phase0.Slot(63)) scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) - fetchDutiesCall, executeDutiesCall := setupAttesterGenesisDutiesMock(scheduler, dutiesMap, waitForDuties) + fetchDutiesCall, executeDutiesCall := setupDutiesMockAttesterGenesis(scheduler, dutiesMap) startFn() dutiesMap.Set(phase0.Epoch(2), []*eth2apiv1.AttesterDuty{ @@ -389,7 +378,6 @@ func TestScheduler_Attester_Genesis_Reorg_Previous_Epoch_Transition_Indices_Chan }) // STEP 1: wait for attester duties to be fetched for next epoch - waitForDuties.Set(true) ticker.Send(currentSlot.Get()) waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) @@ -450,11 +438,11 @@ func TestScheduler_Attester_Genesis_Reorg_Previous_Epoch_Transition_Indices_Chan // STEP 8: The second assigned duty should be executed currentSlot.Set(phase0.Slot(67)) duties, _ = dutiesMap.Get(phase0.Epoch(2)) - expected := expectedExecutedGenesisAttesterDuties(handler, duties) + expected := expectedExecutedDutiesAttesterGenesis(handler, duties) setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) + waitForDutiesExecutionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) // Stop scheduler & wait for graceful exit. cancel() @@ -464,11 +452,10 @@ func TestScheduler_Attester_Genesis_Reorg_Previous_Epoch_Transition_Indices_Chan // reorg previous dependent root changed func TestScheduler_Attester_Genesis_Reorg_Previous(t *testing.T) { var ( - handler = NewAttesterHandler(dutystore.NewDuties[eth2apiv1.AttesterDuty]()) - currentSlot = &SafeValue[phase0.Slot]{} - dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() - waitForDuties = &SafeValue[bool]{} - forkEpoch = goclient.FarFutureEpoch + handler = NewAttesterHandler(dutystore.NewDuties[eth2apiv1.AttesterDuty]()) + currentSlot = &SafeValue[phase0.Slot]{} + dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() + forkEpoch = goclient.FarFutureEpoch ) dutiesMap.Set(phase0.Epoch(1), []*eth2apiv1.AttesterDuty{ { @@ -481,11 +468,11 @@ func TestScheduler_Attester_Genesis_Reorg_Previous(t *testing.T) { // STEP 1: wait for attester duties to be fetched (handle initial duties) scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) - fetchDutiesCall, executeDutiesCall := setupAttesterGenesisDutiesMock(scheduler, dutiesMap, waitForDuties) + fetchDutiesCall, executeDutiesCall := setupDutiesMockAttesterGenesis(scheduler, dutiesMap) startFn() ticker.Send(currentSlot.Get()) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 2: trigger head event e := ð2apiv1.Event{ @@ -498,7 +485,6 @@ func TestScheduler_Attester_Genesis_Reorg_Previous(t *testing.T) { waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 3: Ticker with no action - waitForDuties.Set(true) currentSlot.Set(phase0.Slot(33)) ticker.Send(currentSlot.Get()) waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) @@ -533,11 +519,11 @@ func TestScheduler_Attester_Genesis_Reorg_Previous(t *testing.T) { // STEP 7: The second assigned duty should be executed currentSlot.Set(phase0.Slot(36)) duties, _ := dutiesMap.Get(phase0.Epoch(1)) - expected := expectedExecutedGenesisAttesterDuties(handler, duties) + expected := expectedExecutedDutiesAttesterGenesis(handler, duties) setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) + waitForDutiesExecutionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) // Stop scheduler & wait for graceful exit. cancel() @@ -547,11 +533,10 @@ func TestScheduler_Attester_Genesis_Reorg_Previous(t *testing.T) { // reorg previous dependent root changed and the indices changed the same slot func TestScheduler_Attester_Genesis_Reorg_Previous_Indices_Change_Same_Slot(t *testing.T) { var ( - handler = NewAttesterHandler(dutystore.NewDuties[eth2apiv1.AttesterDuty]()) - currentSlot = &SafeValue[phase0.Slot]{} - dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() - waitForDuties = &SafeValue[bool]{} - forkEpoch = goclient.FarFutureEpoch + handler = NewAttesterHandler(dutystore.NewDuties[eth2apiv1.AttesterDuty]()) + currentSlot = &SafeValue[phase0.Slot]{} + dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() + forkEpoch = goclient.FarFutureEpoch ) dutiesMap.Set(phase0.Epoch(1), []*eth2apiv1.AttesterDuty{ { @@ -564,11 +549,11 @@ func TestScheduler_Attester_Genesis_Reorg_Previous_Indices_Change_Same_Slot(t *t // STEP 1: wait for attester duties to be fetched (handle initial duties) scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) - fetchDutiesCall, executeDutiesCall := setupAttesterGenesisDutiesMock(scheduler, dutiesMap, waitForDuties) + fetchDutiesCall, executeDutiesCall := setupDutiesMockAttesterGenesis(scheduler, dutiesMap) startFn() ticker.Send(currentSlot.Get()) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 2: trigger head event e := ð2apiv1.Event{ @@ -582,7 +567,6 @@ func TestScheduler_Attester_Genesis_Reorg_Previous_Indices_Change_Same_Slot(t *t // STEP 3: Ticker with no action currentSlot.Set(phase0.Slot(33)) - waitForDuties.Set(true) ticker.Send(currentSlot.Get()) waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) @@ -626,11 +610,11 @@ func TestScheduler_Attester_Genesis_Reorg_Previous_Indices_Change_Same_Slot(t *t // STEP 8: The second and new from indices change assigned duties should be executed currentSlot.Set(phase0.Slot(36)) duties, _ = dutiesMap.Get(phase0.Epoch(1)) - expected := expectedExecutedGenesisAttesterDuties(handler, duties) + expected := expectedExecutedDutiesAttesterGenesis(handler, duties) setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) + waitForDutiesExecutionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) // Stop scheduler & wait for graceful exit. cancel() @@ -640,15 +624,14 @@ func TestScheduler_Attester_Genesis_Reorg_Previous_Indices_Change_Same_Slot(t *t // reorg current dependent root changed func TestScheduler_Attester_Genesis_Reorg_Current(t *testing.T) { var ( - handler = NewAttesterHandler(dutystore.NewDuties[eth2apiv1.AttesterDuty]()) - currentSlot = &SafeValue[phase0.Slot]{} - dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() - waitForDuties = &SafeValue[bool]{} - forkEpoch = goclient.FarFutureEpoch + handler = NewAttesterHandler(dutystore.NewDuties[eth2apiv1.AttesterDuty]()) + currentSlot = &SafeValue[phase0.Slot]{} + dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() + forkEpoch = goclient.FarFutureEpoch ) currentSlot.Set(phase0.Slot(48)) scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) - fetchDutiesCall, executeDutiesCall := setupAttesterGenesisDutiesMock(scheduler, dutiesMap, waitForDuties) + fetchDutiesCall, executeDutiesCall := setupDutiesMockAttesterGenesis(scheduler, dutiesMap) startFn() dutiesMap.Set(phase0.Epoch(2), []*eth2apiv1.AttesterDuty{ @@ -660,7 +643,6 @@ func TestScheduler_Attester_Genesis_Reorg_Current(t *testing.T) { }) // STEP 1: wait for attester duties to be fetched for next epoch - waitForDuties.Set(true) ticker.Send(currentSlot.Get()) waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) @@ -717,11 +699,11 @@ func TestScheduler_Attester_Genesis_Reorg_Current(t *testing.T) { // STEP 8: The second assigned duty should be executed currentSlot.Set(phase0.Slot(65)) duties, _ := dutiesMap.Get(phase0.Epoch(2)) - expected := expectedExecutedGenesisAttesterDuties(handler, duties) + expected := expectedExecutedDutiesAttesterGenesis(handler, duties) setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) + waitForDutiesExecutionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) // Stop scheduler & wait for graceful exit. cancel() @@ -731,15 +713,15 @@ func TestScheduler_Attester_Genesis_Reorg_Current(t *testing.T) { // reorg current dependent root changed including indices change in the same slot func TestScheduler_Attester_Genesis_Reorg_Current_Indices_Changed(t *testing.T) { var ( - handler = NewAttesterHandler(dutystore.NewDuties[eth2apiv1.AttesterDuty]()) - currentSlot = &SafeValue[phase0.Slot]{} - dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() - waitForDuties = &SafeValue[bool]{} - forkEpoch = goclient.FarFutureEpoch + handler = NewAttesterHandler(dutystore.NewDuties[eth2apiv1.AttesterDuty]()) + currentSlot = &SafeValue[phase0.Slot]{} + dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() + + forkEpoch = goclient.FarFutureEpoch ) currentSlot.Set(phase0.Slot(48)) scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) - fetchDutiesCall, executeDutiesCall := setupAttesterGenesisDutiesMock(scheduler, dutiesMap, waitForDuties) + fetchDutiesCall, executeDutiesCall := setupDutiesMockAttesterGenesis(scheduler, dutiesMap) startFn() dutiesMap.Set(phase0.Epoch(2), []*eth2apiv1.AttesterDuty{ @@ -751,7 +733,6 @@ func TestScheduler_Attester_Genesis_Reorg_Current_Indices_Changed(t *testing.T) }) // STEP 1: wait for attester duties to be fetched for next epoch - waitForDuties.Set(true) ticker.Send(currentSlot.Get()) waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) @@ -818,11 +799,11 @@ func TestScheduler_Attester_Genesis_Reorg_Current_Indices_Changed(t *testing.T) // STEP 9: The second assigned duty should be executed currentSlot.Set(phase0.Slot(65)) duties, _ = dutiesMap.Get(phase0.Epoch(2)) - expected := expectedExecutedGenesisAttesterDuties(handler, duties) + expected := expectedExecutedDutiesAttesterGenesis(handler, duties) setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) + waitForDutiesExecutionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) // Stop scheduler & wait for graceful exit. cancel() @@ -831,11 +812,10 @@ func TestScheduler_Attester_Genesis_Reorg_Current_Indices_Changed(t *testing.T) func TestScheduler_Attester_Genesis_Early_Block(t *testing.T) { var ( - handler = NewAttesterHandler(dutystore.NewDuties[eth2apiv1.AttesterDuty]()) - currentSlot = &SafeValue[phase0.Slot]{} - dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() - waitForDuties = &SafeValue[bool]{} - forkEpoch = goclient.FarFutureEpoch + handler = NewAttesterHandler(dutystore.NewDuties[eth2apiv1.AttesterDuty]()) + currentSlot = &SafeValue[phase0.Slot]{} + dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() + forkEpoch = goclient.FarFutureEpoch ) dutiesMap.Set(phase0.Epoch(0), []*eth2apiv1.AttesterDuty{ { @@ -848,11 +828,11 @@ func TestScheduler_Attester_Genesis_Early_Block(t *testing.T) { // STEP 1: wait for attester duties to be fetched (handle initial duties) scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) - fetchDutiesCall, executeDutiesCall := setupAttesterGenesisDutiesMock(scheduler, dutiesMap, waitForDuties) + fetchDutiesCall, executeDutiesCall := setupDutiesMockAttesterGenesis(scheduler, dutiesMap) startFn() ticker.Send(currentSlot.Get()) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 2: wait for no action to be taken currentSlot.Set(phase0.Slot(1)) @@ -864,7 +844,7 @@ func TestScheduler_Attester_Genesis_Early_Block(t *testing.T) { currentSlot.Set(phase0.Slot(2)) ticker.Send(currentSlot.Get()) duties, _ := dutiesMap.Get(phase0.Epoch(0)) - expected := expectedExecutedGenesisAttesterDuties(handler, duties) + expected := expectedExecutedDutiesAttesterGenesis(handler, duties) setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) // STEP 4: trigger head event (block arrival) @@ -874,7 +854,7 @@ func TestScheduler_Attester_Genesis_Early_Block(t *testing.T) { }, } scheduler.HandleHeadEvent(logger)(e) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) + waitForDutiesExecutionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) require.Less(t, time.Since(startTime), scheduler.network.Beacon.SlotDurationSec()/3) // Stop scheduler & wait for graceful exit. @@ -884,15 +864,15 @@ func TestScheduler_Attester_Genesis_Early_Block(t *testing.T) { func TestScheduler_Attester_Genesis_Start_In_The_End_Of_The_Epoch(t *testing.T) { var ( - handler = NewAttesterHandler(dutystore.NewDuties[eth2apiv1.AttesterDuty]()) - currentSlot = &SafeValue[phase0.Slot]{} - dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() - waitForDuties = &SafeValue[bool]{} - forkEpoch = goclient.FarFutureEpoch + handler = NewAttesterHandler(dutystore.NewDuties[eth2apiv1.AttesterDuty]()) + currentSlot = &SafeValue[phase0.Slot]{} + dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() + + forkEpoch = goclient.FarFutureEpoch ) currentSlot.Set(phase0.Slot(31)) scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) - fetchDutiesCall, executeDutiesCall := setupAttesterGenesisDutiesMock(scheduler, dutiesMap, waitForDuties) + fetchDutiesCall, executeDutiesCall := setupDutiesMockAttesterGenesis(scheduler, dutiesMap) startFn() dutiesMap.Set(phase0.Epoch(1), []*eth2apiv1.AttesterDuty{ @@ -904,18 +884,17 @@ func TestScheduler_Attester_Genesis_Start_In_The_End_Of_The_Epoch(t *testing.T) }) // STEP 1: wait for attester duties to be fetched for the next epoch - waitForDuties.Set(true) ticker.Send(currentSlot.Get()) waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 2: wait for attester duties to be executed currentSlot.Set(phase0.Slot(32)) duties, _ := dutiesMap.Get(phase0.Epoch(1)) - expected := expectedExecutedGenesisAttesterDuties(handler, duties) + expected := expectedExecutedDutiesAttesterGenesis(handler, duties) setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) + waitForDutiesExecutionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) // Stop scheduler & wait for graceful exit. cancel() @@ -924,15 +903,14 @@ func TestScheduler_Attester_Genesis_Start_In_The_End_Of_The_Epoch(t *testing.T) func TestScheduler_Attester_Genesis_Fetch_Execute_Next_Epoch_Duty(t *testing.T) { var ( - handler = NewAttesterHandler(dutystore.NewDuties[eth2apiv1.AttesterDuty]()) - currentSlot = &SafeValue[phase0.Slot]{} - dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() - waitForDuties = &SafeValue[bool]{} - forkEpoch = goclient.FarFutureEpoch + handler = NewAttesterHandler(dutystore.NewDuties[eth2apiv1.AttesterDuty]()) + currentSlot = &SafeValue[phase0.Slot]{} + dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() + forkEpoch = goclient.FarFutureEpoch ) - currentSlot.Set(phase0.Slot(13)) + currentSlot.Set(phase0.Slot(14)) scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) - fetchDutiesCall, executeDutiesCall := setupAttesterGenesisDutiesMock(scheduler, dutiesMap, waitForDuties) + fetchDutiesCall, executeDutiesCall := setupDutiesMockAttesterGenesis(scheduler, dutiesMap) startFn() dutiesMap.Set(phase0.Epoch(1), []*eth2apiv1.AttesterDuty{ @@ -948,24 +926,23 @@ func TestScheduler_Attester_Genesis_Fetch_Execute_Next_Epoch_Duty(t *testing.T) waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 2: wait for no action to be taken - currentSlot.Set(phase0.Slot(14)) + currentSlot.Set(phase0.Slot(15)) ticker.Send(currentSlot.Get()) waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 3: wait for duties to be fetched for the next epoch - currentSlot.Set(phase0.Slot(15)) - waitForDuties.Set(true) + currentSlot.Set(phase0.Slot(16)) ticker.Send(currentSlot.Get()) waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 4: wait for attester duties to be executed currentSlot.Set(phase0.Slot(32)) duties, _ := dutiesMap.Get(phase0.Epoch(1)) - expected := expectedExecutedGenesisAttesterDuties(handler, duties) + expected := expectedExecutedDutiesAttesterGenesis(handler, duties) setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) + waitForDutiesExecutionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) // Stop scheduler & wait for graceful exit. cancel() diff --git a/operator/duties/committee.go b/operator/duties/committee.go index 76495843aa..0a0b625f25 100644 --- a/operator/duties/committee.go +++ b/operator/duties/committee.go @@ -20,6 +20,8 @@ type CommitteeHandler struct { attHandler *AttesterHandler syncHandler *SyncCommitteeHandler + + firstRun bool } type committeeDuty struct { @@ -32,6 +34,7 @@ func NewCommitteeHandler(attHandler *AttesterHandler, syncHandler *SyncCommittee h := &CommitteeHandler{ attHandler: attHandler, syncHandler: syncHandler, + firstRun: true, } return h @@ -59,6 +62,9 @@ func (h *CommitteeHandler) HandleDuties(ctx context.Context) { tickerID := fields.FormatSlotTickerCommitteeID(period, epoch, slot) if !h.network.PastAlanForkAtEpoch(epoch) { + if h.firstRun { + h.firstRun = false + } h.logger.Debug("🛠 ticker event", fields.SlotTickerID(tickerID), zap.String("status", "alan not forked yet"), @@ -67,12 +73,12 @@ func (h *CommitteeHandler) HandleDuties(ctx context.Context) { } h.logger.Debug("🛠 ticker event", fields.SlotTickerID(tickerID)) + if h.firstRun { + h.processFirstRun(ctx, period, epoch, slot) + } h.processExecution(period, epoch, slot) if h.indicesChanged { - h.attHandler.duties.Reset(epoch) - // we should not reset sync committee duties here as it is necessary for message validation - // but this can lead to executing duties for deleted/liquidated validators - h.indicesChanged = false + h.processIndicesChange(period, epoch, slot) } h.processFetching(ctx, period, epoch, slot) h.processSlotTransition(period, epoch, slot) @@ -94,12 +100,28 @@ func (h *CommitteeHandler) HandleDuties(ctx context.Context) { h.logger.Info("🔁 indices change received", fields.SlotTickerID(tickerID)) h.indicesChanged = true - h.attHandler.processIndicesChange(epoch, slot) - h.syncHandler.processIndicesChange(period, slot) } } } +func (h *CommitteeHandler) processFirstRun(ctx context.Context, period uint64, epoch phase0.Epoch, slot phase0.Slot) { + var wg sync.WaitGroup + wg.Add(2) + + go func() { + defer wg.Done() + h.attHandler.processFirstRun(ctx, epoch, slot) + }() + + go func() { + defer wg.Done() + h.syncHandler.processFirstRun(ctx, period, slot) + }() + + wg.Wait() + h.firstRun = false +} + func (h *CommitteeHandler) processExecution(period uint64, epoch phase0.Epoch, slot phase0.Slot) { attDuties := h.attHandler.duties.CommitteeSlotDuties(epoch, slot) syncDuties := h.syncHandler.duties.CommitteePeriodDuties(period) @@ -120,18 +142,24 @@ func (h *CommitteeHandler) processFetching(ctx context.Context, period uint64, e wg.Add(2) go func() { - defer wg.Done() // Mark this goroutine as done once it completes + defer wg.Done() h.attHandler.processFetching(ctx, epoch, slot) }() go func() { - defer wg.Done() // Mark this goroutine as done once it completes + defer wg.Done() h.syncHandler.processFetching(ctx, period, slot, true) }() wg.Wait() } +func (h *CommitteeHandler) processIndicesChange(period uint64, epoch phase0.Epoch, slot phase0.Slot) { + h.attHandler.processIndicesChange(epoch, slot) + h.syncHandler.processIndicesChange(period, slot) + h.indicesChanged = false +} + func (h *CommitteeHandler) processSlotTransition(period uint64, epoch phase0.Epoch, slot phase0.Slot) { h.attHandler.processSlotTransition(epoch, slot) h.syncHandler.processSlotTransition(period, slot) diff --git a/operator/duties/committee_test.go b/operator/duties/committee_test.go index e46b1927b4..541a4047a7 100644 --- a/operator/duties/committee_test.go +++ b/operator/duties/committee_test.go @@ -18,12 +18,11 @@ import ( ssvtypes "github.com/ssvlabs/ssv/protocol/v2/types" ) -func setupCommitteeDutiesMock( +func setupDutiesMockCommittee( s *Scheduler, activeShares []*ssvtypes.SSVShare, attDuties *hashmap.Map[phase0.Epoch, []*eth2apiv1.AttesterDuty], syncDuties *hashmap.Map[uint64, []*eth2apiv1.SyncCommitteeDuty], - waitForDuties *SafeValue[bool], ) (chan struct{}, chan committeeDutiesMap) { fetchDutiesCall := make(chan struct{}) executeDutiesCall := make(chan committeeDutiesMap) @@ -57,18 +56,14 @@ func setupCommitteeDutiesMock( s.beaconNode.(*MockBeaconNode).EXPECT().AttesterDuties(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn( func(ctx context.Context, epoch phase0.Epoch, indices []phase0.ValidatorIndex) ([]*eth2apiv1.AttesterDuty, error) { - if waitForDuties.Get() { - fetchDutiesCall <- struct{}{} - } + fetchDutiesCall <- struct{}{} duties, _ := attDuties.Get(epoch) return duties, nil }).AnyTimes() s.beaconNode.(*MockBeaconNode).EXPECT().SyncCommitteeDuties(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn( func(ctx context.Context, epoch phase0.Epoch, indices []phase0.ValidatorIndex) ([]*eth2apiv1.SyncCommitteeDuty, error) { - if waitForDuties.Get() { - fetchDutiesCall <- struct{}{} - } + fetchDutiesCall <- struct{}{} period := s.network.Beacon.EstimatedSyncCommitteePeriodAtEpoch(epoch) duties, _ := syncDuties.Get(period) return duties, nil @@ -96,7 +91,6 @@ func TestScheduler_Committee_Same_Slot_Attester_Only(t *testing.T) { commHandler = NewCommitteeHandler(attHandler, syncHandler) alanForkEpoch = phase0.Epoch(0) currentSlot = &SafeValue[phase0.Slot]{} - waitForDuties = &SafeValue[bool]{} attDuties = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() syncDuties = hashmap.New[uint64, []*eth2apiv1.SyncCommitteeDuty]() activeShares = []*ssvtypes.SSVShare{{ @@ -118,18 +112,20 @@ func TestScheduler_Committee_Same_Slot_Attester_Only(t *testing.T) { currentSlot.Set(phase0.Slot(1)) scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot, alanForkEpoch) - fetchDutiesCall, executeDutiesCall := setupCommitteeDutiesMock(scheduler, activeShares, attDuties, syncDuties, waitForDuties) + fetchDutiesCall, executeDutiesCall := setupDutiesMockCommittee(scheduler, activeShares, attDuties, syncDuties) startFn() // STEP 1: wait for attester duties to be fetched and executed at the same slot duties, _ := attDuties.Get(phase0.Epoch(0)) committeeMap := commHandler.buildCommitteeDuties(duties, nil, 0, currentSlot.Get()) - setExecuteDutyFuncs(scheduler, executeDutiesCall, len(committeeMap)) startTime := time.Now() ticker.Send(currentSlot.Get()) - + // wait for attester duties to be fetched + waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + // wait for sync committee duties to be fetched + waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) waitForDutiesExecutionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout, committeeMap) // validate the 1/3 of the slot waiting time @@ -148,7 +144,6 @@ func TestScheduler_Committee_Same_Slot_SyncCommittee_Only(t *testing.T) { commHandler = NewCommitteeHandler(attHandler, syncHandler) alanForkEpoch = phase0.Epoch(0) currentSlot = &SafeValue[phase0.Slot]{} - waitForDuties = &SafeValue[bool]{} attDuties = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() syncDuties = hashmap.New[uint64, []*eth2apiv1.SyncCommitteeDuty]() activeShares = []*ssvtypes.SSVShare{{ @@ -169,18 +164,20 @@ func TestScheduler_Committee_Same_Slot_SyncCommittee_Only(t *testing.T) { currentSlot.Set(phase0.Slot(1)) scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot, alanForkEpoch) - fetchDutiesCall, executeDutiesCall := setupCommitteeDutiesMock(scheduler, activeShares, attDuties, syncDuties, waitForDuties) + fetchDutiesCall, executeDutiesCall := setupDutiesMockCommittee(scheduler, activeShares, attDuties, syncDuties) startFn() // STEP 1: wait for attester duties to be fetched and executed at the same slot duties, _ := syncDuties.Get(0) committeeMap := commHandler.buildCommitteeDuties(nil, duties, 0, currentSlot.Get()) - setExecuteDutyFuncs(scheduler, executeDutiesCall, len(committeeMap)) startTime := time.Now() ticker.Send(currentSlot.Get()) - + // wait for attester duties to be fetched + waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + // wait for sync committee duties to be fetched + waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) waitForDutiesExecutionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout, committeeMap) // validate the 1/3 of the slot waiting time @@ -199,7 +196,6 @@ func TestScheduler_Committee_Same_Slot(t *testing.T) { commHandler = NewCommitteeHandler(attHandler, syncHandler) alanForkEpoch = phase0.Epoch(0) currentSlot = &SafeValue[phase0.Slot]{} - waitForDuties = &SafeValue[bool]{} attDuties = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() syncDuties = hashmap.New[uint64, []*eth2apiv1.SyncCommitteeDuty]() activeShares = []*ssvtypes.SSVShare{{ @@ -227,19 +223,21 @@ func TestScheduler_Committee_Same_Slot(t *testing.T) { currentSlot.Set(phase0.Slot(1)) scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot, alanForkEpoch) - fetchDutiesCall, executeDutiesCall := setupCommitteeDutiesMock(scheduler, activeShares, attDuties, syncDuties, waitForDuties) + fetchDutiesCall, executeDutiesCall := setupDutiesMockCommittee(scheduler, activeShares, attDuties, syncDuties) startFn() // STEP 1: wait for attester duties to be fetched and executed at the same slot aDuties, _ := attDuties.Get(0) sDuties, _ := syncDuties.Get(0) committeeMap := commHandler.buildCommitteeDuties(aDuties, sDuties, 0, currentSlot.Get()) - setExecuteDutyFuncs(scheduler, executeDutiesCall, len(committeeMap)) startTime := time.Now() ticker.Send(currentSlot.Get()) - + // wait for attester duties to be fetched + waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + // wait for sync committee duties to be fetched + waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) waitForDutiesExecutionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout, committeeMap) // validate the 1/3 of the slot waiting time @@ -258,7 +256,6 @@ func TestScheduler_Committee_Diff_Slot_Attester_Only(t *testing.T) { commHandler = NewCommitteeHandler(attHandler, syncHandler) alanForkEpoch = phase0.Epoch(0) currentSlot = &SafeValue[phase0.Slot]{} - waitForDuties = &SafeValue[bool]{} attDuties = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() syncDuties = hashmap.New[uint64, []*eth2apiv1.SyncCommitteeDuty]() activeShares = []*ssvtypes.SSVShare{{ @@ -278,15 +275,15 @@ func TestScheduler_Committee_Diff_Slot_Attester_Only(t *testing.T) { }, }) - // STEP 1: wait for attester duties to be fetched using handle initial duties scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot, alanForkEpoch) - fetchDutiesCall, executeDutiesCall := setupCommitteeDutiesMock(scheduler, activeShares, attDuties, syncDuties, waitForDuties) + fetchDutiesCall, executeDutiesCall := setupDutiesMockCommittee(scheduler, activeShares, attDuties, syncDuties) startFn() - // STEP 2: wait for no action to be taken + // STEP 1: wait for committee duties to be fetched currentSlot.Set(phase0.Slot(1)) ticker.Send(currentSlot.Get()) - waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 3: wait for committee duties to be executed currentSlot.Set(phase0.Slot(2)) @@ -307,6 +304,103 @@ func TestScheduler_Committee_Diff_Slot_Attester_Only(t *testing.T) { require.NoError(t, schedulerPool.Wait()) } +func TestScheduler_Committee_Current_Next_Periods(t *testing.T) { + var ( + dutyStore = dutystore.New() + attHandler = NewAttesterHandler(dutyStore.Attester) + syncHandler = NewSyncCommitteeHandler(dutyStore.SyncCommittee) + commHandler = NewCommitteeHandler(attHandler, syncHandler) + alanForkEpoch = phase0.Epoch(0) + currentSlot = &SafeValue[phase0.Slot]{} + attDuties = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() + syncDuties = hashmap.New[uint64, []*eth2apiv1.SyncCommitteeDuty]() + activeShares = []*ssvtypes.SSVShare{ + { + Share: spectypes.Share{ + Committee: []*spectypes.ShareMember{ + {Signer: 1}, {Signer: 2}, {Signer: 3}, {Signer: 4}, + }, + ValidatorIndex: 1, + }, + }, + { + Share: spectypes.Share{ + Committee: []*spectypes.ShareMember{ + {Signer: 1}, {Signer: 2}, {Signer: 3}, {Signer: 4}, + }, + ValidatorIndex: 2, + }, + }, + } + ) + attDuties.Set(phase0.Epoch(254), []*eth2apiv1.AttesterDuty{ + { + PubKey: phase0.BLSPubKey{1, 2, 3}, + Slot: phase0.Slot(256*32 - 49), + ValidatorIndex: phase0.ValidatorIndex(1), + }, + }) + attDuties.Set(phase0.Epoch(255), []*eth2apiv1.AttesterDuty{ + { + PubKey: phase0.BLSPubKey{1, 2, 4}, + Slot: phase0.Slot(254 * 32), + ValidatorIndex: phase0.ValidatorIndex(2), + }, + }) + syncDuties.Set(0, []*eth2apiv1.SyncCommitteeDuty{ + { + PubKey: phase0.BLSPubKey{1, 2, 3}, + ValidatorIndex: phase0.ValidatorIndex(1), + }, + }) + syncDuties.Set(1, []*eth2apiv1.SyncCommitteeDuty{ + { + PubKey: phase0.BLSPubKey{1, 2, 4}, + ValidatorIndex: phase0.ValidatorIndex(2), + }, + }) + + currentSlot.Set(phase0.Slot(256*32 - 49)) + scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot, alanForkEpoch) + fetchDutiesCall, executeDutiesCall := setupDutiesMockCommittee(scheduler, activeShares, attDuties, syncDuties) + startFn() + + // STEP 1: wait for committee duties to be fetched and executed + aDuties, _ := attDuties.Get(phase0.Epoch(254)) + sDuties, _ := syncDuties.Get(0) + committeeMap := commHandler.buildCommitteeDuties(aDuties, sDuties, 2, currentSlot.Get()) + setExecuteDutyFuncs(scheduler, executeDutiesCall, len(committeeMap)) + + ticker.Send(currentSlot.Get()) + // wait for attester duties to be fetched + waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + // wait for sync committee duties to be fetched + waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + waitForDutiesExecutionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout, committeeMap) + + // STEP 2: wait for committee duty to be executed + currentSlot.Set(phase0.Slot(256*32 - 48)) + aDuties, _ = attDuties.Get(0) + sDuties, _ = syncDuties.Get(0) + committeeMap = commHandler.buildCommitteeDuties(aDuties, sDuties, 0, currentSlot.Get()) + setExecuteDutyFuncs(scheduler, executeDutiesCall, len(committeeMap)) + + startTime := time.Now() + ticker.Send(currentSlot.Get()) + // wait for attester duties to be fetched + waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + // wait for sync committee duties to be fetched + waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + waitForDutiesExecutionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout, committeeMap) + + // Validate execution within 1/3 of the slot time + require.Less(t, scheduler.network.Beacon.SlotDurationSec()/3, time.Since(startTime)) + + // Stop scheduler & wait for graceful exit + cancel() + require.NoError(t, schedulerPool.Wait()) +} + func TestScheduler_Committee_Indices_Changed_Attester_Only(t *testing.T) { var ( dutyStore = dutystore.New() @@ -315,7 +409,6 @@ func TestScheduler_Committee_Indices_Changed_Attester_Only(t *testing.T) { commHandler = NewCommitteeHandler(attHandler, syncHandler) alanForkEpoch = phase0.Epoch(0) currentSlot = &SafeValue[phase0.Slot]{} - waitForDuties = &SafeValue[bool]{} attDuties = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() syncDuties = hashmap.New[uint64, []*eth2apiv1.SyncCommitteeDuty]() activeShares = []*ssvtypes.SSVShare{ @@ -347,13 +440,15 @@ func TestScheduler_Committee_Indices_Changed_Attester_Only(t *testing.T) { ) scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot, alanForkEpoch) - fetchDutiesCall, executeDutiesCall := setupCommitteeDutiesMock(scheduler, activeShares, attDuties, syncDuties, waitForDuties) + fetchDutiesCall, executeDutiesCall := setupDutiesMockCommittee(scheduler, activeShares, attDuties, syncDuties) startFn() // STEP 1: wait for no action to be taken ticker.Send(currentSlot.Get()) - // no execution should happen in slot 0 - waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + // wait for attester duties to be fetched + waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + // wait for sync committee duties to be fetched + waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 2: trigger a change in active indices scheduler.indicesChg <- struct{}{} @@ -378,7 +473,6 @@ func TestScheduler_Committee_Indices_Changed_Attester_Only(t *testing.T) { // STEP 3: wait for attester duties to be fetched currentSlot.Set(phase0.Slot(1)) - waitForDuties.Set(true) ticker.Send(currentSlot.Get()) // wait for attester duties to be fetched @@ -414,7 +508,6 @@ func TestScheduler_Committee_Indices_Changed_Attester_Only_2(t *testing.T) { commHandler = NewCommitteeHandler(attHandler, syncHandler) alanForkEpoch = phase0.Epoch(0) currentSlot = &SafeValue[phase0.Slot]{} - waitForDuties = &SafeValue[bool]{} attDuties = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() syncDuties = hashmap.New[uint64, []*eth2apiv1.SyncCommitteeDuty]() activeShares = []*ssvtypes.SSVShare{ @@ -446,13 +539,15 @@ func TestScheduler_Committee_Indices_Changed_Attester_Only_2(t *testing.T) { ) scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot, alanForkEpoch) - fetchDutiesCall, executeDutiesCall := setupCommitteeDutiesMock(scheduler, activeShares, attDuties, syncDuties, waitForDuties) + fetchDutiesCall, executeDutiesCall := setupDutiesMockCommittee(scheduler, activeShares, attDuties, syncDuties) startFn() // STEP 1: wait for no action to be taken ticker.Send(currentSlot.Get()) - // no execution should happen in slot 0 - waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + // wait for attester duties to be fetched + waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + // wait for sync committee duties to be fetched + waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 2: trigger a change in active indices scheduler.indicesChg <- struct{}{} @@ -477,7 +572,6 @@ func TestScheduler_Committee_Indices_Changed_Attester_Only_2(t *testing.T) { // STEP 3: wait for attester duties to be fetched currentSlot.Set(phase0.Slot(1)) - waitForDuties.Set(true) ticker.Send(currentSlot.Get()) // wait for attester duties to be fetched @@ -513,7 +607,6 @@ func TestScheduler_Committee_Indices_Changed_Attester_Only_3(t *testing.T) { commHandler = NewCommitteeHandler(attHandler, syncHandler) alanForkEpoch = phase0.Epoch(0) currentSlot = &SafeValue[phase0.Slot]{} - waitForDuties = &SafeValue[bool]{} attDuties = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() syncDuties = hashmap.New[uint64, []*eth2apiv1.SyncCommitteeDuty]() activeShares = []*ssvtypes.SSVShare{ @@ -543,13 +636,17 @@ func TestScheduler_Committee_Indices_Changed_Attester_Only_3(t *testing.T) { }, }) - // STEP 1: wait for attester duties to be fetched using handle initial duties + // STEP 1: wait for attester duties to be fetched scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot, alanForkEpoch) - fetchDutiesCall, executeDutiesCall := setupCommitteeDutiesMock(scheduler, activeShares, attDuties, syncDuties, waitForDuties) + fetchDutiesCall, executeDutiesCall := setupDutiesMockCommittee(scheduler, activeShares, attDuties, syncDuties) startFn() // STEP 1: wait for no action to be taken ticker.Send(currentSlot.Get()) + // wait for attester duties to be fetched + waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + // wait for sync committee duties to be fetched + waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // no execution should happen in slot 0 waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) @@ -566,7 +663,6 @@ func TestScheduler_Committee_Indices_Changed_Attester_Only_3(t *testing.T) { // STEP 3: wait for attester duties to be fetched currentSlot.Set(phase0.Slot(1)) - waitForDuties.Set(true) ticker.Send(currentSlot.Get()) // wait for attester duties to be fetched @@ -603,7 +699,6 @@ func TestScheduler_Committee_Reorg_Previous_Epoch_Transition_Attester_Only(t *te commHandler = NewCommitteeHandler(attHandler, syncHandler) alanForkEpoch = phase0.Epoch(0) currentSlot = &SafeValue[phase0.Slot]{} - waitForDuties = &SafeValue[bool]{} attDuties = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() syncDuties = hashmap.New[uint64, []*eth2apiv1.SyncCommitteeDuty]() activeShares = []*ssvtypes.SSVShare{ @@ -620,7 +715,7 @@ func TestScheduler_Committee_Reorg_Previous_Epoch_Transition_Attester_Only(t *te currentSlot.Set(phase0.Slot(63)) scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot, alanForkEpoch) - fetchDutiesCall, executeDutiesCall := setupCommitteeDutiesMock(scheduler, activeShares, attDuties, syncDuties, waitForDuties) + fetchDutiesCall, executeDutiesCall := setupDutiesMockCommittee(scheduler, activeShares, attDuties, syncDuties) startFn() attDuties.Set(phase0.Epoch(2), []*eth2apiv1.AttesterDuty{ @@ -632,10 +727,13 @@ func TestScheduler_Committee_Reorg_Previous_Epoch_Transition_Attester_Only(t *te }) // STEP 1: wait for attester duties to be fetched for next epoch - waitForDuties.Set(true) ticker.Send(currentSlot.Get()) // wait for attester duties to be fetched waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + // wait for sync committee duties to be fetched + waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + // wait for next epoch attester duties to be fetched + waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 2: trigger head event e := ð2apiv1.Event{ @@ -701,7 +799,6 @@ func TestScheduler_Committee_Reorg_Previous_Epoch_Transition_Indices_Changed_Att commHandler = NewCommitteeHandler(attHandler, syncHandler) alanForkEpoch = phase0.Epoch(0) currentSlot = &SafeValue[phase0.Slot]{} - waitForDuties = &SafeValue[bool]{} attDuties = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() syncDuties = hashmap.New[uint64, []*eth2apiv1.SyncCommitteeDuty]() activeShares = []*ssvtypes.SSVShare{ @@ -726,7 +823,7 @@ func TestScheduler_Committee_Reorg_Previous_Epoch_Transition_Indices_Changed_Att currentSlot.Set(phase0.Slot(63)) scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot, alanForkEpoch) - fetchDutiesCall, executeDutiesCall := setupCommitteeDutiesMock(scheduler, activeShares, attDuties, syncDuties, waitForDuties) + fetchDutiesCall, executeDutiesCall := setupDutiesMockCommittee(scheduler, activeShares, attDuties, syncDuties) startFn() attDuties.Set(phase0.Epoch(2), []*eth2apiv1.AttesterDuty{ @@ -738,10 +835,13 @@ func TestScheduler_Committee_Reorg_Previous_Epoch_Transition_Indices_Changed_Att }) // STEP 1: wait for attester duties to be fetched for next epoch - waitForDuties.Set(true) ticker.Send(currentSlot.Get()) // wait for attester duties to be fetched waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + // wait for sync committee duties to be fetched + waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + // wait for next epoch attester duties to be fetched + waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 2: trigger head event e := ð2apiv1.Event{ @@ -817,7 +917,6 @@ func TestScheduler_Committee_Reorg_Previous_Attester_Only(t *testing.T) { commHandler = NewCommitteeHandler(attHandler, syncHandler) alanForkEpoch = phase0.Epoch(0) currentSlot = &SafeValue[phase0.Slot]{} - waitForDuties = &SafeValue[bool]{} attDuties = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() syncDuties = hashmap.New[uint64, []*eth2apiv1.SyncCommitteeDuty]() activeShares = []*ssvtypes.SSVShare{ @@ -839,13 +938,17 @@ func TestScheduler_Committee_Reorg_Previous_Attester_Only(t *testing.T) { }, }) - // STEP 1: wait for attester duties to be fetched (handle initial duties) + // STEP 1: wait for attester duties to be fetched currentSlot.Set(phase0.Slot(32)) scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot, alanForkEpoch) - fetchDutiesCall, executeDutiesCall := setupCommitteeDutiesMock(scheduler, activeShares, attDuties, syncDuties, waitForDuties) + fetchDutiesCall, executeDutiesCall := setupDutiesMockCommittee(scheduler, activeShares, attDuties, syncDuties) startFn() ticker.Send(currentSlot.Get()) + // wait for attester duties to be fetched + waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + // wait for sync committee duties to be fetched + waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 2: trigger head event @@ -859,7 +962,6 @@ func TestScheduler_Committee_Reorg_Previous_Attester_Only(t *testing.T) { waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 3: Ticker with no action - waitForDuties.Set(true) currentSlot.Set(phase0.Slot(33)) ticker.Send(currentSlot.Get()) waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) @@ -915,7 +1017,6 @@ func TestScheduler_Committee_Reorg_Previous_Indices_Changed_Attester_Only(t *tes commHandler = NewCommitteeHandler(attHandler, syncHandler) alanForkEpoch = phase0.Epoch(0) currentSlot = &SafeValue[phase0.Slot]{} - waitForDuties = &SafeValue[bool]{} attDuties = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() syncDuties = hashmap.New[uint64, []*eth2apiv1.SyncCommitteeDuty]() activeShares = []*ssvtypes.SSVShare{ @@ -945,13 +1046,17 @@ func TestScheduler_Committee_Reorg_Previous_Indices_Changed_Attester_Only(t *tes }, }) - // STEP 1: wait for attester duties to be fetched (handle initial duties) + // STEP 1: wait for attester duties to be fetched currentSlot.Set(phase0.Slot(32)) scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot, alanForkEpoch) - fetchDutiesCall, executeDutiesCall := setupCommitteeDutiesMock(scheduler, activeShares, attDuties, syncDuties, waitForDuties) + fetchDutiesCall, executeDutiesCall := setupDutiesMockCommittee(scheduler, activeShares, attDuties, syncDuties) startFn() ticker.Send(currentSlot.Get()) + // wait for attester duties to be fetched + waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + // wait for sync committee duties to be fetched + waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 2: trigger head event @@ -966,7 +1071,6 @@ func TestScheduler_Committee_Reorg_Previous_Indices_Changed_Attester_Only(t *tes // STEP 3: Ticker with no action currentSlot.Set(phase0.Slot(33)) - waitForDuties.Set(true) ticker.Send(currentSlot.Get()) waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) @@ -1035,7 +1139,6 @@ func TestScheduler_Committee_Reorg_Current_Attester_Only(t *testing.T) { commHandler = NewCommitteeHandler(attHandler, syncHandler) alanForkEpoch = phase0.Epoch(0) currentSlot = &SafeValue[phase0.Slot]{} - waitForDuties = &SafeValue[bool]{} attDuties = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() syncDuties = hashmap.New[uint64, []*eth2apiv1.SyncCommitteeDuty]() activeShares = []*ssvtypes.SSVShare{ @@ -1051,7 +1154,7 @@ func TestScheduler_Committee_Reorg_Current_Attester_Only(t *testing.T) { ) currentSlot.Set(phase0.Slot(48)) scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot, alanForkEpoch) - fetchDutiesCall, executeDutiesCall := setupCommitteeDutiesMock(scheduler, activeShares, attDuties, syncDuties, waitForDuties) + fetchDutiesCall, executeDutiesCall := setupDutiesMockCommittee(scheduler, activeShares, attDuties, syncDuties) startFn() attDuties.Set(phase0.Epoch(2), []*eth2apiv1.AttesterDuty{ @@ -1063,8 +1166,12 @@ func TestScheduler_Committee_Reorg_Current_Attester_Only(t *testing.T) { }) // STEP 1: wait for attester duties to be fetched for next epoch - waitForDuties.Set(true) ticker.Send(currentSlot.Get()) + // wait for attester duties to be fetched + waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + // wait for sync committee duties to be fetched + waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + // wait for next epoch attester duties to be fetched waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 2: trigger head event @@ -1140,7 +1247,6 @@ func TestScheduler_Committee_Reorg_Current_Indices_Changed_Attester_Only(t *test commHandler = NewCommitteeHandler(attHandler, syncHandler) alanForkEpoch = phase0.Epoch(0) currentSlot = &SafeValue[phase0.Slot]{} - waitForDuties = &SafeValue[bool]{} attDuties = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() syncDuties = hashmap.New[uint64, []*eth2apiv1.SyncCommitteeDuty]() activeShares = []*ssvtypes.SSVShare{ @@ -1164,7 +1270,7 @@ func TestScheduler_Committee_Reorg_Current_Indices_Changed_Attester_Only(t *test ) currentSlot.Set(phase0.Slot(48)) scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot, alanForkEpoch) - fetchDutiesCall, executeDutiesCall := setupCommitteeDutiesMock(scheduler, activeShares, attDuties, syncDuties, waitForDuties) + fetchDutiesCall, executeDutiesCall := setupDutiesMockCommittee(scheduler, activeShares, attDuties, syncDuties) startFn() attDuties.Set(phase0.Epoch(2), []*eth2apiv1.AttesterDuty{ @@ -1176,8 +1282,12 @@ func TestScheduler_Committee_Reorg_Current_Indices_Changed_Attester_Only(t *test }) // STEP 1: wait for attester duties to be fetched for next epoch - waitForDuties.Set(true) ticker.Send(currentSlot.Get()) + // wait for attester duties to be fetched + waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + // wait for sync committee duties to be fetched + waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + // wait for next epoch attester duties to be fetched waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 2: trigger head event @@ -1268,7 +1378,6 @@ func TestScheduler_Committee_Early_Block_Attester_Only(t *testing.T) { commHandler = NewCommitteeHandler(attHandler, syncHandler) alanForkEpoch = phase0.Epoch(0) currentSlot = &SafeValue[phase0.Slot]{} - waitForDuties = &SafeValue[bool]{} attDuties = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() syncDuties = hashmap.New[uint64, []*eth2apiv1.SyncCommitteeDuty]() activeShares = []*ssvtypes.SSVShare{{ @@ -1287,13 +1396,17 @@ func TestScheduler_Committee_Early_Block_Attester_Only(t *testing.T) { ValidatorIndex: phase0.ValidatorIndex(1), }, }) - // STEP 1: wait for attester duties to be fetched (handle initial duties) + // STEP 1: wait for attester duties to be fetched currentSlot.Set(phase0.Slot(0)) scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot, alanForkEpoch) - fetchDutiesCall, executeDutiesCall := setupCommitteeDutiesMock(scheduler, activeShares, attDuties, syncDuties, waitForDuties) + fetchDutiesCall, executeDutiesCall := setupDutiesMockCommittee(scheduler, activeShares, attDuties, syncDuties) startFn() ticker.Send(currentSlot.Get()) + // wait for attester duties to be fetched + waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + // wait for sync committee duties to be fetched + waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 2: wait for no action to be taken @@ -1333,7 +1446,6 @@ func TestScheduler_Committee_Early_Block(t *testing.T) { commHandler = NewCommitteeHandler(attHandler, syncHandler) alanForkEpoch = phase0.Epoch(0) currentSlot = &SafeValue[phase0.Slot]{} - waitForDuties = &SafeValue[bool]{} attDuties = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() syncDuties = hashmap.New[uint64, []*eth2apiv1.SyncCommitteeDuty]() activeShares = []*ssvtypes.SSVShare{{ @@ -1358,10 +1470,10 @@ func TestScheduler_Committee_Early_Block(t *testing.T) { ValidatorIndex: phase0.ValidatorIndex(1), }, }) - // STEP 1: wait for attester & sync committee duties to be fetched (handle initial duties) + // STEP 1: wait for attester & sync committee duties to be fetched currentSlot.Set(phase0.Slot(1)) scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot, alanForkEpoch) - fetchDutiesCall, executeDutiesCall := setupCommitteeDutiesMock(scheduler, activeShares, attDuties, syncDuties, waitForDuties) + fetchDutiesCall, executeDutiesCall := setupDutiesMockCommittee(scheduler, activeShares, attDuties, syncDuties) startFn() // STEP 2: wait for committee duty to be executed @@ -1372,6 +1484,10 @@ func TestScheduler_Committee_Early_Block(t *testing.T) { startTime := time.Now() ticker.Send(currentSlot.Get()) + // wait for attester duties to be fetched + waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + // wait for sync committee duties to be fetched + waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) waitForDutiesExecutionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout, committeeMap) // validate the 1/3 of the slot waiting time @@ -1408,7 +1524,6 @@ func TestScheduler_Committee_Start_In_The_End_Of_The_Epoch_Attester_Only(t *test commHandler = NewCommitteeHandler(attHandler, syncHandler) alanForkEpoch = phase0.Epoch(0) currentSlot = &SafeValue[phase0.Slot]{} - waitForDuties = &SafeValue[bool]{} attDuties = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() syncDuties = hashmap.New[uint64, []*eth2apiv1.SyncCommitteeDuty]() activeShares = []*ssvtypes.SSVShare{{ @@ -1422,7 +1537,7 @@ func TestScheduler_Committee_Start_In_The_End_Of_The_Epoch_Attester_Only(t *test ) currentSlot.Set(phase0.Slot(31)) scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot, alanForkEpoch) - fetchDutiesCall, executeDutiesCall := setupCommitteeDutiesMock(scheduler, activeShares, attDuties, syncDuties, waitForDuties) + fetchDutiesCall, executeDutiesCall := setupDutiesMockCommittee(scheduler, activeShares, attDuties, syncDuties) startFn() attDuties.Set(phase0.Epoch(1), []*eth2apiv1.AttesterDuty{ @@ -1434,8 +1549,12 @@ func TestScheduler_Committee_Start_In_The_End_Of_The_Epoch_Attester_Only(t *test }) // STEP 1: wait for attester duties to be fetched for the next epoch - waitForDuties.Set(true) ticker.Send(currentSlot.Get()) + // wait for attester duties to be fetched + waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + // wait for sync committee duties to be fetched + waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + // wait for next epoch attester duties to be fetched waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 2: wait for attester duties to be executed @@ -1461,7 +1580,6 @@ func TestScheduler_Committee_Fetch_Execute_Next_Epoch_Duty(t *testing.T) { commHandler = NewCommitteeHandler(attHandler, syncHandler) alanForkEpoch = phase0.Epoch(0) currentSlot = &SafeValue[phase0.Slot]{} - waitForDuties = &SafeValue[bool]{} attDuties = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() syncDuties = hashmap.New[uint64, []*eth2apiv1.SyncCommitteeDuty]() activeShares = []*ssvtypes.SSVShare{{ @@ -1473,11 +1591,6 @@ func TestScheduler_Committee_Fetch_Execute_Next_Epoch_Duty(t *testing.T) { }, }} ) - currentSlot.Set(phase0.Slot(13)) - scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot, alanForkEpoch) - fetchDutiesCall, executeDutiesCall := setupCommitteeDutiesMock(scheduler, activeShares, attDuties, syncDuties, waitForDuties) - startFn() - attDuties.Set(phase0.Epoch(1), []*eth2apiv1.AttesterDuty{ { PubKey: phase0.BLSPubKey{1, 2, 3}, @@ -1486,18 +1599,25 @@ func TestScheduler_Committee_Fetch_Execute_Next_Epoch_Duty(t *testing.T) { }, }) - // STEP 1: wait for no action to be taken + currentSlot.Set(phase0.Slot(14)) + scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot, alanForkEpoch) + fetchDutiesCall, executeDutiesCall := setupDutiesMockCommittee(scheduler, activeShares, attDuties, syncDuties) + startFn() + + // STEP 1: wait for duties to be fetched ticker.Send(currentSlot.Get()) - waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + // wait for attester duties to be fetched + waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + // wait for sync committee duties to be fetched + waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 2: wait for no action to be taken - currentSlot.Set(phase0.Slot(14)) + currentSlot.Set(phase0.Slot(15)) ticker.Send(currentSlot.Get()) waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 3: wait for duties to be fetched for the next epoch - currentSlot.Set(phase0.Slot(15)) - waitForDuties.Set(true) + currentSlot.Set(phase0.Slot(16)) ticker.Send(currentSlot.Get()) waitForDutiesFetchCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) @@ -1524,7 +1644,6 @@ func TestScheduler_Committee_On_Fork_Attester_Only(t *testing.T) { commHandler = NewCommitteeHandler(attHandler, syncHandler) alanForkEpoch = phase0.Epoch(2) currentSlot = &SafeValue[phase0.Slot]{} - waitForDuties = &SafeValue[bool]{} attDuties = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() syncDuties = hashmap.New[uint64, []*eth2apiv1.SyncCommitteeDuty]() activeShares = []*ssvtypes.SSVShare{{ @@ -1536,6 +1655,13 @@ func TestScheduler_Committee_On_Fork_Attester_Only(t *testing.T) { }, }} ) + currentSlot.Set(phase0.Slot(1)) + scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot, alanForkEpoch) + fetchDutiesCallAttester, executeDutiesCallAttester := setupDutiesMockAttesterGenesis(scheduler, attDuties) + fetchDutiesCallSyncCommittee, executeDutiesCallSyncCommittee := setupGenesisDutiesMockSyncCommittee(scheduler, activeShares, syncDuties) + fetchDutiesCall, executeDutiesCall := setupDutiesMockCommittee(scheduler, activeShares, attDuties, syncDuties) + startFn() + attDuties.Set(phase0.Epoch(0), []*eth2apiv1.AttesterDuty{ { PubKey: phase0.BLSPubKey{1, 2, 3}, @@ -1551,19 +1677,15 @@ func TestScheduler_Committee_On_Fork_Attester_Only(t *testing.T) { }, }) - currentSlot.Set(phase0.Slot(1)) - scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot, alanForkEpoch) - fetchAttesterDutiesCall, executeAttesterDutiesCall := setupAttesterGenesisDutiesMock(scheduler, attDuties, waitForDuties) - fetchDutiesCall, executeDutiesCall := setupCommitteeDutiesMock(scheduler, activeShares, attDuties, syncDuties, waitForDuties) - startFn() - aDuties, _ := attDuties.Get(0) - aExpected := expectedExecutedGenesisAttesterDuties(attHandler, aDuties) - setExecuteGenesisDutyFunc(scheduler, executeAttesterDutiesCall, len(aExpected)) + aExpected := expectedExecutedDutiesAttesterGenesis(attHandler, aDuties) + setExecuteGenesisDutyFunc(scheduler, executeDutiesCallAttester, len(aExpected)) startTime := time.Now() ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchAttesterDutiesCall, executeAttesterDutiesCall, timeout, aExpected) + waitForDutiesFetchGenesis(t, logger, fetchDutiesCallAttester, executeDutiesCallAttester, timeout) + waitForDutiesFetchGenesis(t, logger, fetchDutiesCallSyncCommittee, executeDutiesCallSyncCommittee, timeout) + waitForDutiesExecutionGenesis(t, logger, fetchDutiesCallAttester, executeDutiesCallAttester, timeout, aExpected) waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // validate the 1/3 of the slot waiting time @@ -1573,16 +1695,15 @@ func TestScheduler_Committee_On_Fork_Attester_Only(t *testing.T) { currentSlot.Set(phase0.Slot(2)) for slot := currentSlot.Get(); slot < 48; slot++ { ticker.Send(slot) - waitForNoActionGenesis(t, logger, fetchAttesterDutiesCall, executeAttesterDutiesCall, timeout) + waitForNoActionGenesis(t, logger, fetchDutiesCallAttester, executeDutiesCallAttester, timeout) waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) currentSlot.Set(slot + 1) } // wait for duties to be fetched for the next fork epoch currentSlot.Set(phase0.Slot(48)) - waitForDuties.Set(true) ticker.Send(currentSlot.Get()) - waitForDutiesFetchGenesis(t, logger, fetchAttesterDutiesCall, executeAttesterDutiesCall, timeout) + waitForDutiesFetchGenesis(t, logger, fetchDutiesCallAttester, executeDutiesCallAttester, timeout) currentSlot.Set(phase0.Slot(64)) aDuties, _ = attDuties.Get(2) @@ -1591,8 +1712,7 @@ func TestScheduler_Committee_On_Fork_Attester_Only(t *testing.T) { startTime = time.Now() ticker.Send(currentSlot.Get()) - waitForDutiesExecutionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout, committeeMap) - waitForNoActionGenesis(t, logger, fetchAttesterDutiesCall, executeAttesterDutiesCall, timeout) + waitForNoActionGenesis(t, logger, fetchDutiesCallAttester, executeDutiesCallAttester, timeout) // validate the 1/3 of the slot waiting time require.Less(t, scheduler.network.Beacon.SlotDurationSec()/3, time.Since(startTime)) @@ -1610,7 +1730,6 @@ func TestScheduler_Committee_On_Fork(t *testing.T) { commHandler = NewCommitteeHandler(attHandler, syncHandler) alanForkEpoch = phase0.Epoch(256) currentSlot = &SafeValue[phase0.Slot]{} - waitForDuties = &SafeValue[bool]{} attDuties = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() syncDuties = hashmap.New[uint64, []*eth2apiv1.SyncCommitteeDuty]() activeShares = []*ssvtypes.SSVShare{{ @@ -1647,27 +1766,36 @@ func TestScheduler_Committee_On_Fork(t *testing.T) { currentSlot.Set(phase0.Slot(lastPeriodEpoch * 32)) scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot, alanForkEpoch) - fetchAttesterDutiesCall, executeAttesterDutiesCall := setupAttesterGenesisDutiesMock(scheduler, attDuties, waitForDuties) - fetchDutiesCall, executeDutiesCall := setupCommitteeDutiesMock(scheduler, activeShares, attDuties, syncDuties, waitForDuties) + fetchDutiesCallAttester, executeDutiesCallAttester := setupDutiesMockAttesterGenesis(scheduler, attDuties) + fetchDutiesCallSyncCommittee, executeDutiesCallSyncCommittee := setupGenesisDutiesMockSyncCommittee(scheduler, activeShares, syncDuties) + fetchDutiesCall, executeDutiesCall := setupDutiesMockCommittee(scheduler, activeShares, attDuties, syncDuties) startFn() aDuties, _ := attDuties.Get(lastPeriodEpoch) - aExpected := expectedExecutedGenesisAttesterDuties(attHandler, aDuties) - setExecuteGenesisDutyFunc(scheduler, executeAttesterDutiesCall, len(aExpected)) + aExpected := expectedExecutedDutiesAttesterGenesis(attHandler, aDuties) + setExecuteGenesisDutyFunc(scheduler, executeDutiesCallAttester, len(aExpected)) ticker.Send(currentSlot.Get()) - waitForNoActionGenesis(t, logger, fetchAttesterDutiesCall, executeAttesterDutiesCall, timeout) + waitForDutiesFetchGenesis(t, logger, fetchDutiesCallAttester, executeDutiesCallAttester, timeout) + waitForDutiesFetchGenesis(t, logger, fetchDutiesCallSyncCommittee, executeDutiesCallSyncCommittee, timeout) + // next period + waitForDutiesFetchGenesis(t, logger, fetchDutiesCallSyncCommittee, executeDutiesCallSyncCommittee, timeout) waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) currentSlot.Set(phase0.Slot(lastPeriodEpoch*32 + 1)) ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchAttesterDutiesCall, executeAttesterDutiesCall, timeout, aExpected) + waitForDutiesExecutionGenesis(t, logger, fetchDutiesCallAttester, executeDutiesCallAttester, timeout, aExpected) waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) currentSlot.Set(phase0.Slot(lastPeriodEpoch*32 + 2)) for slot := currentSlot.Get(); slot < 256*32; slot++ { ticker.Send(slot) - waitForNoActionGenesis(t, logger, fetchAttesterDutiesCall, executeAttesterDutiesCall, timeout) + if uint64(slot)%32 == scheduler.network.SlotsPerEpoch()/2 { + waitForDutiesFetchGenesis(t, logger, fetchDutiesCallAttester, executeDutiesCallAttester, timeout) + } else { + waitForNoActionGenesis(t, logger, fetchDutiesCallAttester, executeDutiesCallAttester, timeout) + waitForNoActionGenesis(t, logger, fetchDutiesCallSyncCommittee, executeDutiesCallSyncCommittee, timeout) + } waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) currentSlot.Set(slot + 1) } @@ -1680,7 +1808,7 @@ func TestScheduler_Committee_On_Fork(t *testing.T) { ticker.Send(currentSlot.Get()) waitForDutiesExecutionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout, committeeMap) - waitForNoActionGenesis(t, logger, fetchAttesterDutiesCall, executeAttesterDutiesCall, timeout) + waitForNoActionGenesis(t, logger, fetchDutiesCallAttester, executeDutiesCallAttester, timeout) // Stop scheduler & wait for graceful exit. cancel() diff --git a/operator/duties/proposer_genesis_test.go b/operator/duties/proposer_genesis_test.go index c0827d363a..9a2e00c11d 100644 --- a/operator/duties/proposer_genesis_test.go +++ b/operator/duties/proposer_genesis_test.go @@ -19,7 +19,7 @@ import ( "github.com/ssvlabs/ssv/protocol/v2/types" ) -func setupProposerGenesisDutiesMock(s *Scheduler, dutiesMap *hashmap.Map[phase0.Epoch, []*eth2apiv1.ProposerDuty]) (chan struct{}, chan []*genesisspectypes.Duty) { +func setupDutiesMockProposerGenesis(s *Scheduler, dutiesMap *hashmap.Map[phase0.Epoch, []*eth2apiv1.ProposerDuty]) (chan struct{}, chan []*genesisspectypes.Duty) { fetchDutiesCall := make(chan struct{}) executeDutiesCall := make(chan []*genesisspectypes.Duty) @@ -57,7 +57,7 @@ func setupProposerGenesisDutiesMock(s *Scheduler, dutiesMap *hashmap.Map[phase0. return fetchDutiesCall, executeDutiesCall } -func expectedExecutedGenesisProposerDuties(handler *ProposerHandler, duties []*eth2apiv1.ProposerDuty) []*genesisspectypes.Duty { +func expectedExecutedDutiesProposerGenesis(handler *ProposerHandler, duties []*eth2apiv1.ProposerDuty) []*genesisspectypes.Duty { expectedDuties := make([]*genesisspectypes.Duty, 0) for _, d := range duties { expectedDuties = append(expectedDuties, handler.toGenesisSpecDuty(d, genesisspectypes.BNRoleProposer)) @@ -73,7 +73,7 @@ func TestScheduler_Proposer_Genesis_Same_Slot(t *testing.T) { ) currentSlot.Set(phase0.Slot(0)) scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, goclient.FarFutureEpoch) - fetchDutiesCall, executeDutiesCall := setupProposerGenesisDutiesMock(scheduler, dutiesMap) + fetchDutiesCall, executeDutiesCall := setupDutiesMockProposerGenesis(scheduler, dutiesMap) startFn() dutiesMap.Set(phase0.Epoch(0), []*eth2apiv1.ProposerDuty{ @@ -86,12 +86,12 @@ func TestScheduler_Proposer_Genesis_Same_Slot(t *testing.T) { // STEP 1: wait for proposer duties to be fetched and executed at the same slot duties, _ := dutiesMap.Get(phase0.Epoch(0)) - expected := expectedExecutedGenesisProposerDuties(handler, duties) + expected := expectedExecutedDutiesProposerGenesis(handler, duties) setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) ticker.Send(currentSlot.Get()) waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) + waitForDutiesExecutionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) // Stop scheduler & wait for graceful exit. cancel() @@ -106,7 +106,7 @@ func TestScheduler_Proposer_Genesis_Diff_Slots(t *testing.T) { ) currentSlot.Set(phase0.Slot(0)) scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, goclient.FarFutureEpoch) - fetchDutiesCall, executeDutiesCall := setupProposerGenesisDutiesMock(scheduler, dutiesMap) + fetchDutiesCall, executeDutiesCall := setupDutiesMockProposerGenesis(scheduler, dutiesMap) startFn() dutiesMap.Set(phase0.Epoch(0), []*eth2apiv1.ProposerDuty{ @@ -129,11 +129,11 @@ func TestScheduler_Proposer_Genesis_Diff_Slots(t *testing.T) { // STEP 3: wait for proposer duties to be executed currentSlot.Set(phase0.Slot(2)) duties, _ := dutiesMap.Get(phase0.Epoch(0)) - expected := expectedExecutedGenesisProposerDuties(handler, duties) + expected := expectedExecutedDutiesProposerGenesis(handler, duties) setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) + waitForDutiesExecutionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) // Stop scheduler & wait for graceful exit. cancel() @@ -149,7 +149,7 @@ func TestScheduler_Proposer_Genesis_Indices_Changed(t *testing.T) { ) currentSlot.Set(phase0.Slot(0)) scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, goclient.FarFutureEpoch) - fetchDutiesCall, executeDutiesCall := setupProposerGenesisDutiesMock(scheduler, dutiesMap) + fetchDutiesCall, executeDutiesCall := setupDutiesMockProposerGenesis(scheduler, dutiesMap) startFn() // STEP 1: wait for no action to be taken @@ -193,11 +193,11 @@ func TestScheduler_Proposer_Genesis_Indices_Changed(t *testing.T) { // STEP 4: wait for proposer duties to be executed currentSlot.Set(phase0.Slot(3)) duties, _ := dutiesMap.Get(phase0.Epoch(0)) - expected := expectedExecutedGenesisProposerDuties(handler, []*eth2apiv1.ProposerDuty{duties[2]}) + expected := expectedExecutedDutiesProposerGenesis(handler, []*eth2apiv1.ProposerDuty{duties[2]}) setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) + waitForDutiesExecutionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) // Stop scheduler & wait for graceful exit. cancel() @@ -212,7 +212,7 @@ func TestScheduler_Proposer_Genesis_Multiple_Indices_Changed_Same_Slot(t *testin ) currentSlot.Set(phase0.Slot(0)) scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, goclient.FarFutureEpoch) - fetchDutiesCall, executeDutiesCall := setupProposerGenesisDutiesMock(scheduler, dutiesMap) + fetchDutiesCall, executeDutiesCall := setupDutiesMockProposerGenesis(scheduler, dutiesMap) startFn() dutiesMap.Set(phase0.Epoch(0), []*eth2apiv1.ProposerDuty{ @@ -255,29 +255,29 @@ func TestScheduler_Proposer_Genesis_Multiple_Indices_Changed_Same_Slot(t *testin // STEP 5: wait for proposer duties to be executed currentSlot.Set(phase0.Slot(2)) duties, _ = dutiesMap.Get(phase0.Epoch(0)) - expected := expectedExecutedGenesisProposerDuties(handler, []*eth2apiv1.ProposerDuty{duties[0]}) + expected := expectedExecutedDutiesProposerGenesis(handler, []*eth2apiv1.ProposerDuty{duties[0]}) setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) + waitForDutiesExecutionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) // STEP 6: wait for proposer duties to be executed currentSlot.Set(phase0.Slot(3)) duties, _ = dutiesMap.Get(phase0.Epoch(0)) - expected = expectedExecutedGenesisProposerDuties(handler, []*eth2apiv1.ProposerDuty{duties[1]}) + expected = expectedExecutedDutiesProposerGenesis(handler, []*eth2apiv1.ProposerDuty{duties[1]}) setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) + waitForDutiesExecutionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) // STEP 7: wait for proposer duties to be executed currentSlot.Set(phase0.Slot(4)) duties, _ = dutiesMap.Get(phase0.Epoch(0)) - expected = expectedExecutedGenesisProposerDuties(handler, []*eth2apiv1.ProposerDuty{duties[2]}) + expected = expectedExecutedDutiesProposerGenesis(handler, []*eth2apiv1.ProposerDuty{duties[2]}) setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) + waitForDutiesExecutionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) // Stop scheduler & wait for graceful exit. cancel() @@ -293,7 +293,7 @@ func TestScheduler_Proposer_Genesis_Reorg_Current(t *testing.T) { ) currentSlot.Set(phase0.Slot(34)) scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, goclient.FarFutureEpoch) - fetchDutiesCall, executeDutiesCall := setupProposerGenesisDutiesMock(scheduler, dutiesMap) + fetchDutiesCall, executeDutiesCall := setupDutiesMockProposerGenesis(scheduler, dutiesMap) startFn() dutiesMap.Set(phase0.Epoch(1), []*eth2apiv1.ProposerDuty{ @@ -349,11 +349,11 @@ func TestScheduler_Proposer_Genesis_Reorg_Current(t *testing.T) { // STEP 7: The second assigned duty should be executed currentSlot.Set(phase0.Slot(37)) duties, _ := dutiesMap.Get(phase0.Epoch(1)) - expected := expectedExecutedGenesisProposerDuties(handler, duties) + expected := expectedExecutedDutiesProposerGenesis(handler, duties) setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) + waitForDutiesExecutionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) // Stop scheduler & wait for graceful exit. cancel() @@ -369,7 +369,7 @@ func TestScheduler_Proposer_Genesis_Reorg_Current_Indices_Changed(t *testing.T) ) currentSlot.Set(phase0.Slot(34)) scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, goclient.FarFutureEpoch) - fetchDutiesCall, executeDutiesCall := setupProposerGenesisDutiesMock(scheduler, dutiesMap) + fetchDutiesCall, executeDutiesCall := setupDutiesMockProposerGenesis(scheduler, dutiesMap) startFn() dutiesMap.Set(phase0.Epoch(1), []*eth2apiv1.ProposerDuty{ @@ -435,20 +435,20 @@ func TestScheduler_Proposer_Genesis_Reorg_Current_Indices_Changed(t *testing.T) // STEP 7: The second assigned duty should be executed currentSlot.Set(phase0.Slot(37)) duties, _ = dutiesMap.Get(phase0.Epoch(1)) - expected := expectedExecutedGenesisProposerDuties(handler, []*eth2apiv1.ProposerDuty{duties[0]}) + expected := expectedExecutedDutiesProposerGenesis(handler, []*eth2apiv1.ProposerDuty{duties[0]}) setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) + waitForDutiesExecutionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) // STEP 8: The second assigned duty should be executed currentSlot.Set(phase0.Slot(38)) duties, _ = dutiesMap.Get(phase0.Epoch(1)) - expected = expectedExecutedGenesisProposerDuties(handler, []*eth2apiv1.ProposerDuty{duties[1]}) + expected = expectedExecutedDutiesProposerGenesis(handler, []*eth2apiv1.ProposerDuty{duties[1]}) setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) + waitForDutiesExecutionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) // Stop scheduler & wait for graceful exit. cancel() diff --git a/operator/duties/proposer_test.go b/operator/duties/proposer_test.go index 6fefb61b98..c3760752ae 100644 --- a/operator/duties/proposer_test.go +++ b/operator/duties/proposer_test.go @@ -17,7 +17,7 @@ import ( "github.com/ssvlabs/ssv/protocol/v2/types" ) -func setupProposerDutiesMock(s *Scheduler, dutiesMap *hashmap.Map[phase0.Epoch, []*eth2apiv1.ProposerDuty]) (chan struct{}, chan []*spectypes.ValidatorDuty) { +func setupDutiesMockProposer(s *Scheduler, dutiesMap *hashmap.Map[phase0.Epoch, []*eth2apiv1.ProposerDuty]) (chan struct{}, chan []*spectypes.ValidatorDuty) { fetchDutiesCall := make(chan struct{}) executeDutiesCall := make(chan []*spectypes.ValidatorDuty) @@ -55,7 +55,7 @@ func setupProposerDutiesMock(s *Scheduler, dutiesMap *hashmap.Map[phase0.Epoch, return fetchDutiesCall, executeDutiesCall } -func expectedExecutedProposerDuties(handler *ProposerHandler, duties []*eth2apiv1.ProposerDuty) []*spectypes.ValidatorDuty { +func expectedExecutedDutiesProposer(handler *ProposerHandler, duties []*eth2apiv1.ProposerDuty) []*spectypes.ValidatorDuty { expectedDuties := make([]*spectypes.ValidatorDuty, 0) for _, d := range duties { expectedDuties = append(expectedDuties, handler.toSpecDuty(d, spectypes.BNRoleProposer)) @@ -71,7 +71,7 @@ func TestScheduler_Proposer_Same_Slot(t *testing.T) { ) currentSlot.Set(phase0.Slot(0)) scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, 0) - fetchDutiesCall, executeDutiesCall := setupProposerDutiesMock(scheduler, dutiesMap) + fetchDutiesCall, executeDutiesCall := setupDutiesMockProposer(scheduler, dutiesMap) startFn() dutiesMap.Set(phase0.Epoch(0), []*eth2apiv1.ProposerDuty{ @@ -84,7 +84,7 @@ func TestScheduler_Proposer_Same_Slot(t *testing.T) { // STEP 1: wait for proposer duties to be fetched and executed at the same slot duties, _ := dutiesMap.Get(phase0.Epoch(0)) - expected := expectedExecutedProposerDuties(handler, duties) + expected := expectedExecutedDutiesProposer(handler, duties) setExecuteDutyFunc(scheduler, executeDutiesCall, len(expected)) ticker.Send(currentSlot.Get()) @@ -104,7 +104,7 @@ func TestScheduler_Proposer_Diff_Slots(t *testing.T) { ) currentSlot.Set(phase0.Slot(0)) scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, 0) - fetchDutiesCall, executeDutiesCall := setupProposerDutiesMock(scheduler, dutiesMap) + fetchDutiesCall, executeDutiesCall := setupDutiesMockProposer(scheduler, dutiesMap) startFn() dutiesMap.Set(phase0.Epoch(0), []*eth2apiv1.ProposerDuty{ @@ -127,7 +127,7 @@ func TestScheduler_Proposer_Diff_Slots(t *testing.T) { // STEP 3: wait for proposer duties to be executed currentSlot.Set(phase0.Slot(2)) duties, _ := dutiesMap.Get(phase0.Epoch(0)) - expected := expectedExecutedProposerDuties(handler, duties) + expected := expectedExecutedDutiesProposer(handler, duties) setExecuteDutyFunc(scheduler, executeDutiesCall, len(expected)) ticker.Send(currentSlot.Get()) @@ -147,7 +147,7 @@ func TestScheduler_Proposer_Indices_Changed(t *testing.T) { ) currentSlot.Set(phase0.Slot(0)) scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, 0) - fetchDutiesCall, executeDutiesCall := setupProposerDutiesMock(scheduler, dutiesMap) + fetchDutiesCall, executeDutiesCall := setupDutiesMockProposer(scheduler, dutiesMap) startFn() // STEP 1: wait for no action to be taken @@ -191,7 +191,7 @@ func TestScheduler_Proposer_Indices_Changed(t *testing.T) { // STEP 4: wait for proposer duties to be executed currentSlot.Set(phase0.Slot(3)) duties, _ := dutiesMap.Get(phase0.Epoch(0)) - expected := expectedExecutedProposerDuties(handler, []*eth2apiv1.ProposerDuty{duties[2]}) + expected := expectedExecutedDutiesProposer(handler, []*eth2apiv1.ProposerDuty{duties[2]}) setExecuteDutyFunc(scheduler, executeDutiesCall, len(expected)) ticker.Send(currentSlot.Get()) @@ -210,7 +210,7 @@ func TestScheduler_Proposer_Multiple_Indices_Changed_Same_Slot(t *testing.T) { ) currentSlot.Set(phase0.Slot(0)) scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, 0) - fetchDutiesCall, executeDutiesCall := setupProposerDutiesMock(scheduler, dutiesMap) + fetchDutiesCall, executeDutiesCall := setupDutiesMockProposer(scheduler, dutiesMap) startFn() dutiesMap.Set(phase0.Epoch(0), []*eth2apiv1.ProposerDuty{ @@ -253,7 +253,7 @@ func TestScheduler_Proposer_Multiple_Indices_Changed_Same_Slot(t *testing.T) { // STEP 5: wait for proposer duties to be executed currentSlot.Set(phase0.Slot(2)) duties, _ = dutiesMap.Get(phase0.Epoch(0)) - expected := expectedExecutedProposerDuties(handler, []*eth2apiv1.ProposerDuty{duties[0]}) + expected := expectedExecutedDutiesProposer(handler, []*eth2apiv1.ProposerDuty{duties[0]}) setExecuteDutyFunc(scheduler, executeDutiesCall, len(expected)) ticker.Send(currentSlot.Get()) @@ -262,7 +262,7 @@ func TestScheduler_Proposer_Multiple_Indices_Changed_Same_Slot(t *testing.T) { // STEP 6: wait for proposer duties to be executed currentSlot.Set(phase0.Slot(3)) duties, _ = dutiesMap.Get(phase0.Epoch(0)) - expected = expectedExecutedProposerDuties(handler, []*eth2apiv1.ProposerDuty{duties[1]}) + expected = expectedExecutedDutiesProposer(handler, []*eth2apiv1.ProposerDuty{duties[1]}) setExecuteDutyFunc(scheduler, executeDutiesCall, len(expected)) ticker.Send(currentSlot.Get()) @@ -271,7 +271,7 @@ func TestScheduler_Proposer_Multiple_Indices_Changed_Same_Slot(t *testing.T) { // STEP 7: wait for proposer duties to be executed currentSlot.Set(phase0.Slot(4)) duties, _ = dutiesMap.Get(phase0.Epoch(0)) - expected = expectedExecutedProposerDuties(handler, []*eth2apiv1.ProposerDuty{duties[2]}) + expected = expectedExecutedDutiesProposer(handler, []*eth2apiv1.ProposerDuty{duties[2]}) setExecuteDutyFunc(scheduler, executeDutiesCall, len(expected)) ticker.Send(currentSlot.Get()) @@ -291,7 +291,7 @@ func TestScheduler_Proposer_Reorg_Current(t *testing.T) { ) currentSlot.Set(phase0.Slot(34)) scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, 0) - fetchDutiesCall, executeDutiesCall := setupProposerDutiesMock(scheduler, dutiesMap) + fetchDutiesCall, executeDutiesCall := setupDutiesMockProposer(scheduler, dutiesMap) startFn() dutiesMap.Set(phase0.Epoch(1), []*eth2apiv1.ProposerDuty{ @@ -347,7 +347,7 @@ func TestScheduler_Proposer_Reorg_Current(t *testing.T) { // STEP 7: The second assigned duty should be executed currentSlot.Set(phase0.Slot(37)) duties, _ := dutiesMap.Get(phase0.Epoch(1)) - expected := expectedExecutedProposerDuties(handler, duties) + expected := expectedExecutedDutiesProposer(handler, duties) setExecuteDutyFunc(scheduler, executeDutiesCall, len(expected)) ticker.Send(currentSlot.Get()) @@ -367,7 +367,7 @@ func TestScheduler_Proposer_Reorg_Current_Indices_Changed(t *testing.T) { ) currentSlot.Set(phase0.Slot(34)) scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, 0) - fetchDutiesCall, executeDutiesCall := setupProposerDutiesMock(scheduler, dutiesMap) + fetchDutiesCall, executeDutiesCall := setupDutiesMockProposer(scheduler, dutiesMap) startFn() dutiesMap.Set(phase0.Epoch(1), []*eth2apiv1.ProposerDuty{ @@ -433,7 +433,7 @@ func TestScheduler_Proposer_Reorg_Current_Indices_Changed(t *testing.T) { // STEP 7: The second assigned duty should be executed currentSlot.Set(phase0.Slot(37)) duties, _ = dutiesMap.Get(phase0.Epoch(1)) - expected := expectedExecutedProposerDuties(handler, []*eth2apiv1.ProposerDuty{duties[0]}) + expected := expectedExecutedDutiesProposer(handler, []*eth2apiv1.ProposerDuty{duties[0]}) setExecuteDutyFunc(scheduler, executeDutiesCall, len(expected)) ticker.Send(currentSlot.Get()) @@ -442,7 +442,7 @@ func TestScheduler_Proposer_Reorg_Current_Indices_Changed(t *testing.T) { // STEP 8: The second assigned duty should be executed currentSlot.Set(phase0.Slot(38)) duties, _ = dutiesMap.Get(phase0.Epoch(1)) - expected = expectedExecutedProposerDuties(handler, []*eth2apiv1.ProposerDuty{duties[1]}) + expected = expectedExecutedDutiesProposer(handler, []*eth2apiv1.ProposerDuty{duties[1]}) setExecuteDutyFunc(scheduler, executeDutiesCall, len(expected)) ticker.Send(currentSlot.Get()) @@ -462,8 +462,8 @@ func TestScheduler_Proposer_On_Fork(t *testing.T) { ) currentSlot.Set(phase0.Slot(0)) scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, alanForkEpoch) - fetchDutiesCallGenesis, executeDutiesCallGenesis := setupProposerGenesisDutiesMock(scheduler, dutiesMap) - _, executeDutiesCall := setupProposerDutiesMock(scheduler, dutiesMap) + fetchDutiesCallGenesis, executeDutiesCallGenesis := setupDutiesMockProposerGenesis(scheduler, dutiesMap) + _, executeDutiesCall := setupDutiesMockProposer(scheduler, dutiesMap) startFn() dutiesMap.Set(phase0.Epoch(0), []*eth2apiv1.ProposerDuty{ @@ -493,11 +493,11 @@ func TestScheduler_Proposer_On_Fork(t *testing.T) { // STEP 3: wait for proposer duties to be executed currentSlot.Set(phase0.Slot(2)) duties, _ := dutiesMap.Get(phase0.Epoch(0)) - expectedGenesis := expectedExecutedGenesisProposerDuties(handler, duties) + expectedGenesis := expectedExecutedDutiesProposerGenesis(handler, duties) setExecuteGenesisDutyFunc(scheduler, executeDutiesCallGenesis, len(expectedGenesis)) ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCallGenesis, executeDutiesCallGenesis, timeout, expectedGenesis) + waitForDutiesExecutionGenesis(t, logger, fetchDutiesCallGenesis, executeDutiesCallGenesis, timeout, expectedGenesis) waitForNoAction(t, logger, fetchDutiesCallGenesis, executeDutiesCall, timeout) // skip to the next epoch @@ -511,7 +511,7 @@ func TestScheduler_Proposer_On_Fork(t *testing.T) { // fork epoch currentSlot.Set(phase0.Slot(32)) duties, _ = dutiesMap.Get(phase0.Epoch(1)) - expected := expectedExecutedProposerDuties(handler, duties) + expected := expectedExecutedDutiesProposer(handler, duties) setExecuteDutyFunc(scheduler, executeDutiesCall, len(expected)) ticker.Send(currentSlot.Get()) diff --git a/operator/duties/scheduler_test.go b/operator/duties/scheduler_test.go index 32eef3491b..d7c5a76f81 100644 --- a/operator/duties/scheduler_test.go +++ b/operator/duties/scheduler_test.go @@ -317,7 +317,7 @@ func waitForDutiesExecution(t *testing.T, logger *zap.Logger, fetchDutiesCall ch } } -func waitForGenesisDutiesExecution(t *testing.T, logger *zap.Logger, fetchDutiesCall chan struct{}, executeDutiesCall chan []*genesisspectypes.Duty, timeout time.Duration, expectedDuties []*genesisspectypes.Duty) { +func waitForDutiesExecutionGenesis(t *testing.T, logger *zap.Logger, fetchDutiesCall chan struct{}, executeDutiesCall chan []*genesisspectypes.Duty, timeout time.Duration, expectedDuties []*genesisspectypes.Duty) { select { case <-fetchDutiesCall: require.FailNow(t, "unexpected duties call") diff --git a/operator/duties/sync_committee.go b/operator/duties/sync_committee.go index f3fa693e3d..c588021cb6 100644 --- a/operator/duties/sync_committee.go +++ b/operator/duties/sync_committee.go @@ -26,13 +26,15 @@ type SyncCommitteeHandler struct { // preparationSlots is the number of slots ahead of the sync committee // period change at which to prepare the relevant duties. preparationSlots uint64 + firstRun bool } func NewSyncCommitteeHandler(duties *dutystore.SyncCommitteeDuties) *SyncCommitteeHandler { h := &SyncCommitteeHandler{ - duties: duties, + duties: duties, + firstRun: true, } - h.fetchCurrentPeriod = true + return h } @@ -49,10 +51,6 @@ func (h *SyncCommitteeHandler) HandleDuties(ctx context.Context) { // The 1.5 epochs timing helps ensure setup occurs when the beacon node is likely less busy. h.preparationSlots = h.network.Beacon.SlotsPerEpoch() * 3 / 2 - if h.shouldFetchNextPeriod(h.network.Beacon.EstimatedCurrentSlot()) { - h.fetchNextPeriod = true - } - next := h.ticker.Next() for { select { @@ -68,7 +66,13 @@ func (h *SyncCommitteeHandler) HandleDuties(ctx context.Context) { h.logger.Debug("🛠 ticker event", fields.SlotTickerID(tickerID)) if !h.network.PastAlanForkAtEpoch(epoch) { + if h.firstRun { + h.processFirstRun(ctx, period, slot) + } h.processExecution(period, slot) + if h.indicesChanged { + h.processIndicesChange(period, slot) + } h.processFetching(ctx, period, slot, true) h.processSlotTransition(period, slot) } @@ -91,20 +95,21 @@ func (h *SyncCommitteeHandler) HandleDuties(ctx context.Context) { h.logger.Info("🔁 indices change received", fields.SlotTickerID(tickerID)) if !h.network.PastAlanForkAtEpoch(epoch) { - h.processIndicesChange(period, slot) + h.indicesChanged = true } } } } -func (h *SyncCommitteeHandler) HandleInitialDuties(ctx context.Context) { - ctx, cancel := context.WithTimeout(ctx, h.network.Beacon.SlotDurationSec()/2) - defer cancel() - - slot := h.network.Beacon.EstimatedCurrentSlot() - epoch := h.network.Beacon.EstimatedCurrentEpoch() - period := h.network.Beacon.EstimatedSyncCommitteePeriodAtEpoch(epoch) +func (h *SyncCommitteeHandler) processFirstRun(ctx context.Context, period uint64, slot phase0.Slot) { + h.fetchCurrentPeriod = true h.processFetching(ctx, period, slot, false) + + periodSlots := h.slotsPerPeriod() + if uint64(slot)%periodSlots > periodSlots-h.preparationSlots-1 { + h.fetchNextPeriod = true + } + h.firstRun = false } func (h *SyncCommitteeHandler) processFetching(ctx context.Context, period uint64, slot phase0.Slot, waitForInitial bool) { @@ -146,6 +151,8 @@ func (h *SyncCommitteeHandler) processExecution(period uint64, slot phase0.Slot) } func (h *SyncCommitteeHandler) processIndicesChange(period uint64, slot phase0.Slot) { + // we should not reset sync committee duties here as it is necessary for message validation + // but this can lead to executing duties for deleted/liquidated validators h.fetchCurrentPeriod = true // reset next period duties if in appropriate slot range @@ -153,6 +160,8 @@ func (h *SyncCommitteeHandler) processIndicesChange(period uint64, slot phase0.S h.duties.Reset(period + 1) h.fetchNextPeriod = true } + + h.indicesChanged = false } func (h *SyncCommitteeHandler) processReorg(period uint64, reorgEvent ReorgEvent) { @@ -302,7 +311,7 @@ func calculateSubscriptions(endEpoch phase0.Epoch, duties []*eth2apiv1.SyncCommi func (h *SyncCommitteeHandler) shouldFetchNextPeriod(slot phase0.Slot) bool { periodSlots := h.slotsPerPeriod() - return uint64(slot)%periodSlots > periodSlots-h.preparationSlots-2 + return uint64(slot)%periodSlots >= periodSlots-h.preparationSlots-1 } func (h *SyncCommitteeHandler) slotsPerPeriod() uint64 { diff --git a/operator/duties/sync_committee_genesis_test.go b/operator/duties/sync_committee_genesis_test.go index ed5e3c1ec0..4a1a08795b 100644 --- a/operator/duties/sync_committee_genesis_test.go +++ b/operator/duties/sync_committee_genesis_test.go @@ -21,11 +21,10 @@ import ( ssvtypes "github.com/ssvlabs/ssv/protocol/v2/types" ) -func setupSyncCommitteeGenesisDutiesMock( +func setupGenesisDutiesMockSyncCommittee( s *Scheduler, activeShares []*ssvtypes.SSVShare, dutiesMap *hashmap.Map[uint64, []*v1.SyncCommitteeDuty], - waitForDuties *SafeValue[bool], ) (chan struct{}, chan []*genesisspectypes.Duty) { fetchDutiesCall := make(chan struct{}) executeDutiesCall := make(chan []*genesisspectypes.Duty) @@ -59,9 +58,7 @@ func setupSyncCommitteeGenesisDutiesMock( s.beaconNode.(*MockBeaconNode).EXPECT().SyncCommitteeDuties(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn( func(ctx context.Context, epoch phase0.Epoch, indices []phase0.ValidatorIndex) ([]*v1.SyncCommitteeDuty, error) { - if waitForDuties.Get() { - fetchDutiesCall <- struct{}{} - } + fetchDutiesCall <- struct{}{} period := s.network.Beacon.EstimatedSyncCommitteePeriodAtEpoch(epoch) duties, _ := dutiesMap.Get(period) return duties, nil @@ -80,7 +77,7 @@ func setupSyncCommitteeGenesisDutiesMock( return fetchDutiesCall, executeDutiesCall } -func expectedExecutedGenesisSyncCommitteeDuties(handler *SyncCommitteeHandler, duties []*v1.SyncCommitteeDuty, slot phase0.Slot) []*genesisspectypes.Duty { +func expectedExecutedDutiesSyncCommitteeGenesis(handler *SyncCommitteeHandler, duties []*v1.SyncCommitteeDuty, slot phase0.Slot) []*genesisspectypes.Duty { expectedDuties := make([]*genesisspectypes.Duty, 0) for _, d := range duties { expectedDuties = append(expectedDuties, handler.toGenesisSpecDuty(d, slot, genesisspectypes.BNRoleSyncCommittee)) @@ -91,12 +88,11 @@ func expectedExecutedGenesisSyncCommitteeDuties(handler *SyncCommitteeHandler, d func TestScheduler_SyncCommittee_Genesis_Same_Period(t *testing.T) { var ( - handler = NewSyncCommitteeHandler(dutystore.NewSyncCommitteeDuties()) - currentSlot = &SafeValue[phase0.Slot]{} - waitForDuties = &SafeValue[bool]{} - forkEpoch = goclient.FarFutureEpoch - dutiesMap = hashmap.New[uint64, []*v1.SyncCommitteeDuty]() - activeShares = []*ssvtypes.SSVShare{{ + handler = NewSyncCommitteeHandler(dutystore.NewSyncCommitteeDuties()) + currentSlot = &SafeValue[phase0.Slot]{} + forkEpoch = goclient.FarFutureEpoch + dutiesMap = hashmap.New[uint64, []*v1.SyncCommitteeDuty]() + activeShares = []*ssvtypes.SSVShare{{ Share: spectypes.Share{ Committee: []*spectypes.ShareMember{ {Signer: 1}, {Signer: 2}, {Signer: 3}, {Signer: 4}, @@ -115,34 +111,35 @@ func TestScheduler_SyncCommittee_Genesis_Same_Period(t *testing.T) { // STEP 1: wait for sync committee duties to be fetched (handle initial duties) currentSlot.Set(phase0.Slot(1)) scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) - fetchDutiesCall, executeDutiesCall := setupSyncCommitteeGenesisDutiesMock(scheduler, activeShares, dutiesMap, waitForDuties) + fetchDutiesCall, executeDutiesCall := setupGenesisDutiesMockSyncCommittee(scheduler, activeShares, dutiesMap) startFn() // STEP 1: wait for sync committee duties to be fetched and executed at the same slot duties, _ := dutiesMap.Get(0) - expected := expectedExecutedGenesisSyncCommitteeDuties(handler, duties, currentSlot.Get()) + expected := expectedExecutedDutiesSyncCommitteeGenesis(handler, duties, currentSlot.Get()) setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) + waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + waitForDutiesExecutionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) // STEP 2: expect sync committee duties to be executed at the same period currentSlot.Set(phase0.Slot(2)) duties, _ = dutiesMap.Get(0) - expected = expectedExecutedGenesisSyncCommitteeDuties(handler, duties, currentSlot.Get()) + expected = expectedExecutedDutiesSyncCommitteeGenesis(handler, duties, currentSlot.Get()) setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) + waitForDutiesExecutionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) // STEP 3: expect sync committee duties to be executed at the last slot of the period currentSlot.Set(scheduler.network.Beacon.LastSlotOfSyncPeriod(0)) duties, _ = dutiesMap.Get(0) - expected = expectedExecutedGenesisSyncCommitteeDuties(handler, duties, currentSlot.Get()) + expected = expectedExecutedDutiesSyncCommitteeGenesis(handler, duties, currentSlot.Get()) setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) + waitForDutiesExecutionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) // STEP 4: expect no action to be taken as we are in the next period firstSlotOfNextPeriod := scheduler.network.Beacon.GetEpochFirstSlot(scheduler.network.Beacon.FirstEpochOfSyncPeriod(1)) @@ -157,12 +154,11 @@ func TestScheduler_SyncCommittee_Genesis_Same_Period(t *testing.T) { func TestScheduler_SyncCommittee_Genesis_Current_Next_Periods(t *testing.T) { var ( - handler = NewSyncCommitteeHandler(dutystore.NewSyncCommitteeDuties()) - currentSlot = &SafeValue[phase0.Slot]{} - waitForDuties = &SafeValue[bool]{} - forkEpoch = goclient.FarFutureEpoch - dutiesMap = hashmap.New[uint64, []*v1.SyncCommitteeDuty]() - activeShares = []*ssvtypes.SSVShare{ + handler = NewSyncCommitteeHandler(dutystore.NewSyncCommitteeDuties()) + currentSlot = &SafeValue[phase0.Slot]{} + forkEpoch = goclient.FarFutureEpoch + dutiesMap = hashmap.New[uint64, []*v1.SyncCommitteeDuty]() + activeShares = []*ssvtypes.SSVShare{ { Share: spectypes.Share{ Committee: []*spectypes.ShareMember{ @@ -197,44 +193,46 @@ func TestScheduler_SyncCommittee_Genesis_Current_Next_Periods(t *testing.T) { // STEP 1: wait for sync committee duties to be fetched (handle initial duties) currentSlot.Set(phase0.Slot(256*32 - 49)) scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) - fetchDutiesCall, executeDutiesCall := setupSyncCommitteeGenesisDutiesMock(scheduler, activeShares, dutiesMap, waitForDuties) + fetchDutiesCall, executeDutiesCall := setupGenesisDutiesMockSyncCommittee(scheduler, activeShares, dutiesMap) startFn() duties, _ := dutiesMap.Get(0) - expected := expectedExecutedGenesisSyncCommitteeDuties(handler, duties, currentSlot.Get()) + expected := expectedExecutedDutiesSyncCommitteeGenesis(handler, duties, currentSlot.Get()) setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) + waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + waitForDutiesExecutionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) // STEP 2: wait for sync committee duties to be executed currentSlot.Set(phase0.Slot(256*32 - 48)) duties, _ = dutiesMap.Get(0) - expected = expectedExecutedGenesisSyncCommitteeDuties(handler, duties, currentSlot.Get()) + expected = expectedExecutedDutiesSyncCommitteeGenesis(handler, duties, currentSlot.Get()) setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) + waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + waitForDutiesExecutionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) // STEP 3: wait for sync committee duties to be executed currentSlot.Set(phase0.Slot(256*32 - 47)) duties, _ = dutiesMap.Get(0) - expected = expectedExecutedGenesisSyncCommitteeDuties(handler, duties, currentSlot.Get()) + expected = expectedExecutedDutiesSyncCommitteeGenesis(handler, duties, currentSlot.Get()) setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) + waitForDutiesExecutionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) // ... // STEP 4: new period, wait for sync committee duties to be executed currentSlot.Set(phase0.Slot(256 * 32)) duties, _ = dutiesMap.Get(1) - expected = expectedExecutedGenesisSyncCommitteeDuties(handler, duties, currentSlot.Get()) + expected = expectedExecutedDutiesSyncCommitteeGenesis(handler, duties, currentSlot.Get()) setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) + waitForDutiesExecutionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) // Stop scheduler & wait for graceful exit. cancel() @@ -243,12 +241,11 @@ func TestScheduler_SyncCommittee_Genesis_Current_Next_Periods(t *testing.T) { func TestScheduler_SyncCommittee_Genesis_Indices_Changed(t *testing.T) { var ( - handler = NewSyncCommitteeHandler(dutystore.NewSyncCommitteeDuties()) - currentSlot = &SafeValue[phase0.Slot]{} - waitForDuties = &SafeValue[bool]{} - forkEpoch = goclient.FarFutureEpoch - dutiesMap = hashmap.New[uint64, []*v1.SyncCommitteeDuty]() - activeShares = []*ssvtypes.SSVShare{ + handler = NewSyncCommitteeHandler(dutystore.NewSyncCommitteeDuties()) + currentSlot = &SafeValue[phase0.Slot]{} + forkEpoch = goclient.FarFutureEpoch + dutiesMap = hashmap.New[uint64, []*v1.SyncCommitteeDuty]() + activeShares = []*ssvtypes.SSVShare{ { Share: spectypes.Share{ Committee: []*spectypes.ShareMember{ @@ -269,7 +266,7 @@ func TestScheduler_SyncCommittee_Genesis_Indices_Changed(t *testing.T) { ) currentSlot.Set(phase0.Slot(256*32 - 3)) scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) - fetchDutiesCall, executeDutiesCall := setupSyncCommitteeGenesisDutiesMock(scheduler, activeShares, dutiesMap, waitForDuties) + fetchDutiesCall, executeDutiesCall := setupGenesisDutiesMockSyncCommittee(scheduler, activeShares, dutiesMap) startFn() dutiesMap.Set(1, []*v1.SyncCommitteeDuty{ @@ -279,10 +276,10 @@ func TestScheduler_SyncCommittee_Genesis_Indices_Changed(t *testing.T) { }, }) - // STEP 1: wait for sync committee duties to be fetched for next period - waitForDuties.Set(true) + // STEP 1: wait for sync committee duties to be fetched for current and next period ticker.Send(currentSlot.Get()) waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 2: trigger a change in active indices scheduler.indicesChg <- struct{}{} @@ -307,11 +304,11 @@ func TestScheduler_SyncCommittee_Genesis_Indices_Changed(t *testing.T) { // STEP 5: execute duties currentSlot.Set(phase0.Slot(256 * 32)) duties, _ = dutiesMap.Get(1) - expected := expectedExecutedGenesisSyncCommitteeDuties(handler, duties, currentSlot.Get()) + expected := expectedExecutedDutiesSyncCommitteeGenesis(handler, duties, currentSlot.Get()) setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) + waitForDutiesExecutionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) // Stop scheduler & wait for graceful exit. cancel() @@ -320,12 +317,11 @@ func TestScheduler_SyncCommittee_Genesis_Indices_Changed(t *testing.T) { func TestScheduler_SyncCommittee_Genesis_Multiple_Indices_Changed_Same_Slot(t *testing.T) { var ( - handler = NewSyncCommitteeHandler(dutystore.NewSyncCommitteeDuties()) - currentSlot = &SafeValue[phase0.Slot]{} - waitForDuties = &SafeValue[bool]{} - forkEpoch = goclient.FarFutureEpoch - dutiesMap = hashmap.New[uint64, []*v1.SyncCommitteeDuty]() - activeShares = []*ssvtypes.SSVShare{ + handler = NewSyncCommitteeHandler(dutystore.NewSyncCommitteeDuties()) + currentSlot = &SafeValue[phase0.Slot]{} + forkEpoch = goclient.FarFutureEpoch + dutiesMap = hashmap.New[uint64, []*v1.SyncCommitteeDuty]() + activeShares = []*ssvtypes.SSVShare{ { Share: spectypes.Share{ Committee: []*spectypes.ShareMember{ @@ -346,12 +342,13 @@ func TestScheduler_SyncCommittee_Genesis_Multiple_Indices_Changed_Same_Slot(t *t ) currentSlot.Set(phase0.Slot(256*32 - 3)) scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) - fetchDutiesCall, executeDutiesCall := setupSyncCommitteeGenesisDutiesMock(scheduler, activeShares, dutiesMap, waitForDuties) + fetchDutiesCall, executeDutiesCall := setupGenesisDutiesMockSyncCommittee(scheduler, activeShares, dutiesMap) startFn() - // STEP 1: wait for no action to be taken + // STEP 1: wait for sync committee duties to be fetched for current and next period ticker.Send(currentSlot.Get()) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 2: trigger a change in active indices scheduler.indicesChg <- struct{}{} @@ -374,7 +371,6 @@ func TestScheduler_SyncCommittee_Genesis_Multiple_Indices_Changed_Same_Slot(t *t // STEP 4: wait for sync committee duties to be fetched again currentSlot.Set(phase0.Slot(256*32 - 2)) - waitForDuties.Set(true) ticker.Send(currentSlot.Get()) waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) @@ -387,11 +383,11 @@ func TestScheduler_SyncCommittee_Genesis_Multiple_Indices_Changed_Same_Slot(t *t // STEP 6: The first assigned duty should not be executed, but the second one should currentSlot.Set(phase0.Slot(256 * 32)) duties, _ = dutiesMap.Get(1) - expected := expectedExecutedGenesisSyncCommitteeDuties(handler, duties, currentSlot.Get()) + expected := expectedExecutedDutiesSyncCommitteeGenesis(handler, duties, currentSlot.Get()) setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) + waitForDutiesExecutionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) // Stop scheduler & wait for graceful exit. cancel() @@ -401,12 +397,11 @@ func TestScheduler_SyncCommittee_Genesis_Multiple_Indices_Changed_Same_Slot(t *t // reorg current dependent root changed func TestScheduler_SyncCommittee_Genesis_Reorg_Current(t *testing.T) { var ( - handler = NewSyncCommitteeHandler(dutystore.NewSyncCommitteeDuties()) - currentSlot = &SafeValue[phase0.Slot]{} - waitForDuties = &SafeValue[bool]{} - forkEpoch = goclient.FarFutureEpoch - dutiesMap = hashmap.New[uint64, []*v1.SyncCommitteeDuty]() - activeShares = []*ssvtypes.SSVShare{ + handler = NewSyncCommitteeHandler(dutystore.NewSyncCommitteeDuties()) + currentSlot = &SafeValue[phase0.Slot]{} + forkEpoch = goclient.FarFutureEpoch + dutiesMap = hashmap.New[uint64, []*v1.SyncCommitteeDuty]() + activeShares = []*ssvtypes.SSVShare{ { Share: spectypes.Share{ Committee: []*spectypes.ShareMember{ @@ -427,7 +422,7 @@ func TestScheduler_SyncCommittee_Genesis_Reorg_Current(t *testing.T) { ) currentSlot.Set(phase0.Slot(256*32 - 3)) scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) - fetchDutiesCall, executeDutiesCall := setupSyncCommitteeGenesisDutiesMock(scheduler, activeShares, dutiesMap, waitForDuties) + fetchDutiesCall, executeDutiesCall := setupGenesisDutiesMockSyncCommittee(scheduler, activeShares, dutiesMap) startFn() dutiesMap.Set(1, []*v1.SyncCommitteeDuty{ @@ -438,9 +433,9 @@ func TestScheduler_SyncCommittee_Genesis_Reorg_Current(t *testing.T) { }) // STEP 1: wait for sync committee duties to be fetched and executed at the same slot - waitForDuties.Set(true) ticker.Send(currentSlot.Get()) waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 2: trigger head event e := &v1.Event{ @@ -481,11 +476,11 @@ func TestScheduler_SyncCommittee_Genesis_Reorg_Current(t *testing.T) { // STEP 6: The first assigned duty should not be executed, but the second one should currentSlot.Set(phase0.Slot(256 * 32)) duties, _ := dutiesMap.Get(1) - expected := expectedExecutedGenesisSyncCommitteeDuties(handler, duties, currentSlot.Get()) + expected := expectedExecutedDutiesSyncCommitteeGenesis(handler, duties, currentSlot.Get()) setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) + waitForDutiesExecutionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) // Stop scheduler & wait for graceful exit. cancel() @@ -495,12 +490,11 @@ func TestScheduler_SyncCommittee_Genesis_Reorg_Current(t *testing.T) { // reorg current dependent root changed including indices change in the same slot func TestScheduler_SyncCommittee_Genesis_Reorg_Current_Indices_Changed(t *testing.T) { var ( - handler = NewSyncCommitteeHandler(dutystore.NewSyncCommitteeDuties()) - currentSlot = &SafeValue[phase0.Slot]{} - waitForDuties = &SafeValue[bool]{} - forkEpoch = goclient.FarFutureEpoch - dutiesMap = hashmap.New[uint64, []*v1.SyncCommitteeDuty]() - activeShares = []*ssvtypes.SSVShare{ + handler = NewSyncCommitteeHandler(dutystore.NewSyncCommitteeDuties()) + currentSlot = &SafeValue[phase0.Slot]{} + forkEpoch = goclient.FarFutureEpoch + dutiesMap = hashmap.New[uint64, []*v1.SyncCommitteeDuty]() + activeShares = []*ssvtypes.SSVShare{ { Share: spectypes.Share{ Committee: []*spectypes.ShareMember{ @@ -529,7 +523,7 @@ func TestScheduler_SyncCommittee_Genesis_Reorg_Current_Indices_Changed(t *testin ) currentSlot.Set(phase0.Slot(256*32 - 3)) scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) - fetchDutiesCall, executeDutiesCall := setupSyncCommitteeGenesisDutiesMock(scheduler, activeShares, dutiesMap, waitForDuties) + fetchDutiesCall, executeDutiesCall := setupGenesisDutiesMockSyncCommittee(scheduler, activeShares, dutiesMap) startFn() dutiesMap.Set(1, []*v1.SyncCommitteeDuty{ @@ -540,9 +534,9 @@ func TestScheduler_SyncCommittee_Genesis_Reorg_Current_Indices_Changed(t *testin }) // STEP 1: wait for sync committee duties to be fetched and executed at the same slot - waitForDuties.Set(true) ticker.Send(currentSlot.Get()) waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) // STEP 2: trigger head event e := &v1.Event{ @@ -593,11 +587,11 @@ func TestScheduler_SyncCommittee_Genesis_Reorg_Current_Indices_Changed(t *testin // STEP 6: The first assigned duty should not be executed, but the second and the new from indices change should currentSlot.Set(phase0.Slot(256 * 32)) duties, _ = dutiesMap.Get(1) - expected := expectedExecutedGenesisSyncCommitteeDuties(handler, duties, currentSlot.Get()) + expected := expectedExecutedDutiesSyncCommitteeGenesis(handler, duties, currentSlot.Get()) setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) + waitForDutiesExecutionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) // Stop scheduler & wait for graceful exit. cancel() @@ -606,12 +600,11 @@ func TestScheduler_SyncCommittee_Genesis_Reorg_Current_Indices_Changed(t *testin func TestScheduler_SyncCommittee_Genesis_Early_Block(t *testing.T) { var ( - handler = NewSyncCommitteeHandler(dutystore.NewSyncCommitteeDuties()) - currentSlot = &SafeValue[phase0.Slot]{} - waitForDuties = &SafeValue[bool]{} - forkEpoch = goclient.FarFutureEpoch - dutiesMap = hashmap.New[uint64, []*v1.SyncCommitteeDuty]() - activeShares = []*ssvtypes.SSVShare{{ + handler = NewSyncCommitteeHandler(dutystore.NewSyncCommitteeDuties()) + currentSlot = &SafeValue[phase0.Slot]{} + forkEpoch = goclient.FarFutureEpoch + dutiesMap = hashmap.New[uint64, []*v1.SyncCommitteeDuty]() + activeShares = []*ssvtypes.SSVShare{{ Share: spectypes.Share{ Committee: []*spectypes.ShareMember{ {Signer: 1}, {Signer: 2}, {Signer: 3}, {Signer: 4}, @@ -629,31 +622,32 @@ func TestScheduler_SyncCommittee_Genesis_Early_Block(t *testing.T) { currentSlot.Set(phase0.Slot(0)) scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) - fetchDutiesCall, executeDutiesCall := setupSyncCommitteeGenesisDutiesMock(scheduler, activeShares, dutiesMap, waitForDuties) + fetchDutiesCall, executeDutiesCall := setupGenesisDutiesMockSyncCommittee(scheduler, activeShares, dutiesMap) startFn() duties, _ := dutiesMap.Get(0) - expected := expectedExecutedGenesisSyncCommitteeDuties(handler, duties, currentSlot.Get()) + expected := expectedExecutedDutiesSyncCommitteeGenesis(handler, duties, currentSlot.Get()) setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) // STEP 1: wait for sync committee duties to be fetched and executed at the same slot ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) + waitForDutiesFetchGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) + waitForDutiesExecutionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) // STEP 2: expect sync committee duties to be executed at the same period currentSlot.Set(phase0.Slot(1)) duties, _ = dutiesMap.Get(0) - expected = expectedExecutedGenesisSyncCommitteeDuties(handler, duties, currentSlot.Get()) + expected = expectedExecutedDutiesSyncCommitteeGenesis(handler, duties, currentSlot.Get()) setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) + waitForDutiesExecutionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) // STEP 3: wait for sync committee duties to be executed faster than 1/3 of the slot duration startTime := time.Now() currentSlot.Set(phase0.Slot(2)) duties, _ = dutiesMap.Get(0) - expected = expectedExecutedGenesisSyncCommitteeDuties(handler, duties, currentSlot.Get()) + expected = expectedExecutedDutiesSyncCommitteeGenesis(handler, duties, currentSlot.Get()) setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) ticker.Send(currentSlot.Get()) @@ -665,7 +659,7 @@ func TestScheduler_SyncCommittee_Genesis_Early_Block(t *testing.T) { }, } scheduler.HandleHeadEvent(logger)(e) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) + waitForDutiesExecutionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) require.Less(t, time.Since(startTime), scheduler.network.Beacon.SlotDurationSec()/3) // Stop scheduler & wait for graceful exit. diff --git a/operator/duties/voluntary_exit_genesis_test.go b/operator/duties/voluntary_exit_genesis_test.go index e8719d8922..6e053388f5 100644 --- a/operator/duties/voluntary_exit_genesis_test.go +++ b/operator/duties/voluntary_exit_genesis_test.go @@ -91,7 +91,7 @@ func TestVoluntaryExitHandler_HandleGenesisDuties(t *testing.T) { t.Run("slot = 5, block = 1 - executing duty, fetching block number", func(t *testing.T) { currentSlot.Set(phase0.Slot(normalExit.BlockNumber) + voluntaryExitSlotsToPostpone) ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, nil, executeDutiesCall, timeout, expectedDuties[:1]) + waitForDutiesExecutionGenesis(t, logger, nil, executeDutiesCall, timeout, expectedDuties[:1]) require.EqualValues(t, 2, blockByNumberCalls.Load()) }) @@ -100,7 +100,7 @@ func TestVoluntaryExitHandler_HandleGenesisDuties(t *testing.T) { t.Run("slot = 5, block = 1 - executing another duty, no block number fetch", func(t *testing.T) { currentSlot.Set(phase0.Slot(sameBlockExit.BlockNumber) + voluntaryExitSlotsToPostpone) ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, nil, executeDutiesCall, timeout, expectedDuties[1:2]) + waitForDutiesExecutionGenesis(t, logger, nil, executeDutiesCall, timeout, expectedDuties[1:2]) require.EqualValues(t, 2, blockByNumberCalls.Load()) }) @@ -116,7 +116,7 @@ func TestVoluntaryExitHandler_HandleGenesisDuties(t *testing.T) { t.Run("slot = 6, block = 1 - executing new duty, fetching block number", func(t *testing.T) { currentSlot.Set(phase0.Slot(newBlockExit.BlockNumber) + voluntaryExitSlotsToPostpone) ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, nil, executeDutiesCall, timeout, expectedDuties[2:3]) + waitForDutiesExecutionGenesis(t, logger, nil, executeDutiesCall, timeout, expectedDuties[2:3]) require.EqualValues(t, 3, blockByNumberCalls.Load()) }) @@ -125,7 +125,7 @@ func TestVoluntaryExitHandler_HandleGenesisDuties(t *testing.T) { t.Run("slot = 10, block = 5 - executing past duty, fetching block number", func(t *testing.T) { currentSlot.Set(phase0.Slot(pastBlockExit.BlockNumber) + voluntaryExitSlotsToPostpone + 1) ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, nil, executeDutiesCall, timeout, expectedDuties[3:4]) + waitForDutiesExecutionGenesis(t, logger, nil, executeDutiesCall, timeout, expectedDuties[3:4]) require.EqualValues(t, 4, blockByNumberCalls.Load()) }) From 9fa010daca4adf32cde7e29e303d2908991dcf01 Mon Sep 17 00:00:00 2001 From: olegshmuelov <45327364+olegshmuelov@users.noreply.github.com> Date: Sun, 20 Oct 2024 14:50:31 +0300 Subject: [PATCH 13/14] adapt to stage --- operator/duties/attester.go | 1 - 1 file changed, 1 deletion(-) diff --git a/operator/duties/attester.go b/operator/duties/attester.go index d3a2be06f2..852a33b7da 100644 --- a/operator/duties/attester.go +++ b/operator/duties/attester.go @@ -138,7 +138,6 @@ func (h *AttesterHandler) processExecution(epoch phase0.Epoch, slot phase0.Slot) } func (h *AttesterHandler) processIndicesChange(epoch phase0.Epoch, slot phase0.Slot) { - h.duties.Reset(epoch) h.fetchCurrentEpoch = true // reset next epoch duties if in appropriate slot range From d33748881aeb746a796d0ce3413d9cc6feae52a3 Mon Sep 17 00:00:00 2001 From: olegshmuelov <45327364+olegshmuelov@users.noreply.github.com> Date: Sun, 20 Oct 2024 15:28:20 +0300 Subject: [PATCH 14/14] remove non relevant comment --- operator/duties/sync_committee.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/operator/duties/sync_committee.go b/operator/duties/sync_committee.go index 8f2dc091aa..4f63b214c6 100644 --- a/operator/duties/sync_committee.go +++ b/operator/duties/sync_committee.go @@ -151,8 +151,6 @@ func (h *SyncCommitteeHandler) processExecution(period uint64, slot phase0.Slot) } func (h *SyncCommitteeHandler) processIndicesChange(period uint64, slot phase0.Slot) { - // we should not reset sync committee duties here as it is necessary for message validation - // but this can lead to executing duties for deleted/liquidated validators h.fetchCurrentPeriod = true // reset next period duties if in appropriate slot range