mempool/backend/src/api/mining.ts

187 lines
6.3 KiB
TypeScript
Raw Normal View History

import { PoolInfo, PoolStats } from '../mempool.interfaces';
import BlocksRepository, { EmptyBlocks } from '../repositories/BlocksRepository';
import PoolsRepository from '../repositories/PoolsRepository';
2022-02-19 12:45:02 +01:00
import HashratesRepository from '../repositories/HashratesRepository';
import bitcoinClient from './bitcoin/bitcoin-client';
2022-02-19 12:45:02 +01:00
import logger from '../logger';
import blocks from './blocks';
class Mining {
2022-02-19 12:45:02 +01:00
hashrateIndexingStarted = false;
constructor() {
}
/**
* Generate high level overview of the pool ranks and general stats
*/
public async $getPoolsStats(interval: string | null): Promise<object> {
const poolsStatistics = {};
2022-02-09 11:41:05 +01:00
const poolsInfo: PoolInfo[] = await PoolsRepository.$getPoolsInfo(interval);
const emptyBlocks: EmptyBlocks[] = await BlocksRepository.$getEmptyBlocks(null, interval);
const poolsStats: PoolStats[] = [];
let rank = 1;
poolsInfo.forEach((poolInfo: PoolInfo) => {
const poolStat: PoolStats = {
poolId: poolInfo.poolId, // mysql row id
name: poolInfo.name,
link: poolInfo.link,
blockCount: poolInfo.blockCount,
rank: rank++,
emptyBlocks: 0
};
for (let i = 0; i < emptyBlocks.length; ++i) {
if (emptyBlocks[i].poolId === poolInfo.poolId) {
poolStat.emptyBlocks++;
}
}
poolsStats.push(poolStat);
});
poolsStatistics['pools'] = poolsStats;
const oldestBlock = new Date(await BlocksRepository.$oldestBlockTimestamp());
poolsStatistics['oldestIndexedBlockTimestamp'] = oldestBlock.getTime();
2022-02-09 11:41:05 +01:00
const blockCount: number = await BlocksRepository.$blockCount(null, interval);
poolsStatistics['blockCount'] = blockCount;
const blockHeightTip = await bitcoinClient.getBlockCount();
2022-02-19 12:45:02 +01:00
const lastBlockHashrate = await bitcoinClient.getNetworkHashPs(144, blockHeightTip);
poolsStatistics['lastEstimatedHashrate'] = lastBlockHashrate;
return poolsStatistics;
}
/**
* Get all mining pool stats for a pool
*/
public async $getPoolStat(interval: string | null, poolId: number): Promise<object> {
const pool = await PoolsRepository.$getPool(poolId);
if (!pool) {
2022-02-09 11:41:05 +01:00
throw new Error(`This mining pool does not exist`);
}
2022-02-09 11:41:05 +01:00
const blockCount: number = await BlocksRepository.$blockCount(poolId, interval);
const emptyBlocks: EmptyBlocks[] = await BlocksRepository.$getEmptyBlocks(poolId, interval);
return {
pool: pool,
2022-02-09 11:41:05 +01:00
blockCount: blockCount,
emptyBlocks: emptyBlocks,
};
}
/**
* Return the historical difficulty adjustments and oldest indexed block timestamp
*/
public async $getHistoricalDifficulty(interval: string | null): Promise<object> {
const difficultyAdjustments = await BlocksRepository.$getBlocksDifficulty(interval);
const oldestBlock = new Date(await BlocksRepository.$oldestBlockTimestamp());
return {
adjustments: difficultyAdjustments,
oldestIndexedBlockTimestamp: oldestBlock.getTime(),
};
}
2022-02-19 12:45:02 +01:00
2022-02-19 14:09:35 +01:00
/**
* Return the historical hashrates and oldest indexed block timestamp
*/
public async $getHistoricalHashrates(interval: string | null): Promise<object> {
2022-02-19 14:09:35 +01:00
const hashrates = await HashratesRepository.$get(interval);
const oldestBlock = new Date(await BlocksRepository.$oldestBlockTimestamp());
return {
hashrates: hashrates,
oldestIndexedBlockTimestamp: oldestBlock.getTime(),
};
2022-02-19 14:09:35 +01:00
}
2022-02-19 12:45:02 +01:00
/**
* Generate daily hashrate data
2022-02-19 12:45:02 +01:00
*/
public async $generateNetworkHashrateHistory(): Promise<void> {
// We only run this once a day
const latestTimestamp = await HashratesRepository.$getLatestRunTimestamp();
const now = new Date().getTime() / 1000;
if (now - latestTimestamp < 86400) {
return;
}
if (!blocks.blockIndexingCompleted || this.hashrateIndexingStarted) {
2022-02-19 12:45:02 +01:00
return;
}
this.hashrateIndexingStarted = true;
logger.info(`Indexing hashrates`);
2022-02-21 06:36:48 +01:00
const totalDayIndexed = (await BlocksRepository.$blockCount(null, null)) / 144;
2022-02-19 14:09:35 +01:00
const indexedTimestamp = (await HashratesRepository.$get(null)).map(hashrate => hashrate.timestamp);
2022-02-21 06:36:48 +01:00
let startedAt = new Date().getTime() / 1000;
2022-02-19 12:45:02 +01:00
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);
2022-02-21 06:36:48 +01:00
let indexedThisRun = 0;
let totalIndexed = 0;
2022-02-19 12:45:02 +01:00
const hashrates: any[] = [];
2022-02-19 12:45:02 +01:00
while (toTimestamp > genesisTimestamp) {
const fromTimestamp = toTimestamp - 86400;
if (indexedTimestamp.includes(fromTimestamp)) {
toTimestamp -= 86400;
2022-02-21 06:36:48 +01:00
++totalIndexed;
2022-02-19 12:45:02 +01:00
continue;
}
const blockStats: any = await BlocksRepository.$blockCountBetweenTimestamp(
null, fromTimestamp, toTimestamp);
if (blockStats.blockCount === 0) { // We are done indexing, no blocks left
break;
2022-02-19 14:09:35 +01:00
}
2022-02-19 12:45:02 +01:00
let lastBlockHashrate = 0;
lastBlockHashrate = await bitcoinClient.getNetworkHashPs(blockStats.blockCount,
blockStats.lastBlockHeight);
2022-02-21 06:36:48 +01:00
const elapsedSeconds = Math.max(1, Math.round((new Date().getTime() / 1000) - startedAt));
if (elapsedSeconds > 10) {
2022-02-21 06:36:48 +01:00
const daysPerSeconds = Math.max(1, Math.round(indexedThisRun / elapsedSeconds));
2022-02-19 12:45:02 +01:00
const formattedDate = new Date(fromTimestamp * 1000).toUTCString();
2022-02-21 06:36:48 +01:00
const daysLeft = Math.round(totalDayIndexed - totalIndexed);
logger.debug(`Getting hashrate for ${formattedDate} | ~${daysPerSeconds} days/sec | ~${daysLeft} days left to index`);
startedAt = new Date().getTime() / 1000;
indexedThisRun = 0;
2022-02-19 12:45:02 +01:00
}
hashrates.push({
2022-02-19 12:45:02 +01:00
hashrateTimestamp: fromTimestamp,
avgHashrate: lastBlockHashrate,
poolId: null,
});
if (hashrates.length > 100) {
await HashratesRepository.$saveHashrates(hashrates);
hashrates.length = 0;
}
2022-02-19 12:45:02 +01:00
toTimestamp -= 86400;
2022-02-21 06:36:48 +01:00
++indexedThisRun;
++totalIndexed;
2022-02-19 12:45:02 +01:00
}
2022-02-19 14:09:35 +01:00
await HashratesRepository.$saveHashrates(hashrates);
await HashratesRepository.$setLatestRunTimestamp();
this.hashrateIndexingStarted = false;
2022-02-19 14:09:35 +01:00
logger.info(`Hashrates indexing completed`);
2022-02-19 12:45:02 +01:00
}
}
export default new Mining();