Skip to content

Commit

Permalink
(BIDS-2495) refactor income charts (#2565)
Browse files Browse the repository at this point in the history
  • Loading branch information
LuccaBitfly authored Sep 25, 2023
1 parent a0a6efb commit 30c4c56
Show file tree
Hide file tree
Showing 7 changed files with 198 additions and 316 deletions.
58 changes: 58 additions & 0 deletions handlers/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"math"
"math/big"
"net/http"
"sort"
"strconv"
"strings"
"syscall"
Expand Down Expand Up @@ -727,3 +728,60 @@ func GetWithdrawableCountFromCursor(epoch uint64, validatorindex uint64, cursor
return 0, nil
}
}

func getExecutionChartData(indices []uint64, currency string, lowerBoundDay uint64) ([]*types.ChartDataPoint, error) {
var limit uint64 = 300
blockList, consMap, err := findExecBlockNumbersByProposerIndex(indices, 0, limit, false, true, lowerBoundDay)
if err != nil {
return nil, err
}

blocks, err := db.BigtableClient.GetBlocksIndexedMultiple(blockList, limit)
if err != nil {
return nil, err
}
relaysData, err := db.GetRelayDataForIndexedBlocks(blocks)
if err != nil {
return nil, err
}

var chartData = []*types.ChartDataPoint{}
epochsPerDay := utils.EpochsPerDay()
color := "#90ed7d"

// Map to keep track of the cumulative reward for each day
dayRewardMap := make(map[int64]float64)

for _, block := range blocks {
consData := consMap[block.Number]
day := int64(consData.Epoch / epochsPerDay)

var totalReward float64
if relayData, ok := relaysData[common.BytesToHash(block.Hash)]; ok {
totalReward = utils.WeiToEther(relayData.MevBribe.BigInt()).InexactFloat64()
} else {
totalReward = utils.WeiToEther(utils.Eth1TotalReward(block)).InexactFloat64()
}

// Add the reward to the existing reward for the day or set it if not previously set
dayRewardMap[day] += totalReward
}

// Now populate the chartData array using the dayRewardMap
exchangeRate := utils.ExchangeRateForCurrency(currency)
for day, reward := range dayRewardMap {
ts := float64(utils.DayToTime(day).Unix() * 1000)
chartData = append(chartData, &types.ChartDataPoint{
X: ts,
Y: exchangeRate * reward,
Color: color,
})
}

// If needed, sort chartData based on X values
sort.Slice(chartData, func(i, j int) bool {
return chartData[i].X < chartData[j].X
})

return chartData, nil
}
51 changes: 0 additions & 51 deletions handlers/dashboard.go
Original file line number Diff line number Diff line change
Expand Up @@ -509,57 +509,6 @@ func DashboardDataBalanceCombined(w http.ResponseWriter, r *http.Request) {
}
}

func getExecutionChartData(indices []uint64, currency string, lowerBoundDay uint64) ([]*types.ChartDataPoint, error) {
var limit uint64 = 300
blockList, consMap, err := findExecBlockNumbersByProposerIndex(indices, 0, limit, false, true, lowerBoundDay)
if err != nil {
return nil, err
}

blocks, err := db.BigtableClient.GetBlocksIndexedMultiple(blockList, limit)
if err != nil {
return nil, err
}
relaysData, err := db.GetRelayDataForIndexedBlocks(blocks)
if err != nil {
return nil, err
}

var chartData = make([]*types.ChartDataPoint, len(blocks))
epochsPerDay := utils.EpochsPerDay()
lastFinalizedEpoch := services.LatestFinalizedEpoch()
color := "#90ed7d"

for i := len(blocks) - 1; i >= 0; i-- {
blockEpoch := utils.TimeToEpoch(blocks[i].Time.AsTime())
consData := consMap[blocks[i].Number]
day := int64(consData.Epoch / epochsPerDay)
ts := float64(utils.DayToTime(day).Unix() * 1000)
if blockEpoch > int64(lastFinalizedEpoch) {
// we need to fill also the first items in the list, otherwise the charts break
chartData[len(blocks)-1-i] = &types.ChartDataPoint{
X: ts,
Y: 0,
Color: color,
}
continue
}
var totalReward float64
if relayData, ok := relaysData[common.BytesToHash(blocks[i].Hash)]; ok {
totalReward = utils.WeiToEther(relayData.MevBribe.BigInt()).InexactFloat64()
} else {
totalReward = utils.WeiToEther(utils.Eth1TotalReward(blocks[i])).InexactFloat64()
}

chartData[len(blocks)-1-i] = &types.ChartDataPoint{
X: ts,
Y: utils.ExchangeRateForCurrency(currency) * totalReward,
Color: color,
}
}
return chartData, nil
}

