From 9ed7b2aad331af596f92878275e5a5c9dc007bcb Mon Sep 17 00:00:00 2001 From: nymkappa Date: Wed, 6 Jul 2022 21:03:55 +0200 Subject: [PATCH] Add hashrate & difficulty chart resolution scaling --- backend/src/api/mining.ts | 19 +++++++-- backend/src/index.ts | 1 + backend/src/repositories/BlocksRepository.ts | 2 +- .../DifficultyAdjustmentsRepository.ts | 9 +++- .../src/repositories/HashratesRepository.ts | 6 ++- .../hashrate-chart.component.ts | 41 ++++++++++++------- .../mining-dashboard.component.html | 2 +- 7 files changed, 58 insertions(+), 22 deletions(-) diff --git a/backend/src/api/mining.ts b/backend/src/api/mining.ts index 672f0970f..a198688a7 100644 --- a/backend/src/api/mining.ts +++ b/backend/src/api/mining.ts @@ -8,6 +8,7 @@ import { Common } from './common'; import loadingIndicators from './loading-indicators'; import { escape } from 'mysql2'; import DifficultyAdjustmentsRepository from '../repositories/DifficultyAdjustmentsRepository'; +import config from '../config'; class Mining { constructor() { @@ -302,7 +303,7 @@ class Mining { while (toTimestamp > genesisTimestamp) { const fromTimestamp = toTimestamp - 86400000; - // Skip already indexed weeks + // Skip already indexed days if (indexedTimestamp.includes(toTimestamp / 1000)) { toTimestamp -= 86400000; ++totalIndexed; @@ -313,7 +314,7 @@ class Mining { // we are currently indexing has complete data) const blockStatsPreviousDay: any = await BlocksRepository.$blockCountBetweenTimestamp( null, (fromTimestamp - 86400000) / 1000, (toTimestamp - 86400000) / 1000); - if (blockStatsPreviousDay.blockCount === 0) { // We are done indexing + if (blockStatsPreviousDay.blockCount === 0 && config.MEMPOOL.NETWORK === 'mainnet') { // We are done indexing break; } @@ -357,9 +358,10 @@ class Mining { // Add genesis block manually if (toTimestamp <= genesisTimestamp && !indexedTimestamp.includes(genesisTimestamp)) { hashrates.push({ - hashrateTimestamp: genesisTimestamp, + hashrateTimestamp: genesisTimestamp / 1000, avgHashrate: await bitcoinClient.getNetworkHashPs(1, 1), - poolId: null, + poolId: 0, + share: 1, type: 'daily', }); } @@ -393,6 +395,15 @@ class Mining { let currentDifficulty = 0; let totalIndexed = 0; + if (indexedHeights[0] === false) { + await DifficultyAdjustmentsRepository.$saveAdjustments({ + time: 1231006505, + height: 0, + difficulty: 1.0, + adjustment: 0.0, + }); + } + for (const block of blocks) { if (block.difficulty !== currentDifficulty) { if (block.height === 0 || indexedHeights[block.height] === true) { // Already indexed diff --git a/backend/src/index.ts b/backend/src/index.ts index 28215945f..b86e45029 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -285,6 +285,7 @@ class Server { .get(config.MEMPOOL.API_URL_PREFIX + 'mining/pool/:slug', routes.$getPool) .get(config.MEMPOOL.API_URL_PREFIX + 'mining/hashrate/pools/:interval', routes.$getPoolsHistoricalHashrate) .get(config.MEMPOOL.API_URL_PREFIX + 'mining/hashrate/:interval', routes.$getHistoricalHashrate) + .get(config.MEMPOOL.API_URL_PREFIX + 'mining/difficulty-adjustments', routes.$getDifficultyAdjustments) .get(config.MEMPOOL.API_URL_PREFIX + 'mining/reward-stats/:blockCount', routes.$getRewardStats) .get(config.MEMPOOL.API_URL_PREFIX + 'mining/blocks/fees/:interval', routes.$getHistoricalBlockFees) .get(config.MEMPOOL.API_URL_PREFIX + 'mining/blocks/rewards/:interval', routes.$getHistoricalBlockRewards) diff --git a/backend/src/repositories/BlocksRepository.ts b/backend/src/repositories/BlocksRepository.ts index e88ac7877..c6b14ff51 100644 --- a/backend/src/repositories/BlocksRepository.ts +++ b/backend/src/repositories/BlocksRepository.ts @@ -436,7 +436,7 @@ class BlocksRepository { } if (blocks[idx].previous_block_hash !== blocks[idx - 1].hash) { - logger.warn(`Chain divergence detected at block ${blocks[idx - 1].height}, re-indexing newer blocks and hashrates`); + logger.warn(`Chain divergence detected at block ${blocks[idx - 1].height}`); await this.$deleteBlocksFrom(blocks[idx - 1].height); await BlocksSummariesRepository.$deleteBlocksFrom(blocks[idx - 1].height); await HashratesRepository.$deleteHashratesFromTimestamp(blocks[idx - 1].timestamp - 604800); diff --git a/backend/src/repositories/DifficultyAdjustmentsRepository.ts b/backend/src/repositories/DifficultyAdjustmentsRepository.ts index 76324b5e6..6952b3be9 100644 --- a/backend/src/repositories/DifficultyAdjustmentsRepository.ts +++ b/backend/src/repositories/DifficultyAdjustmentsRepository.ts @@ -1,4 +1,5 @@ import { Common } from '../api/common'; +import config from '../config'; import DB from '../database'; import logger from '../logger'; import { IndexedDifficultyAdjustment } from '../mempool.interfaces'; @@ -31,13 +32,19 @@ class DifficultyAdjustmentsRepository { public async $getAdjustments(interval: string | null, descOrder: boolean = false): Promise { interval = Common.getSqlInterval(interval); - let query = `SELECT UNIX_TIMESTAMP(time) as time, height, difficulty, adjustment + let query = `SELECT + CAST(AVG(UNIX_TIMESTAMP(time)) as INT) as time, + CAST(AVG(height) AS INT) as height, + CAST(AVG(difficulty) as DOUBLE) as difficulty, + CAST(AVG(adjustment) as DOUBLE) as adjustment FROM difficulty_adjustments`; if (interval) { query += ` WHERE time BETWEEN DATE_SUB(NOW(), INTERVAL ${interval}) AND NOW()`; } + query += ` GROUP BY UNIX_TIMESTAMP(time) DIV ${86400}`; + if (descOrder === true) { query += ` ORDER BY time DESC`; } else { diff --git a/backend/src/repositories/HashratesRepository.ts b/backend/src/repositories/HashratesRepository.ts index 531b6cdcf..09dbc720f 100644 --- a/backend/src/repositories/HashratesRepository.ts +++ b/backend/src/repositories/HashratesRepository.ts @@ -1,5 +1,6 @@ import { escape } from 'mysql2'; import { Common } from '../api/common'; +import config from '../config'; import DB from '../database'; import logger from '../logger'; import PoolsRepository from './PoolsRepository'; @@ -32,7 +33,9 @@ class HashratesRepository { public async $getNetworkDailyHashrate(interval: string | null): Promise { interval = Common.getSqlInterval(interval); - let query = `SELECT UNIX_TIMESTAMP(hashrate_timestamp) as timestamp, avg_hashrate as avgHashrate + let query = `SELECT + CAST(AVG(UNIX_TIMESTAMP(hashrate_timestamp)) as INT) as timestamp, + CAST(AVG(avg_hashrate) as DOUBLE) as avgHashrate FROM hashrates`; if (interval) { @@ -42,6 +45,7 @@ class HashratesRepository { query += ` WHERE hashrates.type = 'daily'`; } + query += ` GROUP BY UNIX_TIMESTAMP(hashrate_timestamp) DIV ${86400}`; query += ` ORDER by hashrate_timestamp`; try { diff --git a/frontend/src/app/components/hashrate-chart/hashrate-chart.component.ts b/frontend/src/app/components/hashrate-chart/hashrate-chart.component.ts index 1ee3c20c0..807aad436 100644 --- a/frontend/src/app/components/hashrate-chart/hashrate-chart.component.ts +++ b/frontend/src/app/components/hashrate-chart/hashrate-chart.component.ts @@ -11,6 +11,7 @@ import { StorageService } from 'src/app/services/storage.service'; import { MiningService } from 'src/app/services/mining.service'; import { download } from 'src/app/shared/graphs.utils'; import { ActivatedRoute } from '@angular/router'; +import { StateService } from 'src/app/services/state.service'; @Component({ selector: 'app-hashrate-chart', @@ -47,7 +48,7 @@ export class HashrateChartComponent implements OnInit { formatNumber = formatNumber; timespan = ''; chartInstance: any = undefined; - maResolution: number = 30; + network = ''; constructor( @Inject(LOCALE_ID) public locale: string, @@ -57,10 +58,13 @@ export class HashrateChartComponent implements OnInit { private storageService: StorageService, private miningService: MiningService, private route: ActivatedRoute, + private stateService: StateService ) { } ngOnInit(): void { + this.stateService.networkChanged$.subscribe((network) => this.network = network); + let firstRun = true; if (this.widget) { @@ -124,17 +128,14 @@ export class HashrateChartComponent implements OnInit { ++diffIndex; } - this.maResolution = 30; - if (["3m", "6m"].includes(this.timespan)) { - this.maResolution = 7; - } + let maResolution = 15; const hashrateMa = []; - for (let i = this.maResolution - 1; i < data.hashrates.length; ++i) { + for (let i = maResolution - 1; i < data.hashrates.length; ++i) { let avg = 0; - for (let y = this.maResolution - 1; y >= 0; --y) { + for (let y = maResolution - 1; y >= 0; --y) { avg += data.hashrates[i - y].avgHashrate; } - avg /= this.maResolution; + avg /= maResolution; hashrateMa.push([data.hashrates[i].timestamp * 1000, avg]); } @@ -276,17 +277,17 @@ export class HashrateChartComponent implements OnInit { }, }, { - name: $localize`::Difficulty`, + name: $localize`:@@25148835d92465353fc5fe8897c27d5369978e5a:Difficulty`, inactiveColor: 'rgb(110, 112, 121)', - textStyle: { + textStyle: { color: 'white', }, icon: 'roundRect', }, { - name: $localize`Hashrate` + ` (MA${this.maResolution})`, + name: $localize`Hashrate (MA)`, inactiveColor: 'rgb(110, 112, 121)', - textStyle: { + textStyle: { color: 'white', }, icon: 'roundRect', @@ -295,11 +296,19 @@ export class HashrateChartComponent implements OnInit { }, }, ], + selected: JSON.parse(this.storageService.getValue('hashrate_difficulty_legend')) ?? { + '$localize`:@@79a9dc5b1caca3cbeb1733a19515edacc5fc7920:Hashrate`': true, + '$localize`::Difficulty`': this.network === '', + '$localize`Hashrate (MA)`': true, + }, }, yAxis: data.hashrates.length === 0 ? undefined : [ { min: (value) => { - return value.min * 0.9; + const selectedPowerOfTen: any = selectPowerOfTen(value.min); + const newMin = Math.floor(value.min / selectedPowerOfTen.divider / 10); + console.log(newMin); + return newMin * selectedPowerOfTen.divider * 10; }, type: 'value', axisLabel: { @@ -363,7 +372,7 @@ export class HashrateChartComponent implements OnInit { }, { zlevel: 2, - name: $localize`Hashrate` + ` (MA${this.maResolution})`, + name: $localize`Hashrate (MA)`, showSymbol: false, symbol: 'none', data: data.hashrateMa, @@ -404,6 +413,10 @@ export class HashrateChartComponent implements OnInit { onChartInit(ec) { this.chartInstance = ec; + + this.chartInstance.on('legendselectchanged', (e) => { + this.storageService.setValue('hashrate_difficulty_legend', JSON.stringify(e.selected)); + }); } isMobile() { diff --git a/frontend/src/app/components/mining-dashboard/mining-dashboard.component.html b/frontend/src/app/components/mining-dashboard/mining-dashboard.component.html index b0ec46ac5..5549698df 100644 --- a/frontend/src/app/components/mining-dashboard/mining-dashboard.component.html +++ b/frontend/src/app/components/mining-dashboard/mining-dashboard.component.html @@ -40,7 +40,7 @@