mirror of
https://github.com/mempool/mempool.git
synced 2025-01-06 21:49:15 +01:00
b92eef8198
refs #76
445 lines
14 KiB
TypeScript
445 lines
14 KiB
TypeScript
import memPool from './mempool';
|
|
import { DB } from '../database';
|
|
|
|
import { Statistic, TransactionExtended, OptimizedStatistic } from '../interfaces';
|
|
|
|
class Statistics {
|
|
protected intervalTimer: NodeJS.Timer | undefined;
|
|
protected newStatisticsEntryCallback: ((stats: OptimizedStatistic) => void) | undefined;
|
|
|
|
public setNewStatisticsEntryCallback(fn: (stats: OptimizedStatistic) => void) {
|
|
this.newStatisticsEntryCallback = fn;
|
|
}
|
|
|
|
constructor() { }
|
|
|
|
public startStatistics(): void {
|
|
console.log('Starting statistics service');
|
|
|
|
const now = new Date();
|
|
const nextInterval = new Date(now.getFullYear(), now.getMonth(), now.getDate(), now.getHours(),
|
|
Math.floor(now.getMinutes() / 1) * 1 + 1, 0, 0);
|
|
const difference = nextInterval.getTime() - now.getTime();
|
|
|
|
setTimeout(() => {
|
|
this.runStatistics();
|
|
this.intervalTimer = setInterval(() => {
|
|
if (!memPool.isInSync()) {
|
|
return;
|
|
}
|
|
this.runStatistics();
|
|
}, 1 * 60 * 1000);
|
|
}, difference);
|
|
}
|
|
|
|
private async runStatistics(): Promise<void> {
|
|
const currentMempool = memPool.getMempool();
|
|
const txPerSecond = memPool.getTxPerSecond();
|
|
const vBytesPerSecond = memPool.getVBytesPerSecond();
|
|
|
|
console.log('Running statistics');
|
|
|
|
let memPoolArray: TransactionExtended[] = [];
|
|
for (const i in currentMempool) {
|
|
if (currentMempool.hasOwnProperty(i)) {
|
|
memPoolArray.push(currentMempool[i]);
|
|
}
|
|
}
|
|
// Remove 0 and undefined
|
|
memPoolArray = memPoolArray.filter((tx) => tx.feePerVsize);
|
|
|
|
if (!memPoolArray.length) {
|
|
return;
|
|
}
|
|
|
|
memPoolArray.sort((a, b) => a.feePerVsize - b.feePerVsize);
|
|
const totalWeight = memPoolArray.map((tx) => tx.vsize).reduce((acc, curr) => acc + curr) * 4;
|
|
const totalFee = memPoolArray.map((tx) => tx.fee).reduce((acc, curr) => acc + curr);
|
|
|
|
const logFees = [1, 2, 3, 4, 5, 6, 8, 10, 12, 15, 20, 30, 40, 50, 60, 70, 80, 90, 100, 125, 150, 175, 200,
|
|
250, 300, 350, 400, 500, 600, 700, 800, 900, 1000, 1200, 1400, 1600, 1800, 2000];
|
|
|
|
const weightVsizeFees: { [feePerWU: number]: number } = {};
|
|
|
|
memPoolArray.forEach((transaction) => {
|
|
for (let i = 0; i < logFees.length; i++) {
|
|
if ((logFees[i] === 2000 && transaction.feePerVsize >= 2000) || transaction.feePerVsize <= logFees[i]) {
|
|
if (weightVsizeFees[logFees[i]]) {
|
|
weightVsizeFees[logFees[i]] += transaction.vsize;
|
|
} else {
|
|
weightVsizeFees[logFees[i]] = transaction.vsize;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
});
|
|
|
|
const insertId = await this.$create({
|
|
added: 'NOW()',
|
|
unconfirmed_transactions: memPoolArray.length,
|
|
tx_per_second: txPerSecond,
|
|
vbytes_per_second: Math.round(vBytesPerSecond),
|
|
mempool_byte_weight: totalWeight,
|
|
total_fee: totalFee,
|
|
fee_data: '',
|
|
vsize_1: weightVsizeFees['1'] || 0,
|
|
vsize_2: weightVsizeFees['2'] || 0,
|
|
vsize_3: weightVsizeFees['3'] || 0,
|
|
vsize_4: weightVsizeFees['4'] || 0,
|
|
vsize_5: weightVsizeFees['5'] || 0,
|
|
vsize_6: weightVsizeFees['6'] || 0,
|
|
vsize_8: weightVsizeFees['8'] || 0,
|
|
vsize_10: weightVsizeFees['10'] || 0,
|
|
vsize_12: weightVsizeFees['12'] || 0,
|
|
vsize_15: weightVsizeFees['15'] || 0,
|
|
vsize_20: weightVsizeFees['20'] || 0,
|
|
vsize_30: weightVsizeFees['30'] || 0,
|
|
vsize_40: weightVsizeFees['40'] || 0,
|
|
vsize_50: weightVsizeFees['50'] || 0,
|
|
vsize_60: weightVsizeFees['60'] || 0,
|
|
vsize_70: weightVsizeFees['70'] || 0,
|
|
vsize_80: weightVsizeFees['80'] || 0,
|
|
vsize_90: weightVsizeFees['90'] || 0,
|
|
vsize_100: weightVsizeFees['100'] || 0,
|
|
vsize_125: weightVsizeFees['125'] || 0,
|
|
vsize_150: weightVsizeFees['150'] || 0,
|
|
vsize_175: weightVsizeFees['175'] || 0,
|
|
vsize_200: weightVsizeFees['200'] || 0,
|
|
vsize_250: weightVsizeFees['250'] || 0,
|
|
vsize_300: weightVsizeFees['300'] || 0,
|
|
vsize_350: weightVsizeFees['350'] || 0,
|
|
vsize_400: weightVsizeFees['400'] || 0,
|
|
vsize_500: weightVsizeFees['500'] || 0,
|
|
vsize_600: weightVsizeFees['600'] || 0,
|
|
vsize_700: weightVsizeFees['700'] || 0,
|
|
vsize_800: weightVsizeFees['800'] || 0,
|
|
vsize_900: weightVsizeFees['900'] || 0,
|
|
vsize_1000: weightVsizeFees['1000'] || 0,
|
|
vsize_1200: weightVsizeFees['1200'] || 0,
|
|
vsize_1400: weightVsizeFees['1400'] || 0,
|
|
vsize_1600: weightVsizeFees['1600'] || 0,
|
|
vsize_1800: weightVsizeFees['1800'] || 0,
|
|
vsize_2000: weightVsizeFees['2000'] || 0,
|
|
});
|
|
|
|
if (this.newStatisticsEntryCallback && insertId) {
|
|
const newStats = await this.$get(insertId);
|
|
if (newStats) {
|
|
this.newStatisticsEntryCallback(newStats);
|
|
}
|
|
}
|
|
}
|
|
|
|
private async $create(statistics: Statistic): Promise<number | undefined> {
|
|
try {
|
|
const connection = await DB.pool.getConnection();
|
|
const query = `INSERT INTO statistics(
|
|
added,
|
|
unconfirmed_transactions,
|
|
tx_per_second,
|
|
vbytes_per_second,
|
|
mempool_byte_weight,
|
|
fee_data,
|
|
total_fee,
|
|
vsize_1,
|
|
vsize_2,
|
|
vsize_3,
|
|
vsize_4,
|
|
vsize_5,
|
|
vsize_6,
|
|
vsize_8,
|
|
vsize_10,
|
|
vsize_12,
|
|
vsize_15,
|
|
vsize_20,
|
|
vsize_30,
|
|
vsize_40,
|
|
vsize_50,
|
|
vsize_60,
|
|
vsize_70,
|
|
vsize_80,
|
|
vsize_90,
|
|
vsize_100,
|
|
vsize_125,
|
|
vsize_150,
|
|
vsize_175,
|
|
vsize_200,
|
|
vsize_250,
|
|
vsize_300,
|
|
vsize_350,
|
|
vsize_400,
|
|
vsize_500,
|
|
vsize_600,
|
|
vsize_700,
|
|
vsize_800,
|
|
vsize_900,
|
|
vsize_1000,
|
|
vsize_1200,
|
|
vsize_1400,
|
|
vsize_1600,
|
|
vsize_1800,
|
|
vsize_2000
|
|
)
|
|
VALUES (${statistics.added}, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
|
|
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`;
|
|
|
|
const params: (string | number)[] = [
|
|
statistics.unconfirmed_transactions,
|
|
statistics.tx_per_second,
|
|
statistics.vbytes_per_second,
|
|
statistics.mempool_byte_weight,
|
|
statistics.fee_data,
|
|
statistics.total_fee,
|
|
statistics.vsize_1,
|
|
statistics.vsize_2,
|
|
statistics.vsize_3,
|
|
statistics.vsize_4,
|
|
statistics.vsize_5,
|
|
statistics.vsize_6,
|
|
statistics.vsize_8,
|
|
statistics.vsize_10,
|
|
statistics.vsize_12,
|
|
statistics.vsize_15,
|
|
statistics.vsize_20,
|
|
statistics.vsize_30,
|
|
statistics.vsize_40,
|
|
statistics.vsize_50,
|
|
statistics.vsize_60,
|
|
statistics.vsize_70,
|
|
statistics.vsize_80,
|
|
statistics.vsize_90,
|
|
statistics.vsize_100,
|
|
statistics.vsize_125,
|
|
statistics.vsize_150,
|
|
statistics.vsize_175,
|
|
statistics.vsize_200,
|
|
statistics.vsize_250,
|
|
statistics.vsize_300,
|
|
statistics.vsize_350,
|
|
statistics.vsize_400,
|
|
statistics.vsize_500,
|
|
statistics.vsize_600,
|
|
statistics.vsize_700,
|
|
statistics.vsize_800,
|
|
statistics.vsize_900,
|
|
statistics.vsize_1000,
|
|
statistics.vsize_1200,
|
|
statistics.vsize_1400,
|
|
statistics.vsize_1600,
|
|
statistics.vsize_1800,
|
|
statistics.vsize_2000,
|
|
];
|
|
const [result]: any = await connection.query(query, params);
|
|
connection.release();
|
|
return result.insertId;
|
|
} catch (e) {
|
|
console.log('$create() error', e);
|
|
}
|
|
}
|
|
|
|
private getQueryForDays(days: number, groupBy: number) {
|
|
|
|
return `SELECT id, added, unconfirmed_transactions,
|
|
AVG(tx_per_second) AS tx_per_second,
|
|
AVG(vbytes_per_second) AS vbytes_per_second,
|
|
AVG(vsize_1) AS vsize_1,
|
|
AVG(vsize_2) AS vsize_2,
|
|
AVG(vsize_3) AS vsize_3,
|
|
AVG(vsize_4) AS vsize_4,
|
|
AVG(vsize_5) AS vsize_5,
|
|
AVG(vsize_6) AS vsize_6,
|
|
AVG(vsize_8) AS vsize_8,
|
|
AVG(vsize_10) AS vsize_10,
|
|
AVG(vsize_12) AS vsize_12,
|
|
AVG(vsize_15) AS vsize_15,
|
|
AVG(vsize_20) AS vsize_20,
|
|
AVG(vsize_30) AS vsize_30,
|
|
AVG(vsize_40) AS vsize_40,
|
|
AVG(vsize_50) AS vsize_50,
|
|
AVG(vsize_60) AS vsize_60,
|
|
AVG(vsize_70) AS vsize_70,
|
|
AVG(vsize_80) AS vsize_80,
|
|
AVG(vsize_90) AS vsize_90,
|
|
AVG(vsize_100) AS vsize_100,
|
|
AVG(vsize_125) AS vsize_125,
|
|
AVG(vsize_150) AS vsize_150,
|
|
AVG(vsize_175) AS vsize_175,
|
|
AVG(vsize_200) AS vsize_200,
|
|
AVG(vsize_250) AS vsize_250,
|
|
AVG(vsize_300) AS vsize_300,
|
|
AVG(vsize_350) AS vsize_350,
|
|
AVG(vsize_400) AS vsize_400,
|
|
AVG(vsize_500) AS vsize_500,
|
|
AVG(vsize_600) AS vsize_600,
|
|
AVG(vsize_700) AS vsize_700,
|
|
AVG(vsize_800) AS vsize_800,
|
|
AVG(vsize_900) AS vsize_900,
|
|
AVG(vsize_1000) AS vsize_1000,
|
|
AVG(vsize_1200) AS vsize_1200,
|
|
AVG(vsize_1400) AS vsize_1400,
|
|
AVG(vsize_1600) AS vsize_1600,
|
|
AVG(vsize_1800) AS vsize_1800,
|
|
AVG(vsize_2000) AS vsize_2000 FROM statistics GROUP BY UNIX_TIMESTAMP(added) DIV ${groupBy} ORDER BY id DESC LIMIT ${days}`;
|
|
}
|
|
|
|
public async $get(id: number): Promise<OptimizedStatistic | undefined> {
|
|
try {
|
|
const connection = await DB.pool.getConnection();
|
|
const query = `SELECT * FROM statistics WHERE id = ?`;
|
|
const [rows] = await connection.query<any>(query, [id]);
|
|
connection.release();
|
|
if (rows[0]) {
|
|
return this.mapStatisticToOptimizedStatistic([rows[0]])[0];
|
|
}
|
|
} catch (e) {
|
|
console.log('$list2H() error', e);
|
|
}
|
|
}
|
|
|
|
public async $list2H(): Promise<OptimizedStatistic[]> {
|
|
try {
|
|
const connection = await DB.pool.getConnection();
|
|
const query = `SELECT * FROM statistics ORDER BY id DESC LIMIT 120`;
|
|
const [rows] = await connection.query<any>(query);
|
|
connection.release();
|
|
return this.mapStatisticToOptimizedStatistic(rows);
|
|
} catch (e) {
|
|
console.log('$list2H() error', e);
|
|
return [];
|
|
}
|
|
}
|
|
|
|
public async $list24H(): Promise<OptimizedStatistic[]> {
|
|
try {
|
|
const connection = await DB.pool.getConnection();
|
|
const query = this.getQueryForDays(120, 720);
|
|
const [rows] = await connection.query<any>(query);
|
|
connection.release();
|
|
return this.mapStatisticToOptimizedStatistic(rows);
|
|
} catch (e) {
|
|
return [];
|
|
}
|
|
}
|
|
|
|
public async $list1W(): Promise<OptimizedStatistic[]> {
|
|
try {
|
|
const connection = await DB.pool.getConnection();
|
|
const query = this.getQueryForDays(120, 5040);
|
|
const [rows] = await connection.query<any>(query);
|
|
connection.release();
|
|
return this.mapStatisticToOptimizedStatistic(rows);
|
|
} catch (e) {
|
|
console.log('$list1W() error', e);
|
|
return [];
|
|
}
|
|
}
|
|
|
|
public async $list1M(): Promise<OptimizedStatistic[]> {
|
|
try {
|
|
const connection = await DB.pool.getConnection();
|
|
const query = this.getQueryForDays(120, 20160);
|
|
const [rows] = await connection.query<any>(query);
|
|
connection.release();
|
|
return this.mapStatisticToOptimizedStatistic(rows);
|
|
} catch (e) {
|
|
console.log('$list1M() error', e);
|
|
return [];
|
|
}
|
|
}
|
|
|
|
public async $list3M(): Promise<OptimizedStatistic[]> {
|
|
try {
|
|
const connection = await DB.pool.getConnection();
|
|
const query = this.getQueryForDays(120, 60480);
|
|
const [rows] = await connection.query<any>(query);
|
|
connection.release();
|
|
return this.mapStatisticToOptimizedStatistic(rows);
|
|
} catch (e) {
|
|
console.log('$list3M() error', e);
|
|
return [];
|
|
}
|
|
}
|
|
|
|
public async $list6M(): Promise<OptimizedStatistic[]> {
|
|
try {
|
|
const connection = await DB.pool.getConnection();
|
|
const query = this.getQueryForDays(120, 120960);
|
|
const [rows] = await connection.query<any>(query);
|
|
connection.release();
|
|
return this.mapStatisticToOptimizedStatistic(rows);
|
|
} catch (e) {
|
|
console.log('$list6M() error', e);
|
|
return [];
|
|
}
|
|
}
|
|
|
|
public async $list1Y(): Promise<OptimizedStatistic[]> {
|
|
try {
|
|
const connection = await DB.pool.getConnection();
|
|
const query = this.getQueryForDays(120, 241920);
|
|
const [rows] = await connection.query<any>(query);
|
|
connection.release();
|
|
return this.mapStatisticToOptimizedStatistic(rows);
|
|
} catch (e) {
|
|
console.log('$list6M() error', e);
|
|
return [];
|
|
}
|
|
}
|
|
private mapStatisticToOptimizedStatistic(statistic: Statistic[]): OptimizedStatistic[] {
|
|
return statistic.map((s) => {
|
|
return {
|
|
id: s.id || 0,
|
|
added: s.added,
|
|
unconfirmed_transactions: s.unconfirmed_transactions,
|
|
tx_per_second: s.tx_per_second,
|
|
vbytes_per_second: s.vbytes_per_second,
|
|
mempool_byte_weight: s.mempool_byte_weight,
|
|
total_fee: s.total_fee,
|
|
vsizes: [
|
|
s.vsize_1,
|
|
s.vsize_2,
|
|
s.vsize_3,
|
|
s.vsize_4,
|
|
s.vsize_5,
|
|
s.vsize_6,
|
|
s.vsize_8,
|
|
s.vsize_10,
|
|
s.vsize_12,
|
|
s.vsize_15,
|
|
s.vsize_20,
|
|
s.vsize_30,
|
|
s.vsize_40,
|
|
s.vsize_50,
|
|
s.vsize_60,
|
|
s.vsize_70,
|
|
s.vsize_80,
|
|
s.vsize_90,
|
|
s.vsize_100,
|
|
s.vsize_125,
|
|
s.vsize_150,
|
|
s.vsize_175,
|
|
s.vsize_200,
|
|
s.vsize_250,
|
|
s.vsize_300,
|
|
s.vsize_350,
|
|
s.vsize_400,
|
|
s.vsize_500,
|
|
s.vsize_600,
|
|
s.vsize_700,
|
|
s.vsize_800,
|
|
s.vsize_900,
|
|
s.vsize_1000,
|
|
s.vsize_1200,
|
|
s.vsize_1400,
|
|
s.vsize_1600,
|
|
s.vsize_1800,
|
|
s.vsize_2000,
|
|
]
|
|
};
|
|
});
|
|
}
|
|
|
|
}
|
|
|
|
export default new Statistics();
|