Generate daily average hashrate data
This commit is contained in:
parent
38b37a3ee7
commit
6fe8f6fa1e
@ -170,10 +170,7 @@ class Blocks {
|
|||||||
* Index all blocks metadata for the mining dashboard
|
* Index all blocks metadata for the mining dashboard
|
||||||
*/
|
*/
|
||||||
public async $generateBlockDatabase() {
|
public async $generateBlockDatabase() {
|
||||||
if (this.blockIndexingStarted === true ||
|
if (this.blockIndexingStarted) {
|
||||||
!Common.indexingEnabled() ||
|
|
||||||
memPool.hasPriority()
|
|
||||||
) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ import logger from '../logger';
|
|||||||
const sleep = (ms: number) => new Promise(res => setTimeout(res, ms));
|
const sleep = (ms: number) => new Promise(res => setTimeout(res, ms));
|
||||||
|
|
||||||
class DatabaseMigration {
|
class DatabaseMigration {
|
||||||
private static currentVersion = 6;
|
private static currentVersion = 7;
|
||||||
private queryTimeout = 120000;
|
private queryTimeout = 120000;
|
||||||
private statisticsAddedIndexed = false;
|
private statisticsAddedIndexed = false;
|
||||||
|
|
||||||
@ -116,6 +116,12 @@ class DatabaseMigration {
|
|||||||
await this.$executeQuery(connection, 'ALTER TABLE blocks ADD `merkle_root` varchar(65) NOT NULL DEFAULT ""');
|
await this.$executeQuery(connection, 'ALTER TABLE blocks ADD `merkle_root` varchar(65) NOT NULL DEFAULT ""');
|
||||||
await this.$executeQuery(connection, 'ALTER TABLE blocks ADD `previous_block_hash` varchar(65) NULL');
|
await this.$executeQuery(connection, 'ALTER TABLE blocks ADD `previous_block_hash` varchar(65) NULL');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (databaseSchemaVersion < 7 && isBitcoin === true) {
|
||||||
|
await this.$executeQuery(connection, 'DROP table IF EXISTS hashrates;');
|
||||||
|
await this.$executeQuery(connection, this.getCreateDailyStatsTableQuery(), await this.$checkIfTableExists('hashrates'));
|
||||||
|
}
|
||||||
|
|
||||||
connection.release();
|
connection.release();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
connection.release();
|
connection.release();
|
||||||
@ -398,6 +404,17 @@ class DatabaseMigration {
|
|||||||
FOREIGN KEY (pool_id) REFERENCES pools (id)
|
FOREIGN KEY (pool_id) REFERENCES pools (id)
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;`;
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8;`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getCreateDailyStatsTableQuery(): string {
|
||||||
|
return `CREATE TABLE IF NOT EXISTS hashrates (
|
||||||
|
hashrate_timestamp timestamp NOT NULL,
|
||||||
|
avg_hashrate double unsigned DEFAULT '0',
|
||||||
|
pool_id smallint unsigned NULL,
|
||||||
|
PRIMARY KEY (hashrate_timestamp),
|
||||||
|
INDEX (pool_id),
|
||||||
|
FOREIGN KEY (pool_id) REFERENCES pools (id)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8;`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default new DatabaseMigration();
|
export default new DatabaseMigration();
|
||||||
|
@ -1,9 +1,13 @@
|
|||||||
import { PoolInfo, PoolStats } from '../mempool.interfaces';
|
import { PoolInfo, PoolStats } from '../mempool.interfaces';
|
||||||
import BlocksRepository, { EmptyBlocks } from '../repositories/BlocksRepository';
|
import BlocksRepository, { EmptyBlocks } from '../repositories/BlocksRepository';
|
||||||
import PoolsRepository from '../repositories/PoolsRepository';
|
import PoolsRepository from '../repositories/PoolsRepository';
|
||||||
|
import HashratesRepository from '../repositories/HashratesRepository';
|
||||||
import bitcoinClient from './bitcoin/bitcoin-client';
|
import bitcoinClient from './bitcoin/bitcoin-client';
|
||||||
|
import logger from '../logger';
|
||||||
|
|
||||||
class Mining {
|
class Mining {
|
||||||
|
hashrateIndexingStarted = false;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,7 +49,7 @@ class Mining {
|
|||||||
poolsStatistics['blockCount'] = blockCount;
|
poolsStatistics['blockCount'] = blockCount;
|
||||||
|
|
||||||
const blockHeightTip = await bitcoinClient.getBlockCount();
|
const blockHeightTip = await bitcoinClient.getBlockCount();
|
||||||
const lastBlockHashrate = await bitcoinClient.getNetworkHashPs(120, blockHeightTip);
|
const lastBlockHashrate = await bitcoinClient.getNetworkHashPs(144, blockHeightTip);
|
||||||
poolsStatistics['lastEstimatedHashrate'] = lastBlockHashrate;
|
poolsStatistics['lastEstimatedHashrate'] = lastBlockHashrate;
|
||||||
|
|
||||||
return poolsStatistics;
|
return poolsStatistics;
|
||||||
@ -82,6 +86,52 @@ class Mining {
|
|||||||
oldestIndexedBlockTimestamp: oldestBlock.getTime(),
|
oldestIndexedBlockTimestamp: oldestBlock.getTime(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public async $generateNetworkHashrateHistory() : Promise<void> {
|
||||||
|
if (this.hashrateIndexingStarted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.hashrateIndexingStarted = true;
|
||||||
|
|
||||||
|
const totalIndexed = await BlocksRepository.$blockCount(null, null);
|
||||||
|
const indexedTimestamp = await HashratesRepository.$getAllTimestamp();
|
||||||
|
|
||||||
|
const genesisTimestamp = 1231006505; // bitcoin-cli getblock 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f
|
||||||
|
const lastMidnight = new Date();
|
||||||
|
lastMidnight.setUTCHours(0); lastMidnight.setUTCMinutes(0); lastMidnight.setUTCSeconds(0); lastMidnight.setUTCMilliseconds(0);
|
||||||
|
let toTimestamp = Math.round(lastMidnight.getTime() / 1000);
|
||||||
|
|
||||||
|
while (toTimestamp > genesisTimestamp) {
|
||||||
|
const fromTimestamp = toTimestamp - 86400;
|
||||||
|
if (indexedTimestamp.includes(fromTimestamp)) {
|
||||||
|
toTimestamp -= 86400;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const blockStats: any = await BlocksRepository.$blockCountBetweenTimestamp(
|
||||||
|
null, fromTimestamp, toTimestamp
|
||||||
|
);
|
||||||
|
let lastBlockHashrate = await bitcoinClient.getNetworkHashPs(blockStats.blockCount, blockStats.lastBlockHeight);
|
||||||
|
|
||||||
|
if (toTimestamp % 864000 === 0) {
|
||||||
|
const progress = Math.round((totalIndexed - blockStats.lastBlockHeight) / totalIndexed * 100);
|
||||||
|
const formattedDate = new Date(fromTimestamp * 1000).toUTCString();
|
||||||
|
logger.debug(`Counting blocks and hashrate for ${formattedDate}. Progress: ${progress}%`);
|
||||||
|
}
|
||||||
|
|
||||||
|
await HashratesRepository.$saveDailyStat({
|
||||||
|
hashrateTimestamp: fromTimestamp,
|
||||||
|
avgHashrate: lastBlockHashrate,
|
||||||
|
poolId: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
toTimestamp -= 86400;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default new Mining();
|
export default new Mining();
|
||||||
|
@ -26,6 +26,7 @@ import poolsParser from './api/pools-parser';
|
|||||||
import syncAssets from './sync-assets';
|
import syncAssets from './sync-assets';
|
||||||
import icons from './api/liquid/icons';
|
import icons from './api/liquid/icons';
|
||||||
import { Common } from './api/common';
|
import { Common } from './api/common';
|
||||||
|
import mining from './api/mining';
|
||||||
|
|
||||||
class Server {
|
class Server {
|
||||||
private wss: WebSocket.Server | undefined;
|
private wss: WebSocket.Server | undefined;
|
||||||
@ -138,7 +139,7 @@ class Server {
|
|||||||
}
|
}
|
||||||
await blocks.$updateBlocks();
|
await blocks.$updateBlocks();
|
||||||
await memPool.$updateMempool();
|
await memPool.$updateMempool();
|
||||||
blocks.$generateBlockDatabase();
|
this.runIndexingWhenReady();
|
||||||
|
|
||||||
setTimeout(this.runMainUpdateLoop.bind(this), config.MEMPOOL.POLL_RATE_MS);
|
setTimeout(this.runMainUpdateLoop.bind(this), config.MEMPOOL.POLL_RATE_MS);
|
||||||
this.currentBackendRetryInterval = 5;
|
this.currentBackendRetryInterval = 5;
|
||||||
@ -157,6 +158,14 @@ class Server {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async runIndexingWhenReady() {
|
||||||
|
if (!Common.indexingEnabled() || mempool.hasPriority()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await blocks.$generateBlockDatabase();
|
||||||
|
await mining.$generateNetworkHashrateHistory();
|
||||||
|
}
|
||||||
|
|
||||||
setUpWebsocketHandling() {
|
setUpWebsocketHandling() {
|
||||||
if (this.wss) {
|
if (this.wss) {
|
||||||
websocketHandler.setWebsocketServer(this.wss);
|
websocketHandler.setWebsocketServer(this.wss);
|
||||||
|
@ -149,6 +149,40 @@ class BlocksRepository {
|
|||||||
return <number>rows[0].blockCount;
|
return <number>rows[0].blockCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get blocks count between two dates
|
||||||
|
* @param poolId
|
||||||
|
* @param from - The oldest timestamp
|
||||||
|
* @param to - The newest timestamp
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
public async $blockCountBetweenTimestamp(poolId: number | null, from: number, to: number): Promise<number> {
|
||||||
|
const params: any[] = [];
|
||||||
|
let query = `SELECT
|
||||||
|
count(height) as blockCount,
|
||||||
|
max(height) as lastBlockHeight
|
||||||
|
FROM blocks`;
|
||||||
|
|
||||||
|
if (poolId) {
|
||||||
|
query += ` WHERE pool_id = ?`;
|
||||||
|
params.push(poolId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (poolId) {
|
||||||
|
query += ` AND`;
|
||||||
|
} else {
|
||||||
|
query += ` WHERE`;
|
||||||
|
}
|
||||||
|
query += ` UNIX_TIMESTAMP(blockTimestamp) BETWEEN '${from}' AND '${to}'`;
|
||||||
|
|
||||||
|
// logger.debug(query);
|
||||||
|
const connection = await DB.pool.getConnection();
|
||||||
|
const [rows] = await connection.query(query, params);
|
||||||
|
connection.release();
|
||||||
|
|
||||||
|
return <number>rows[0];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the oldest indexed block
|
* Get the oldest indexed block
|
||||||
*/
|
*/
|
||||||
|
42
backend/src/repositories/HashratesRepository.ts
Normal file
42
backend/src/repositories/HashratesRepository.ts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import { DB } from '../database';
|
||||||
|
import logger from '../logger';
|
||||||
|
|
||||||
|
class HashratesRepository {
|
||||||
|
/**
|
||||||
|
* Save indexed block data in the database
|
||||||
|
*/
|
||||||
|
public async $saveDailyStat(dailyStat: any) {
|
||||||
|
const connection = await DB.pool.getConnection();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const query = `INSERT INTO
|
||||||
|
hashrates(hashrate_timestamp, avg_hashrate, pool_id)
|
||||||
|
VALUE (FROM_UNIXTIME(?), ?, ?)`;
|
||||||
|
|
||||||
|
const params: any[] = [
|
||||||
|
dailyStat.hashrateTimestamp, dailyStat.avgHashrate,
|
||||||
|
dailyStat.poolId
|
||||||
|
];
|
||||||
|
|
||||||
|
// logger.debug(query);
|
||||||
|
await connection.query(query, params);
|
||||||
|
} catch (e: any) {
|
||||||
|
logger.err('$saveHashrateInDatabase() error' + (e instanceof Error ? e.message : e));
|
||||||
|
}
|
||||||
|
|
||||||
|
connection.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array of all timestamp we've already indexed
|
||||||
|
*/
|
||||||
|
public async $getAllTimestamp(): Promise<number[]> {
|
||||||
|
const connection = await DB.pool.getConnection();
|
||||||
|
const [rows]: any[] = await connection.query(`SELECT UNIX_TIMESTAMP(hashrate_timestamp) as timestamp from hashrates`);
|
||||||
|
connection.release();
|
||||||
|
|
||||||
|
return rows.map(val => val.timestamp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new HashratesRepository();
|
@ -22,7 +22,6 @@ import elementsParser from './api/liquid/elements-parser';
|
|||||||
import icons from './api/liquid/icons';
|
import icons from './api/liquid/icons';
|
||||||
import miningStats from './api/mining';
|
import miningStats from './api/mining';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import PoolsRepository from './repositories/PoolsRepository';
|
|
||||||
import mining from './api/mining';
|
import mining from './api/mining';
|
||||||
import BlocksRepository from './repositories/BlocksRepository';
|
import BlocksRepository from './repositories/BlocksRepository';
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user