diff --git a/backend/src/api/database-migration.ts b/backend/src/api/database-migration.ts index 154068164..098394664 100644 --- a/backend/src/api/database-migration.ts +++ b/backend/src/api/database-migration.ts @@ -261,6 +261,10 @@ class DatabaseMigration { } } + if (version < 7) { + queries.push(`INSERT INTO state(name, number, string) VALUES ('last_hashrates_indexing', 0, NULL)`); + } + return queries; } diff --git a/backend/src/api/mining.ts b/backend/src/api/mining.ts index 6f90ab357..792e93f45 100644 --- a/backend/src/api/mining.ts +++ b/backend/src/api/mining.ts @@ -14,7 +14,7 @@ class Mining { /** * Generate high level overview of the pool ranks and general stats */ - public async $getPoolsStats(interval: string | null) : Promise { + public async $getPoolsStats(interval: string | null): Promise { const poolsStatistics = {}; const poolsInfo: PoolInfo[] = await PoolsRepository.$getPoolsInfo(interval); @@ -30,8 +30,8 @@ class Mining { link: poolInfo.link, blockCount: poolInfo.blockCount, rank: rank++, - emptyBlocks: 0, - } + emptyBlocks: 0 + }; for (let i = 0; i < emptyBlocks.length; ++i) { if (emptyBlocks[i].poolId === poolInfo.poolId) { poolStat.emptyBlocks++; @@ -84,32 +84,41 @@ class Mining { return { adjustments: difficultyAdjustments, oldestIndexedBlockTimestamp: oldestBlock.getTime(), - } + }; } /** * Return the historical hashrates and oldest indexed block timestamp */ - public async $getHistoricalHashrates(interval: string | null): Promise { + public async $getHistoricalHashrates(interval: string | null): Promise { const hashrates = await HashratesRepository.$get(interval); const oldestBlock = new Date(await BlocksRepository.$oldestBlockTimestamp()); return { hashrates: hashrates, oldestIndexedBlockTimestamp: oldestBlock.getTime(), - } + }; } /** - * + * Generate daily hashrate data */ - public async $generateNetworkHashrateHistory() : Promise { + public async $generateNetworkHashrateHistory(): Promise { + // We only run this once a day + const latestTimestamp = await HashratesRepository.$getLatestRunTimestamp(); + const now = new Date().getTime() / 1000; + if (now - latestTimestamp < 86400) { + return; + } + + logger.info(`Indexing hashrates`); + if (this.hashrateIndexingStarted) { return; } this.hashrateIndexingStarted = true; - const totalIndexed = await BlocksRepository.$blockCount(null, null); + const oldestIndexedBlockHeight = await BlocksRepository.$getOldestIndexedBlockHeight(); const indexedTimestamp = (await HashratesRepository.$get(null)).map(hashrate => hashrate.timestamp); const genesisTimestamp = 1231006505; // bitcoin-cli getblock 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f @@ -128,16 +137,18 @@ class Mining { null, fromTimestamp, toTimestamp ); - let lastBlockHashrate = 0; - if (blockStats.blockCount > 0) { - lastBlockHashrate = await bitcoinClient.getNetworkHashPs(blockStats.blockCount, - blockStats.lastBlockHeight); + if (blockStats.blockCount === 0) { // We are done indexing, no blocks left + break; } - if (toTimestamp % 864000 === 0) { - const progress = Math.round((totalIndexed - blockStats.lastBlockHeight) / totalIndexed * 100); + let lastBlockHashrate = 0; + lastBlockHashrate = await bitcoinClient.getNetworkHashPs(blockStats.blockCount, + blockStats.lastBlockHeight); + + if (toTimestamp % 864000 === 0) { // Log every 10 days during initial indexing const formattedDate = new Date(fromTimestamp * 1000).toUTCString(); - logger.debug(`Counting blocks and hashrate for ${formattedDate}. Progress: ${progress}%`); + const blocksLeft = blockStats.lastBlockHeight - oldestIndexedBlockHeight; + logger.debug(`Counting blocks and hashrate for ${formattedDate}. ${blocksLeft} blocks left`); } await HashratesRepository.$saveDailyStat({ @@ -149,6 +160,9 @@ class Mining { toTimestamp -= 86400; } + await HashratesRepository.$setLatestRunTimestamp(); + this.hashrateIndexingStarted = false; + logger.info(`Hashrates indexing completed`); } diff --git a/backend/src/repositories/BlocksRepository.ts b/backend/src/repositories/BlocksRepository.ts index 937320a3a..9c7e9b778 100644 --- a/backend/src/repositories/BlocksRepository.ts +++ b/backend/src/repositories/BlocksRepository.ts @@ -281,6 +281,13 @@ class BlocksRepository { return rows; } + + public async $getOldestIndexedBlockHeight(): Promise { + const connection = await DB.pool.getConnection(); + const [rows]: any[] = await connection.query(`SELECT MIN(height) as minHeight FROM blocks`); + connection.release(); + return rows[0].minHeight; + } } export default new BlocksRepository(); diff --git a/backend/src/repositories/HashratesRepository.ts b/backend/src/repositories/HashratesRepository.ts index 837569cd8..2a898e5bd 100644 --- a/backend/src/repositories/HashratesRepository.ts +++ b/backend/src/repositories/HashratesRepository.ts @@ -43,11 +43,28 @@ class HashratesRepository { query += ` WHERE hashrate_timestamp BETWEEN DATE_SUB(NOW(), INTERVAL ${interval}) AND NOW()`; } + query += ` ORDER by hashrate_timestamp DESC`; + const [rows]: any[] = await connection.query(query); connection.release(); return rows; } + + public async $setLatestRunTimestamp() { + const connection = await DB.pool.getConnection(); + const query = `UPDATE state SET number = ? WHERE name = 'last_hashrates_indexing'`; + await connection.query(query, [Math.round(new Date().getTime() / 1000)]); + connection.release(); + } + + public async $getLatestRunTimestamp(): Promise { + const connection = await DB.pool.getConnection(); + const query = `SELECT number FROM state WHERE name = 'last_hashrates_indexing'`; + const [rows] = await connection.query(query); + connection.release(); + return rows[0]['number']; + } } export default new HashratesRepository(); diff --git a/frontend/src/app/components/difficulty-chart/difficulty-chart.component.ts b/frontend/src/app/components/difficulty-chart/difficulty-chart.component.ts index 350e3c4be..a8865ec09 100644 --- a/frontend/src/app/components/difficulty-chart/difficulty-chart.component.ts +++ b/frontend/src/app/components/difficulty-chart/difficulty-chart.component.ts @@ -134,17 +134,18 @@ export class DifficultyChartComponent implements OnInit { } } }, - series: [ - { - data: data, - type: 'line', - smooth: false, - lineStyle: { - width: 3, - }, - areaStyle: {} + series: { + showSymbol: false, + data: data, + type: 'line', + smooth: false, + lineStyle: { + width: 2, }, - ], + areaStyle: { + opacity: 0.25 + }, + }, }; } diff --git a/frontend/src/app/components/hashrate-chart/hashrate-chart.component.html b/frontend/src/app/components/hashrate-chart/hashrate-chart.component.html index 04534f176..cfcd15bfe 100644 --- a/frontend/src/app/components/hashrate-chart/hashrate-chart.component.html +++ b/frontend/src/app/components/hashrate-chart/hashrate-chart.component.html @@ -4,50 +4,29 @@
- -
-
+ +
+
-
- - -
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 4739c2c30..8a3413db5 100644 --- a/frontend/src/app/components/hashrate-chart/hashrate-chart.component.ts +++ b/frontend/src/app/components/hashrate-chart/hashrate-chart.component.ts @@ -66,15 +66,15 @@ export class HashrateChartComponent implements OnInit { }; }), ); - }), - share() - ); + }), + share() + ); } prepareChartOptions(data) { this.chartOptions = { title: { - text: this.widget? '' : $localize`:@@mining.hashrate:Hashrate`, + text: this.widget ? '' : $localize`:@@mining.hashrate:Hashrate`, left: 'center', textStyle: { color: '#FFF', @@ -102,7 +102,7 @@ export class HashrateChartComponent implements OnInit { giga: Math.pow(10, 9), mega: Math.pow(10, 6), kilo: Math.pow(10, 3), - } + }; let selectedPowerOfTen = { divider: powerOfTen.exa, unit: 'E' }; if (val < powerOfTen.mega) { @@ -135,9 +135,11 @@ export class HashrateChartComponent implements OnInit { type: 'line', smooth: false, lineStyle: { - width: 3, + width: 2, + }, + areaStyle: { + opacity: 0.25 }, - areaStyle: {}, }, dataZoom: this.widget ? null : [{ type: 'inside',