diff --git a/backend/cmd/api/main.go b/backend/cmd/api/main.go index f6f08e181..65b64415b 100644 --- a/backend/cmd/api/main.go +++ b/backend/cmd/api/main.go @@ -18,7 +18,7 @@ import ( // TODO load these from config const ( host = "0.0.0.0" - port = "8081" + port = "8080" dummyApi = false ) diff --git a/backend/pkg/api/data_access/data_access.go b/backend/pkg/api/data_access/data_access.go index ccfc6502b..6c6cc462c 100644 --- a/backend/pkg/api/data_access/data_access.go +++ b/backend/pkg/api/data_access/data_access.go @@ -18,6 +18,7 @@ import ( "github.com/gobitfly/beaconchain/pkg/commons/types" "github.com/gobitfly/beaconchain/pkg/commons/utils" "github.com/jmoiron/sqlx" + "github.com/lib/pq" "golang.org/x/sync/errgroup" ) @@ -224,13 +225,19 @@ func (d DataAccessService) RemoveValidatorDashboardPublicId(dashboardId uint64, return d.dummy.RemoveValidatorDashboardPublicId(dashboardId, publicDashboardId) } +var getValidatorDashboardSlotVizMux = &sync.Mutex{} + func (d DataAccessService) GetValidatorDashboardSlotViz(dashboardId uint64) ([]t.SlotVizEpoch, error) { + log.Infof("retrieving data for dashboard with id %d", dashboardId) + // make sure that the function is only executed once during development not to go oom + getValidatorDashboardSlotVizMux.Lock() + defer getValidatorDashboardSlotVizMux.Unlock() // TODO: Get the validators from the dashboardId - dummyValidators := []uint64{900000, 900001, 900002, 900003, 900004, 900005, 900006, 900007, 900008, 1053541} - ValidatorsMap := make(map[uint64]bool) + dummyValidators := []uint64{900005, 900006, 900007, 900008, 900009} + validatorsMap := make(map[uint64]bool) for _, v := range dummyValidators { - ValidatorsMap[v] = true + validatorsMap[v] = true } // Get min/max slot/epoch @@ -240,16 +247,14 @@ func (d DataAccessService) GetValidatorDashboardSlotViz(dashboardId uint64) ([]t minEpoch := headEpoch - 2 maxEpoch := headEpoch + 1 - minSlot := minEpoch * slotsPerEpoch - // create waiting group for concurrency gOuter := &errgroup.Group{} // Get the fulfilled duties - var validatorDuties []types.ValidatorDutyInfo + var validatorDutiesInfo []types.ValidatorDutyInfo gOuter.Go(func() error { var err error - validatorDuties, err = db.GetValidatorDuties(d.readerDb, minSlot) + validatorDutiesInfo, err = db.GetValidatorDutiesInfo(d.readerDb, minEpoch*slotsPerEpoch) return err }) @@ -292,7 +297,7 @@ func (d DataAccessService) GetValidatorDashboardSlotViz(dashboardId uint64) ([]t // Proposals for slot, propValidator := range decodedRedisCachedEpochAssignments.Assignments.ProposerAssignments { // Only add results for validators we care about - if _, isValid := ValidatorsMap[propValidator]; isValid { + if _, isValid := validatorsMap[propValidator]; isValid { muxPropAssignmentsForSlot.Lock() propAssignmentsForSlot[slot] = propValidator muxPropAssignmentsForSlot.Unlock() @@ -312,7 +317,7 @@ func (d DataAccessService) GetValidatorDashboardSlotViz(dashboardId uint64) ([]t attAssignmentsForSlot[slot] = make(map[uint64]bool, 0) } // Only add results for validators we care about - if _, isValid := ValidatorsMap[attValidator]; isValid { + if _, isValid := validatorsMap[attValidator]; isValid { attAssignmentsForSlot[slot][attValidator] = true } muxAttAssignmentsForSlot.Unlock() @@ -328,7 +333,7 @@ func (d DataAccessService) GetValidatorDashboardSlotViz(dashboardId uint64) ([]t } for _, validator := range decodedRedisCachedEpochAssignments.Assignments.SyncAssignments { // Only add results for validators we care about - if _, isValid := ValidatorsMap[validator]; isValid { + if _, isValid := validatorsMap[validator]; isValid { syncAssignmentsForEpoch[epoch][validator] = true } } @@ -344,13 +349,15 @@ func (d DataAccessService) GetValidatorDashboardSlotViz(dashboardId uint64) ([]t return nil, err } - // Restructure proposal status, attestations and sync duties + // Restructure proposal status, attestations, sync duties and slashings latestSlot := uint64(0) slotStatus := make(map[uint64]uint64) slotBlock := make(map[uint64]uint64) slotAttested := make(map[uint64]map[uint64]bool) slotSyncParticipated := make(map[uint64]map[uint64]bool) - for _, duty := range validatorDuties { + slotValiPropSlashed := make(map[uint64]bool) + slotValiAttSlashed := make(map[uint64]bool) + for _, duty := range validatorDutiesInfo { if duty.Slot > latestSlot { latestSlot = duty.Slot } @@ -376,6 +383,13 @@ func (d DataAccessService) GetValidatorDashboardSlotViz(dashboardId uint64) ([]t slotSyncParticipated[duty.Slot][validator] = true } } + // Slashings + if duty.ProposerSlashingsCount > 0 { + slotValiPropSlashed[duty.Slot] = true + } + if duty.AttesterSlashingsCount > 0 { + slotValiAttSlashed[duty.Slot] = true + } } } @@ -429,32 +443,91 @@ func (d DataAccessService) GetValidatorDashboardSlotViz(dashboardId uint64) ([]t } // Get the attestation summary for the slot - slotVizEpochs[epochIdx].Slots[slotIdx].Attestations = &t.VDBSlotVizPassiveDuty{} - for validator := range attAssignmentsForSlot[slot] { - if slot >= latestSlot { - // If the latest slot is the one that must be attested we still show it as pending - // as the attestation cannot yet have been included in a block - slotVizEpochs[epochIdx].Slots[slotIdx].Attestations.PendingCount++ - } else if _, ok := slotAttested[slot][validator]; ok { - slotVizEpochs[epochIdx].Slots[slotIdx].Attestations.SuccessCount++ - } else { - slotVizEpochs[epochIdx].Slots[slotIdx].Attestations.FailedCount++ + if len(attAssignmentsForSlot[slot]) > 0 { + slotVizEpochs[epochIdx].Slots[slotIdx].Attestations = &t.VDBSlotVizPassiveDuty{} + for validator := range attAssignmentsForSlot[slot] { + if slot >= latestSlot { + // If the latest slot is the one that must be attested we still show it as pending + // as the attestation cannot yet have been included in a block + slotVizEpochs[epochIdx].Slots[slotIdx].Attestations.PendingCount++ + } else if _, ok := slotAttested[slot][validator]; ok { + slotVizEpochs[epochIdx].Slots[slotIdx].Attestations.SuccessCount++ + } else { + slotVizEpochs[epochIdx].Slots[slotIdx].Attestations.FailedCount++ + } } } // Get the sync summary for the slot - for validator := range syncAssignmentsForEpoch[epoch] { + if len(syncAssignmentsForEpoch[epoch]) > 0 { slotVizEpochs[epochIdx].Slots[slotIdx].Sync = &t.VDBSlotVizPassiveDuty{} - if slot > latestSlot { - slotVizEpochs[epochIdx].Slots[slotIdx].Sync.PendingCount++ - } else if _, ok := slotSyncParticipated[slot][validator]; ok { - slotVizEpochs[epochIdx].Slots[slotIdx].Sync.SuccessCount++ - } else { - slotVizEpochs[epochIdx].Slots[slotIdx].Sync.FailedCount++ + for validator := range syncAssignmentsForEpoch[epoch] { + if slot > latestSlot { + slotVizEpochs[epochIdx].Slots[slotIdx].Sync.PendingCount++ + } else if _, ok := slotSyncParticipated[slot][validator]; ok { + slotVizEpochs[epochIdx].Slots[slotIdx].Sync.SuccessCount++ + } else { + slotVizEpochs[epochIdx].Slots[slotIdx].Sync.FailedCount++ + } } } - // TODO: Get the slashings for the slot + // Get the slashings for the slot + slashedValidators := []uint64{} + if _, ok := slotValiPropSlashed[slot]; ok { + slashedPropValidators := []uint64{} + err := d.readerDb.Select(&slashedPropValidators, ` + SELECT + proposerindex + FROM blocks_proposerslashings + WHERE block_slot = $1`, slot) + if err != nil { + return nil, err + } + + slashedValidators = append(slashedValidators, slashedPropValidators...) + } + if _, ok := slotValiAttSlashed[slot]; ok { + slashedAttValidators := []pq.Int64Array{} + + err := d.readerDb.Select(&slashedAttValidators, ` + SELECT + attestation2_indices + FROM blocks_attesterslashings + WHERE block_slot = $1`, slot) + if err != nil { + return nil, err + } + + for _, slashedAttValidator := range slashedAttValidators { + for _, validator := range slashedAttValidator { + slashedValidators = append(slashedValidators, uint64(validator)) + } + } + } + if _, ok := propAssignmentsForSlot[slot]; ok { + // One of the dashboard validators slashed + for _, validator := range slashedValidators { + slotVizEpochs[epochIdx].Slots[slotIdx].Slashing = append(slotVizEpochs[epochIdx].Slots[slotIdx].Slashing, + t.VDBSlotVizActiveDuty{ + Status: "success", + Validator: propAssignmentsForSlot[slot], // Dashboard validator + DutyObject: validator, // Validator that got slashed + }) + } + } + for _, validator := range slashedValidators { + if _, ok := validatorsMap[validator]; !ok { + continue + } + // One of the dashboard validators got slashed + slotVizEpochs[epochIdx].Slots[slotIdx].Slashing = append(slotVizEpochs[epochIdx].Slots[slotIdx].Slashing, + t.VDBSlotVizActiveDuty{ + Status: "failed", + Validator: validator, // Dashboard validator + DutyObject: validator, // Validator that got slashed + }) + } } } diff --git a/backend/pkg/commons/db/db.go b/backend/pkg/commons/db/db.go index 8226d7b3f..f1466ed79 100644 --- a/backend/pkg/commons/db/db.go +++ b/backend/pkg/commons/db/db.go @@ -2132,7 +2132,7 @@ func GetValidatorProposals(validators []uint64, proposals *[]types.ValidatorProp `, validatorsPQArray) } -func GetValidatorDuties(readerDb *sqlx.DB, startSlot uint64) ([]types.ValidatorDutyInfo, error) { +func GetValidatorDutiesInfo(readerDb *sqlx.DB, startSlot uint64) ([]types.ValidatorDutyInfo, error) { validatorDutyInfo := []types.ValidatorDutyInfo{} err := readerDb.Select(&validatorDutyInfo, `