@@ -12,6 +12,7 @@ import (
1212
1313 "github.com/doug-martin/goqu/v9"
1414 _ "github.com/doug-martin/goqu/v9/dialect/postgres"
15+ "github.com/doug-martin/goqu/v9/exp"
1516 "github.com/gobitfly/beaconchain/pkg/api/enums"
1617 t "github.com/gobitfly/beaconchain/pkg/api/types"
1718 "github.com/gobitfly/beaconchain/pkg/commons/cache"
@@ -705,37 +706,88 @@ func (d *DataAccessService) GetValidatorDashboardGroupRewards(ctx context.Contex
705706 return ret , nil
706707}
707708
708- func (d * DataAccessService ) GetValidatorDashboardRewardsChart (ctx context.Context , dashboardId t.VDBId , protocolModes t.VDBProtocolModes ) (* t.ChartData [int , decimal.Decimal ], error ) {
709+ func (d * DataAccessService ) GetValidatorDashboardRewardsChart (ctx context.Context , dashboardId t.VDBId , groupIds [] int64 , protocolModes t.VDBProtocolModes , aggregation enums. ChartAggregation , afterTs uint64 , beforeTs uint64 ) (* t.ChartData [int , decimal.Decimal ], error ) {
709710 // @DATA-ACCESS incorporate protocolModes
710711 // bar chart for the CL and EL rewards for each group for each epoch.
711712 // NO series for all groups combined except if AggregateGroups is true.
712713 // series id is group id, series property is 'cl' or 'el'
714+ ret := & t.ChartData [int , decimal.Decimal ]{}
713715
714- wg := errgroup.Group {}
716+ if len (groupIds ) == 0 {
717+ return ret , nil
718+ }
715719
716- latestFinalizedEpoch := cache .LatestFinalizedEpoch .Get ()
717- const epochLookBack = 224
718- startEpoch := uint64 (0 )
719- if latestFinalizedEpoch > epochLookBack {
720- startEpoch = latestFinalizedEpoch - epochLookBack
720+ var err error
721+
722+ var dataTable exp.LiteralExpression
723+ dataColumn := goqu .C ("t" )
724+ switch aggregation {
725+ case enums .IntervalEpoch :
726+ dataTable = goqu .L ("validator_dashboard_data_epoch AS e" )
727+ dataColumn = goqu .C ("epoch_timestamp" )
728+ case enums .IntervalHourly :
729+ dataTable = goqu .L ("validator_dashboard_data_hourly AS e FINAL" )
730+ case enums .IntervalDaily :
731+ dataTable = goqu .L ("validator_dashboard_data_daily AS e FINAL" )
732+ case enums .IntervalWeekly :
733+ dataTable = goqu .L ("validator_dashboard_data_weekly AS e FINAL" )
734+ default :
735+ return nil , fmt .Errorf ("unexpected aggregation type: %v" , aggregation )
736+ }
737+
738+ requestedAllGroups := dashboardId .AggregateGroups
739+ for _ , groupId := range groupIds {
740+ if groupId == t .AllGroups {
741+ // note: requesting all groups is only convenience on api level, this will NOT result in a "total" series as it wouldn't make sense for this endpoint
742+ requestedAllGroups = true
743+ break
744+ }
721745 }
722746
723747 // ------------------------------------------------------------------------------------------------------------------
724748 // Build the query that serves as base for both the main and EL rewards queries
749+ // CL
725750 rewardsDs := goqu .Dialect ("postgres" ).
726- Select (
727- goqu .L ("e.epoch" ),
728- goqu .L (`SUM(COALESCE(e.attestations_reward, 0) + COALESCE(e.blocks_cl_reward, 0) + COALESCE(e.sync_reward, 0)) AS cl_rewards` )).
729- From (goqu .L ("validator_dashboard_data_epoch e" )).
730- With ("validators" , goqu .L ("(SELECT validator_index as validator_index, group_id FROM users_val_dashboards_validators WHERE dashboard_id = ?)" , dashboardId .Id )).
731- Where (goqu .L ("e.epoch_timestamp >= fromUnixTimestamp(?)" , utils .EpochToTime (startEpoch ).Unix ()))
751+ Select (goqu .L (`SUM(COALESCE(e.attestations_reward, 0) + COALESCE(e.blocks_cl_reward, 0) + COALESCE(e.sync_reward, 0)) AS cl_rewards` )).
752+ From (dataTable ).
753+ With ("validators" , goqu .Dialect ("postgres" ).
754+ From (goqu .T ("users_val_dashboards_validators" )).
755+ Select (
756+ goqu .I ("validator_index" ),
757+ goqu .I ("group_id" ),
758+ ).
759+ Where (
760+ goqu .I ("dashboard_id" ).Eq (dashboardId .Id ),
761+ goqu .Or (
762+ goqu .I ("group_id" ).In (groupIds ),
763+ goqu .V (requestedAllGroups ),
764+ ),
765+ ),
766+ ).
767+ Where (
768+ dataColumn .Between (goqu .Range (
769+ goqu .L ("fromUnixTimestamp(?)" , afterTs ),
770+ goqu .L ("fromUnixTimestamp(?)" , beforeTs ))),
771+ )
772+
773+ if aggregation == enums .IntervalEpoch {
774+ rewardsDs = rewardsDs .
775+ SelectAppend (goqu .L ("e.epoch" ).As ("epoch_start" )).
776+ SelectAppend (goqu .L ("e.epoch" ).As ("epoch_end" ))
777+ } else {
778+ rewardsDs = rewardsDs .
779+ SelectAppend (goqu .L ("epoch_start" )).
780+ SelectAppend (goqu .L ("epoch_end" ))
781+ }
732782
783+ // EL
733784 elDs := goqu .Dialect ("postgres" ).
734785 Select (
735- goqu .L ("b.epoch" ),
786+ goqu .L ("epoch_start" ),
787+ // goqu.L("epoch_end"), not needed
736788 goqu .L ("SUM(COALESCE(rb.value, ep.fee_recipient_reward * 1e18, 0)) AS el_rewards" )).
737789 From (goqu .L ("users_val_dashboards_validators v" )).
738- LeftJoin (goqu .L ("blocks b" ), goqu .On (goqu .L ("v.validator_index = b.proposer AND b.status = '1'" ))).
790+ RightJoin (goqu .L ("blocks b" ), goqu .On (goqu .L ("v.validator_index = b.proposer AND b.status = '1'" ))).
739791 LeftJoin (goqu .L ("execution_payloads ep" ), goqu .On (goqu .L ("ep.block_hash = b.exec_block_hash" ))).
740792 LeftJoin (
741793 goqu .Lateral (goqu .Dialect ("postgres" ).
@@ -746,8 +798,16 @@ func (d *DataAccessService) GetValidatorDashboardRewardsChart(ctx context.Contex
746798 Where (goqu .L ("relays_blocks.exec_block_hash = b.exec_block_hash" )).
747799 GroupBy ("exec_block_hash" )).As ("rb" ),
748800 goqu .On (goqu .L ("rb.exec_block_hash = b.exec_block_hash" )),
749- ).
750- Where (goqu .L ("b.epoch >= ?" , startEpoch ))
801+ )
802+
803+ // grouping, ordering
804+ rewardsDs = rewardsDs .
805+ GroupBy (goqu .L ("epoch_start, epoch_end" )).
806+ Order (goqu .L ("epoch_start" ).Asc ())
807+
808+ elDs = elDs .
809+ GroupBy (goqu .L ("epoch_start, epoch_end" )).
810+ Order (goqu .L ("epoch_start" ).Asc ())
751811
752812 if dashboardId .Validators == nil {
753813 rewardsDs = rewardsDs .
@@ -758,149 +818,147 @@ func (d *DataAccessService) GetValidatorDashboardRewardsChart(ctx context.Contex
758818
759819 if dashboardId .AggregateGroups {
760820 rewardsDs = rewardsDs .
761- SelectAppend (goqu .L ("?::smallint AS result_group_id" , t .DefaultGroupId )).
762- GroupBy (goqu .L ("e.epoch" )).
763- Order (goqu .L ("e.epoch" ).Asc ())
821+ SelectAppend (goqu .L ("?::smallint AS result_group_id" , t .DefaultGroupId ))
764822 elDs = elDs .
765- SelectAppend (goqu .L ("?::smallint AS result_group_id" , t .DefaultGroupId )).
766- GroupBy (goqu .L ("b.epoch" )).
767- Order (goqu .L ("b.epoch" ).Asc ())
823+ SelectAppend (goqu .L ("?::smallint AS result_group_id" , t .DefaultGroupId ))
768824 } else {
769825 rewardsDs = rewardsDs .
770826 SelectAppend (goqu .L ("v.group_id AS result_group_id" )).
771- GroupBy ( goqu . L ( "e.epoch" ), goqu .L ("result_group_id" )).
772- Order ( goqu . L ( "e.epoch" ). Asc (), goqu .L ("result_group_id" ).Asc ())
827+ GroupByAppend ( goqu .L ("result_group_id" )).
828+ OrderAppend ( goqu .L ("result_group_id" ).Asc ())
773829 elDs = elDs .
774830 SelectAppend (goqu .L ("v.group_id AS result_group_id" )).
775- GroupBy ( goqu . L ( "b.epoch" ), goqu .L ("result_group_id" )).
776- Order ( goqu . L ( "b.epoch" ). Asc (), goqu .L ("result_group_id" ).Asc ())
831+ GroupByAppend ( goqu .L ("result_group_id" )).
832+ OrderAppend ( goqu .L ("result_group_id" ).Asc ())
777833 }
778834 } else {
779835 // In case a list of validators is provided set the group to the default id
780836 rewardsDs = rewardsDs .
781837 SelectAppend (goqu .L ("?::smallint AS result_group_id" , t .DefaultGroupId )).
782- Where (goqu .L ("e.validator_index IN ?" , dashboardId .Validators )).
783- GroupBy (goqu .L ("e.epoch" )).
784- Order (goqu .L ("e.epoch" ).Asc ())
838+ Where (goqu .L ("e.validator_index IN ?" , dashboardId .Validators ))
785839 elDs = elDs .
786840 SelectAppend (goqu .L ("?::smallint AS result_group_id" , t .DefaultGroupId )).
787- Where (goqu .L ("b.proposer = ANY(?)" , pq .Array (dashboardId .Validators ))).
788- GroupBy (goqu .L ("b.epoch" )).
789- Order (goqu .L ("b.epoch" ).Asc ())
841+ Where (goqu .L ("b.proposer = ANY(?)" , pq .Array (dashboardId .Validators )))
790842 }
791843
792844 // ------------------------------------------------------------------------------------------------------------------
793845 // Build the main query and get the data
794846 queryResult := []struct {
795- Epoch uint64 `db:"epoch"`
796- GroupId uint64 `db:"result_group_id"`
797- ClRewards int64 `db:"cl_rewards"`
847+ EpochStart uint64 `db:"epoch_start"`
848+ EpochEnd uint64 `db:"epoch_end"`
849+ GroupId uint64 `db:"result_group_id"`
850+ ClRewards int64 `db:"cl_rewards"`
798851 }{}
799852
800- wg .Go (func () error {
801- query , args , err := rewardsDs .Prepared (true ).ToSQL ()
802- if err != nil {
803- return fmt .Errorf ("error preparing query: %w" , err )
804- }
853+ query , args , err := rewardsDs .Prepared (true ).ToSQL ()
854+ if err != nil {
855+ return nil , fmt .Errorf ("error preparing query: %w" , err )
856+ }
805857
806- err = d .clickhouseReader .SelectContext (ctx , & queryResult , query , args ... )
807- if err != nil {
808- return fmt .Errorf ("error retrieving rewards chart data: %w" , err )
809- }
810- return nil
811- })
858+ err = d .clickhouseReader .SelectContext (ctx , & queryResult , query , args ... )
859+ if err != nil {
860+ return nil , fmt .Errorf ("error retrieving rewards chart data: %w" , err )
861+ }
862+
863+ if len (queryResult ) == 0 {
864+ return ret , nil
865+ }
866+
867+ var epochStarts , epochEnds []uint64
868+ for _ , res := range queryResult {
869+ epochStarts = append (epochStarts , res .EpochStart )
870+ epochEnds = append (epochEnds , res .EpochEnd )
871+ }
872+ elDs = elDs .
873+ With ("epoch_ranges(epoch_start, epoch_end)" , goqu .L ("(SELECT * FROM unnest(?::int[], ?::int[]))" , pq .Array (epochStarts ), pq .Array (epochEnds ))).
874+ InnerJoin (goqu .L ("epoch_ranges" ), goqu .On (goqu .L ("b.epoch BETWEEN epoch_ranges.epoch_start AND epoch_ranges.epoch_end" ))).
875+ Where (goqu .L ("b.epoch BETWEEN ? AND ?" , epochStarts [0 ], epochEnds [len (epochEnds )- 1 ]))
812876
813877 // ------------------------------------------------------------------------------------------------------------------
814878 // Get the EL rewards
815879 elRewards := make (map [uint64 ]map [uint64 ]decimal.Decimal )
816- wg .Go (func () error {
817- elQueryResult := []struct {
818- Epoch uint64 `db:"epoch"`
819- GroupId uint64 `db:"result_group_id"`
820- ElRewards decimal.Decimal `db:"el_rewards"`
821- }{}
822880
823- query , args , err := elDs .Prepared (true ).ToSQL ()
824- if err != nil {
825- return fmt .Errorf ("error preparing query: %w" , err )
826- }
881+ elQueryResult := []struct {
882+ EpochStart uint64 `db:"epoch_start"`
883+ GroupId uint64 `db:"result_group_id"`
884+ ElRewards decimal.Decimal `db:"el_rewards"`
885+ }{}
827886
828- err = d . readerDb . SelectContext ( ctx , & elQueryResult , query , args ... )
829- if err != nil {
830- return fmt .Errorf ("error retrieving el rewards data for rewards chart : %w" , err )
831- }
887+ query , args , err = elDs . Prepared ( true ). ToSQL ( )
888+ if err != nil {
889+ return nil , fmt .Errorf ("error preparing query : %w" , err )
890+ }
832891
833- for _ , entry := range elQueryResult {
834- if _ , ok := elRewards [entry .Epoch ]; ! ok {
835- elRewards [entry .Epoch ] = make (map [uint64 ]decimal.Decimal )
836- }
837- elRewards [entry.Epoch ][entry.GroupId ] = entry .ElRewards
892+ err = d .readerDb .SelectContext (ctx , & elQueryResult , query , args ... )
893+ if err != nil {
894+ return nil , fmt .Errorf ("error retrieving el rewards data for rewards chart: %w" , err )
895+ }
896+
897+ for _ , entry := range elQueryResult {
898+ if _ , ok := elRewards [entry .EpochStart ]; ! ok {
899+ elRewards [entry .EpochStart ] = make (map [uint64 ]decimal.Decimal )
838900 }
839- return nil
840- })
901+ elRewards [entry. EpochStart ][entry. GroupId ] = entry . ElRewards
902+ }
841903
842- err := wg .Wait ()
843904 if err != nil {
844905 return nil , fmt .Errorf ("error retrieving validator dashboard rewards chart data: %w" , err )
845906 }
846907
847908 // ------------------------------------------------------------------------------------------------------------------
848909 // Create a map structure to store the data
849- epochData := make (map [uint64 ]map [uint64 ]t.ClElValue [decimal.Decimal ])
850- epochList := make ([]uint64 , 0 )
910+ epochStartData := make (map [uint64 ]map [int ]t.ClElValue [decimal.Decimal ])
911+ epochStartList := make ([]uint64 , 0 )
851912
852913 for _ , res := range queryResult {
853- if _ , ok := epochData [res .Epoch ]; ! ok {
854- epochData [res .Epoch ] = make (map [uint64 ]t.ClElValue [decimal.Decimal ])
855- epochList = append (epochList , res .Epoch )
914+ if _ , ok := epochStartData [res .EpochStart ]; ! ok {
915+ epochStartData [res .EpochStart ] = make (map [int ]t.ClElValue [decimal.Decimal ])
916+ epochStartList = append (epochStartList , res .EpochStart )
856917 }
857918
858- epochData [res.Epoch ][ res.GroupId ] = t.ClElValue [decimal.Decimal ]{
859- El : elRewards [res.Epoch ][res.GroupId ],
919+ epochStartData [res .EpochStart ][ int ( res .GroupId ) ] = t.ClElValue [decimal.Decimal ]{
920+ El : elRewards [res.EpochStart ][res.GroupId ],
860921 Cl : utils .GWeiToWei (big .NewInt (res .ClRewards )),
861922 }
862923 }
863924
864925 // Get the list of groups
865- // It should be identical for all epochs
866- var groupList []uint64
867- for _ , groupData := range epochData {
926+ // It should be identical for all epochs (in most cases)
927+ var groupList []int
928+ for _ , groupData := range epochStartData {
868929 for groupId := range groupData {
869930 groupList = append (groupList , groupId )
870931 }
871932 break
872933 }
873934 slices .Sort (groupList )
874935
875- // Create the result
876- var result t.ChartData [int , decimal.Decimal ]
877-
878936 // Create the series structure
879937 propertyNames := []string {"el" , "cl" }
880938 for _ , groupId := range groupList {
881939 for _ , propertyName := range propertyNames {
882- result .Series = append (result .Series , t.ChartSeries [int , decimal.Decimal ]{
883- Id : int ( groupId ) ,
940+ ret .Series = append (ret .Series , t.ChartSeries [int , decimal.Decimal ]{
941+ Id : groupId ,
884942 Property : propertyName ,
885943 })
886944 }
887945 }
888946
889947 // Fill the epoch data
890- for _ , epoch := range epochList {
891- result .Categories = append (result .Categories , epoch )
892- for idx , series := range result .Series {
948+ for _ , epoch := range epochStartList {
949+ ret .Categories = append (ret .Categories , uint64 ( utils . EpochToTime ( epoch ). Unix ()) )
950+ for idx , series := range ret .Series {
893951 if series .Property == "el" {
894- result .Series [idx ].Data = append (result .Series [idx ].Data , epochData [epoch ][uint64 ( series .Id ) ].El )
952+ ret .Series [idx ].Data = append (ret .Series [idx ].Data , epochStartData [epoch ][series.Id ].El )
895953 } else if series .Property == "cl" {
896- result .Series [idx ].Data = append (result .Series [idx ].Data , epochData [epoch ][uint64 ( series .Id ) ].Cl )
954+ ret .Series [idx ].Data = append (ret .Series [idx ].Data , epochStartData [epoch ][series.Id ].Cl )
897955 } else {
898956 return nil , fmt .Errorf ("unknown series property: %s" , series .Property )
899957 }
900958 }
901959 }
902960
903- return & result , nil
961+ return ret , nil
904962}
905963
906964func (d * DataAccessService ) GetValidatorDashboardDuties (ctx context.Context , dashboardId t.VDBId , epoch uint64 , groupId int64 , cursor string , colSort t.Sort [enums.VDBDutiesColumn ], search string , limit uint64 , protocolModes t.VDBProtocolModes ) ([]t.VDBEpochDutiesTableRow , * t.Paging , error ) {
0 commit comments