// DashboardDataBalance retrieves the income history of a set of validators
func DashboardDataBalance(w http.ResponseWriter, r *http.Request) {
currency := GetCurrency(r)
Expand Down
132 changes: 2 additions & 130 deletions static/js/dashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -1381,136 +1381,8 @@ $(document).ready(function () {

function createIncomeChart(income, executionIncomeHistory) {
executionIncomeHistory = executionIncomeHistory || []
incomeChart = Highcharts.stockChart("balance-chart", {
colors: ["#90ed7d", "#7cb5ec"],
exporting: {
scale: 1,
},
rangeSelector: {
enabled: false,
},
chart: {
type: "column",
height: "627px",
pointInterval: 24 * 3600 * 1000,
events: {
load: function () {
$("#load-income-btn").removeClass("d-none")
},
},
},
credits: {
enabled: false,
},
legend: {
enabled: true,
},
title: {
text: "Daily Income for all Validators",
},
navigator: {
series: {
data: income,
color: "#7cb5ec",
},
},
plotOptions: {
column: {
stacking: "stacked",
dataLabels: {
enabled: false,
},
pointInterval: 24 * 3600 * 1000,
},
series: {
turboThreshold: 10000,
},
},
xAxis: {
type: "datetime",
range: 31 * 24 * 60 * 60 * 1000,
labels: {
formatter: function () {
var epoch = timeToEpoch(this.value)
var orig = this.axis.defaultLabelFormatter.call(this)
return `${orig}<br/>Epoch ${epoch}`
},
},
},
tooltip: {
split: false,
shared: true,
formatter: (tooltip) => {
var text = ``
var total = 0

// date and epochs
const startEpoch = timeToEpoch(tooltip.chart.hoverPoint.x)
const timeForOneDay = 24 * 60 * 60 * 1000
const endEpoch = timeToEpoch(tooltip.chart.hoverPoint.x + timeForOneDay) - 1
const startDate = luxon.DateTime.fromMillis(tooltip.chart.hoverPoints[0].x)
const endDate = luxon.DateTime.fromMillis(epochToTime(endEpoch + 1))
text += `${startDate.toFormat("MMM-dd-yyyy HH:mm:ss")} - ${endDate.toFormat("MMM-dd-yyyy HH:mm:ss")}<br> Epochs ${startEpoch} - ${endEpoch}<br/>`

// income
for (var i = 0; i < tooltip.chart.hoverPoints.length; i++) {
const value = tooltip.chart.hoverPoints[i].y
text += `<span style="color:${tooltip.chart.hoverPoints[i].series.color}">\u25CF</span> <b>${tooltip.chart.hoverPoints[i].series.name}:</b> ${getIncomeChartValueString(value, currency, 1)}<br/>`
total += value
}

// add total if hovered point contains rewards for both EL and CL
if (tooltip.chart.hoverPoints.length > 1) {
text += `<b>Total:</b> ${getIncomeChartValueString(total, currency, 1)}`
}

return text
},
},
yAxis: [
{
title: {
text: "Income [" + currency + "]",
},
opposite: false,
labels: {
formatter: function () {
if (currency !== "ETH") {
return this.value.toFixed(2)
}
return this.value.toFixed(5)
},
},
},
],
series: [
{
name: "Daily Execution Income",
data: executionIncomeHistory,
},
{
name: "Daily Consensus Income",
data: income,
},
],
responsive: {
rules: [
{
condition: {
callback: function () {
return window.innerWidth >= 820
},
},
chartOptions: {
legend: {
itemMarginTop: 7,
itemMarginBottom: -7,
},
},
},
],
},
})
const incomeChartOptions = getIncomeChartOptions(income, executionIncomeHistory, "Daily Income for all Validators", 627, currency)
incomeChart = Highcharts.stockChart("balance-chart", incomeChartOptions)
}

function createProposedChart(data) {
Expand Down
132 changes: 132 additions & 0 deletions static/js/income_chart_options.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
function getIncomeChartOptions(incomeHistory, executionIncomeHistory, title, height, currency) {
return {
colors: ["#90ed7d", "#7cb5ec"],
exporting: {
scale: 1,
},
rangeSelector: {
enabled: false,
},
chart: {
type: "column",
height: `${height}px"`,
pointInterval: 24 * 3600 * 1000,
events: {
load: function () {
$("#load-income-btn").removeClass("d-none")
},
},
},
title: {
text: title,
},
credits: {
enabled: false,
},
legend: {
enabled: true,
},
plotOptions: {
column: {
stacking: "stacked",
dataLabels: {
enabled: false,
},
pointInterval: 24 * 3600 * 1000,
},
series: {
turboThreshold: 10000,
},
},
xAxis: {
type: "datetime",
range: 31 * 24 * 60 * 60 * 1000,
labels: {
formatter: function () {
var epoch = timeToEpoch(this.value)
var orig = this.axis.defaultLabelFormatter.call(this)
return `${orig}<br/>Epoch ${epoch}`
},
},
},
yAxis: [
{
title: {
text: `Income [${currency}]`,
},
opposite: false,
labels: {
formatter: function () {
return currency === "ETH" ? trimToken(this.value) : trimCurrency(this.value)
},
},
},
],
series: [
{
name: "Execution Income",
data: executionIncomeHistory,
showInNavigator: false,
dataGrouping: {
enabled: false,
},
},
{
name: "Consensus Income",
data: incomeHistory,
showInNavigator: true,
dataGrouping: {
enabled: false,
},
},
],
tooltip: {
split: false,
shared: true,
formatter: (tooltip) => {
var text = ``
var total = 0

// time range for hovered point
const ts = tooltip.chart.hoverPoints[0].x
const startEpoch = timeToEpoch(ts)
const timeForOneDay = 24 * 60 * 60 * 1000
const endEpoch = timeToEpoch(ts + timeForOneDay) - 1
const startDate = luxon.DateTime.fromMillis(ts)
const endDate = luxon.DateTime.fromMillis(epochToTime(endEpoch + 1))
text += `${startDate.toFormat("MMM-dd-yyyy HH:mm:ss")} - ${endDate.toFormat("MMM-dd-yyyy HH:mm:ss")}<br> Epochs ${startEpoch} - ${endEpoch}<br/>`

// income
for (var i = 0; i < tooltip.chart.hoverPoints.length; i++) {
const value = tooltip.chart.hoverPoints[i].y
text += `<span style="color:${tooltip.chart.hoverPoints[i].series.color}">\u25CF</span> <b>${tooltip.chart.hoverPoints[i].series.name}:</b> ${getIncomeChartValueString(value, currency)}<br/>`
total += value
}

// add total if hovered point contains rewards for both EL and CL
if (tooltip.chart.hoverPoints.length > 1) {
text += `<b>Total:</b> ${getIncomeChartValueString(total, currency)}`
}

return text
},
},
responsive: {
rules: [
{
condition: {
callback: function () {
return window.innerWidth >= 820
},
},
chartOptions: {
legend: {
itemMarginTop: 7,
itemMarginBottom: -7,
},
},
},
],
},
}
}
Loading

0 comments on commit 30c4c56

Please sign in to comment.