diff --git a/backend/src/api/audit.ts b/backend/src/api/audit.ts index 5b67dc965..d0d677740 100644 --- a/backend/src/api/audit.ts +++ b/backend/src/api/audit.ts @@ -5,9 +5,9 @@ const PROPAGATION_MARGIN = 180; // in seconds, time since a transaction is first class Audit { auditBlock(transactions: TransactionExtended[], projectedBlocks: MempoolBlockWithTransactions[], mempool: { [txId: string]: TransactionExtended }) - : { censored: string[], added: string[], fresh: string[], score: number } { + : { censored: string[], added: string[], fresh: string[], score: number, similarity: number } { if (!projectedBlocks?.[0]?.transactionIds || !mempool) { - return { censored: [], added: [], fresh: [], score: 0 }; + return { censored: [], added: [], fresh: [], score: 0, similarity: 1 }; } const matches: string[] = []; // present in both mined block and template @@ -16,6 +16,8 @@ class Audit { const isCensored = {}; // missing, without excuse const isDisplaced = {}; let displacedWeight = 0; + let matchedWeight = 0; + let projectedWeight = 0; const inBlock = {}; const inTemplate = {}; @@ -38,11 +40,16 @@ class Audit { isCensored[txid] = true; } displacedWeight += mempool[txid].weight; + } else { + matchedWeight += mempool[txid].weight; } + projectedWeight += mempool[txid].weight; inTemplate[txid] = true; } displacedWeight += (4000 - transactions[0].weight); + projectedWeight += transactions[0].weight; + matchedWeight += transactions[0].weight; // we can expect an honest miner to include 'displaced' transactions in place of recent arrivals and censored txs // these displaced transactions should occupy the first N weight units of the next projected block @@ -121,12 +128,14 @@ class Audit { const numCensored = Object.keys(isCensored).length; const numMatches = matches.length - 1; // adjust for coinbase tx const score = numMatches > 0 ? (numMatches / (numMatches + numCensored)) : 0; + const similarity = projectedWeight ? matchedWeight / projectedWeight : 1; return { censored: Object.keys(isCensored), added, fresh, - score + score, + similarity, }; } } diff --git a/backend/src/api/websocket-handler.ts b/backend/src/api/websocket-handler.ts index a96264825..1eeb17d2e 100644 --- a/backend/src/api/websocket-handler.ts +++ b/backend/src/api/websocket-handler.ts @@ -432,7 +432,7 @@ class WebsocketHandler { } if (Common.indexingEnabled() && memPool.isInSync()) { - const { censored, added, fresh, score } = Audit.auditBlock(transactions, projectedBlocks, auditMempool); + const { censored, added, fresh, score, similarity } = Audit.auditBlock(transactions, projectedBlocks, auditMempool); const matchRate = Math.round(score * 100 * 100) / 100; const stripped = projectedBlocks[0]?.transactions ? projectedBlocks[0].transactions.map((tx) => { @@ -464,6 +464,7 @@ class WebsocketHandler { if (block.extras) { block.extras.matchRate = matchRate; + block.extras.similarity = similarity; } } } diff --git a/backend/src/mempool.interfaces.ts b/backend/src/mempool.interfaces.ts index 8662770bc..9961632c3 100644 --- a/backend/src/mempool.interfaces.ts +++ b/backend/src/mempool.interfaces.ts @@ -153,6 +153,7 @@ export interface BlockExtension { feeRange: number[]; // fee rate percentiles reward: number; matchRate: number | null; + similarity?: number; pool: { id: number; // Note - This is the `unique_id`, not to mix with the auto increment `id` name: string; diff --git a/frontend/src/app/components/mempool-blocks/mempool-blocks.component.ts b/frontend/src/app/components/mempool-blocks/mempool-blocks.component.ts index 1dc280514..84b2b7bc0 100644 --- a/frontend/src/app/components/mempool-blocks/mempool-blocks.component.ts +++ b/frontend/src/app/components/mempool-blocks/mempool-blocks.component.ts @@ -174,7 +174,7 @@ export class MempoolBlocksComponent implements OnInit, OnDestroy { } this.chainTip = this.stateService.latestBlockHeight; - if (block?.extras?.matchRate >= 66 && !this.tabHidden) { + if ((block?.extras?.similarity == null || block?.extras?.similarity > 0.5) && !this.tabHidden) { this.blockIndex++; } }); @@ -225,7 +225,7 @@ export class MempoolBlocksComponent implements OnInit, OnDestroy { } trackByFn(index: number, block: MempoolBlock) { - return (block.isStack) ? 'stack' : block.height; + return (block.isStack) ? 'stack' : block.index; } reduceMempoolBlocksToFitScreen(blocks: MempoolBlock[]): MempoolBlock[] { diff --git a/frontend/src/app/interfaces/node-api.interface.ts b/frontend/src/app/interfaces/node-api.interface.ts index cad623f9f..024376ca6 100644 --- a/frontend/src/app/interfaces/node-api.interface.ts +++ b/frontend/src/app/interfaces/node-api.interface.ts @@ -118,6 +118,7 @@ export interface BlockExtension { reward?: number; coinbaseRaw?: string; matchRate?: number; + similarity?: number; pool?: { id: number; name: string;