mirror of
https://github.com/mempool/mempool.git
synced 2025-03-03 17:47:01 +01:00
Add expected weight to audit table
This commit is contained in:
parent
aedaf53137
commit
3013386ca5
9 changed files with 98 additions and 22 deletions
|
@ -534,7 +534,8 @@ class DatabaseMigration {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (databaseSchemaVersion < 62 && isBitcoin === true) {
|
if (databaseSchemaVersion < 62 && isBitcoin === true) {
|
||||||
await this.$executeQuery('ALTER TABLE `blocks_audits` ADD expected_fees BIGINT UNSIGNED NOT NULL DEFAULT "0"');
|
await this.$executeQuery('ALTER TABLE `blocks_audits` ADD expected_fees BIGINT UNSIGNED DEFAULT NULL');
|
||||||
|
await this.$executeQuery('ALTER TABLE `blocks_audits` ADD expected_weight BIGINT UNSIGNED DEFAULT NULL');
|
||||||
await this.updateToSchemaVersion(62);
|
await this.updateToSchemaVersion(62);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -559,8 +559,6 @@ class WebsocketHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Common.indexingEnabled() && memPool.isInSync()) {
|
if (Common.indexingEnabled() && memPool.isInSync()) {
|
||||||
logger.debug(`Auditing block ${block.height} (${block.id})`);
|
|
||||||
|
|
||||||
const { censored, added, fresh, sigop, score, similarity } = Audit.auditBlock(transactions, projectedBlocks, auditMempool);
|
const { censored, added, fresh, sigop, score, similarity } = Audit.auditBlock(transactions, projectedBlocks, auditMempool);
|
||||||
const matchRate = Math.round(score * 100 * 100) / 100;
|
const matchRate = Math.round(score * 100 * 100) / 100;
|
||||||
|
|
||||||
|
@ -573,8 +571,12 @@ class WebsocketHandler {
|
||||||
};
|
};
|
||||||
}) : [];
|
}) : [];
|
||||||
|
|
||||||
const totalFees = stripped.reduce((total, transaction) => total + transaction.fee, 0);
|
let totalFees = 0;
|
||||||
logger.debug(`Projected block fees: ${totalFees} sats`);
|
let totalWeight = 0;
|
||||||
|
for (const tx of stripped) {
|
||||||
|
totalFees += tx.fee;
|
||||||
|
totalWeight += (tx.vsize * 4);
|
||||||
|
}
|
||||||
|
|
||||||
BlocksSummariesRepository.$saveTemplate({
|
BlocksSummariesRepository.$saveTemplate({
|
||||||
height: block.height,
|
height: block.height,
|
||||||
|
@ -593,7 +595,8 @@ class WebsocketHandler {
|
||||||
freshTxs: fresh,
|
freshTxs: fresh,
|
||||||
sigopTxs: sigop,
|
sigopTxs: sigop,
|
||||||
matchRate: matchRate,
|
matchRate: matchRate,
|
||||||
expectedFees: totalFees
|
expectedFees: totalFees,
|
||||||
|
expectedWeight: totalWeight,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (block.extras) {
|
if (block.extras) {
|
||||||
|
|
|
@ -35,13 +35,15 @@ export interface BlockAudit {
|
||||||
sigopTxs: string[],
|
sigopTxs: string[],
|
||||||
addedTxs: string[],
|
addedTxs: string[],
|
||||||
matchRate: number,
|
matchRate: number,
|
||||||
expectedFees: number;
|
expectedFees?: number,
|
||||||
|
expectedWeight?: number,
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AuditScore {
|
export interface AuditScore {
|
||||||
hash: string,
|
hash: string,
|
||||||
matchRate?: number,
|
matchRate?: number,
|
||||||
expectedFees?: number
|
expectedFees?: number
|
||||||
|
expectedWeight?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MempoolBlock {
|
export interface MempoolBlock {
|
||||||
|
@ -185,6 +187,7 @@ export interface BlockExtension {
|
||||||
reward: number;
|
reward: number;
|
||||||
matchRate: number | null;
|
matchRate: number | null;
|
||||||
expectedFees: number | null;
|
expectedFees: number | null;
|
||||||
|
expectedWeight: number | null;
|
||||||
similarity?: number;
|
similarity?: number;
|
||||||
pool: {
|
pool: {
|
||||||
id: number; // Note - This is the `unique_id`, not to mix with the auto increment `id`
|
id: number; // Note - This is the `unique_id`, not to mix with the auto increment `id`
|
||||||
|
|
|
@ -6,9 +6,9 @@ import { BlockAudit, AuditScore } from '../mempool.interfaces';
|
||||||
class BlocksAuditRepositories {
|
class BlocksAuditRepositories {
|
||||||
public async $saveAudit(audit: BlockAudit): Promise<void> {
|
public async $saveAudit(audit: BlockAudit): Promise<void> {
|
||||||
try {
|
try {
|
||||||
await DB.query(`INSERT INTO blocks_audits(time, height, hash, missing_txs, added_txs, fresh_txs, sigop_txs, match_rate, expected_fees)
|
await DB.query(`INSERT INTO blocks_audits(time, height, hash, missing_txs, added_txs, fresh_txs, sigop_txs, match_rate, expected_fees, expected_weight)
|
||||||
VALUE (FROM_UNIXTIME(?), ?, ?, ?, ?, ?, ?, ?, ?)`, [audit.time, audit.height, audit.hash, JSON.stringify(audit.missingTxs),
|
VALUE (FROM_UNIXTIME(?), ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [audit.time, audit.height, audit.hash, JSON.stringify(audit.missingTxs),
|
||||||
JSON.stringify(audit.addedTxs), JSON.stringify(audit.freshTxs), JSON.stringify(audit.sigopTxs), audit.matchRate, audit.expectedFees]);
|
JSON.stringify(audit.addedTxs), JSON.stringify(audit.freshTxs), JSON.stringify(audit.sigopTxs), audit.matchRate, audit.expectedFees, audit.expectedWeight]);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
if (e.errno === 1062) { // ER_DUP_ENTRY - This scenario is possible upon node backend restart
|
if (e.errno === 1062) { // ER_DUP_ENTRY - This scenario is possible upon node backend restart
|
||||||
logger.debug(`Cannot save block audit for block ${audit.hash} because it has already been indexed, ignoring`);
|
logger.debug(`Cannot save block audit for block ${audit.hash} because it has already been indexed, ignoring`);
|
||||||
|
@ -51,7 +51,15 @@ class BlocksAuditRepositories {
|
||||||
const [rows]: any[] = await DB.query(
|
const [rows]: any[] = await DB.query(
|
||||||
`SELECT blocks.height, blocks.hash as id, UNIX_TIMESTAMP(blocks.blockTimestamp) as timestamp, blocks.size,
|
`SELECT blocks.height, blocks.hash as id, UNIX_TIMESTAMP(blocks.blockTimestamp) as timestamp, blocks.size,
|
||||||
blocks.weight, blocks.tx_count,
|
blocks.weight, blocks.tx_count,
|
||||||
transactions, template, missing_txs as missingTxs, added_txs as addedTxs, fresh_txs as freshTxs, sigop_txs as sigopTxs, match_rate as matchRate, expected_fees as expectedFees
|
transactions,
|
||||||
|
template,
|
||||||
|
missing_txs as missingTxs,
|
||||||
|
added_txs as addedTxs,
|
||||||
|
fresh_txs as freshTxs,
|
||||||
|
sigop_txs as sigopTxs,
|
||||||
|
match_rate as matchRate,
|
||||||
|
expected_fees as expectedFees,
|
||||||
|
expected_weight as expectedWeight
|
||||||
FROM blocks_audits
|
FROM blocks_audits
|
||||||
JOIN blocks ON blocks.hash = blocks_audits.hash
|
JOIN blocks ON blocks.hash = blocks_audits.hash
|
||||||
JOIN blocks_templates ON blocks_templates.id = blocks_audits.hash
|
JOIN blocks_templates ON blocks_templates.id = blocks_audits.hash
|
||||||
|
@ -81,7 +89,7 @@ class BlocksAuditRepositories {
|
||||||
public async $getBlockAuditScore(hash: string): Promise<AuditScore> {
|
public async $getBlockAuditScore(hash: string): Promise<AuditScore> {
|
||||||
try {
|
try {
|
||||||
const [rows]: any[] = await DB.query(
|
const [rows]: any[] = await DB.query(
|
||||||
`SELECT hash, match_rate as matchRate, expected_fees as expectedFees
|
`SELECT hash, match_rate as matchRate, expected_fees as expectedFees, expected_weight as expectedWeight
|
||||||
FROM blocks_audits
|
FROM blocks_audits
|
||||||
WHERE blocks_audits.hash = "${hash}"
|
WHERE blocks_audits.hash = "${hash}"
|
||||||
`);
|
`);
|
||||||
|
@ -95,7 +103,7 @@ class BlocksAuditRepositories {
|
||||||
public async $getBlockAuditScores(maxHeight: number, minHeight: number): Promise<AuditScore[]> {
|
public async $getBlockAuditScores(maxHeight: number, minHeight: number): Promise<AuditScore[]> {
|
||||||
try {
|
try {
|
||||||
const [rows]: any[] = await DB.query(
|
const [rows]: any[] = await DB.query(
|
||||||
`SELECT hash, match_rate as matchRate, expected_fees as expectedFees
|
`SELECT hash, match_rate as matchRate, expected_fees as expectedFees, expected_weight as expectedWeight
|
||||||
FROM blocks_audits
|
FROM blocks_audits
|
||||||
WHERE blocks_audits.height BETWEEN ? AND ?
|
WHERE blocks_audits.height BETWEEN ? AND ?
|
||||||
`, [minHeight, maxHeight]);
|
`, [minHeight, maxHeight]);
|
||||||
|
|
|
@ -1038,6 +1038,7 @@ class BlocksRepository {
|
||||||
if (auditScore != null) {
|
if (auditScore != null) {
|
||||||
extras.matchRate = auditScore.matchRate;
|
extras.matchRate = auditScore.matchRate;
|
||||||
extras.expectedFees = auditScore.expectedFees;
|
extras.expectedFees = auditScore.expectedFees;
|
||||||
|
extras.expectedWeight = auditScore.expectedWeight;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -72,15 +72,6 @@
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr *ngIf="auditAvailable">
|
|
||||||
<td i18n="latest-blocks.expected_fees">Expected total fees</td>
|
|
||||||
<td>
|
|
||||||
<app-amount [satoshis]="blockAudit?.expectedFees" digitsInfo="1.2-3" [noFiat]="true"></app-amount>
|
|
||||||
<span class="fiat">
|
|
||||||
<app-fiat [blockConversion]="blockConversion" [value]="blockAudit?.expectedFees" digitsInfo="1.0-0"></app-fiat>
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-template #skeletonRows>
|
<ng-template #skeletonRows>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -235,6 +226,9 @@
|
||||||
(txClickEvent)="onTxClick($event)" (txHoverEvent)="onTxHover($event)" [unavailable]="!isMobile && !showAudit"></app-block-overview-graph>
|
(txClickEvent)="onTxClick($event)" (txHoverEvent)="onTxHover($event)" [unavailable]="!isMobile && !showAudit"></app-block-overview-graph>
|
||||||
<ng-container *ngIf="!isMobile || mode !== 'actual'; else emptyBlockInfo"></ng-container>
|
<ng-container *ngIf="!isMobile || mode !== 'actual'; else emptyBlockInfo"></ng-container>
|
||||||
</div>
|
</div>
|
||||||
|
<ng-container *ngIf="network !== 'liquid'">
|
||||||
|
<ng-container *ngTemplateOutlet="isMobile && mode === 'actual' ? actualDetails : expectedDetails"></ng-container>
|
||||||
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm" *ngIf="!isMobile">
|
<div class="col-sm" *ngIf="!isMobile">
|
||||||
<h3 class="block-subtitle actual" *ngIf="!isMobile"><ng-container i18n="block.actual-block">Actual Block</ng-container> <a class="info-link" [routerLink]="['/docs/faq' | relativeUrl ]" fragment="how-do-block-audits-work"><fa-icon [icon]="['fas', 'info-circle']" [fixedWidth]="true"></fa-icon></a></h3>
|
<h3 class="block-subtitle actual" *ngIf="!isMobile"><ng-container i18n="block.actual-block">Actual Block</ng-container> <a class="info-link" [routerLink]="['/docs/faq' | relativeUrl ]" fragment="how-do-block-audits-work"><fa-icon [icon]="['fas', 'info-circle']" [fixedWidth]="true"></fa-icon></a></h3>
|
||||||
|
@ -244,6 +238,9 @@
|
||||||
(txClickEvent)="onTxClick($event)" (txHoverEvent)="onTxHover($event)" [unavailable]="isMobile && !showAudit"></app-block-overview-graph>
|
(txClickEvent)="onTxClick($event)" (txHoverEvent)="onTxHover($event)" [unavailable]="isMobile && !showAudit"></app-block-overview-graph>
|
||||||
<ng-container *ngTemplateOutlet="emptyBlockInfo"></ng-container>
|
<ng-container *ngTemplateOutlet="emptyBlockInfo"></ng-container>
|
||||||
</div>
|
</div>
|
||||||
|
<ng-container *ngIf="network !== 'liquid'">
|
||||||
|
<ng-container *ngTemplateOutlet="actualDetails"></ng-container>
|
||||||
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -394,5 +391,54 @@
|
||||||
</a>
|
</a>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
|
<ng-template #expectedDetails>
|
||||||
|
<table *ngIf="block && blockAudit" class="table table-borderless table-striped audit-details-table">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td i18n="block.expected-total-fees|Expected total fees in a block">Expected fees</td>
|
||||||
|
<td>
|
||||||
|
<app-amount [satoshis]="blockAudit.expectedFees" digitsInfo="1.2-3" [noFiat]="true"></app-amount>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td i18n="block.expected-weight">Expected weight</td>
|
||||||
|
<td [innerHTML]="'‎' + (blockAudit.expectedWeight | wuBytes: 2)"></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td i18n="block.expected_transaction_count">Expected transactions</td>
|
||||||
|
<td>{{ blockAudit.template?.length || 0 }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<ng-template #actualDetails>
|
||||||
|
<table *ngIf="block && blockAudit" class="table table-borderless table-striped audit-details-table">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td i18n="block.actual-total-fees|Actual total fees in a block">Actual fees</td>
|
||||||
|
<td>
|
||||||
|
<app-amount [satoshis]="block.extras.totalFees" digitsInfo="1.2-3" [noFiat]="true"></app-amount>
|
||||||
|
<span *ngIf="blockAudit.feeDelta" class="difference" [class.positive]="blockAudit.feeDelta <= 0" [class.negative]="blockAudit.feeDelta > 0">
|
||||||
|
{{ blockAudit.feeDelta < 0 ? '+' : '' }}{{ (-blockAudit.feeDelta * 100) | amountShortener: 2 }}%
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td i18n="block.actual-weight">Actual weight</td>
|
||||||
|
<td [innerHTML]>
|
||||||
|
<span [innerHTML]="'‎' + (block.weight | wuBytes: 2)"></span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td i18n="block.actual_transaction_count">Actual transactions</td>
|
||||||
|
<td>
|
||||||
|
{{ block.tx_count }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
|
|
|
@ -38,6 +38,17 @@
|
||||||
color: rgba(255, 255, 255, 0.4);
|
color: rgba(255, 255, 255, 0.4);
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.difference {
|
||||||
|
margin-left: 0.5em;
|
||||||
|
|
||||||
|
&.positive {
|
||||||
|
color: rgb(66, 183, 71);
|
||||||
|
}
|
||||||
|
&.negative {
|
||||||
|
color: rgb(183, 66, 66);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -388,6 +388,7 @@ export class BlockComponent implements OnInit, OnDestroy {
|
||||||
for (const tx of blockAudit.transactions) {
|
for (const tx of blockAudit.transactions) {
|
||||||
inBlock[tx.txid] = true;
|
inBlock[tx.txid] = true;
|
||||||
}
|
}
|
||||||
|
blockAudit.feeDelta = (blockAudit.expectedFees - this.block.extras.totalFees) / blockAudit.expectedFees;
|
||||||
this.setAuditAvailable(true);
|
this.setAuditAvailable(true);
|
||||||
} else {
|
} else {
|
||||||
this.setAuditAvailable(false);
|
this.setAuditAvailable(false);
|
||||||
|
|
|
@ -151,6 +151,8 @@ export interface BlockAudit extends BlockExtended {
|
||||||
addedTxs: string[],
|
addedTxs: string[],
|
||||||
matchRate: number,
|
matchRate: number,
|
||||||
expectedFees: number,
|
expectedFees: number,
|
||||||
|
expectedWeight: number,
|
||||||
|
feeDelta?: number,
|
||||||
template: TransactionStripped[],
|
template: TransactionStripped[],
|
||||||
transactions: TransactionStripped[],
|
transactions: TransactionStripped[],
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue