Generate daily average hashrate data

This commit is contained in:
nymkappa 2022-02-19 20:45:02 +09:00
parent 38b37a3ee7
commit 6fe8f6fa1e
No known key found for this signature in database
GPG Key ID: E155910B16E8BD04
7 changed files with 156 additions and 8 deletions

View File

@ -170,10 +170,7 @@ class Blocks {
* Index all blocks metadata for the mining dashboard
*/
public async $generateBlockDatabase() {
if (this.blockIndexingStarted === true ||
!Common.indexingEnabled() ||
memPool.hasPriority()
) {
if (this.blockIndexingStarted) {
return;
}

View File

@ -6,7 +6,7 @@ import logger from '../logger';
const sleep = (ms: number) => new Promise(res => setTimeout(res, ms));
class DatabaseMigration {
private static currentVersion = 6;
private static currentVersion = 7;
private queryTimeout = 120000;
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 `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();
} catch (e) {
connection.release();
@ -398,6 +404,17 @@ class DatabaseMigration {
FOREIGN KEY (pool_id) REFERENCES pools (id)
) 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();

View File

@ -1,9 +1,13 @@
import { PoolInfo, PoolStats } from '../mempool.interfaces';
import BlocksRepository, { EmptyBlocks } from '../repositories/BlocksRepository';
import PoolsRepository from '../repositories/PoolsRepository';
import HashratesRepository from '../repositories/HashratesRepository';
import bitcoinClient from './bitcoin/bitcoin-client';
import logger from '../logger';
class Mining {
hashrateIndexingStarted = false;
constructor() {
}
@ -45,7 +49,7 @@ class Mining {
poolsStatistics['blockCount'] = blockCount;
const blockHeightTip = await bitcoinClient.getBlockCount();
const lastBlockHashrate = await bitcoinClient.getNetworkHashPs(120, blockHeightTip);
const lastBlockHashrate = await bitcoinClient.getNetworkHashPs(144, blockHeightTip);
poolsStatistics['lastEstimatedHashrate'] = lastBlockHashrate;
return poolsStatistics;
@ -82,6 +86,52 @@ class Mining {
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();

View File

@ -26,6 +26,7 @@ import poolsParser from './api/pools-parser';
import syncAssets from './sync-assets';
import icons from './api/liquid/icons';
import { Common } from './api/common';
import mining from './api/mining';
class Server {
private wss: WebSocket.Server | undefined;
@ -138,7 +139,7 @@ class Server {
}
await blocks.$updateBlocks();
await memPool.$updateMempool();
blocks.$generateBlockDatabase();
this.runIndexingWhenReady();
setTimeout(this.runMainUpdateLoop.bind(this), config.MEMPOOL.POLL_RATE_MS);
this.currentBackendRetryInterval = 5;
@ -157,6 +158,14 @@ class Server {
}
}
async runIndexingWhenReady() {
if (!Common.indexingEnabled() || mempool.hasPriority()) {
return;
}
await blocks.$generateBlockDatabase();
await mining.$generateNetworkHashrateHistory();
}
setUpWebsocketHandling() {
if (this.wss) {
websocketHandler.setWebsocketServer(this.wss);

View File

@ -149,6 +149,40 @@ class BlocksRepository {
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
*/

View 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();

View File

@ -22,7 +22,6 @@ import elementsParser from './api/liquid/elements-parser';
import icons from './api/liquid/icons';
import miningStats from './api/mining';
import axios from 'axios';
import PoolsRepository from './repositories/PoolsRepository';
import mining from './api/mining';
import BlocksRepository from './repositories/BlocksRepository';