Merge branch 'master' into regtest-1

This commit is contained in:
Antoni Spaanderman
2022-03-09 17:55:09 +01:00
committed by GitHub
14 changed files with 358 additions and 102 deletions

View File

@@ -218,7 +218,7 @@ class BitcoinApi implements AbstractBitcoinApi {
if (map[outputType]) {
return map[outputType];
} else {
return '';
return 'unknown';
}
}

View File

@@ -1,5 +1,5 @@
import { PoolInfo, PoolStats } from '../mempool.interfaces';
import BlocksRepository, { EmptyBlocks } from '../repositories/BlocksRepository';
import BlocksRepository from '../repositories/BlocksRepository';
import PoolsRepository from '../repositories/PoolsRepository';
import HashratesRepository from '../repositories/HashratesRepository';
import bitcoinClient from './bitcoin/bitcoin-client';
@@ -20,25 +20,21 @@ class Mining {
const poolsStatistics = {};
const poolsInfo: PoolInfo[] = await PoolsRepository.$getPoolsInfo(interval);
const emptyBlocks: EmptyBlocks[] = await BlocksRepository.$getEmptyBlocks(null, interval);
const emptyBlocks: any[] = await BlocksRepository.$countEmptyBlocks(null, interval);
const poolsStats: PoolStats[] = [];
let rank = 1;
poolsInfo.forEach((poolInfo: PoolInfo) => {
const emptyBlocksCount = emptyBlocks.filter((emptyCount) => emptyCount.poolId === poolInfo.poolId);
const poolStat: PoolStats = {
poolId: poolInfo.poolId, // mysql row id
name: poolInfo.name,
link: poolInfo.link,
blockCount: poolInfo.blockCount,
rank: rank++,
emptyBlocks: 0
emptyBlocks: emptyBlocksCount.length > 0 ? emptyBlocksCount[0]['count'] : 0
};
for (let i = 0; i < emptyBlocks.length; ++i) {
if (emptyBlocks[i].poolId === poolInfo.poolId) {
poolStat.emptyBlocks++;
}
}
poolsStats.push(poolStat);
});
@@ -58,19 +54,19 @@ class Mining {
/**
* Get all mining pool stats for a pool
*/
public async $getPoolStat(interval: string | null, poolId: number): Promise<object> {
public async $getPoolStat(poolId: number): Promise<object> {
const pool = await PoolsRepository.$getPool(poolId);
if (!pool) {
throw new Error(`This mining pool does not exist`);
}
const blockCount: number = await BlocksRepository.$blockCount(poolId, interval);
const emptyBlocks: EmptyBlocks[] = await BlocksRepository.$getEmptyBlocks(poolId, interval);
const blockCount: number = await BlocksRepository.$blockCount(poolId);
const emptyBlocksCount = await BlocksRepository.$countEmptyBlocks(poolId);
return {
pool: pool,
blockCount: blockCount,
emptyBlocks: emptyBlocks,
emptyBlocks: emptyBlocksCount.length > 0 ? emptyBlocksCount[0]['count'] : 0,
};
}

View File

@@ -299,6 +299,7 @@ class Server {
.get(config.MEMPOOL.API_URL_PREFIX + 'mining/pools/2y', routes.$getPools.bind(routes, '2y'))
.get(config.MEMPOOL.API_URL_PREFIX + 'mining/pools/3y', routes.$getPools.bind(routes, '3y'))
.get(config.MEMPOOL.API_URL_PREFIX + 'mining/pools/all', routes.$getPools.bind(routes, 'all'))
.get(config.MEMPOOL.API_URL_PREFIX + 'mining/pool/:poolId/hashrate', routes.$getPoolHistoricalHashrate)
.get(config.MEMPOOL.API_URL_PREFIX + 'mining/pool/:poolId/blocks', routes.$getPoolBlocks)
.get(config.MEMPOOL.API_URL_PREFIX + 'mining/pool/:poolId/blocks/:height', routes.$getPoolBlocks)
.get(config.MEMPOOL.API_URL_PREFIX + 'mining/pool/:poolId', routes.$getPool)

View File

@@ -3,11 +3,6 @@ import { DB } from '../database';
import logger from '../logger';
import { Common } from '../api/common';
export interface EmptyBlocks {
emptyBlocks: number;
poolId: number;
}
class BlocksRepository {
/**
* Save indexed block data in the database
@@ -100,12 +95,13 @@ class BlocksRepository {
/**
* Get empty blocks for one or all pools
*/
public async $getEmptyBlocks(poolId: number | null, interval: string | null = null): Promise<EmptyBlocks[]> {
public async $countEmptyBlocks(poolId: number | null, interval: string | null = null): Promise<any> {
interval = Common.getSqlInterval(interval);
const params: any[] = [];
let query = `SELECT height, hash, tx_count, size, pool_id, weight, UNIX_TIMESTAMP(blockTimestamp) as timestamp
let query = `SELECT count(height) as count, pools.id as poolId
FROM blocks
JOIN pools on pools.id = blocks.pool_id
WHERE tx_count = 1`;
if (poolId) {
@@ -117,13 +113,14 @@ class BlocksRepository {
query += ` AND blockTimestamp BETWEEN DATE_SUB(NOW(), INTERVAL ${interval}) AND NOW()`;
}
// logger.debug(query);
query += ` GROUP by pools.id`;
const connection = await DB.pool.getConnection();
try {
const [rows] = await connection.query(query, params);
connection.release();
return <EmptyBlocks[]>rows;
return rows;
} catch (e) {
connection.release();
logger.err('$getEmptyBlocks() error' + (e instanceof Error ? e.message : e));
@@ -134,7 +131,7 @@ class BlocksRepository {
/**
* Get blocks count for a period
*/
public async $blockCount(poolId: number | null, interval: string | null): Promise<number> {
public async $blockCount(poolId: number | null, interval: string | null = null): Promise<number> {
interval = Common.getSqlInterval(interval);
const params: any[] = [];

View File

@@ -116,6 +116,52 @@ class HashratesRepository {
}
}
/**
* Returns a pool hashrate history
*/
public async $getPoolWeeklyHashrate(poolId: number): Promise<any[]> {
const connection = await DB.pool.getConnection();
// Find hashrate boundaries
let query = `SELECT MIN(hashrate_timestamp) as firstTimestamp, MAX(hashrate_timestamp) as lastTimestamp
FROM hashrates
JOIN pools on pools.id = pool_id
WHERE hashrates.type = 'weekly' AND pool_id = ? AND avg_hashrate != 0
ORDER by hashrate_timestamp LIMIT 1`;
let boundaries = {
firstTimestamp: '1970-01-01',
lastTimestamp: '9999-01-01'
};
try {
const [rows]: any[] = await connection.query(query, [poolId]);
boundaries = rows[0];
connection.release();
} catch (e) {
connection.release();
logger.err('$getPoolWeeklyHashrate() error' + (e instanceof Error ? e.message : e));
}
// Get hashrates entries between boundaries
query = `SELECT UNIX_TIMESTAMP(hashrate_timestamp) as timestamp, avg_hashrate as avgHashrate, share, pools.name as poolName
FROM hashrates
JOIN pools on pools.id = pool_id
WHERE hashrates.type = 'weekly' AND hashrate_timestamp BETWEEN ? AND ?
AND pool_id = ?
ORDER by hashrate_timestamp`;
try {
const [rows]: any[] = await connection.query(query, [boundaries.firstTimestamp, boundaries.lastTimestamp, poolId]);
connection.release();
return rows;
} catch (e) {
connection.release();
logger.err('$getPoolWeeklyHashrate() 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 = ?`;
@@ -136,6 +182,9 @@ class HashratesRepository {
const [rows] = await connection.query<any>(query, [key]);
connection.release();
if (rows.length === 0) {
return 0;
}
return rows[0]['number'];
} catch (e) {
connection.release();

View File

@@ -538,7 +538,7 @@ class Routes {
public async $getPool(req: Request, res: Response) {
try {
const stats = await mining.$getPoolStat(req.params.interval ?? null, parseInt(req.params.poolId, 10));
const stats = await mining.$getPoolStat(parseInt(req.params.poolId, 10));
res.header('Pragma', 'public');
res.header('Cache-control', 'public');
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
@@ -603,6 +603,22 @@ class Routes {
}
}
public async $getPoolHistoricalHashrate(req: Request, res: Response) {
try {
const hashrates = await HashratesRepository.$getPoolWeeklyHashrate(parseInt(req.params.poolId, 10));
const oldestIndexedBlockTimestamp = await BlocksRepository.$oldestBlockTimestamp();
res.header('Pragma', 'public');
res.header('Cache-control', 'public');
res.setHeader('Expires', new Date(Date.now() + 1000 * 300).toUTCString());
res.json({
oldestIndexedBlockTimestamp: oldestIndexedBlockTimestamp,
hashrates: hashrates,
});
} catch (e) {
res.status(500).send(e instanceof Error ? e.message : e);
}
}
public async $getHistoricalHashrate(req: Request, res: Response) {
try {
const hashrates = await HashratesRepository.$getNetworkDailyHashrate(req.params.interval ?? null);