diff --git a/backend/src/api/database-migration.ts b/backend/src/api/database-migration.ts index ff06d3811..a87fdc0fd 100644 --- a/backend/src/api/database-migration.ts +++ b/backend/src/api/database-migration.ts @@ -534,7 +534,8 @@ class DatabaseMigration { } 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); } diff --git a/backend/src/api/websocket-handler.ts b/backend/src/api/websocket-handler.ts index 8d9a74d12..b2c529220 100644 --- a/backend/src/api/websocket-handler.ts +++ b/backend/src/api/websocket-handler.ts @@ -559,8 +559,6 @@ class WebsocketHandler { } 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 matchRate = Math.round(score * 100 * 100) / 100; @@ -573,8 +571,12 @@ class WebsocketHandler { }; }) : []; - const totalFees = stripped.reduce((total, transaction) => total + transaction.fee, 0); - logger.debug(`Projected block fees: ${totalFees} sats`); + let totalFees = 0; + let totalWeight = 0; + for (const tx of stripped) { + totalFees += tx.fee; + totalWeight += (tx.vsize * 4); + } BlocksSummariesRepository.$saveTemplate({ height: block.height, @@ -593,7 +595,8 @@ class WebsocketHandler { freshTxs: fresh, sigopTxs: sigop, matchRate: matchRate, - expectedFees: totalFees + expectedFees: totalFees, + expectedWeight: totalWeight, }); if (block.extras) { diff --git a/backend/src/mempool.interfaces.ts b/backend/src/mempool.interfaces.ts index bc73c0a8a..478e88c33 100644 --- a/backend/src/mempool.interfaces.ts +++ b/backend/src/mempool.interfaces.ts @@ -35,13 +35,15 @@ export interface BlockAudit { sigopTxs: string[], addedTxs: string[], matchRate: number, - expectedFees: number; + expectedFees?: number, + expectedWeight?: number, } export interface AuditScore { hash: string, matchRate?: number, expectedFees?: number + expectedWeight?: number } export interface MempoolBlock { @@ -185,6 +187,7 @@ export interface BlockExtension { reward: number; matchRate: number | null; expectedFees: number | null; + expectedWeight: number | null; similarity?: number; pool: { id: number; // Note - This is the `unique_id`, not to mix with the auto increment `id` diff --git a/backend/src/repositories/BlocksAuditsRepository.ts b/backend/src/repositories/BlocksAuditsRepository.ts index 449c1d189..d755654ea 100644 --- a/backend/src/repositories/BlocksAuditsRepository.ts +++ b/backend/src/repositories/BlocksAuditsRepository.ts @@ -6,9 +6,9 @@ import { BlockAudit, AuditScore } from '../mempool.interfaces'; class BlocksAuditRepositories { public async $saveAudit(audit: BlockAudit): Promise { try { - await DB.query(`INSERT INTO blocks_audits(time, height, hash, missing_txs, added_txs, fresh_txs, sigop_txs, match_rate, expected_fees) - 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]); + 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), + JSON.stringify(audit.addedTxs), JSON.stringify(audit.freshTxs), JSON.stringify(audit.sigopTxs), audit.matchRate, audit.expectedFees, audit.expectedWeight]); } catch (e: any) { 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`); @@ -51,7 +51,15 @@ class BlocksAuditRepositories { const [rows]: any[] = await DB.query( `SELECT blocks.height, blocks.hash as id, UNIX_TIMESTAMP(blocks.blockTimestamp) as timestamp, blocks.size, 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 JOIN blocks ON blocks.hash = 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 { try { 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 WHERE blocks_audits.hash = "${hash}" `); @@ -95,7 +103,7 @@ class BlocksAuditRepositories { public async $getBlockAuditScores(maxHeight: number, minHeight: number): Promise { try { 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 WHERE blocks_audits.height BETWEEN ? AND ? `, [minHeight, maxHeight]); diff --git a/backend/src/repositories/BlocksRepository.ts b/backend/src/repositories/BlocksRepository.ts index fff9c1b1b..9b07e6f03 100644 --- a/backend/src/repositories/BlocksRepository.ts +++ b/backend/src/repositories/BlocksRepository.ts @@ -1038,6 +1038,7 @@ class BlocksRepository { if (auditScore != null) { extras.matchRate = auditScore.matchRate; extras.expectedFees = auditScore.expectedFees; + extras.expectedWeight = auditScore.expectedWeight; } } diff --git a/frontend/src/app/components/block/block.component.html b/frontend/src/app/components/block/block.component.html index d14b70389..4e58d32f2 100644 --- a/frontend/src/app/components/block/block.component.html +++ b/frontend/src/app/components/block/block.component.html @@ -72,15 +72,6 @@ - - Expected total fees - - - - - - - @@ -235,6 +226,9 @@ (txClickEvent)="onTxClick($event)" (txHoverEvent)="onTxHover($event)" [unavailable]="!isMobile && !showAudit"> + + +

Actual Block

@@ -244,6 +238,9 @@ (txClickEvent)="onTxClick($event)" (txHoverEvent)="onTxHover($event)" [unavailable]="isMobile && !showAudit">
+ + + @@ -394,5 +391,54 @@
+ + + + + + + + + + + + + + + + +
Expected fees + +
Expected weight
Expected transactions{{ blockAudit.template?.length || 0 }}
+
+ + + + + + + + + + + + + + + + + +
Actual fees + + + {{ blockAudit.feeDelta < 0 ? '+' : '' }}{{ (-blockAudit.feeDelta * 100) | amountShortener: 2 }}% + +
Actual weight + +
Actual transactions + {{ block.tx_count }} +
+
+

diff --git a/frontend/src/app/components/block/block.component.scss b/frontend/src/app/components/block/block.component.scss index 319f53804..48f094926 100644 --- a/frontend/src/app/components/block/block.component.scss +++ b/frontend/src/app/components/block/block.component.scss @@ -38,6 +38,17 @@ color: rgba(255, 255, 255, 0.4); margin-left: 5px; } + + .difference { + margin-left: 0.5em; + + &.positive { + color: rgb(66, 183, 71); + } + &.negative { + color: rgb(183, 66, 66); + } + } } } diff --git a/frontend/src/app/components/block/block.component.ts b/frontend/src/app/components/block/block.component.ts index f5fe1a469..671fd9d5b 100644 --- a/frontend/src/app/components/block/block.component.ts +++ b/frontend/src/app/components/block/block.component.ts @@ -388,6 +388,7 @@ export class BlockComponent implements OnInit, OnDestroy { for (const tx of blockAudit.transactions) { inBlock[tx.txid] = true; } + blockAudit.feeDelta = (blockAudit.expectedFees - this.block.extras.totalFees) / blockAudit.expectedFees; this.setAuditAvailable(true); } else { this.setAuditAvailable(false); diff --git a/frontend/src/app/interfaces/node-api.interface.ts b/frontend/src/app/interfaces/node-api.interface.ts index 3de82b910..5aaeda545 100644 --- a/frontend/src/app/interfaces/node-api.interface.ts +++ b/frontend/src/app/interfaces/node-api.interface.ts @@ -151,6 +151,8 @@ export interface BlockAudit extends BlockExtended { addedTxs: string[], matchRate: number, expectedFees: number, + expectedWeight: number, + feeDelta?: number, template: TransactionStripped[], transactions: TransactionStripped[], }