diff --git a/cmd/dcrdata/internal/explorer/templates.go b/cmd/dcrdata/internal/explorer/templates.go index f0342b7af..ac2236648 100644 --- a/cmd/dcrdata/internal/explorer/templates.go +++ b/cmd/dcrdata/internal/explorer/templates.go @@ -35,6 +35,11 @@ type templates struct { exec func(string, interface{}) (string, error) } +type hashrateDisplay struct { + Parts []string + Unit string +} + func newTemplates(folder string, reload bool, common []string, helpers template.FuncMap) templates { com := make([]string, 0, len(common)) for _, file := range common { @@ -210,6 +215,34 @@ func amountAsDecimalPartsTrimmed(v, numPlaces int64, useCommas bool) []string { return []string{left, right, tail} } +func hashRateFormatting(phRate float64) hashrateDisplay { + units := []struct { + label string + multiplier float64 + }{ + {label: "Th/s", multiplier: 1}, + {label: "Ph/s", multiplier: 1e-3}, + {label: "Eh/s", multiplier: 1e-6}, + } + + thRate := phRate * 1e3 + unitIndex := 0 + if thRate > 0 { + unitIndex = int(math.Floor(math.Log10(thRate) / 3)) + if unitIndex < 0 { + unitIndex = 0 + } else if unitIndex >= len(units) { + unitIndex = len(units) - 1 + } + } + + value := thRate * units[unitIndex].multiplier + return hashrateDisplay{ + Parts: float64Formatting(value, 8, true, 2), + Unit: units[unitIndex].label, + } +} + // threeSigFigs returns a representation of the float formatted to three // significant figures, with an appropriate magnitude prefix (k, M, B). // For (k, M, G) prefixes for file/memory sizes, use humanize.Bytes. @@ -406,6 +439,7 @@ func makeTemplateFuncMap(params *chaincfg.Params) template.FuncMap { return p }, "float64AsDecimalParts": float64Formatting, + "hashRateFormatting": hashRateFormatting, "amountAsDecimalParts": func(v int64, useCommas bool) []string { return float64Formatting(dcrutil.Amount(v).ToCoin(), 8, useCommas) }, diff --git a/cmd/dcrdata/public/js/controllers/charts_controller.js b/cmd/dcrdata/public/js/controllers/charts_controller.js index fa9f774ee..9f55f01cc 100644 --- a/cmd/dcrdata/public/js/controllers/charts_controller.js +++ b/cmd/dcrdata/public/js/controllers/charts_controller.js @@ -25,7 +25,6 @@ const multiYAxisChart = ['ticket-price', 'coin-supply', 'privacy-participation'] // index 0 represents y1 and 1 represents y2 axes. const yValueRanges = { 'ticket-price': [1] } const chainworkUnits = ['exahash', 'zettahash', 'yottahash'] -const hashrateUnits = ['Th/s', 'Ph/s', 'Eh/s'] let ticketPoolSizeTarget, premine, stakeValHeight, stakeShare let baseSubsidy, subsidyInterval, subsidyExponent, windowSize, avgBlockTime let rawCoinSupply, rawPoolValue @@ -626,10 +625,13 @@ export default class extends Controller { break case 'hashrate': // Total chainwork over time - d = zip2D(data, data.rate, 1e-3, data.offset) - assign(gOptions, mapDygraphOptions(d, [xlabel, 'Network Hashrate (petahash/s)'], - false, 'Network Hashrate (petahash/s)', true, false)) - yFormatter = customYFormatter(y => withBigUnits(y * 1e3, hashrateUnits)) + { + const unit = humanize.hashrateUnit(data.rate, 'Th/s') + d = zip2D(data, data.rate, unit.multiplier, data.offset) + assign(gOptions, mapDygraphOptions(d, [xlabel, `Network Hashrate (${unit.label})`], + false, `Network Hashrate (${unit.description})`, true, false)) + yFormatter = customYFormatter(y => `${y.toFixed(3)} ${unit.label}`) + } break case 'missed-votes': diff --git a/cmd/dcrdata/public/js/controllers/homepage_controller.js b/cmd/dcrdata/public/js/controllers/homepage_controller.js index 02f117801..1ba95ce71 100644 --- a/cmd/dcrdata/public/js/controllers/homepage_controller.js +++ b/cmd/dcrdata/public/js/controllers/homepage_controller.js @@ -39,7 +39,7 @@ export default class extends Controller { 'bsubsidyTotal', 'bsubsidyPow', 'bsubsidyPos', 'bsubsidyDev', 'coinSupply', 'blocksdiff', 'devFund', 'windowIndex', 'posBar', 'rewardIdx', 'powBar', 'poolSize', 'poolValue', 'ticketReward', - 'targetPct', 'poolSizePct', 'hashrate', 'hashrateDelta', + 'targetPct', 'poolSizePct', 'hashrate', 'hashrateUnit', 'hashrateDelta', 'nextExpectedSdiff', 'nextExpectedMin', 'nextExpectedMax', 'mempool', 'mpRegTotal', 'mpRegCount', 'mpTicketTotal', 'mpTicketCount', 'mpVoteTotal', 'mpVoteCount', 'mpRevTotal', 'mpRevCount', 'mpRegBar', 'mpVoteBar', 'mpTicketBar', @@ -188,7 +188,9 @@ export default class extends Controller { this.poolSizePctTarget.textContent = parseFloat(ex.pool_info.percent).toFixed(2) const treasuryTotal = ex.dev_fund + ex.treasury_bal.balance this.devFundTarget.innerHTML = humanize.decimalParts(treasuryTotal / 100000000, true, 0) - this.hashrateTarget.innerHTML = humanize.decimalParts(ex.hash_rate, false, 8, 2) + const hashrate = humanize.hashrate(ex.hash_rate, 'Ph/s') + this.hashrateTarget.innerHTML = humanize.decimalParts(hashrate.value, false, 8, 2) + this.hashrateUnitTarget.textContent = hashrate.unit this.hashrateDeltaTarget.innerHTML = humanize.fmtPercentage(ex.hash_rate_change_month) this.blockVotesTarget.dataset.hash = blockData.block.hash this.setVotes() diff --git a/cmd/dcrdata/public/js/helpers/humanize_helper.js b/cmd/dcrdata/public/js/helpers/humanize_helper.js index c25397ce8..b29909947 100644 --- a/cmd/dcrdata/public/js/helpers/humanize_helper.js +++ b/cmd/dcrdata/public/js/helpers/humanize_helper.js @@ -8,6 +8,12 @@ function round (value, precision) { return Math.round(value * multiplier) / multiplier } +const hashrateUnits = [ + { label: 'Th/s', description: 'terahash/s', multiplier: 1 }, + { label: 'Ph/s', description: 'petahash/s', multiplier: 1e-3 }, + { label: 'Eh/s', description: 'exahash/s', multiplier: 1e-6 } +] + function hashParts (hash) { const clipLen = 6 const hashLen = hash.length - clipLen @@ -73,6 +79,32 @@ const humanize = { return htmlString }, + hashrateUnit: function (rates, sourceUnit) { + let maxRate = 0 + const sourceMultiplier = sourceUnit === 'Ph/s' ? 1e3 : sourceUnit === 'Eh/s' ? 1e6 : 1 + if (!Array.isArray(rates)) rates = [rates] + for (const rate of rates) { + const thRate = rate * sourceMultiplier + if (thRate > maxRate) maxRate = thRate + } + if (!Number.isFinite(maxRate) || maxRate === 0) return hashrateUnits[0] + + const unitIndex = Math.min( + Math.max(Math.floor(Math.log10(maxRate) / 3), 0), + hashrateUnits.length - 1 + ) + return hashrateUnits[unitIndex] + }, + hashrate: function (rate, sourceUnit) { + const sourceMultiplier = sourceUnit === 'Ph/s' ? 1e3 : sourceUnit === 'Eh/s' ? 1e6 : 1 + const thRate = rate * sourceMultiplier + const unit = this.hashrateUnit(rate, sourceUnit) + return { + value: thRate * unit.multiplier, + unit: unit.label, + description: unit.description + } + }, threeSigFigs: function (v) { const sign = v >= 0 ? '' : '-' v = Math.abs(v) diff --git a/cmd/dcrdata/views/home.tmpl b/cmd/dcrdata/views/home.tmpl index a25c8bcc1..67dcadedc 100644 --- a/cmd/dcrdata/views/home.tmpl +++ b/cmd/dcrdata/views/home.tmpl @@ -314,9 +314,10 @@