Skip to content

Commit

Permalink
update heatmap data structures (#263)
Browse files Browse the repository at this point in the history
  • Loading branch information
LuccaBitfly authored May 6, 2024
1 parent cf0994f commit b903c38
Show file tree
Hide file tree
Showing 13 changed files with 223 additions and 292 deletions.
18 changes: 16 additions & 2 deletions backend/pkg/api/data_access/dummy.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package dataaccess

import (
"time"

"github.com/go-faker/faker/v4"
"github.com/go-faker/faker/v4/pkg/options"
"github.com/gobitfly/beaconchain/pkg/api/enums"
Expand Down Expand Up @@ -198,13 +200,25 @@ func (d *DummyService) GetValidatorDashboardBlocks(dashboardId t.VDBId, cursor s
return r, &p, err
}

func (d *DummyService) GetValidatorDashboardHeatmap(dashboardId t.VDBId) (*t.VDBHeatmap, error) {
func (d *DummyService) GetValidatorDashboardEpochHeatmap(dashboardId t.VDBId) (*t.VDBHeatmap, error) {
r := t.VDBHeatmap{}
err := commonFakeData(&r)
return &r, err
}

func (d *DummyService) GetValidatorDashboardDailyHeatmap(dashboardId t.VDBId, period enums.TimePeriod) (*t.VDBHeatmap, error) {
r := t.VDBHeatmap{}
err := commonFakeData(&r)
return &r, err
}

func (d *DummyService) GetValidatorDashboardGroupHeatmap(dashboardId t.VDBId, groupId uint64, epoch uint64) (*t.VDBHeatmapTooltipData, error) {
func (d *DummyService) GetValidatorDashboardGroupEpochHeatmap(dashboardId t.VDBId, groupId uint64, epoch uint64) (*t.VDBHeatmapTooltipData, error) {
r := t.VDBHeatmapTooltipData{}
err := commonFakeData(&r)
return &r, err
}

func (d *DummyService) GetValidatorDashboardGroupDailyHeatmap(dashboardId t.VDBId, groupId uint64, day time.Time) (*t.VDBHeatmapTooltipData, error) {
r := t.VDBHeatmapTooltipData{}
err := commonFakeData(&r)
return &r, err
Expand Down
2 changes: 1 addition & 1 deletion backend/pkg/api/data_access/vdb_deposits.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ func (d *DataAccessService) GetValidatorDashboardElDeposits(dashboardId t.VDBId,
responseData[i] = t.VDBExecutionDepositsTableRow{
PublicKey: t.PubKey(pubkeys[i]),
Block: uint64(row.BlockNumber),
Timestamp: row.Timestamp,
Timestamp: row.Timestamp.Unix(),
From: t.Address{Hash: t.Hash(hexutil.Encode(row.From))},
TxHash: t.Hash(hexutil.Encode(row.TxHash)),
WithdrawalCredentials: t.Hash(hexutil.Encode(row.WithdrawalCredentials)),
Expand Down
225 changes: 17 additions & 208 deletions backend/pkg/api/data_access/vdb_heatmap.go
Original file line number Diff line number Diff line change
@@ -1,221 +1,30 @@
package dataaccess

import (
"time"

"github.com/gobitfly/beaconchain/pkg/api/enums"
t "github.com/gobitfly/beaconchain/pkg/api/types"
"github.com/shopspring/decimal"
"golang.org/x/sync/errgroup"
)

func (d *DataAccessService) GetValidatorDashboardHeatmap(dashboardId t.VDBId) (*t.VDBHeatmap, error) {
// retrieve data for last hour
func (d *DataAccessService) GetValidatorDashboardEpochHeatmap(dashboardId t.VDBId) (*t.VDBHeatmap, error) {
// WORKING Rami
ret := &t.VDBHeatmap{}
queryResult := []struct {
GroupId uint64 `db:"group_id"`
Epoch uint64 `db:"epoch"`
Rewards float64 `db:"attestations_eff"`
}{}
// WIP
// epoch based data doesn't seem possible, we only store data of the last ~14 epochs
// -> wait for redesign of the heatmap (prob daily data)
if dashboardId.Validators == nil {
wg := errgroup.Group{}

wg.Go(func() error {
query := `
SELECT
id
FROM
users_val_dashboards_groups
WHERE
dashboard_id = $1
ORDER BY
id`
return d.alloyReader.Select(&ret.GroupIds, query, dashboardId.Id)
})

wg.Go(func() error {
query := `
SELECT
group_id,
epoch,
SUM(attestations_reward)::decimal / NULLIF(SUM(attestations_ideal_reward), 0) AS attestations_eff
FROM
validator_dashboard_data_epoch epochs
LEFT JOIN users_val_dashboards_validators validators ON validators.validator_index = epochs.validator_index
WHERE
dashboard_id = $1
GROUP BY
epoch, group_id
ORDER BY
epoch`

return d.alloyReader.Select(&queryResult, query, dashboardId.Id)
})
err := wg.Wait()
if err != nil {
return nil, err
}
} else {
validators, err := d.getDashboardValidators(dashboardId)
if err != nil || len(validators) == 0 {
return ret, err
}

query := `
SELECT
$2::int AS group_id,
epoch,
SUM(attestations_reward)::decimal / NULLIF(SUM(attestations_ideal_reward), 0) AS attestations_eff
FROM
validator_dashboard_data_epoch
WHERE
validator_index = ANY($1)
GROUP BY
epoch
ORDER BY
epoch`

err = d.alloyReader.Select(&queryResult, query, validators, t.DefaultGroupId)
if err != nil {
return ret, err
}
ret.GroupIds = append(ret.GroupIds, t.DefaultGroupId)
}
return d.dummy.GetValidatorDashboardEpochHeatmap(dashboardId)
}

groupIdxMap := make(map[uint64]uint64)
for i, group := range ret.GroupIds {
groupIdxMap[group] = uint64(i)
}
for _, res := range queryResult {
if len(ret.Epochs) == 0 || ret.Epochs[len(ret.Epochs)-1] < res.Epoch {
ret.Epochs = append(ret.Epochs, res.Epoch)
}
cell := t.VDBHeatmapCell{
X: uint64(len(ret.Epochs) - 1),
Y: groupIdxMap[res.GroupId],
}
if res.Rewards > 0 {
cell.Value = res.Rewards * 100
}
ret.Data = append(ret.Data, cell)
}
return ret, nil
// allowed periods are: last_7d, last_30d, last_365d
func (d *DataAccessService) GetValidatorDashboardDailyHeatmap(dashboardId t.VDBId, period enums.TimePeriod) (*t.VDBHeatmap, error) {
// TODO @remoterami
return d.dummy.GetValidatorDashboardDailyHeatmap(dashboardId, period)
}

func (d *DataAccessService) GetValidatorDashboardGroupHeatmap(dashboardId t.VDBId, groupId uint64, epoch uint64) (*t.VDBHeatmapTooltipData, error) {
func (d *DataAccessService) GetValidatorDashboardGroupEpochHeatmap(dashboardId t.VDBId, groupId uint64, epoch uint64) (*t.VDBHeatmapTooltipData, error) {
// WORKING Rami
ret := &t.VDBHeatmapTooltipData{Epoch: epoch}

var validators []uint64
err := d.alloyReader.Select(&validators, `SELECT validator_index FROM users_val_dashboards_validators WHERE dashboard_id = $1 AND group_id = $2`, dashboardId.Id, groupId)
if err != nil {
return nil, err
}

queryResult := []struct {
Validator uint64 `db:"validator_index"`
BlocksScheduled uint8 `db:"blocks_scheduled"`
BlocksProposed uint8 `db:"blocks_proposed"`
SyncScheduled uint8 `db:"sync_scheduled"`
SyncExecuted uint8 `db:"sync_executed"`
Slashed bool `db:"slashed"`
AttestationReward int64 `db:"attestations_reward"`
AttestationIdealReward int64 `db:"attestations_ideal_reward"`
AttestationsScheduled uint8 `db:"attestations_scheduled"`
AttestationsHeadExecuted uint8 `db:"attestation_head_executed"`
AttestationsSourceExecuted uint8 `db:"attestation_source_executed"`
AttestationsTargetExecuted uint8 `db:"attestation_target_executed"`
}{}
wg := errgroup.Group{}
wg.Go(func() error {
query := `
SELECT
validator_index,
blocks_scheduled,
blocks_proposed,
sync_scheduled,
sync_executed,
slashed,
attestations_reward,
attestations_ideal_reward,
attestations_scheduled,
attestation_head_executed,
attestation_source_executed,
attestation_target_executed
FROM
validator_dashboard_data_epoch
WHERE
validator_index = ANY($1) AND epoch = $2`

return d.alloyReader.Select(&queryResult, query, validators, epoch)
})

var slashings []uint64
wg.Go(func() error {
query := `
SELECT
validator_index
FROM
validator_dashboard_data_rolling_daily
WHERE
slashed_by = ANY($1)`

return d.alloyReader.Select(&slashings, query, validators, epoch)
})

if err := wg.Wait(); err != nil {
return nil, err
}

for _, slashed := range slashings {
ret.Slashings = append(ret.Slashings, t.VDBHeatmapTooltipDuty{
Validator: slashed,
Status: "success",
})
}

var totalAttestationReward int64
var totalAttestationIdealReward int64
var totalAttestationsScheduled uint64
var totalAttestationsHeadExecuted uint64
var totalAttestationsSourceExecuted uint64
var totalAttestationsTargetExecuted uint64
for _, res := range queryResult {
if res.Slashed {
ret.Slashings = append(ret.Slashings, t.VDBHeatmapTooltipDuty{
Validator: res.Validator,
Status: "failed",
})
}
if res.SyncScheduled > 0 /* && epoch % 256 == 0 */ { // move to circle logic
ret.Syncs = append(ret.Syncs, res.Validator)
}
for i := uint8(0); i < res.BlocksScheduled; i++ {
status := "success"
if i >= res.BlocksProposed {
status = "failed"
}
ret.Proposers = append(ret.Proposers, t.VDBHeatmapTooltipDuty{
Validator: res.Validator,
Status: status,
})
}

totalAttestationReward += res.AttestationReward
totalAttestationIdealReward += res.AttestationIdealReward
totalAttestationsScheduled += uint64(res.AttestationsScheduled)
totalAttestationsHeadExecuted += uint64(res.AttestationsHeadExecuted)
totalAttestationsSourceExecuted += uint64(res.AttestationsSourceExecuted)
totalAttestationsTargetExecuted += uint64(res.AttestationsTargetExecuted)
}

ret.AttestationIncome = decimal.NewFromInt(totalAttestationReward)
if totalAttestationIdealReward != 0 {
ret.AttestationEfficiency = float64(totalAttestationReward) / float64(totalAttestationIdealReward)
}
ret.AttestationsHead = t.StatusCount{Success: totalAttestationsHeadExecuted, Failed: totalAttestationsScheduled - totalAttestationsHeadExecuted}
ret.AttestationsSource = t.StatusCount{Success: totalAttestationsSourceExecuted, Failed: totalAttestationsScheduled - totalAttestationsSourceExecuted}
ret.AttestationsTarget = t.StatusCount{Success: totalAttestationsTargetExecuted, Failed: totalAttestationsScheduled - totalAttestationsTargetExecuted}
return d.dummy.GetValidatorDashboardGroupEpochHeatmap(dashboardId, groupId, epoch)
}

return ret, nil
func (d *DataAccessService) GetValidatorDashboardGroupDailyHeatmap(dashboardId t.VDBId, groupId uint64, day time.Time) (*t.VDBHeatmapTooltipData, error) {
// TODO @remoterami
return d.dummy.GetValidatorDashboardGroupDailyHeatmap(dashboardId, groupId, day)
}
7 changes: 5 additions & 2 deletions backend/pkg/api/data_access/vdb_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package dataaccess

import (
"database/sql"
"time"

"github.com/gobitfly/beaconchain/pkg/api/enums"
t "github.com/gobitfly/beaconchain/pkg/api/types"
Expand Down Expand Up @@ -44,8 +45,10 @@ type ValidatorDashboardRepository interface {

GetValidatorDashboardBlocks(dashboardId t.VDBId, cursor string, colSort t.Sort[enums.VDBBlocksColumn], search string, limit uint64) ([]t.VDBBlocksTableRow, *t.Paging, error)

GetValidatorDashboardHeatmap(dashboardId t.VDBId) (*t.VDBHeatmap, error)
GetValidatorDashboardGroupHeatmap(dashboardId t.VDBId, groupId uint64, epoch uint64) (*t.VDBHeatmapTooltipData, error)
GetValidatorDashboardEpochHeatmap(dashboardId t.VDBId) (*t.VDBHeatmap, error)
GetValidatorDashboardDailyHeatmap(dashboardId t.VDBId, period enums.TimePeriod) (*t.VDBHeatmap, error)
GetValidatorDashboardGroupEpochHeatmap(dashboardId t.VDBId, groupId uint64, epoch uint64) (*t.VDBHeatmapTooltipData, error)
GetValidatorDashboardGroupDailyHeatmap(dashboardId t.VDBId, groupId uint64, date time.Time) (*t.VDBHeatmapTooltipData, error)

GetValidatorDashboardElDeposits(dashboardId t.VDBId, cursor string, search string, limit uint64) ([]t.VDBExecutionDepositsTableRow, *t.Paging, error)
GetValidatorDashboardClDeposits(dashboardId t.VDBId, cursor string, search string, limit uint64) ([]t.VDBConsensusDepositsTableRow, *t.Paging, error)
Expand Down
2 changes: 1 addition & 1 deletion backend/pkg/api/data_access/vdb_management.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ func (d *DataAccessService) CreateValidatorDashboard(userId uint64, name string,
err = tx.Get(result, `
INSERT INTO users_val_dashboards (user_id, network, name)
VALUES ($1, $2, $3)
RETURNING id, user_id, name, network, created_at
RETURNING id, user_id, name, network, (EXTRACT(epoch FROM created_at))::BIGINT as created_at
`, userId, network, name)
if err != nil {
return nil, err
Expand Down
1 change: 1 addition & 0 deletions backend/pkg/api/data_access/vdb_summary.go
Original file line number Diff line number Diff line change
Expand Up @@ -763,6 +763,7 @@ func (d *DataAccessService) GetValidatorDashboardSummaryChart(dashboardId t.VDBI
return ret, nil
}

// allowed periods are: all_time, last_24h, last_7d, last_30d
func (d *DataAccessService) GetValidatorDashboardValidatorIndices(dashboardId t.VDBId, groupId int64, duty enums.ValidatorDuty, period enums.TimePeriod) ([]uint64, error) {
var validators []uint64
if dashboardId.Validators == nil {
Expand Down
Loading

0 comments on commit b903c38

Please sign in to comment.