Store block first seen time in block audits

This commit is contained in:
natsoni
2024-10-04 21:54:13 +09:00
parent 67295c1b9b
commit 1a75e3e317
8 changed files with 65 additions and 12 deletions

View File

@@ -1,3 +1,4 @@
import * as fs from 'fs';
import config from '../config';
import logger from '../logger';
import { MempoolTransactionExtended, MempoolBlockWithTransactions } from '../mempool.interfaces';
@@ -7,10 +8,10 @@ import transactionUtils from './transaction-utils';
const PROPAGATION_MARGIN = 180; // in seconds, time since a transaction is first seen after which it is assumed to have propagated to all miners
class Audit {
auditBlock(height: number, transactions: MempoolTransactionExtended[], projectedBlocks: MempoolBlockWithTransactions[], mempool: { [txId: string]: MempoolTransactionExtended })
: { unseen: string[], censored: string[], added: string[], prioritized: string[], fresh: string[], sigop: string[], fullrbf: string[], accelerated: string[], score: number, similarity: number } {
auditBlock(height: number, transactions: MempoolTransactionExtended[], projectedBlocks: MempoolBlockWithTransactions[], mempool: { [txId: string]: MempoolTransactionExtended }, hash: string)
: { unseen: string[], censored: string[], added: string[], prioritized: string[], fresh: string[], sigop: string[], fullrbf: string[], accelerated: string[], score: number, similarity: number, firstSeen: string | undefined } {
if (!projectedBlocks?.[0]?.transactionIds || !mempool) {
return { unseen: [], censored: [], added: [], prioritized: [], fresh: [], sigop: [], fullrbf: [], accelerated: [], score: 1, similarity: 1 };
return { unseen: [], censored: [], added: [], prioritized: [], fresh: [], sigop: [], fullrbf: [], accelerated: [], score: 1, similarity: 1, firstSeen: undefined };
}
const matches: string[] = []; // present in both mined block and template
@@ -176,6 +177,8 @@ class Audit {
}
const similarity = projectedWeight ? matchedWeight / projectedWeight : 1;
const firstSeen = this.getFirstSeenFromLogs(hash);
return {
unseen,
censored: Object.keys(isCensored),
@@ -187,8 +190,39 @@ class Audit {
accelerated,
score,
similarity,
firstSeen
};
}
getFirstSeenFromLogs(hash: string): string | undefined {
const debugLogPath = config.CORE_RPC.DEBUG_LOG_PATH;
if (debugLogPath) {
try {
const fileDescriptor = fs.openSync(debugLogPath, 'r');
const bufferSize = 2048; // Read the last few lines of the file
const buffer = Buffer.alloc(bufferSize);
const fileSize = fs.statSync(debugLogPath).size;
const chunkSize = Math.min(bufferSize, fileSize);
fs.readSync(fileDescriptor, buffer, 0, chunkSize, fileSize - chunkSize);
const lines = buffer.toString('utf8', 0, chunkSize).split('\n');
fs.closeSync(fileDescriptor);
for (let i = lines.length - 1; i >= 0; i--) {
const line = lines[i];
if (line && line.includes(`Saw new header hash=${hash}`)) {
// Extract time from log: "2021-08-31T12:34:56Z" or "2021-08-31T12:34:56.123456Z" if logtimemicros=1
const dateMatch = line.match(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.?\d{6})?Z/);
if (dateMatch) {
return dateMatch[0].replace("T", " ").replace("Z", "");
}
}
}
} catch (e) {
logger.err(`Cannot parse block first seen time from Core logs. Reason: ` + (e instanceof Error ? e.message : e));
}
}
return undefined;
}
}
export default new Audit();

View File

@@ -7,7 +7,7 @@ import cpfpRepository from '../repositories/CpfpRepository';
import { RowDataPacket } from 'mysql2';
class DatabaseMigration {
private static currentVersion = 82;
private static currentVersion = 83;
private queryTimeout = 3600_000;
private statisticsAddedIndexed = false;
private uniqueLogs: string[] = [];
@@ -705,6 +705,11 @@ class DatabaseMigration {
await this.$fixBadV1AuditBlocks();
await this.updateToSchemaVersion(82);
}
if (databaseSchemaVersion < 83 && isBitcoin === true) {
await this.$executeQuery('ALTER TABLE `blocks_audits` ADD first_seen timestamp(6) DEFAULT NULL');
await this.updateToSchemaVersion(83);
}
}
/**

View File

@@ -975,7 +975,7 @@ class WebsocketHandler {
}
if (Common.indexingEnabled()) {
const { unseen, censored, added, prioritized, fresh, sigop, fullrbf, accelerated, score, similarity } = Audit.auditBlock(block.height, blockTransactions, projectedBlocks, auditMempool);
const { unseen, censored, added, prioritized, fresh, sigop, fullrbf, accelerated, score, similarity, firstSeen } = Audit.auditBlock(block.height, blockTransactions, projectedBlocks, auditMempool, block.id);
const matchRate = Math.round(score * 100 * 100) / 100;
const stripped = projectedBlocks[0]?.transactions ? projectedBlocks[0].transactions : [];
@@ -1012,6 +1012,7 @@ class WebsocketHandler {
matchRate: matchRate,
expectedFees: totalFees,
expectedWeight: totalWeight,
firstSeen: firstSeen,
});
if (block.extras) {