From 7314582dd191f40714f6469767c34ba8fea4522d Mon Sep 17 00:00:00 2001 From: nymkappa Date: Sun, 6 Mar 2022 12:32:16 +0100 Subject: [PATCH 01/16] Split network daily hashrate indexing and weekly pool hashrate indexing --- backend/src/api/database-migration.ts | 10 +- backend/src/api/mining.ts | 145 +++++++++++++----- backend/src/index.ts | 4 +- .../src/repositories/HashratesRepository.ts | 26 +++- 4 files changed, 139 insertions(+), 46 deletions(-) diff --git a/backend/src/api/database-migration.ts b/backend/src/api/database-migration.ts index 49e9ef9c4..dd5dea399 100644 --- a/backend/src/api/database-migration.ts +++ b/backend/src/api/database-migration.ts @@ -6,7 +6,7 @@ import logger from '../logger'; const sleep = (ms: number) => new Promise(res => setTimeout(res, ms)); class DatabaseMigration { - private static currentVersion = 8; + private static currentVersion = 9; private queryTimeout = 120000; private statisticsAddedIndexed = false; @@ -133,6 +133,10 @@ class DatabaseMigration { await this.$executeQuery(connection, 'ALTER TABLE `hashrates` ADD `type` enum("daily", "weekly") DEFAULT "daily"'); } + if (databaseSchemaVersion < 9) { + await this.$executeQuery(connection, 'ALTER TABLE `state` CHANGE `name` `name` varchar(100)') + } + connection.release(); } catch (e) { connection.release(); @@ -276,6 +280,10 @@ class DatabaseMigration { queries.push(`INSERT INTO state(name, number, string) VALUES ('last_hashrates_indexing', 0, NULL)`); } + if (version < 9) { + queries.push(`INSERT INTO state(name, number, string) VALUES ('last_weekly_hashrates_indexing', 0, NULL)`); + } + return queries; } diff --git a/backend/src/api/mining.ts b/backend/src/api/mining.ts index 1d2f47561..62fa8baca 100644 --- a/backend/src/api/mining.ts +++ b/backend/src/api/mining.ts @@ -8,6 +8,7 @@ import blocks from './blocks'; class Mining { hashrateIndexingStarted = false; + weeklyHashrateIndexingStarted = false; constructor() { } @@ -95,31 +96,112 @@ class Mining { } /** - * Generate daily hashrate data + * Generate weekly mining pool hashrate history */ - 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) { + public async $generatePoolHashrateHistory(): Promise { + if (!blocks.blockIndexingCompleted || this.weeklyHashrateIndexingStarted) { return; } + // We only run this once a week + const latestTimestamp = await HashratesRepository.$getLatestRunTimestamp('last_weekly_hashrates_indexing'); + const now = new Date().getTime() / 1000; + if (now - latestTimestamp < 604800) { + return; + } + + try { + this.weeklyHashrateIndexingStarted = true; + + logger.info(`Indexing mining pools weekly hashrates`); + + const indexedTimestamp = await HashratesRepository.$getWeeklyHashrateTimestamps(); + const hashrates: any[] = []; + const genesisTimestamp = 1231006505; // bitcoin-cli getblock 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f + const lastWeekMidnight = this.getDateMidnight(new Date()); + lastWeekMidnight.setDate(lastWeekMidnight.getDate() - 7); + let toTimestamp = Math.round(lastWeekMidnight.getTime() / 1000); + + while (toTimestamp > genesisTimestamp) { + const fromTimestamp = toTimestamp - 604800; + + // Skip already indexed weeks + if (indexedTimestamp.includes(toTimestamp + 1)) { + toTimestamp -= 604800; + continue; + } + + const blockStats: any = await BlocksRepository.$blockCountBetweenTimestamp( + null, fromTimestamp, toTimestamp); + if (blockStats.blockCount === 0) { // We are done indexing, no blocks left + break; + } + + const fromTxt = new Date(1000 * fromTimestamp); + const toTxt = new Date(1000 * toTimestamp); + logger.debug(`Indexing pools hashrate between ${fromTxt.toUTCString()} and ${toTxt.toUTCString()}`) + + const lastBlockHashrate = await bitcoinClient.getNetworkHashPs(blockStats.blockCount, + blockStats.lastBlockHeight); + + let pools = await PoolsRepository.$getPoolsInfoBetween(fromTimestamp, toTimestamp); + const totalBlocks = pools.reduce((acc, pool) => acc + pool.blockCount, 0); + pools = pools.map((pool: any) => { + pool.hashrate = (pool.blockCount / totalBlocks) * lastBlockHashrate; + pool.share = (pool.blockCount / totalBlocks); + return pool; + }); + + for (const pool of pools) { + hashrates.push({ + hashrateTimestamp: toTimestamp + 1, + avgHashrate: pool['hashrate'], + poolId: pool.poolId, + share: pool['share'], + type: 'weekly', + }); + } + + await HashratesRepository.$saveHashrates(hashrates); + hashrates.length = 0; + + toTimestamp -= 604800; + } + this.weeklyHashrateIndexingStarted = false; + await HashratesRepository.$setLatestRunTimestamp('last_weekly_hashrates_indexing'); + logger.info(`Weekly pools hashrate indexing completed`); + } catch (e) { + this.weeklyHashrateIndexingStarted = false; + throw e; + } + + } + + /** + * Generate daily hashrate data + */ + public async $generateNetworkHashrateHistory(): Promise { if (!blocks.blockIndexingCompleted || this.hashrateIndexingStarted) { return; } + // We only run this once a day + const latestTimestamp = await HashratesRepository.$getLatestRunTimestamp('last_hashrates_indexing'); + const now = new Date().getTime() / 1000; + if (now - latestTimestamp < 86400) { + return; + } + try { this.hashrateIndexingStarted = true; - logger.info(`Indexing hashrates`); + logger.info(`Indexing network daily hashrate`); const totalDayIndexed = (await BlocksRepository.$blockCount(null, null)) / 144; const indexedTimestamp = (await HashratesRepository.$getNetworkDailyHashrate(null)).map(hashrate => hashrate.timestamp); let startedAt = new Date().getTime() / 1000; const genesisTimestamp = 1231006505; // bitcoin-cli getblock 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f - const lastMidnight = new Date(); - lastMidnight.setUTCHours(0); lastMidnight.setUTCMinutes(0); lastMidnight.setUTCSeconds(0); lastMidnight.setUTCMilliseconds(0); + const lastMidnight = this.getDateMidnight(new Date()); let toTimestamp = Math.round(lastMidnight.getTime() / 1000); let indexedThisRun = 0; let totalIndexed = 0; @@ -128,6 +210,8 @@ class Mining { while (toTimestamp > genesisTimestamp) { const fromTimestamp = toTimestamp - 86400; + + // Skip already indexed weeks if (indexedTimestamp.includes(fromTimestamp)) { toTimestamp -= 86400; ++totalIndexed; @@ -140,31 +224,9 @@ class Mining { break; } - let lastBlockHashrate = 0; - lastBlockHashrate = await bitcoinClient.getNetworkHashPs(blockStats.blockCount, + const lastBlockHashrate = await bitcoinClient.getNetworkHashPs(blockStats.blockCount, blockStats.lastBlockHeight); - if (totalIndexed > 7 && totalIndexed % 7 === 0 && !indexedTimestamp.includes(fromTimestamp + 1)) { // Save weekly pools hashrate - logger.debug("Indexing weekly hashrates for mining pools"); - let pools = await PoolsRepository.$getPoolsInfoBetween(fromTimestamp - 604800, fromTimestamp); - const totalBlocks = pools.reduce((acc, pool) => acc + pool.blockCount, 0); - pools = pools.map((pool: any) => { - pool.hashrate = (pool.blockCount / totalBlocks) * lastBlockHashrate; - pool.share = (pool.blockCount / totalBlocks); - return pool; - }); - - for (const pool of pools) { - hashrates.push({ - hashrateTimestamp: fromTimestamp + 1, - avgHashrate: pool['hashrate'], - poolId: pool.poolId, - share: pool['share'], - type: 'weekly', - }); - } - } - hashrates.push({ hashrateTimestamp: fromTimestamp, avgHashrate: lastBlockHashrate, @@ -203,18 +265,25 @@ class Mining { }); } - if (hashrates.length > 0) { - await HashratesRepository.$saveHashrates(hashrates); - } - await HashratesRepository.$setLatestRunTimestamp(); - this.hashrateIndexingStarted = false; + await HashratesRepository.$saveHashrates(hashrates); - logger.info(`Hashrates indexing completed`); + await HashratesRepository.$setLatestRunTimestamp('last_hashrates_indexing'); + this.hashrateIndexingStarted = false; + logger.info(`Daily network hashrate indexing completed`); } catch (e) { this.hashrateIndexingStarted = false; throw e; } } + + private getDateMidnight(date: Date): Date { + date.setUTCHours(0); + date.setUTCMinutes(0); + date.setUTCSeconds(0); + date.setUTCMilliseconds(0); + + return date; + } } export default new Mining(); diff --git a/backend/src/index.ts b/backend/src/index.ts index 51cee34c4..158f2895f 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -167,7 +167,8 @@ class Server { } async $resetHashratesIndexingState() { - return await HashratesRepository.$setLatestRunTimestamp(0); + await HashratesRepository.$setLatestRunTimestamp('last_hashrates_indexing', 0); + await HashratesRepository.$setLatestRunTimestamp('last_week_hashrates_indexing', 0); } async $runIndexingWhenReady() { @@ -178,6 +179,7 @@ class Server { try { await blocks.$generateBlockDatabase(); await mining.$generateNetworkHashrateHistory(); + await mining.$generatePoolHashrateHistory(); } catch (e) { logger.err(`Unable to run indexing right now, trying again later. ` + e); } diff --git a/backend/src/repositories/HashratesRepository.ts b/backend/src/repositories/HashratesRepository.ts index 5a33d7e77..367733cfa 100644 --- a/backend/src/repositories/HashratesRepository.ts +++ b/backend/src/repositories/HashratesRepository.ts @@ -8,6 +8,10 @@ class HashratesRepository { * Save indexed block data in the database */ public async $saveHashrates(hashrates: any) { + if (hashrates.length === 0) { + return; + } + let query = `INSERT INTO hashrates(hashrate_timestamp, avg_hashrate, pool_id, share, type) VALUES`; @@ -53,6 +57,16 @@ class HashratesRepository { return rows; } + public async $getWeeklyHashrateTimestamps(): Promise { + const connection = await DB.pool.getConnection(); + + let query = `SELECT UNIX_TIMESTAMP(hashrate_timestamp) as timestamp FROM hashrates where type = 'weekly' GROUP BY hashrate_timestamp`; + const [rows]: any[] = await connection.query(query); + connection.release(); + + return rows.map(row => row.timestamp); + } + /** * Returns the current biggest pool hashrate history */ @@ -83,18 +97,18 @@ class HashratesRepository { return rows; } - public async $setLatestRunTimestamp(val: any = null) { + public async $setLatestRunTimestamp(key: string, val: any = null) { const connection = await DB.pool.getConnection(); - const query = `UPDATE state SET number = ? WHERE name = 'last_hashrates_indexing'`; + const query = `UPDATE state SET number = ? WHERE name = ?`; - await connection.query(query, (val === null) ? [Math.round(new Date().getTime() / 1000)] : [val]); + await connection.query(query, (val === null) ? [Math.round(new Date().getTime() / 1000), key] : [val, key]); connection.release(); } - public async $getLatestRunTimestamp(): Promise { + public async $getLatestRunTimestamp(key: string): Promise { const connection = await DB.pool.getConnection(); - const query = `SELECT number FROM state WHERE name = 'last_hashrates_indexing'`; - const [rows] = await connection.query(query); + const query = `SELECT number FROM state WHERE name = ?`; + const [rows] = await connection.query(query, [key]); connection.release(); return rows[0]['number']; } From 8532d13a0df7455cad777501c0c232e46c0d52cb Mon Sep 17 00:00:00 2001 From: nymkappa Date: Sun, 6 Mar 2022 12:36:50 +0100 Subject: [PATCH 02/16] Update hashrate indexing logs --- backend/src/api/mining.ts | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/backend/src/api/mining.ts b/backend/src/api/mining.ts index 62fa8baca..a8a8af967 100644 --- a/backend/src/api/mining.ts +++ b/backend/src/api/mining.ts @@ -122,12 +122,18 @@ class Mining { lastWeekMidnight.setDate(lastWeekMidnight.getDate() - 7); let toTimestamp = Math.round(lastWeekMidnight.getTime() / 1000); + const totalWeekIndexed = (await BlocksRepository.$blockCount(null, null)) / 1008; + let indexedThisRun = 0; + let totalIndexed = 0; + let startedAt = new Date().getTime() / 1000; + while (toTimestamp > genesisTimestamp) { const fromTimestamp = toTimestamp - 604800; // Skip already indexed weeks if (indexedTimestamp.includes(toTimestamp + 1)) { toTimestamp -= 604800; + ++totalIndexed; continue; } @@ -137,10 +143,6 @@ class Mining { break; } - const fromTxt = new Date(1000 * fromTimestamp); - const toTxt = new Date(1000 * toTimestamp); - logger.debug(`Indexing pools hashrate between ${fromTxt.toUTCString()} and ${toTxt.toUTCString()}`) - const lastBlockHashrate = await bitcoinClient.getNetworkHashPs(blockStats.blockCount, blockStats.lastBlockHeight); @@ -165,7 +167,19 @@ class Mining { await HashratesRepository.$saveHashrates(hashrates); hashrates.length = 0; + const elapsedSeconds = Math.max(1, Math.round((new Date().getTime() / 1000) - startedAt)); + if (elapsedSeconds > 5) { + const weeksPerSeconds = (indexedThisRun / elapsedSeconds).toFixed(2); + const formattedDate = new Date(fromTimestamp * 1000).toUTCString(); + const weeksLeft = Math.round(totalWeekIndexed - totalIndexed); + logger.debug(`Getting weekly pool hashrate for ${formattedDate} | ~${weeksPerSeconds} weeks/sec | ~${weeksLeft} weeks left to index`); + startedAt = new Date().getTime() / 1000; + indexedThisRun = 0; + } + toTimestamp -= 604800; + ++indexedThisRun; + ++totalIndexed; } this.weeklyHashrateIndexingStarted = false; await HashratesRepository.$setLatestRunTimestamp('last_weekly_hashrates_indexing'); @@ -197,16 +211,16 @@ class Mining { logger.info(`Indexing network daily hashrate`); - const totalDayIndexed = (await BlocksRepository.$blockCount(null, null)) / 144; const indexedTimestamp = (await HashratesRepository.$getNetworkDailyHashrate(null)).map(hashrate => hashrate.timestamp); - let startedAt = new Date().getTime() / 1000; const genesisTimestamp = 1231006505; // bitcoin-cli getblock 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f const lastMidnight = this.getDateMidnight(new Date()); let toTimestamp = Math.round(lastMidnight.getTime() / 1000); + const hashrates: any[] = []; + + const totalDayIndexed = (await BlocksRepository.$blockCount(null, null)) / 144; let indexedThisRun = 0; let totalIndexed = 0; - - const hashrates: any[] = []; + let startedAt = new Date().getTime() / 1000; while (toTimestamp > genesisTimestamp) { const fromTimestamp = toTimestamp - 86400; @@ -245,7 +259,7 @@ class Mining { const daysPerSeconds = (indexedThisRun / elapsedSeconds).toFixed(2); const formattedDate = new Date(fromTimestamp * 1000).toUTCString(); const daysLeft = Math.round(totalDayIndexed - totalIndexed); - logger.debug(`Getting hashrate for ${formattedDate} | ~${daysPerSeconds} days/sec | ~${daysLeft} days left to index`); + logger.debug(`Getting network daily hashrate for ${formattedDate} | ~${daysPerSeconds} days/sec | ~${daysLeft} days left to index`); startedAt = new Date().getTime() / 1000; indexedThisRun = 0; } From 1ced44d9704ff862c149595e383a91aff72fe207 Mon Sep 17 00:00:00 2001 From: nymkappa Date: Sun, 6 Mar 2022 12:47:16 +0100 Subject: [PATCH 03/16] Remove useless mining function wrapper in backend --- backend/src/api/blocks.ts | 2 +- backend/src/api/mining.ts | 25 ++----------------------- backend/src/routes.ts | 9 +++++---- 3 files changed, 8 insertions(+), 28 deletions(-) diff --git a/backend/src/api/blocks.ts b/backend/src/api/blocks.ts index d6d37b5c4..f0c04455b 100644 --- a/backend/src/api/blocks.ts +++ b/backend/src/api/blocks.ts @@ -171,7 +171,7 @@ class Blocks { } /** - * Index all blocks metadata for the mining dashboard + * [INDEXING] Index all blocks metadata for the mining dashboard */ public async $generateBlockDatabase() { if (this.blockIndexingStarted) { diff --git a/backend/src/api/mining.ts b/backend/src/api/mining.ts index a8a8af967..18d82669f 100644 --- a/backend/src/api/mining.ts +++ b/backend/src/api/mining.ts @@ -75,28 +75,7 @@ class Mining { } /** - * Return the historical difficulty adjustments and oldest indexed block timestamp - */ - public async $getHistoricalDifficulty(interval: string | null): Promise { - return await BlocksRepository.$getBlocksDifficulty(interval); - } - - /** - * Return the historical hashrates and oldest indexed block timestamp - */ - public async $getNetworkHistoricalHashrates(interval: string | null): Promise { - return await HashratesRepository.$getNetworkDailyHashrate(interval); - } - - /** - * Return the historical hashrates and oldest indexed block timestamp for one or all pools - */ - public async $getPoolsHistoricalHashrates(interval: string | null, poolId: number): Promise { - return await HashratesRepository.$getPoolsWeeklyHashrate(interval); - } - - /** - * Generate weekly mining pool hashrate history + * [INDEXING] Generate weekly mining pool hashrate history */ public async $generatePoolHashrateHistory(): Promise { if (!blocks.blockIndexingCompleted || this.weeklyHashrateIndexingStarted) { @@ -192,7 +171,7 @@ class Mining { } /** - * Generate daily hashrate data + * [INDEXING] Generate daily hashrate data */ public async $generateNetworkHashrateHistory(): Promise { if (!blocks.blockIndexingCompleted || this.hashrateIndexingStarted) { diff --git a/backend/src/routes.ts b/backend/src/routes.ts index dea9da2f2..6b1a365b6 100644 --- a/backend/src/routes.ts +++ b/backend/src/routes.ts @@ -24,6 +24,7 @@ import miningStats from './api/mining'; import axios from 'axios'; import mining from './api/mining'; import BlocksRepository from './repositories/BlocksRepository'; +import HashratesRepository from './repositories/HashratesRepository'; class Routes { constructor() {} @@ -576,7 +577,7 @@ class Routes { public async $getHistoricalDifficulty(req: Request, res: Response) { try { - const stats = await mining.$getHistoricalDifficulty(req.params.interval ?? null); + const stats = await BlocksRepository.$getBlocksDifficulty(req.params.interval ?? null); res.header('Pragma', 'public'); res.header('Cache-control', 'public'); res.setHeader('Expires', new Date(Date.now() + 1000 * 300).toUTCString()); @@ -588,7 +589,7 @@ class Routes { public async $getPoolsHistoricalHashrate(req: Request, res: Response) { try { - const hashrates = await mining.$getPoolsHistoricalHashrates(req.params.interval ?? null, parseInt(req.params.poolId, 10)); + const hashrates = await HashratesRepository.$getPoolsWeeklyHashrate(req.params.interval ?? null); const oldestIndexedBlockTimestamp = await BlocksRepository.$oldestBlockTimestamp(); res.header('Pragma', 'public'); res.header('Cache-control', 'public'); @@ -604,8 +605,8 @@ class Routes { public async $getHistoricalHashrate(req: Request, res: Response) { try { - const hashrates = await mining.$getNetworkHistoricalHashrates(req.params.interval ?? null); - const difficulty = await mining.$getHistoricalDifficulty(req.params.interval ?? null); + const hashrates = await HashratesRepository.$getNetworkDailyHashrate(req.params.interval ?? null); + const difficulty = await BlocksRepository.$getBlocksDifficulty(req.params.interval ?? null); const oldestIndexedBlockTimestamp = await BlocksRepository.$oldestBlockTimestamp(); res.header('Pragma', 'public'); res.header('Cache-control', 'public'); From 89411f23d8253fd20bd572fa2873a8a67264806a Mon Sep 17 00:00:00 2001 From: nymkappa Date: Sun, 6 Mar 2022 16:44:09 +0100 Subject: [PATCH 04/16] Set connection pool timezone to UTC - Close mysql connections upon error --- backend/src/api/database-migration.ts | 3 +- backend/src/api/mining.ts | 6 +- backend/src/database.ts | 1 + backend/src/repositories/BlocksRepository.ts | 172 ++++++++++++------ .../src/repositories/HashratesRepository.ts | 65 +++++-- backend/src/repositories/PoolsRepository.ts | 42 +++-- 6 files changed, 196 insertions(+), 93 deletions(-) diff --git a/backend/src/api/database-migration.ts b/backend/src/api/database-migration.ts index dd5dea399..d072fe496 100644 --- a/backend/src/api/database-migration.ts +++ b/backend/src/api/database-migration.ts @@ -134,7 +134,8 @@ class DatabaseMigration { } if (databaseSchemaVersion < 9) { - await this.$executeQuery(connection, 'ALTER TABLE `state` CHANGE `name` `name` varchar(100)') + await this.$executeQuery(connection, 'ALTER TABLE `state` CHANGE `name` `name` varchar(100)'); + await this.$executeQuery(connection, 'ALTER TABLE `hashrates` ADD UNIQUE `hashrate_timestamp_pool_id` (`hashrate_timestamp`, `pool_id`)'); } connection.release(); diff --git a/backend/src/api/mining.ts b/backend/src/api/mining.ts index 18d82669f..f15c52c88 100644 --- a/backend/src/api/mining.ts +++ b/backend/src/api/mining.ts @@ -97,9 +97,8 @@ class Mining { const indexedTimestamp = await HashratesRepository.$getWeeklyHashrateTimestamps(); const hashrates: any[] = []; const genesisTimestamp = 1231006505; // bitcoin-cli getblock 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f - const lastWeekMidnight = this.getDateMidnight(new Date()); - lastWeekMidnight.setDate(lastWeekMidnight.getDate() - 7); - let toTimestamp = Math.round(lastWeekMidnight.getTime() / 1000); + const lastMidnight = this.getDateMidnight(new Date()); + let toTimestamp = Math.round((lastMidnight.getTime() - 604800) / 1000); const totalWeekIndexed = (await BlocksRepository.$blockCount(null, null)) / 1008; let indexedThisRun = 0; @@ -167,7 +166,6 @@ class Mining { this.weeklyHashrateIndexingStarted = false; throw e; } - } /** diff --git a/backend/src/database.ts b/backend/src/database.ts index b0d39b301..9f2655016 100644 --- a/backend/src/database.ts +++ b/backend/src/database.ts @@ -11,6 +11,7 @@ export class DB { password: config.DATABASE.PASSWORD, connectionLimit: 10, supportBigNumbers: true, + timezone: '+00:00', }); } diff --git a/backend/src/repositories/BlocksRepository.ts b/backend/src/repositories/BlocksRepository.ts index d91777880..844f62bad 100644 --- a/backend/src/repositories/BlocksRepository.ts +++ b/backend/src/repositories/BlocksRepository.ts @@ -53,15 +53,17 @@ class BlocksRepository { // logger.debug(query); await connection.query(query, params); + connection.release(); } catch (e: any) { + connection.release(); if (e.errno === 1062) { // ER_DUP_ENTRY logger.debug(`$saveBlockInDatabase() - Block ${block.height} has already been indexed, ignoring`); } else { + connection.release(); logger.err('$saveBlockInDatabase() error' + (e instanceof Error ? e.message : e)); + throw e; } } - - connection.release(); } /** @@ -73,20 +75,26 @@ class BlocksRepository { } const connection = await DB.pool.getConnection(); - const [rows]: any[] = await connection.query(` - SELECT height - FROM blocks - WHERE height <= ? AND height >= ? - ORDER BY height DESC; - `, [startHeight, endHeight]); - connection.release(); + try { + const [rows]: any[] = await connection.query(` + SELECT height + FROM blocks + WHERE height <= ? AND height >= ? + ORDER BY height DESC; + `, [startHeight, endHeight]); + connection.release(); - const indexedBlockHeights: number[] = []; - rows.forEach((row: any) => { indexedBlockHeights.push(row.height); }); - const seekedBlocks: number[] = Array.from(Array(startHeight - endHeight + 1).keys(), n => n + endHeight).reverse(); - const missingBlocksHeights = seekedBlocks.filter(x => indexedBlockHeights.indexOf(x) === -1); + const indexedBlockHeights: number[] = []; + rows.forEach((row: any) => { indexedBlockHeights.push(row.height); }); + const seekedBlocks: number[] = Array.from(Array(startHeight - endHeight + 1).keys(), n => n + endHeight).reverse(); + const missingBlocksHeights = seekedBlocks.filter(x => indexedBlockHeights.indexOf(x) === -1); - return missingBlocksHeights; + return missingBlocksHeights; + } catch (e) { + connection.release(); + logger.err('$getMissingBlocksBetweenHeights() error' + (e instanceof Error ? e.message : e)); + throw e; + } } /** @@ -111,10 +119,16 @@ class BlocksRepository { // logger.debug(query); const connection = await DB.pool.getConnection(); - const [rows] = await connection.query(query, params); - connection.release(); + try { + const [rows] = await connection.query(query, params); + connection.release(); - return rows; + return rows; + } catch (e) { + connection.release(); + logger.err('$getEmptyBlocks() error' + (e instanceof Error ? e.message : e)); + throw e; + } } /** @@ -143,10 +157,16 @@ class BlocksRepository { // logger.debug(query); const connection = await DB.pool.getConnection(); - const [rows] = await connection.query(query, params); - connection.release(); + try { + const [rows] = await connection.query(query, params); + connection.release(); - return rows[0].blockCount; + return rows[0].blockCount; + } catch (e) { + connection.release(); + logger.err('$blockCount() error' + (e instanceof Error ? e.message : e)); + throw e; + } } /** @@ -177,10 +197,16 @@ class BlocksRepository { // logger.debug(query); const connection = await DB.pool.getConnection(); - const [rows] = await connection.query(query, params); - connection.release(); + try { + const [rows] = await connection.query(query, params); + connection.release(); - return rows[0]; + return rows[0]; + } catch (e) { + connection.release(); + logger.err('$blockCountBetweenTimestamp() error' + (e instanceof Error ? e.message : e)); + throw e; + } } /** @@ -194,23 +220,26 @@ class BlocksRepository { // logger.debug(query); const connection = await DB.pool.getConnection(); - const [rows]: any[] = await connection.query(query); - connection.release(); + try { + const [rows]: any[] = await connection.query(query); + connection.release(); - if (rows.length <= 0) { - return -1; + if (rows.length <= 0) { + return -1; + } + + return rows[0].blockTimestamp; + } catch (e) { + connection.release(); + logger.err('$oldestBlockTimestamp() error' + (e instanceof Error ? e.message : e)); + throw e; } - - return rows[0].blockTimestamp; } /** * Get blocks mined by a specific mining pool */ - public async $getBlocksByPool( - poolId: number, - startHeight: number | null = null - ): Promise { + public async $getBlocksByPool(poolId: number, startHeight: number | null = null): Promise { const params: any[] = []; let query = `SELECT height, hash as id, tx_count, size, weight, pool_id, UNIX_TIMESTAMP(blockTimestamp) as timestamp, reward FROM blocks @@ -227,14 +256,20 @@ class BlocksRepository { // logger.debug(query); const connection = await DB.pool.getConnection(); - const [rows] = await connection.query(query, params); - connection.release(); + try { + const [rows] = await connection.query(query, params); + connection.release(); - for (const block of rows) { - delete block['blockTimestamp']; + for (const block of rows) { + delete block['blockTimestamp']; + } + + return rows; + } catch (e) { + connection.release(); + logger.err('$getBlocksByPool() error' + (e instanceof Error ? e.message : e)); + throw e; } - - return rows; } /** @@ -242,19 +277,25 @@ class BlocksRepository { */ public async $getBlockByHeight(height: number): Promise { const connection = await DB.pool.getConnection(); - const [rows]: any[] = await connection.query(` - SELECT *, UNIX_TIMESTAMP(blocks.blockTimestamp) as blockTimestamp, pools.id as pool_id, pools.name as pool_name, pools.link as pool_link, pools.addresses as pool_addresses, pools.regexes as pool_regexes - FROM blocks - JOIN pools ON blocks.pool_id = pools.id - WHERE height = ${height}; - `); - connection.release(); + try { + const [rows]: any[] = await connection.query(` + SELECT *, UNIX_TIMESTAMP(blocks.blockTimestamp) as blockTimestamp, pools.id as pool_id, pools.name as pool_name, pools.link as pool_link, pools.addresses as pool_addresses, pools.regexes as pool_regexes + FROM blocks + JOIN pools ON blocks.pool_id = pools.id + WHERE height = ${height}; + `); + connection.release(); - if (rows.length <= 0) { - return null; + if (rows.length <= 0) { + return null; + } + + return rows[0]; + } catch (e) { + connection.release(); + logger.err('$getBlockByHeight() error' + (e instanceof Error ? e.message : e)); + throw e; } - - return rows[0]; } /** @@ -297,21 +338,34 @@ class BlocksRepository { ORDER BY t.height `; - const [rows]: any[] = await connection.query(query); - connection.release(); + try { + const [rows]: any[] = await connection.query(query); + connection.release(); - for (let row of rows) { - delete row['rn']; + for (let row of rows) { + delete row['rn']; + } + + return rows; + } catch (e) { + connection.release(); + logger.err('$getBlocksDifficulty() error' + (e instanceof Error ? e.message : e)); + throw e; } - - 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; + try { + const [rows]: any[] = await connection.query(`SELECT MIN(height) as minHeight FROM blocks`); + connection.release(); + + return rows[0].minHeight; + } catch (e) { + connection.release(); + logger.err('$getOldestIndexedBlockHeight() error' + (e instanceof Error ? e.message : e)); + throw e; + } } } diff --git a/backend/src/repositories/HashratesRepository.ts b/backend/src/repositories/HashratesRepository.ts index 367733cfa..3523004d5 100644 --- a/backend/src/repositories/HashratesRepository.ts +++ b/backend/src/repositories/HashratesRepository.ts @@ -24,12 +24,12 @@ class HashratesRepository { try { // logger.debug(query); await connection.query(query); + connection.release(); } catch (e: any) { + connection.release(); logger.err('$saveHashrateInDatabase() error' + (e instanceof Error ? e.message : e)); throw e; } - - connection.release(); } public async $getNetworkDailyHashrate(interval: string | null): Promise { @@ -51,20 +51,33 @@ class HashratesRepository { query += ` ORDER by hashrate_timestamp`; - const [rows]: any[] = await connection.query(query); - connection.release(); + try { + const [rows]: any[] = await connection.query(query); + connection.release(); - return rows; + return rows; + } catch (e) { + connection.release(); + logger.err('$getNetworkDailyHashrate() error' + (e instanceof Error ? e.message : e)); + throw e; + } } public async $getWeeklyHashrateTimestamps(): Promise { const connection = await DB.pool.getConnection(); - let query = `SELECT UNIX_TIMESTAMP(hashrate_timestamp) as timestamp FROM hashrates where type = 'weekly' GROUP BY hashrate_timestamp`; - const [rows]: any[] = await connection.query(query); - connection.release(); + const query = `SELECT UNIX_TIMESTAMP(hashrate_timestamp) as timestamp FROM hashrates where type = 'weekly' GROUP BY hashrate_timestamp`; - return rows.map(row => row.timestamp); + try { + const [rows]: any[] = await connection.query(query); + connection.release(); + + return rows.map(row => row.timestamp); + } catch (e) { + connection.release(); + logger.err('$getWeeklyHashrateTimestamps() error' + (e instanceof Error ? e.message : e)); + throw e; + } } /** @@ -91,26 +104,44 @@ class HashratesRepository { query += ` ORDER by hashrate_timestamp, FIELD(pool_id, ${topPoolsId})`; - const [rows]: any[] = await connection.query(query); - connection.release(); + try { + const [rows]: any[] = await connection.query(query); + connection.release(); - return rows; + return rows; + } catch (e) { + connection.release(); + logger.err('$getPoolsWeeklyHashrate() error' + (e instanceof Error ? e.message : e)); + throw e; + } } public async $setLatestRunTimestamp(key: string, val: any = null) { const connection = await DB.pool.getConnection(); const query = `UPDATE state SET number = ? WHERE name = ?`; - await connection.query(query, (val === null) ? [Math.round(new Date().getTime() / 1000), key] : [val, key]); - connection.release(); + try { + await connection.query(query, (val === null) ? [Math.round(new Date().getTime() / 1000), key] : [val, key]); + connection.release(); + } catch (e) { + connection.release(); + } } public async $getLatestRunTimestamp(key: string): Promise { const connection = await DB.pool.getConnection(); const query = `SELECT number FROM state WHERE name = ?`; - const [rows] = await connection.query(query, [key]); - connection.release(); - return rows[0]['number']; + + try { + const [rows] = await connection.query(query, [key]); + connection.release(); + + return rows[0]['number']; + } catch (e) { + connection.release(); + logger.err('$setLatestRunTimestamp() error' + (e instanceof Error ? e.message : e)); + throw e; + } } } diff --git a/backend/src/repositories/PoolsRepository.ts b/backend/src/repositories/PoolsRepository.ts index ea617322a..3f904888d 100644 --- a/backend/src/repositories/PoolsRepository.ts +++ b/backend/src/repositories/PoolsRepository.ts @@ -43,26 +43,38 @@ class PoolsRepository { // logger.debug(query); const connection = await DB.pool.getConnection(); - const [rows] = await connection.query(query); - connection.release(); + try { + const [rows] = await connection.query(query); + connection.release(); - return rows; + return rows; + } catch (e) { + connection.release(); + logger.err('$getPoolsInfo() error' + (e instanceof Error ? e.message : e)); + throw e; + } } /** * Get basic pool info and block count between two timestamp */ public async $getPoolsInfoBetween(from: number, to: number): Promise { - let query = `SELECT COUNT(height) as blockCount, pools.id as poolId, pools.name as poolName + const query = `SELECT COUNT(height) as blockCount, pools.id as poolId, pools.name as poolName FROM pools LEFT JOIN blocks on pools.id = blocks.pool_id AND blocks.blockTimestamp BETWEEN FROM_UNIXTIME(?) AND FROM_UNIXTIME(?) GROUP BY pools.id`; const connection = await DB.pool.getConnection(); - const [rows] = await connection.query(query, [from, to]); - connection.release(); + try { + const [rows] = await connection.query(query, [from, to]); + connection.release(); - return rows; + return rows; + } catch (e) { + connection.release(); + logger.err('$getPoolsInfoBetween() error' + (e instanceof Error ? e.message : e)); + throw e; + } } /** @@ -76,13 +88,19 @@ class PoolsRepository { // logger.debug(query); const connection = await DB.pool.getConnection(); - const [rows] = await connection.query(query, [poolId]); - connection.release(); + try { + const [rows] = await connection.query(query, [poolId]); + connection.release(); - rows[0].regexes = JSON.parse(rows[0].regexes); - rows[0].addresses = JSON.parse(rows[0].addresses); + rows[0].regexes = JSON.parse(rows[0].regexes); + rows[0].addresses = JSON.parse(rows[0].addresses); - return rows[0]; + return rows[0]; + } catch (e) { + connection.release(); + logger.err('$getPool() error' + (e instanceof Error ? e.message : e)); + throw e; + } } } From 4b859eb4f641ac34347d61565679e4fa04e7a49e Mon Sep 17 00:00:00 2001 From: nymkappa Date: Sun, 6 Mar 2022 16:48:14 +0100 Subject: [PATCH 05/16] Re-index hashrates because we have different timestamp handling --- backend/src/api/database-migration.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/src/api/database-migration.ts b/backend/src/api/database-migration.ts index d072fe496..c66148fe0 100644 --- a/backend/src/api/database-migration.ts +++ b/backend/src/api/database-migration.ts @@ -134,6 +134,8 @@ class DatabaseMigration { } if (databaseSchemaVersion < 9) { + logger.warn(`'hashrates' table has been truncated. Re-indexing from scratch.'`); + await this.$executeQuery(connection, 'TRUNCATE hashrates;'); // Need to re-index await this.$executeQuery(connection, 'ALTER TABLE `state` CHANGE `name` `name` varchar(100)'); await this.$executeQuery(connection, 'ALTER TABLE `hashrates` ADD UNIQUE `hashrate_timestamp_pool_id` (`hashrate_timestamp`, `pool_id`)'); } From 2a170c07d11809212d803a88f2ac1825f777a33c Mon Sep 17 00:00:00 2001 From: nymkappa Date: Sun, 6 Mar 2022 16:50:59 +0100 Subject: [PATCH 06/16] Remove unnecessary await --- backend/src/index.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/src/index.ts b/backend/src/index.ts index 158f2895f..029e32c96 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -177,9 +177,9 @@ class Server { } try { - await blocks.$generateBlockDatabase(); - await mining.$generateNetworkHashrateHistory(); - await mining.$generatePoolHashrateHistory(); + blocks.$generateBlockDatabase(); + mining.$generateNetworkHashrateHistory(); + mining.$generatePoolHashrateHistory(); } catch (e) { logger.err(`Unable to run indexing right now, trying again later. ` + e); } From 3d1a10cdfca972c998fe613d9f83b211ecd5e198 Mon Sep 17 00:00:00 2001 From: nymkappa Date: Sun, 6 Mar 2022 16:57:40 +0100 Subject: [PATCH 07/16] Use the latest timestamp in hashrate data ticker --- backend/src/api/mining.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/api/mining.ts b/backend/src/api/mining.ts index f15c52c88..4423e5f16 100644 --- a/backend/src/api/mining.ts +++ b/backend/src/api/mining.ts @@ -219,7 +219,7 @@ class Mining { blockStats.lastBlockHeight); hashrates.push({ - hashrateTimestamp: fromTimestamp, + hashrateTimestamp: toTimestamp, avgHashrate: lastBlockHashrate, poolId: null, share: 1, From 2570dbfab45c2a4459fe17df32c1e60911c143dd Mon Sep 17 00:00:00 2001 From: nymkappa Date: Sun, 6 Mar 2022 17:06:55 +0100 Subject: [PATCH 08/16] Fix incorrect state naming --- backend/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/index.ts b/backend/src/index.ts index 029e32c96..4ede865a6 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -168,7 +168,7 @@ class Server { async $resetHashratesIndexingState() { await HashratesRepository.$setLatestRunTimestamp('last_hashrates_indexing', 0); - await HashratesRepository.$setLatestRunTimestamp('last_week_hashrates_indexing', 0); + await HashratesRepository.$setLatestRunTimestamp('last_weekly_hashrates_indexing', 0); } async $runIndexingWhenReady() { From b2dec5e20a3ac2d7aad0ff5161cfdc168c0cffec Mon Sep 17 00:00:00 2001 From: nymkappa Date: Mon, 28 Feb 2022 17:31:10 +0900 Subject: [PATCH 09/16] Mining dashboard layout matches main dashboard layout --- .../difficulty/difficulty.component.html | 2 +- .../difficulty/difficulty.component.ts | 5 ++- .../hashrate-chart.component.scss | 2 +- .../hashrate-chart-pools.component.html | 2 +- .../hashrate-chart-pools.component.scss | 2 +- .../mining-dashboard.component.html | 42 +++++++++++++++---- .../mining-dashboard.component.scss | 4 -- .../pool-ranking/pool-ranking.component.html | 2 +- .../pool-ranking/pool-ranking.component.scss | 2 +- 9 files changed, 43 insertions(+), 20 deletions(-) diff --git a/frontend/src/app/components/difficulty/difficulty.component.html b/frontend/src/app/components/difficulty/difficulty.component.html index fc3030286..bda189eab 100644 --- a/frontend/src/app/components/difficulty/difficulty.component.html +++ b/frontend/src/app/components/difficulty/difficulty.component.html @@ -1,4 +1,4 @@ -
Difficulty Adjustment
+
Difficulty Adjustment
diff --git a/frontend/src/app/components/difficulty/difficulty.component.ts b/frontend/src/app/components/difficulty/difficulty.component.ts index ff44e5aeb..8714ff9f1 100644 --- a/frontend/src/app/components/difficulty/difficulty.component.ts +++ b/frontend/src/app/components/difficulty/difficulty.component.ts @@ -28,8 +28,9 @@ export class DifficultyComponent implements OnInit { isLoadingWebSocket$: Observable; difficultyEpoch$: Observable; - @Input() showProgress: boolean = true; - @Input() showHalving: boolean = false; + @Input() showProgress = true; + @Input() showHalving = false; + @Input() showTitle = true; constructor( public stateService: StateService, diff --git a/frontend/src/app/components/hashrate-chart/hashrate-chart.component.scss b/frontend/src/app/components/hashrate-chart/hashrate-chart.component.scss index 316f0fc47..eb97dc067 100644 --- a/frontend/src/app/components/hashrate-chart/hashrate-chart.component.scss +++ b/frontend/src/app/components/hashrate-chart/hashrate-chart.component.scss @@ -29,7 +29,7 @@ .chart-widget { width: 100%; height: 100%; - max-height: 275px; + max-height: 325px; } .formRadioGroup { diff --git a/frontend/src/app/components/hashrates-chart-pools/hashrate-chart-pools.component.html b/frontend/src/app/components/hashrates-chart-pools/hashrate-chart-pools.component.html index 93cec63ca..8750caa56 100644 --- a/frontend/src/app/components/hashrates-chart-pools/hashrate-chart-pools.component.html +++ b/frontend/src/app/components/hashrates-chart-pools/hashrate-chart-pools.component.html @@ -27,7 +27,7 @@
-
+
diff --git a/frontend/src/app/components/hashrates-chart-pools/hashrate-chart-pools.component.scss b/frontend/src/app/components/hashrates-chart-pools/hashrate-chart-pools.component.scss index 06a3eeb25..7c18b0382 100644 --- a/frontend/src/app/components/hashrates-chart-pools/hashrate-chart-pools.component.scss +++ b/frontend/src/app/components/hashrates-chart-pools/hashrate-chart-pools.component.scss @@ -29,7 +29,7 @@ .chart-widget { width: 100%; height: 100%; - max-height: 275px; + max-height: 325px; } .formRadioGroup { 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 4d811fc9a..3652e9e2d 100644 --- a/frontend/src/app/components/mining-dashboard/mining-dashboard.component.html +++ b/frontend/src/app/components/mining-dashboard/mining-dashboard.component.html @@ -3,26 +3,37 @@
-
+
Placeholder
+
+
+
+ + +
+
Difficulty Adjustment
+
+ +
+
+ + + + + + +
\ No newline at end of file diff --git a/frontend/src/app/components/mining-dashboard/mining-dashboard.component.scss b/frontend/src/app/components/mining-dashboard/mining-dashboard.component.scss index 4c75e9ea6..4f05686a3 100644 --- a/frontend/src/app/components/mining-dashboard/mining-dashboard.component.scss +++ b/frontend/src/app/components/mining-dashboard/mining-dashboard.component.scss @@ -12,10 +12,6 @@ .card { background-color: #1d1f31; - height: 340px; -} -.card.double { - height: 620px; } .card-title { diff --git a/frontend/src/app/components/pool-ranking/pool-ranking.component.html b/frontend/src/app/components/pool-ranking/pool-ranking.component.html index 067c61646..5e6068866 100644 --- a/frontend/src/app/components/pool-ranking/pool-ranking.component.html +++ b/frontend/src/app/components/pool-ranking/pool-ranking.component.html @@ -2,7 +2,7 @@
-
+
diff --git a/frontend/src/app/components/pool-ranking/pool-ranking.component.scss b/frontend/src/app/components/pool-ranking/pool-ranking.component.scss index a41891a8f..470eaa112 100644 --- a/frontend/src/app/components/pool-ranking/pool-ranking.component.scss +++ b/frontend/src/app/components/pool-ranking/pool-ranking.component.scss @@ -7,7 +7,7 @@ .chart-widget { width: 100%; height: 100%; - max-height: 275px; + max-height: 325px; } .formRadioGroup { From 6cdd41a8f7ee08deadbf801409084ec0ee26a24d Mon Sep 17 00:00:00 2001 From: nymkappa Date: Mon, 28 Feb 2022 17:52:55 +0900 Subject: [PATCH 10/16] Add difficulty adjustment table in mining dashboard --- .../hashrate-chart/hashrate-chart.component.html | 13 +++++-------- .../hashrate-chart/hashrate-chart.component.ts | 5 +++-- .../mining-dashboard.component.html | 16 ++++++++++++++-- 3 files changed, 22 insertions(+), 12 deletions(-) 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 ea5c5a2a7..9c30a3334 100644 --- a/frontend/src/app/components/hashrate-chart/hashrate-chart.component.html +++ b/frontend/src/app/components/hashrate-chart/hashrate-chart.component.html @@ -1,6 +1,6 @@
-
+
-
- +
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 7efb83098..db7d79d93 100644 --- a/frontend/src/app/components/hashrate-chart/hashrate-chart.component.ts +++ b/frontend/src/app/components/hashrate-chart/hashrate-chart.component.ts @@ -22,7 +22,8 @@ import { selectPowerOfTen } from 'src/app/bitcoin.utils'; `], }) export class HashrateChartComponent implements OnInit { - @Input() widget: boolean = false; + @Input() tableOnly = false; + @Input() widget = false; @Input() right: number | string = 45; @Input() left: number | string = 75; @@ -114,7 +115,7 @@ export class HashrateChartComponent implements OnInit { } return { availableTimespanDay: availableTimespanDay, - difficulty: tableData + difficulty: this.tableOnly ? tableData.slice(0, 8) : tableData }; }), ); 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 3652e9e2d..9d0eebe88 100644 --- a/frontend/src/app/components/mining-dashboard/mining-dashboard.component.html +++ b/frontend/src/app/components/mining-dashboard/mining-dashboard.component.html @@ -44,11 +44,10 @@
- +
\ No newline at end of file From cf4336eb2eb02ff471e3c1412868bea6e33bb717 Mon Sep 17 00:00:00 2001 From: nymkappa Date: Tue, 1 Mar 2022 18:29:02 +0900 Subject: [PATCH 11/16] Move small pie share into "other" - align labels --- .../pool-ranking/pool-ranking.component.html | 2 +- .../pool-ranking/pool-ranking.component.scss | 2 +- .../pool-ranking/pool-ranking.component.ts | 84 ++++++++++++++++--- 3 files changed, 76 insertions(+), 12 deletions(-) diff --git a/frontend/src/app/components/pool-ranking/pool-ranking.component.html b/frontend/src/app/components/pool-ranking/pool-ranking.component.html index 5e6068866..43b71d4c9 100644 --- a/frontend/src/app/components/pool-ranking/pool-ranking.component.html +++ b/frontend/src/app/components/pool-ranking/pool-ranking.component.html @@ -59,7 +59,7 @@ {{ pool.rank }} {{ pool.name }} - {{ pool.lastEstimatedHashrate }} {{ miningStats.miningUnits.hashrateUnit }} + {{ pool.lastEstimatedHashrate }} {{ miningStats.miningUnits.hashrateUnit }} {{ pool['blockText'] }} {{ pool.emptyBlocks }} ({{ pool.emptyBlockRatio }}%) diff --git a/frontend/src/app/components/pool-ranking/pool-ranking.component.scss b/frontend/src/app/components/pool-ranking/pool-ranking.component.scss index 470eaa112..4ba007598 100644 --- a/frontend/src/app/components/pool-ranking/pool-ranking.component.scss +++ b/frontend/src/app/components/pool-ranking/pool-ranking.component.scss @@ -1,7 +1,7 @@ .chart { max-height: 400px; @media (max-width: 767.98px) { - max-height: 300px; + max-height: 270px; } } .chart-widget { diff --git a/frontend/src/app/components/pool-ranking/pool-ranking.component.ts b/frontend/src/app/components/pool-ranking/pool-ranking.component.ts index cef04cca5..cd2970660 100644 --- a/frontend/src/app/components/pool-ranking/pool-ranking.component.ts +++ b/frontend/src/app/components/pool-ranking/pool-ranking.component.ts @@ -49,7 +49,7 @@ export class PoolRankingComponent implements OnInit { this.poolsWindowPreference = '1w'; } else { this.seoService.setTitle($localize`:@@mining.mining-pools:Mining Pools`); - this.poolsWindowPreference = this.storageService.getValue('poolsWindowPreference') ? this.storageService.getValue('poolsWindowPreference') : '1w'; + this.poolsWindowPreference = this.storageService.getValue('poolsWindowPreference') ? this.storageService.getValue('poolsWindowPreference') : '1w'; } this.radioGroupForm = this.formBuilder.group({ dateSpan: this.poolsWindowPreference }); this.radioGroupForm.controls.dateSpan.setValue(this.poolsWindowPreference); @@ -105,22 +105,37 @@ export class PoolRankingComponent implements OnInit { } generatePoolsChartSerieData(miningStats) { - const poolShareThreshold = this.isMobile() ? 1 : 0.5; // Do not draw pools which hashrate share is lower than that + const poolShareThreshold = this.isMobile() ? 2 : 1; // Do not draw pools which hashrate share is lower than that const data: object[] = []; + let totalShareOther = 0; + let totalBlockOther = 0; + let totalEstimatedHashrateOther = 0; + + let edgeDistance: any = '20%'; + if (this.isMobile() && this.widget) { + edgeDistance = 0; + } else if (this.isMobile() && !this.widget || this.widget) { + edgeDistance = 20; + } miningStats.pools.forEach((pool) => { if (parseFloat(pool.share) < poolShareThreshold) { + totalShareOther += parseFloat(pool.share); + totalBlockOther += pool.blockCount; + totalEstimatedHashrateOther += pool.lastEstimatedHashrate; return; } data.push({ itemStyle: { - color: poolsColor[pool.name.replace(/[^a-zA-Z0-9]/g, "").toLowerCase()], + color: poolsColor[pool.name.replace(/[^a-zA-Z0-9]/g, '').toLowerCase()], }, value: pool.share, name: pool.name + (this.isMobile() ? `` : ` (${pool.share}%)`), label: { + overflow: 'none', color: '#b1b1b1', - overflow: 'break', + alignTo: 'edge', + edgeDistance: edgeDistance, }, tooltip: { backgroundColor: 'rgba(17, 19, 31, 1)', @@ -144,6 +159,42 @@ export class PoolRankingComponent implements OnInit { data: pool.poolId, } as PieSeriesOption); }); + + // 'Other' + data.push({ + itemStyle: { + color: 'grey', + }, + value: totalShareOther, + name: 'Other' + (this.isMobile() ? `` : ` (${totalShareOther.toFixed(2)}%)`), + label: { + overflow: 'none', + color: '#b1b1b1', + alignTo: 'edge', + edgeDistance: edgeDistance + }, + tooltip: { + backgroundColor: 'rgba(17, 19, 31, 1)', + borderRadius: 4, + shadowColor: 'rgba(0, 0, 0, 0.5)', + textStyle: { + color: '#b1b1b1', + }, + borderColor: '#000', + formatter: () => { + if (this.poolsWindowPreference === '24h') { + return `${'Other'} (${totalShareOther.toFixed(2)}%)
` + + totalEstimatedHashrateOther.toString() + ' PH/s' + + `
` + totalBlockOther.toString() + ` blocks`; + } else { + return `${'Other'} (${totalShareOther.toFixed(2)}%)
` + + totalBlockOther.toString() + ` blocks`; + } + } + }, + data: 9999 as any, + } as PieSeriesOption); + return data; } @@ -154,9 +205,18 @@ export class PoolRankingComponent implements OnInit { } network = network.charAt(0).toUpperCase() + network.slice(1); - let radius: any[] = ['20%', '70%']; - if (this.isMobile() || this.widget) { - radius = ['20%', '60%']; + let radius: any[] = ['20%', '80%']; + let top: any = undefined; let bottom = undefined; let height = undefined; + if (this.isMobile() && this.widget) { + radius = ['10%', '50%']; + } else if (this.isMobile() && !this.widget) { + top = 0; + height = 300; + radius = ['10%', '50%']; + } else if (this.widget) { + radius = ['15%', '60%']; + } else { + top = 35; } this.chartOptions = { @@ -180,14 +240,15 @@ export class PoolRankingComponent implements OnInit { }, series: [ { - top: this.widget ? 0 : 35, + minShowLabelAngle: 3.6, + top: top, + bottom: bottom, + height: height, name: 'Mining pool', type: 'pie', radius: radius, data: this.generatePoolsChartSerieData(miningStats), labelLine: { - length: this.isMobile() ? 10 : 15, - length2: this.isMobile() ? 0 : 15, lineStyle: { width: 2, }, @@ -223,6 +284,9 @@ export class PoolRankingComponent implements OnInit { this.chartInstance = ec; this.chartInstance.on('click', (e) => { + if (e.data.data === 9999) { // "Other" + return; + } this.router.navigate(['/mining/pool/', e.data.data]); }); } From f9e361a9c0a963c20c7179731b65cb6e7a760625 Mon Sep 17 00:00:00 2001 From: nymkappa Date: Sat, 5 Mar 2022 17:21:11 +0100 Subject: [PATCH 12/16] "view more" links - placeholders --- .../hashrate-chart.component.html | 12 ++--- .../hashrate-chart.component.scss | 7 ++- .../hashrate-chart.component.ts | 2 +- .../hashrate-chart-pools.component.scss | 2 +- .../mining-dashboard.component.html | 42 ++++++++++----- .../mining-dashboard.component.scss | 53 +++++++++++++++++++ .../pool-ranking/pool-ranking.component.scss | 5 +- .../pool-ranking/pool-ranking.component.ts | 4 ++ 8 files changed, 103 insertions(+), 24 deletions(-) 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 9c30a3334..eaa9fa809 100644 --- a/frontend/src/app/components/hashrate-chart/hashrate-chart.component.html +++ b/frontend/src/app/components/hashrate-chart/hashrate-chart.component.html @@ -31,22 +31,22 @@
-
- +
+
- - + + - - + +
Timestamp AdjustedDifficultyChangeDifficultyChange
‎{{ diffChange.timestamp * 1000 | date:'yyyy-MM-dd HH:mm' }} {{ diffChange.difficultyShorten }}{{ formatNumber(diffChange.change, locale, '1.2-2') }}%{{ diffChange.difficultyShorten }}{{ formatNumber(diffChange.change, locale, '1.2-2') }}%
diff --git a/frontend/src/app/components/hashrate-chart/hashrate-chart.component.scss b/frontend/src/app/components/hashrate-chart/hashrate-chart.component.scss index eb97dc067..4d9e0e5fa 100644 --- a/frontend/src/app/components/hashrate-chart/hashrate-chart.component.scss +++ b/frontend/src/app/components/hashrate-chart/hashrate-chart.component.scss @@ -29,7 +29,7 @@ .chart-widget { width: 100%; height: 100%; - max-height: 325px; + max-height: 293px; } .formRadioGroup { @@ -48,3 +48,8 @@ } } } + +.compact td { + padding: 0 !important; + margin: 0.15rem !important; +} \ No newline at end of file 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 db7d79d93..0ff9115c7 100644 --- a/frontend/src/app/components/hashrate-chart/hashrate-chart.component.ts +++ b/frontend/src/app/components/hashrate-chart/hashrate-chart.component.ts @@ -115,7 +115,7 @@ export class HashrateChartComponent implements OnInit { } return { availableTimespanDay: availableTimespanDay, - difficulty: this.tableOnly ? tableData.slice(0, 8) : tableData + difficulty: this.tableOnly ? (this.isMobile() ? tableData.slice(0, 12) : tableData.slice(0, 9)) : tableData }; }), ); diff --git a/frontend/src/app/components/hashrates-chart-pools/hashrate-chart-pools.component.scss b/frontend/src/app/components/hashrates-chart-pools/hashrate-chart-pools.component.scss index 7c18b0382..4f15e51d6 100644 --- a/frontend/src/app/components/hashrates-chart-pools/hashrate-chart-pools.component.scss +++ b/frontend/src/app/components/hashrates-chart-pools/hashrate-chart-pools.component.scss @@ -29,7 +29,7 @@ .chart-widget { width: 100%; height: 100%; - max-height: 325px; + max-height: 293px; } .formRadioGroup { 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 9d0eebe88..f332aec35 100644 --- a/frontend/src/app/components/mining-dashboard/mining-dashboard.component.html +++ b/frontend/src/app/components/mining-dashboard/mining-dashboard.component.html @@ -20,12 +20,29 @@
-
- - Mining Pools Share (1w) - -
+
+
+
Blocks (1w)
+

+ 1082 +

+
+
+
Miners luck (1w)
+

+ 107.25% +

+
+
+
Miners Gini (1w)
+

+ 0.123 +

+
+
+ +
@@ -35,11 +52,10 @@
- - Hashrate (1y) - + Hashrate (1y)
+
@@ -49,11 +65,10 @@
- - Mining Pools Dominance (1y) - + Mining Pools Dominance (1y)
+
@@ -62,11 +77,10 @@
diff --git a/frontend/src/app/components/mining-dashboard/mining-dashboard.component.scss b/frontend/src/app/components/mining-dashboard/mining-dashboard.component.scss index 4f05686a3..b2e9abde9 100644 --- a/frontend/src/app/components/mining-dashboard/mining-dashboard.component.scss +++ b/frontend/src/app/components/mining-dashboard/mining-dashboard.component.scss @@ -16,6 +16,7 @@ .card-title { font-size: 1rem; + color: #4a68b9; } .card-title > a { color: #4a68b9; @@ -54,3 +55,55 @@ text-align: center; padding-bottom: 3px; } + +.pool-distribution { + min-height: 56px; + display: block; + @media (min-width: 485px) { + display: flex; + flex-direction: row; + } + h5 { + margin-bottom: 10px; + } + .item { + width: 50%; + margin: 0px auto 10px; + display: inline-block; + @media (min-width: 485px) { + margin: 0px auto 10px; + } + @media (min-width: 785px) { + margin: 0px auto 0px; + } + &:last-child { + margin: 0px auto 0px; + } + &:nth-child(2) { + order: 2; + @media (min-width: 485px) { + order: 3; + } + } + &:nth-child(3) { + order: 3; + @media (min-width: 485px) { + order: 2; + display: block; + } + @media (min-width: 768px) { + display: none; + } + @media (min-width: 992px) { + display: block; + } + } + .card-text { + font-size: 18px; + span { + color: #ffffff66; + font-size: 12px; + } + } + } +} \ No newline at end of file diff --git a/frontend/src/app/components/pool-ranking/pool-ranking.component.scss b/frontend/src/app/components/pool-ranking/pool-ranking.component.scss index 4ba007598..ee0cf8e85 100644 --- a/frontend/src/app/components/pool-ranking/pool-ranking.component.scss +++ b/frontend/src/app/components/pool-ranking/pool-ranking.component.scss @@ -7,7 +7,10 @@ .chart-widget { width: 100%; height: 100%; - max-height: 325px; + max-height: 270px; + @media (max-width: 767.98px) { + max-height: 200px; + } } .formRadioGroup { diff --git a/frontend/src/app/components/pool-ranking/pool-ranking.component.ts b/frontend/src/app/components/pool-ranking/pool-ranking.component.ts index cd2970660..ae252330b 100644 --- a/frontend/src/app/components/pool-ranking/pool-ranking.component.ts +++ b/frontend/src/app/components/pool-ranking/pool-ranking.component.ts @@ -208,6 +208,8 @@ export class PoolRankingComponent implements OnInit { let radius: any[] = ['20%', '80%']; let top: any = undefined; let bottom = undefined; let height = undefined; if (this.isMobile() && this.widget) { + top = -30; + height = 270; radius = ['10%', '50%']; } else if (this.isMobile() && !this.widget) { top = 0; @@ -215,6 +217,8 @@ export class PoolRankingComponent implements OnInit { radius = ['10%', '50%']; } else if (this.widget) { radius = ['15%', '60%']; + top = -20; + height = 330; } else { top = 35; } From d45f3c32cfbbf9c7b8de2addd16821504436a8c6 Mon Sep 17 00:00:00 2001 From: nymkappa Date: Sat, 5 Mar 2022 18:47:21 +0100 Subject: [PATCH 13/16] Show more stats in pool ranking pie widget --- .../hashrate-chart-pools.component.ts | 4 +- .../mining-dashboard.component.html | 21 ------- .../mining-dashboard.component.scss | 52 ----------------- .../pool-ranking/pool-ranking.component.html | 21 +++++++ .../pool-ranking/pool-ranking.component.scss | 56 +++++++++++++++++++ .../pool-ranking/pool-ranking.component.ts | 3 +- .../src/app/interfaces/node-api.interface.ts | 9 +-- frontend/src/app/services/mining.service.ts | 2 +- 8 files changed, 83 insertions(+), 85 deletions(-) diff --git a/frontend/src/app/components/hashrates-chart-pools/hashrate-chart-pools.component.ts b/frontend/src/app/components/hashrates-chart-pools/hashrate-chart-pools.component.ts index 3d7935e3d..a131cd979 100644 --- a/frontend/src/app/components/hashrates-chart-pools/hashrate-chart-pools.component.ts +++ b/frontend/src/app/components/hashrates-chart-pools/hashrate-chart-pools.component.ts @@ -78,7 +78,7 @@ export class HashrateChartPoolsComponent implements OnInit { name: name, showSymbol: false, symbol: 'none', - data: grouped[name].map((val) => [val.timestamp * 1000, (val.share * 100).toFixed(2)]), + data: grouped[name].map((val) => [val.timestamp * 1000, val.share * 100]), type: 'line', lineStyle: { width: 0 }, areaStyle: { opacity: 1 }, @@ -149,7 +149,7 @@ export class HashrateChartPoolsComponent implements OnInit { data.sort((a, b) => b.data[1] - a.data[1]); for (const pool of data) { if (pool.data[1] > 0) { - tooltip += `${pool.marker} ${pool.seriesName}: ${pool.data[1]}%
` + tooltip += `${pool.marker} ${pool.seriesName}: ${pool.data[1].toFixed(2)}%
`; } } return tooltip; 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 f332aec35..88406fbf6 100644 --- a/frontend/src/app/components/mining-dashboard/mining-dashboard.component.html +++ b/frontend/src/app/components/mining-dashboard/mining-dashboard.component.html @@ -20,27 +20,6 @@
-
-
-
Blocks (1w)
-

- 1082 -

-
-
-
Miners luck (1w)
-

- 107.25% -

-
-
-
Miners Gini (1w)
-

- 0.123 -

-
-
-
diff --git a/frontend/src/app/components/mining-dashboard/mining-dashboard.component.scss b/frontend/src/app/components/mining-dashboard/mining-dashboard.component.scss index b2e9abde9..dea5e673f 100644 --- a/frontend/src/app/components/mining-dashboard/mining-dashboard.component.scss +++ b/frontend/src/app/components/mining-dashboard/mining-dashboard.component.scss @@ -55,55 +55,3 @@ text-align: center; padding-bottom: 3px; } - -.pool-distribution { - min-height: 56px; - display: block; - @media (min-width: 485px) { - display: flex; - flex-direction: row; - } - h5 { - margin-bottom: 10px; - } - .item { - width: 50%; - margin: 0px auto 10px; - display: inline-block; - @media (min-width: 485px) { - margin: 0px auto 10px; - } - @media (min-width: 785px) { - margin: 0px auto 0px; - } - &:last-child { - margin: 0px auto 0px; - } - &:nth-child(2) { - order: 2; - @media (min-width: 485px) { - order: 3; - } - } - &:nth-child(3) { - order: 3; - @media (min-width: 485px) { - order: 2; - display: block; - } - @media (min-width: 768px) { - display: none; - } - @media (min-width: 992px) { - display: block; - } - } - .card-text { - font-size: 18px; - span { - color: #ffffff66; - font-size: 12px; - } - } - } -} \ No newline at end of file diff --git a/frontend/src/app/components/pool-ranking/pool-ranking.component.html b/frontend/src/app/components/pool-ranking/pool-ranking.component.html index 43b71d4c9..6bba39df4 100644 --- a/frontend/src/app/components/pool-ranking/pool-ranking.component.html +++ b/frontend/src/app/components/pool-ranking/pool-ranking.component.html @@ -1,5 +1,26 @@
+
+
+
Pools luck (1w)
+

+ {{ miningStats['minersLuck'] }}% +

+
+
+
Blocks (1w)
+

+ {{ miningStats.blockCount }} +

+
+
+
Pools count (1w)
+

+ {{ miningStats.pools.length }} +

+
+
+
diff --git a/frontend/src/app/components/pool-ranking/pool-ranking.component.scss b/frontend/src/app/components/pool-ranking/pool-ranking.component.scss index ee0cf8e85..f73486395 100644 --- a/frontend/src/app/components/pool-ranking/pool-ranking.component.scss +++ b/frontend/src/app/components/pool-ranking/pool-ranking.component.scss @@ -47,3 +47,59 @@ .loadingGraphs.widget { top: 25%; } + +.pool-distribution { + min-height: 56px; + display: block; + @media (min-width: 485px) { + display: flex; + flex-direction: row; + } + h5 { + margin-bottom: 10px; + } + .item { + width: 50%; + margin: 0px auto 10px; + display: inline-block; + @media (min-width: 485px) { + margin: 0px auto 10px; + } + @media (min-width: 785px) { + margin: 0px auto 0px; + } + &:last-child { + margin: 0px auto 0px; + } + &:nth-child(2) { + order: 2; + @media (min-width: 485px) { + order: 3; + } + } + &:nth-child(3) { + order: 3; + @media (min-width: 485px) { + order: 2; + display: block; + } + @media (min-width: 768px) { + display: none; + } + @media (min-width: 992px) { + display: block; + } + } + .card-title { + font-size: 1rem; + color: #4a68b9; + } + .card-text { + font-size: 18px; + span { + color: #ffffff66; + font-size: 12px; + } + } + } +} \ No newline at end of file diff --git a/frontend/src/app/components/pool-ranking/pool-ranking.component.ts b/frontend/src/app/components/pool-ranking/pool-ranking.component.ts index ae252330b..eacf4683f 100644 --- a/frontend/src/app/components/pool-ranking/pool-ranking.component.ts +++ b/frontend/src/app/components/pool-ranking/pool-ranking.component.ts @@ -85,6 +85,7 @@ export class PoolRankingComponent implements OnInit { }), map(data => { data.pools = data.pools.map((pool: SinglePoolStats) => this.formatPoolUI(pool)); + data['minersLuck'] = (100 * (data.blockCount / 1008)).toFixed(2); // luck 1w return data; }), tap(data => { @@ -298,7 +299,7 @@ export class PoolRankingComponent implements OnInit { /** * Default mining stats if something goes wrong */ - getEmptyMiningStat() { + getEmptyMiningStat(): MiningStats { return { lastEstimatedHashrate: 'Error', blockCount: 0, diff --git a/frontend/src/app/interfaces/node-api.interface.ts b/frontend/src/app/interfaces/node-api.interface.ts index d8760d1f0..a05671257 100644 --- a/frontend/src/app/interfaces/node-api.interface.ts +++ b/frontend/src/app/interfaces/node-api.interface.ts @@ -64,7 +64,7 @@ export interface SinglePoolStats { blockCount: number; emptyBlocks: number; rank: number; - share: string; + share: number; lastEstimatedHashrate: string; emptyBlockRatio: string; logo: string; @@ -75,13 +75,6 @@ export interface PoolsStats { oldestIndexedBlockTimestamp: number; pools: SinglePoolStats[]; } -export interface MiningStats { - lastEstimatedHashrate: string; - blockCount: number; - totalEmptyBlock: number; - totalEmptyBlockRatio: string; - pools: SinglePoolStats[]; -} /** * Pool component diff --git a/frontend/src/app/services/mining.service.ts b/frontend/src/app/services/mining.service.ts index c216515b0..68f7e9da1 100644 --- a/frontend/src/app/services/mining.service.ts +++ b/frontend/src/app/services/mining.service.ts @@ -73,7 +73,7 @@ export class MiningService { const totalEmptyBlockRatio = (totalEmptyBlock / stats.blockCount * 100).toFixed(2); const poolsStats = stats.pools.map((poolStat) => { return { - share: (poolStat.blockCount / stats.blockCount * 100).toFixed(2), + share: parseFloat((poolStat.blockCount / stats.blockCount * 100).toFixed(2)), lastEstimatedHashrate: (poolStat.blockCount / stats.blockCount * stats.lastEstimatedHashrate / hashrateDivider).toFixed(2), emptyBlockRatio: (poolStat.emptyBlocks / poolStat.blockCount * 100).toFixed(2), logo: `./resources/mining-pools/` + poolStat.name.toLowerCase().replace(' ', '').replace('.', '') + '.svg', From 84ef4247520600de547c5b1b01eac8dfa15f10ee Mon Sep 17 00:00:00 2001 From: nymkappa Date: Sat, 5 Mar 2022 19:05:19 +0100 Subject: [PATCH 14/16] Fix halving calculation --- frontend/src/app/components/difficulty/difficulty.component.ts | 2 +- .../components/mining-dashboard/mining-dashboard.component.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/components/difficulty/difficulty.component.ts b/frontend/src/app/components/difficulty/difficulty.component.ts index 8714ff9f1..b22001ef1 100644 --- a/frontend/src/app/components/difficulty/difficulty.component.ts +++ b/frontend/src/app/components/difficulty/difficulty.component.ts @@ -98,7 +98,7 @@ export class DifficultyComponent implements OnInit { colorPreviousAdjustments = '#ffffff66'; } - const blocksUntilHalving = block.height % 210000; + const blocksUntilHalving = 210000 - (block.height % 210000); const timeUntilHalving = (blocksUntilHalving * timeAvgMins * 60 * 1000) + (now * 1000); return { 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 88406fbf6..cd8330321 100644 --- a/frontend/src/app/components/mining-dashboard/mining-dashboard.component.html +++ b/frontend/src/app/components/mining-dashboard/mining-dashboard.component.html @@ -12,7 +12,7 @@
Difficulty Adjustment
- +
From bc133937784dec2828d77e10dc7121803d062f33 Mon Sep 17 00:00:00 2001 From: nymkappa Date: Mon, 7 Mar 2022 11:22:54 +0100 Subject: [PATCH 15/16] Implement temporary reward stats for the mining dashboard --- .../difficulty/difficulty.component.html | 2 +- .../mining-dashboard.component.html | 41 +++++++-- .../mining-dashboard.component.scss | 88 +++++++++++++++++++ .../mining-dashboard.component.ts | 35 +++++++- .../pool-ranking/pool-ranking.component.ts | 4 +- 5 files changed, 159 insertions(+), 11 deletions(-) diff --git a/frontend/src/app/components/difficulty/difficulty.component.html b/frontend/src/app/components/difficulty/difficulty.component.html index bda189eab..3684b8de4 100644 --- a/frontend/src/app/components/difficulty/difficulty.component.html +++ b/frontend/src/app/components/difficulty/difficulty.component.html @@ -47,7 +47,7 @@
-
Next halving
+
Next Halving
{{ i }} blocks 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 cd8330321..6dbb541c3 100644 --- a/frontend/src/app/components/mining-dashboard/mining-dashboard.component.html +++ b/frontend/src/app/components/mining-dashboard/mining-dashboard.component.html @@ -3,8 +3,35 @@
-
Placeholder
+
Reward stats
+
+
+
+
Miners Reward
+
+ +
in the last 8 blocks
+
+
+
+
Reward Per Tx
+
+ {{ rewardStats.rewardPerTx }} + sats/tx +
in the last 8 blocks
+
+
+
+
Average Fee
+
+ {{ rewardStats.feePerTx }} + sats/tx +
in the last 8 blocks
+
+
+
+
@@ -21,7 +48,8 @@
@@ -34,7 +62,8 @@ Hashrate (1y) - +
@@ -47,7 +76,8 @@ Mining Pools Dominance (1y) - +
@@ -59,7 +89,8 @@ Adjusments - + diff --git a/frontend/src/app/components/mining-dashboard/mining-dashboard.component.scss b/frontend/src/app/components/mining-dashboard/mining-dashboard.component.scss index dea5e673f..828ee7ed0 100644 --- a/frontend/src/app/components/mining-dashboard/mining-dashboard.component.scss +++ b/frontend/src/app/components/mining-dashboard/mining-dashboard.component.scss @@ -55,3 +55,91 @@ text-align: center; padding-bottom: 3px; } + +.general-stats { + min-height: 56px; + display: block; + @media (min-width: 485px) { + display: flex; + flex-direction: row; + } + h5 { + margin-bottom: 10px; + } + .item { + width: 50%; + margin: 0px auto 10px; + display: inline-block; + @media (min-width: 485px) { + margin: 0px auto 10px; + } + @media (min-width: 785px) { + margin: 0px auto 0px; + } + &:last-child { + margin: 0px auto 0px; + } + &:nth-child(2) { + order: 2; + @media (min-width: 485px) { + order: 3; + } + } + &:nth-child(3) { + order: 3; + @media (min-width: 485px) { + order: 2; + display: block; + } + @media (min-width: 768px) { + display: none; + } + @media (min-width: 992px) { + display: block; + } + } + .card-title { + font-size: 1rem; + color: #4a68b9; + } + .card-text { + font-size: 18px; + span { + color: #ffffff66; + font-size: 12px; + } + } + } +} + +.difficulty-adjustment-container { + display: flex; + flex-direction: row; + justify-content: space-around; + height: 76px; + .shared-block { + color: #ffffff66; + font-size: 12px; + } + .item { + padding: 0 5px; + width: 100%; + &:nth-child(1) { + display: none; + @media (min-width: 485px) { + display: table-cell; + } + @media (min-width: 768px) { + display: none; + } + @media (min-width: 992px) { + display: table-cell; + } + } + } + .card-text { + font-size: 22px; + margin-top: -9px; + position: relative; + } +} diff --git a/frontend/src/app/components/mining-dashboard/mining-dashboard.component.ts b/frontend/src/app/components/mining-dashboard/mining-dashboard.component.ts index aac546ca1..b4cc95a1e 100644 --- a/frontend/src/app/components/mining-dashboard/mining-dashboard.component.ts +++ b/frontend/src/app/components/mining-dashboard/mining-dashboard.component.ts @@ -1,5 +1,10 @@ -import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Inject, LOCALE_ID, OnDestroy, OnInit } from '@angular/core'; +import { map } from 'rxjs/operators'; import { SeoService } from 'src/app/services/seo.service'; +import { StateService } from 'src/app/services/state.service'; +import { formatNumber } from '@angular/common'; +import { WebsocketService } from 'src/app/services/websocket.service'; +import { Observable } from 'rxjs'; @Component({ selector: 'app-mining-dashboard', @@ -8,12 +13,36 @@ import { SeoService } from 'src/app/services/seo.service'; changeDetection: ChangeDetectionStrategy.OnPush, }) export class MiningDashboardComponent implements OnInit { + private blocks = []; - constructor(private seoService: SeoService) { + public $rewardStats: Observable; + public totalReward = 0; + public rewardPerTx = '~'; + public feePerTx = '~'; + + constructor(private seoService: SeoService, + public stateService: StateService, + private websocketService: WebsocketService, + @Inject(LOCALE_ID) private locale: string, + ) { this.seoService.setTitle($localize`:@@mining.mining-dashboard:Mining Dashboard`); } ngOnInit(): void { - } + this.$rewardStats = this.stateService.blocks$.pipe( + map(([block]) => { + this.blocks.push(block); + this.blocks = this.blocks.slice(0, 8); + const totalTx = this.blocks.reduce((acc, block) => acc + block.tx_count, 0); + const totalFee = this.blocks.reduce((acc, block) => acc + block.extras?.totalFees ?? 0, 0); + const totalReward = this.blocks.reduce((acc, block) => acc + block.extras?.reward ?? 0, 0); + return { + 'totalReward': totalReward, + 'rewardPerTx': formatNumber(totalReward / totalTx, this.locale, '1.0-0'), + 'feePerTx': formatNumber(totalFee / totalTx, this.locale, '1.0-0'), + } + }) + ); + } } diff --git a/frontend/src/app/components/pool-ranking/pool-ranking.component.ts b/frontend/src/app/components/pool-ranking/pool-ranking.component.ts index eacf4683f..646cae06b 100644 --- a/frontend/src/app/components/pool-ranking/pool-ranking.component.ts +++ b/frontend/src/app/components/pool-ranking/pool-ranking.component.ts @@ -116,7 +116,7 @@ export class PoolRankingComponent implements OnInit { if (this.isMobile() && this.widget) { edgeDistance = 0; } else if (this.isMobile() && !this.widget || this.widget) { - edgeDistance = 20; + edgeDistance = 35; } miningStats.pools.forEach((pool) => { @@ -131,7 +131,7 @@ export class PoolRankingComponent implements OnInit { color: poolsColor[pool.name.replace(/[^a-zA-Z0-9]/g, '').toLowerCase()], }, value: pool.share, - name: pool.name + (this.isMobile() ? `` : ` (${pool.share}%)`), + name: pool.name + ((this.isMobile() || this.widget) ? `` : ` (${pool.share}%)`), label: { overflow: 'none', color: '#b1b1b1', From 05342079b305abbec282f5f3916cadae34d920ba Mon Sep 17 00:00:00 2001 From: nymkappa Date: Mon, 7 Mar 2022 11:41:41 +0100 Subject: [PATCH 16/16] Disable mining charts iteration on mobile widgets --- .../app/components/hashrate-chart/hashrate-chart.component.ts | 1 + .../hashrates-chart-pools/hashrate-chart-pools.component.ts | 1 + .../src/app/components/pool-ranking/pool-ranking.component.ts | 1 + 3 files changed, 3 insertions(+) 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 0ff9115c7..a5f2b63b8 100644 --- a/frontend/src/app/components/hashrate-chart/hashrate-chart.component.ts +++ b/frontend/src/app/components/hashrate-chart/hashrate-chart.component.ts @@ -142,6 +142,7 @@ export class HashrateChartComponent implements OnInit { bottom: this.widget ? 30 : 60, }, tooltip: { + show: !this.isMobile() || !this.widget, trigger: 'axis', axisPointer: { type: 'line' diff --git a/frontend/src/app/components/hashrates-chart-pools/hashrate-chart-pools.component.ts b/frontend/src/app/components/hashrates-chart-pools/hashrate-chart-pools.component.ts index a131cd979..7e3f081a6 100644 --- a/frontend/src/app/components/hashrates-chart-pools/hashrate-chart-pools.component.ts +++ b/frontend/src/app/components/hashrates-chart-pools/hashrate-chart-pools.component.ts @@ -132,6 +132,7 @@ export class HashrateChartPoolsComponent implements OnInit { top: this.widget ? 10 : 40, }, tooltip: { + show: !this.isMobile() || !this.widget, trigger: 'axis', axisPointer: { type: 'line' diff --git a/frontend/src/app/components/pool-ranking/pool-ranking.component.ts b/frontend/src/app/components/pool-ranking/pool-ranking.component.ts index 646cae06b..64641c31d 100644 --- a/frontend/src/app/components/pool-ranking/pool-ranking.component.ts +++ b/frontend/src/app/components/pool-ranking/pool-ranking.component.ts @@ -139,6 +139,7 @@ export class PoolRankingComponent implements OnInit { edgeDistance: edgeDistance, }, tooltip: { + show: !this.isMobile() || !this.widget, backgroundColor: 'rgba(17, 19, 31, 1)', borderRadius: 4, shadowColor: 'rgba(0, 0, 0, 0.5)',