mempool/backend/src/repositories/BlocksRepository.ts

229 lines
6.2 KiB
TypeScript
Raw Normal View History

import { BlockExtended, PoolTag } from '../mempool.interfaces';
import { DB } from '../database';
import logger from '../logger';
2022-02-09 11:41:05 +01:00
import { Common } from '../api/common';
export interface EmptyBlocks {
emptyBlocks: number;
poolId: number;
}
class BlocksRepository {
/**
* Save indexed block data in the database
*/
2022-02-08 07:47:43 +01:00
public async $saveBlockInDatabase(block: BlockExtended) {
const connection = await DB.pool.getConnection();
try {
const query = `INSERT INTO blocks(
height, hash, blockTimestamp, size,
weight, tx_count, coinbase_raw, difficulty,
2022-02-08 07:47:43 +01:00
pool_id, fees, fee_span, median_fee,
reward, version, bits, nonce,
merkle_root, previous_block_hash
) VALUE (
?, ?, FROM_UNIXTIME(?), ?,
?, ?, ?, ?,
2022-02-08 07:47:43 +01:00
?, ?, ?, ?,
?, ?, ?, ?,
?, ?
)`;
const params: any[] = [
block.height,
2022-02-08 07:47:43 +01:00
block.id,
block.timestamp,
block.size,
block.weight,
block.tx_count,
'',
block.difficulty,
block.extras.pool?.id, // Should always be set to something
0,
'[]',
2022-02-08 07:47:43 +01:00
block.extras.medianFee ?? 0,
block.extras.reward ?? 0,
block.version,
block.bits,
block.nonce,
block.merkle_root,
block.previousblockhash
];
2022-02-11 11:12:52 +01:00
// logger.debug(query);
await connection.query(query, params);
} catch (e: any) {
if (e.errno === 1062) { // ER_DUP_ENTRY
logger.debug(`$saveBlockInDatabase() - Block ${block.height} has already been indexed, ignoring`);
} else {
logger.err('$saveBlockInDatabase() error' + (e instanceof Error ? e.message : e));
}
}
connection.release();
}
/**
* Get all block height that have not been indexed between [startHeight, endHeight]
*/
public async $getMissingBlocksBetweenHeights(startHeight: number, endHeight: number): Promise<number[]> {
if (startHeight < endHeight) {
return [];
}
const connection = await DB.pool.getConnection();
2022-02-09 11:41:05 +01:00
const [rows]: any[] = await connection.query(`
SELECT height
FROM blocks
2022-02-09 11:41:05 +01:00
WHERE height <= ? AND height >= ?
ORDER BY height DESC;
2022-02-09 11:41:05 +01:00
`, [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();
2022-02-09 11:41:05 +01:00
const missingBlocksHeights = seekedBlocks.filter(x => indexedBlockHeights.indexOf(x) === -1);
return missingBlocksHeights;
}
/**
2022-02-09 11:41:05 +01:00
* Get empty blocks for one or all pools
*/
2022-02-09 11:41:05 +01:00
public async $getEmptyBlocks(poolId: number | null, interval: string | null = null): Promise<EmptyBlocks[]> {
interval = Common.getSqlInterval(interval);
const params: any[] = [];
let query = `SELECT height, hash, tx_count, size, pool_id, weight, UNIX_TIMESTAMP(blockTimestamp) as timestamp
FROM blocks
2022-02-09 11:41:05 +01:00
WHERE tx_count = 1`;
if (poolId) {
query += ` AND pool_id = ?`;
params.push(poolId);
}
if (interval) {
query += ` AND blockTimestamp BETWEEN DATE_SUB(NOW(), INTERVAL ${interval}) AND NOW()`;
}
2022-02-11 11:12:52 +01:00
// logger.debug(query);
const connection = await DB.pool.getConnection();
2022-02-09 11:41:05 +01:00
const [rows] = await connection.query(query, params);
connection.release();
return <EmptyBlocks[]>rows;
}
/**
* Get blocks count for a period
*/
2022-02-09 11:41:05 +01:00
public async $blockCount(poolId: number | null, interval: string | null): Promise<number> {
interval = Common.getSqlInterval(interval);
2022-02-09 11:41:05 +01:00
const params: any[] = [];
let query = `SELECT count(height) as blockCount
FROM blocks`;
if (poolId) {
query += ` WHERE pool_id = ?`;
params.push(poolId);
}
if (interval) {
if (poolId) {
query += ` AND`;
} else {
query += ` WHERE`;
}
query += ` blockTimestamp BETWEEN DATE_SUB(NOW(), INTERVAL ${interval}) AND NOW()`;
}
2022-02-11 11:12:52 +01:00
// logger.debug(query);
const connection = await DB.pool.getConnection();
2022-02-09 11:41:05 +01:00
const [rows] = await connection.query(query, params);
connection.release();
return <number>rows[0].blockCount;
}
/**
* Get the oldest indexed block
*/
public async $oldestBlockTimestamp(): Promise<number> {
2022-02-09 11:41:05 +01:00
const query = `SELECT blockTimestamp
FROM blocks
ORDER BY height
2022-02-09 11:41:05 +01:00
LIMIT 1;`;
2022-02-11 11:12:52 +01:00
// logger.debug(query);
2022-02-09 11:41:05 +01:00
const connection = await DB.pool.getConnection();
const [rows]: any[] = await connection.query(query);
connection.release();
if (rows.length <= 0) {
return -1;
}
return <number>rows[0].blockTimestamp;
}
2022-02-08 07:47:43 +01:00
/**
* Get blocks mined by a specific mining pool
*/
2022-02-09 11:41:05 +01:00
public async $getBlocksByPool(
poolId: number,
startHeight: number | null = null
): Promise<object[]> {
const params: any[] = [];
2022-02-14 06:11:55 +01:00
let query = `SELECT height, hash as id, tx_count, size, weight, pool_id, UNIX_TIMESTAMP(blockTimestamp) as timestamp, reward
FROM blocks
2022-02-09 11:41:05 +01:00
WHERE pool_id = ?`;
params.push(poolId);
if (startHeight) {
query += ` AND height < ?`;
params.push(startHeight);
}
2022-02-09 11:41:05 +01:00
query += ` ORDER BY height DESC
LIMIT 10`;
2022-02-11 11:12:52 +01:00
// logger.debug(query);
const connection = await DB.pool.getConnection();
2022-02-09 11:41:05 +01:00
const [rows] = await connection.query(query, params);
connection.release();
2022-02-09 11:41:05 +01:00
for (const block of <object[]>rows) {
delete block['blockTimestamp'];
}
return <object[]>rows;
}
2022-02-08 07:47:43 +01:00
/**
* Get one block by height
*/
public async $getBlockByHeight(height: number): Promise<object | null> {
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();
if (rows.length <= 0) {
return null;
}
return rows[0];
}
}
export default new BlocksRepository();