Add local acceleration info APIs

This commit is contained in:
Mononaut 2024-02-28 20:43:32 +00:00
parent 130bdac58c
commit bd0de745e2
No known key found for this signature in database
GPG Key ID: A3F058E41374C04E
2 changed files with 117 additions and 0 deletions

View File

@ -8,6 +8,7 @@ import HashratesRepository from '../../repositories/HashratesRepository';
import bitcoinClient from '../bitcoin/bitcoin-client';
import mining from "./mining";
import PricesRepository from '../../repositories/PricesRepository';
import AccelerationRepository from '../../repositories/AccelerationRepository';
class MiningRoutes {
public initRoutes(app: Application) {
@ -34,6 +35,10 @@ class MiningRoutes {
.get(config.MEMPOOL.API_URL_PREFIX + 'mining/blocks/audit/:hash', this.$getBlockAudit)
.get(config.MEMPOOL.API_URL_PREFIX + 'mining/blocks/timestamp/:timestamp', this.$getHeightFromTimestamp)
.get(config.MEMPOOL.API_URL_PREFIX + 'historical-price', this.$getHistoricalPrice)
.get(config.MEMPOOL.API_URL_PREFIX + 'accelerations/pool/:slug', this.$getAccelerationsByPool)
.get(config.MEMPOOL.API_URL_PREFIX + 'accelerations/block/:height', this.$getAccelerationsByHeight)
.get(config.MEMPOOL.API_URL_PREFIX + 'accelerations/recent/:interval', this.$getRecentAccelerations)
;
}
@ -352,6 +357,52 @@ class MiningRoutes {
res.status(500).send(e instanceof Error ? e.message : e);
}
}
private async $getAccelerationsByPool(req: Request, res: Response): Promise<void> {
try {
res.header('Pragma', 'public');
res.header('Cache-control', 'public');
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
if (!config.MEMPOOL_SERVICES.ACCELERATIONS || ['testnet', 'signet', 'liquidtestnet', 'liquid'].includes(config.MEMPOOL.NETWORK)) {
res.status(400).send('Acceleration data is not available.');
return;
}
res.status(200).send(await AccelerationRepository.$getAccelerationInfo(req.params.slug));
} catch (e) {
res.status(500).send(e instanceof Error ? e.message : e);
}
}
private async $getAccelerationsByHeight(req: Request, res: Response): Promise<void> {
try {
res.header('Pragma', 'public');
res.header('Cache-control', 'public');
res.setHeader('Expires', new Date(Date.now() + 1000 * 3600 * 24).toUTCString());
if (!config.MEMPOOL_SERVICES.ACCELERATIONS || ['testnet', 'signet', 'liquidtestnet', 'liquid'].includes(config.MEMPOOL.NETWORK)) {
res.status(400).send('Acceleration data is not available.');
return;
}
const height = req.params.height === undefined ? undefined : parseInt(req.params.height, 10);
res.status(200).send(await AccelerationRepository.$getAccelerationInfo(null, height));
} catch (e) {
res.status(500).send(e instanceof Error ? e.message : e);
}
}
private async $getRecentAccelerations(req: Request, res: Response): Promise<void> {
try {
res.header('Pragma', 'public');
res.header('Cache-control', 'public');
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
if (!config.MEMPOOL_SERVICES.ACCELERATIONS || ['testnet', 'signet', 'liquidtestnet', 'liquid'].includes(config.MEMPOOL.NETWORK)) {
res.status(400).send('Acceleration data is not available.');
return;
}
res.status(200).send(await AccelerationRepository.$getAccelerationInfo(null, null, req.params.interval));
} catch (e) {
res.status(500).send(e instanceof Error ? e.message : e);
}
}
}
export default new MiningRoutes();

View File

@ -3,6 +3,22 @@ import { ResultSetHeader, RowDataPacket } from 'mysql2';
import DB from '../database';
import logger from '../logger';
import { IEsploraApi } from '../api/bitcoin/esplora-api.interface';
import { Common } from '../api/common';
import config from '../config';
export interface PublicAcceleration {
txid: string,
height: number,
pool: {
id: number,
slug: string,
name: string,
},
effective_vsize: number,
effective_fee: number,
boost_rate: number,
boost_cost: number,
}
class AccelerationRepository {
public async $saveAcceleration(acceleration: AccelerationInfo, block: IEsploraApi.Block, pool_id: number): Promise<void> {
@ -38,6 +54,56 @@ class AccelerationRepository {
// We don't throw, not a critical issue if we miss some accelerations
}
}
public async $getAccelerationInfo(poolSlug: string | null = null, height: number | null = null, interval: string | null = null): Promise<PublicAcceleration[]> {
interval = Common.getSqlInterval(interval);
if (!config.MEMPOOL_SERVICES.ACCELERATIONS || (interval == null && poolSlug == null && height == null)) {
return [];
}
let query = `
SELECT * FROM accelerations
JOIN pools on pools.unique_id = accelerations.pool
`;
let params: any[] = [];
if (interval) {
query += ` WHERE accelerations.added BETWEEN DATE_SUB(NOW(), INTERVAL ${interval}) AND NOW() `;
} else if (height != null) {
query += ` WHERE accelerations.height = ? `;
params.push(height);
} else if (poolSlug != null) {
query += ` WHERE pools.slug = ? `;
params.push(poolSlug);
}
query += ` ORDER BY accelerations.added DESC `;
try {
const [rows] = await DB.query(query, params) as RowDataPacket[][];
if (rows?.length) {
return rows.map(row => ({
txid: row.txid,
height: row.height,
pool: {
id: row.id,
slug: row.slug,
name: row.name,
},
effective_vsize: row.effective_vsize,
effective_fee: row.effective_fee,
boost_rate: row.boost_rate,
boost_cost: row.boost_cost,
}));
} else {
return [];
}
} catch (e) {
logger.err(`Cannot query acceleration info. Reason: ` + (e instanceof Error ? e.message : e));
throw e;
}
}
}
export default new AccelerationRepository();