Merge pull request #4857 from mempool/revert-4852-mononaut/better-audits
Revert "Better audits"
This commit is contained in:
		
						commit
						d279ea3278
					
				| @ -7,14 +7,13 @@ const PROPAGATION_MARGIN = 180; // in seconds, time since a transaction is first | |||||||
| 
 | 
 | ||||||
| class Audit { | class Audit { | ||||||
|   auditBlock(transactions: MempoolTransactionExtended[], projectedBlocks: MempoolBlockWithTransactions[], mempool: { [txId: string]: MempoolTransactionExtended }, useAccelerations: boolean = false) |   auditBlock(transactions: MempoolTransactionExtended[], projectedBlocks: MempoolBlockWithTransactions[], mempool: { [txId: string]: MempoolTransactionExtended }, useAccelerations: boolean = false) | ||||||
|    : { censored: string[], added: string[], prioritized: string[], fresh: string[], sigop: string[], fullrbf: string[], accelerated: string[], score: number, similarity: number } { |    : { censored: string[], added: string[], fresh: string[], sigop: string[], fullrbf: string[], accelerated: string[], score: number, similarity: number } { | ||||||
|     if (!projectedBlocks?.[0]?.transactionIds || !mempool) { |     if (!projectedBlocks?.[0]?.transactionIds || !mempool) { | ||||||
|       return { censored: [], added: [], prioritized: [], fresh: [], sigop: [], fullrbf: [], accelerated: [], score: 1, similarity: 1 }; |       return { censored: [], added: [], fresh: [], sigop: [], fullrbf: [], accelerated: [], score: 1, similarity: 1 }; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const matches: string[] = []; // present in both mined block and template
 |     const matches: string[] = []; // present in both mined block and template
 | ||||||
|     const added: string[] = []; // present in mined block, not in template
 |     const added: string[] = []; // present in mined block, not in template
 | ||||||
|     const prioritized: string[] = [] // present in the mined block, not in the template, but further down in the mempool
 |  | ||||||
|     const fresh: string[] = []; // missing, but firstSeen or lastBoosted within PROPAGATION_MARGIN
 |     const fresh: string[] = []; // missing, but firstSeen or lastBoosted within PROPAGATION_MARGIN
 | ||||||
|     const rbf: string[] = []; // either missing or present, and either part of a full-rbf replacement, or a conflict with the mined block
 |     const rbf: string[] = []; // either missing or present, and either part of a full-rbf replacement, or a conflict with the mined block
 | ||||||
|     const accelerated: string[] = []; // prioritized by the mempool accelerator
 |     const accelerated: string[] = []; // prioritized by the mempool accelerator
 | ||||||
| @ -69,27 +68,20 @@ class Audit { | |||||||
| 
 | 
 | ||||||
|     // we can expect an honest miner to include 'displaced' transactions in place of recent arrivals and censored txs
 |     // 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
 |     // these displaced transactions should occupy the first N weight units of the next projected block
 | ||||||
|     let displacedWeightRemaining = displacedWeight + 4000; |     let displacedWeightRemaining = displacedWeight; | ||||||
|     let index = 0; |     let index = 0; | ||||||
|     let lastFeeRate = Infinity; |     let lastFeeRate = Infinity; | ||||||
|     let failures = 0; |     let failures = 0; | ||||||
|     let blockIndex = 1; |     while (projectedBlocks[1] && index < projectedBlocks[1].transactionIds.length && failures < 500) { | ||||||
|     while (projectedBlocks[blockIndex] && failures < 500) { |       const txid = projectedBlocks[1].transactionIds[index]; | ||||||
|       if (index >= projectedBlocks[blockIndex].transactionIds.length) { |  | ||||||
|         index = 0; |  | ||||||
|         blockIndex++; |  | ||||||
|       } |  | ||||||
|       const txid = projectedBlocks[blockIndex].transactionIds[index]; |  | ||||||
|       const tx = mempool[txid]; |       const tx = mempool[txid]; | ||||||
|       if (tx) { |       if (tx) { | ||||||
|         const fits = (tx.weight - displacedWeightRemaining) < 4000; |         const fits = (tx.weight - displacedWeightRemaining) < 4000; | ||||||
|         // 0.005 margin of error for any remaining vsize rounding issues
 |         const feeMatches = tx.effectiveFeePerVsize >= lastFeeRate; | ||||||
|         const feeMatches = tx.effectiveFeePerVsize >= (lastFeeRate - 0.005); |  | ||||||
|         if (fits || feeMatches) { |         if (fits || feeMatches) { | ||||||
|           isDisplaced[txid] = true; |           isDisplaced[txid] = true; | ||||||
|           if (fits) { |           if (fits) { | ||||||
|             // (tx.effectiveFeePerVsize * tx.vsize) / Math.ceil(tx.vsize) attempts to correct for vsize rounding in the simple non-CPFP case
 |             lastFeeRate = Math.min(lastFeeRate, tx.effectiveFeePerVsize); | ||||||
|             lastFeeRate = Math.min(lastFeeRate, (tx.effectiveFeePerVsize * tx.vsize) / Math.ceil(tx.vsize)); |  | ||||||
|           } |           } | ||||||
|           if (tx.firstSeen == null || (now - (tx?.firstSeen || 0)) > PROPAGATION_MARGIN) { |           if (tx.firstSeen == null || (now - (tx?.firstSeen || 0)) > PROPAGATION_MARGIN) { | ||||||
|             displacedWeightRemaining -= tx.weight; |             displacedWeightRemaining -= tx.weight; | ||||||
| @ -114,11 +106,7 @@ class Audit { | |||||||
|         if (rbfCache.has(tx.txid)) { |         if (rbfCache.has(tx.txid)) { | ||||||
|           rbf.push(tx.txid); |           rbf.push(tx.txid); | ||||||
|         } else if (!isDisplaced[tx.txid]) { |         } else if (!isDisplaced[tx.txid]) { | ||||||
|           if (mempool[tx.txid]) { |           added.push(tx.txid); | ||||||
|             prioritized.push(tx.txid); |  | ||||||
|           } else { |  | ||||||
|             added.push(tx.txid); |  | ||||||
|           } |  | ||||||
|         } |         } | ||||||
|         overflowWeight += tx.weight; |         overflowWeight += tx.weight; | ||||||
|       } |       } | ||||||
| @ -167,7 +155,6 @@ class Audit { | |||||||
|     return { |     return { | ||||||
|       censored: Object.keys(isCensored), |       censored: Object.keys(isCensored), | ||||||
|       added, |       added, | ||||||
|       prioritized, |  | ||||||
|       fresh, |       fresh, | ||||||
|       sigop: [], |       sigop: [], | ||||||
|       fullrbf: rbf, |       fullrbf: rbf, | ||||||
|  | |||||||
| @ -552,7 +552,6 @@ export class Common { | |||||||
|       value: tx.vout.reduce((acc, vout) => acc + (vout.value ? vout.value : 0), 0), |       value: tx.vout.reduce((acc, vout) => acc + (vout.value ? vout.value : 0), 0), | ||||||
|       acc: tx.acceleration || undefined, |       acc: tx.acceleration || undefined, | ||||||
|       rate: tx.effectiveFeePerVsize, |       rate: tx.effectiveFeePerVsize, | ||||||
|       time: tx.firstSeen || undefined, |  | ||||||
|     }; |     }; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -7,7 +7,7 @@ import cpfpRepository from '../repositories/CpfpRepository'; | |||||||
| import { RowDataPacket } from 'mysql2'; | import { RowDataPacket } from 'mysql2'; | ||||||
| 
 | 
 | ||||||
| class DatabaseMigration { | class DatabaseMigration { | ||||||
|   private static currentVersion = 76; |   private static currentVersion = 75; | ||||||
|   private queryTimeout = 3600_000; |   private queryTimeout = 3600_000; | ||||||
|   private statisticsAddedIndexed = false; |   private statisticsAddedIndexed = false; | ||||||
|   private uniqueLogs: string[] = []; |   private uniqueLogs: string[] = []; | ||||||
| @ -654,11 +654,6 @@ class DatabaseMigration { | |||||||
|       await this.$executeQuery('ALTER TABLE `prices` ADD `ZAR` float DEFAULT "-1"'); |       await this.$executeQuery('ALTER TABLE `prices` ADD `ZAR` float DEFAULT "-1"'); | ||||||
|       await this.updateToSchemaVersion(75); |       await this.updateToSchemaVersion(75); | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     if (databaseSchemaVersion < 76 && isBitcoin === true) { |  | ||||||
|       await this.$executeQuery('ALTER TABLE `blocks_audits` ADD prioritized_txs JSON DEFAULT "[]"'); |  | ||||||
|       await this.updateToSchemaVersion(76); |  | ||||||
|     } |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /** |   /** | ||||||
|  | |||||||
| @ -598,8 +598,7 @@ class MempoolBlocks { | |||||||
|         tx.value, |         tx.value, | ||||||
|         Math.round((tx.rate || (tx.fee / tx.vsize)) * 100) / 100, |         Math.round((tx.rate || (tx.fee / tx.vsize)) * 100) / 100, | ||||||
|         tx.flags, |         tx.flags, | ||||||
|         tx.time || 0, |         1 | ||||||
|         1, |  | ||||||
|       ]; |       ]; | ||||||
|     } else { |     } else { | ||||||
|       return [ |       return [ | ||||||
| @ -609,7 +608,6 @@ class MempoolBlocks { | |||||||
|         tx.value, |         tx.value, | ||||||
|         Math.round((tx.rate || (tx.fee / tx.vsize)) * 100) / 100, |         Math.round((tx.rate || (tx.fee / tx.vsize)) * 100) / 100, | ||||||
|         tx.flags, |         tx.flags, | ||||||
|         tx.time || 0, |  | ||||||
|       ]; |       ]; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  | |||||||
| @ -869,7 +869,7 @@ class WebsocketHandler { | |||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       if (Common.indexingEnabled()) { |       if (Common.indexingEnabled()) { | ||||||
|         const { censored, added, prioritized, fresh, sigop, fullrbf, accelerated, score, similarity } = Audit.auditBlock(transactions, projectedBlocks, auditMempool); |         const { censored, added, fresh, sigop, fullrbf, accelerated, score, similarity } = Audit.auditBlock(transactions, projectedBlocks, auditMempool); | ||||||
|         const matchRate = Math.round(score * 100 * 100) / 100; |         const matchRate = Math.round(score * 100 * 100) / 100; | ||||||
| 
 | 
 | ||||||
|         const stripped = projectedBlocks[0]?.transactions ? projectedBlocks[0].transactions : []; |         const stripped = projectedBlocks[0]?.transactions ? projectedBlocks[0].transactions : []; | ||||||
| @ -895,7 +895,6 @@ class WebsocketHandler { | |||||||
|           height: block.height, |           height: block.height, | ||||||
|           hash: block.id, |           hash: block.id, | ||||||
|           addedTxs: added, |           addedTxs: added, | ||||||
|           prioritizedTxs: prioritized, |  | ||||||
|           missingTxs: censored, |           missingTxs: censored, | ||||||
|           freshTxs: fresh, |           freshTxs: fresh, | ||||||
|           sigopTxs: sigop, |           sigopTxs: sigop, | ||||||
|  | |||||||
| @ -37,7 +37,6 @@ export interface BlockAudit { | |||||||
|   sigopTxs: string[], |   sigopTxs: string[], | ||||||
|   fullrbfTxs: string[], |   fullrbfTxs: string[], | ||||||
|   addedTxs: string[], |   addedTxs: string[], | ||||||
|   prioritizedTxs: string[], |  | ||||||
|   acceleratedTxs: string[], |   acceleratedTxs: string[], | ||||||
|   matchRate: number, |   matchRate: number, | ||||||
|   expectedFees?: number, |   expectedFees?: number, | ||||||
| @ -201,7 +200,6 @@ export interface TransactionStripped { | |||||||
|   value: number; |   value: number; | ||||||
|   acc?: boolean; |   acc?: boolean; | ||||||
|   rate?: number; // effective fee rate
 |   rate?: number; // effective fee rate
 | ||||||
|   time?: number; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export interface TransactionClassified extends TransactionStripped { | export interface TransactionClassified extends TransactionStripped { | ||||||
| @ -209,7 +207,7 @@ export interface TransactionClassified extends TransactionStripped { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // [txid, fee, vsize, value, rate, flags, acceleration?]
 | // [txid, fee, vsize, value, rate, flags, acceleration?]
 | ||||||
| export type TransactionCompressed = [string, number, number, number, number, number, number, 1?]; | export type TransactionCompressed = [string, number, number, number, number, number, 1?]; | ||||||
| // [txid, rate, flags, acceleration?]
 | // [txid, rate, flags, acceleration?]
 | ||||||
| export type MempoolDeltaChange = [string, number, number, (1|0)]; | export type MempoolDeltaChange = [string, number, number, (1|0)]; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -114,7 +114,6 @@ class AuditReplication { | |||||||
|       time: auditSummary.timestamp || auditSummary.time, |       time: auditSummary.timestamp || auditSummary.time, | ||||||
|       missingTxs: auditSummary.missingTxs || [], |       missingTxs: auditSummary.missingTxs || [], | ||||||
|       addedTxs: auditSummary.addedTxs || [], |       addedTxs: auditSummary.addedTxs || [], | ||||||
|       prioritizedTxs: auditSummary.prioritizedTxs || [], |  | ||||||
|       freshTxs: auditSummary.freshTxs || [], |       freshTxs: auditSummary.freshTxs || [], | ||||||
|       sigopTxs: auditSummary.sigopTxs || [], |       sigopTxs: auditSummary.sigopTxs || [], | ||||||
|       fullrbfTxs: auditSummary.fullrbfTxs || [], |       fullrbfTxs: auditSummary.fullrbfTxs || [], | ||||||
|  | |||||||
| @ -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, prioritized_txs, fresh_txs, sigop_txs, fullrbf_txs, accelerated_txs, match_rate, expected_fees, expected_weight)
 |       await DB.query(`INSERT INTO blocks_audits(time, height, hash, missing_txs, added_txs, fresh_txs, sigop_txs, fullrbf_txs, accelerated_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.prioritizedTxs), JSON.stringify(audit.freshTxs), JSON.stringify(audit.sigopTxs), JSON.stringify(audit.fullrbfTxs), JSON.stringify(audit.acceleratedTxs), audit.matchRate, audit.expectedFees, audit.expectedWeight]); |           JSON.stringify(audit.addedTxs), JSON.stringify(audit.freshTxs), JSON.stringify(audit.sigopTxs), JSON.stringify(audit.fullrbfTxs), JSON.stringify(audit.acceleratedTxs), 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`); | ||||||
| @ -66,7 +66,6 @@ class BlocksAuditRepositories { | |||||||
|         template, |         template, | ||||||
|         missing_txs as missingTxs, |         missing_txs as missingTxs, | ||||||
|         added_txs as addedTxs, |         added_txs as addedTxs, | ||||||
|         prioritized_txs as prioritizedTxs, |  | ||||||
|         fresh_txs as freshTxs, |         fresh_txs as freshTxs, | ||||||
|         sigop_txs as sigopTxs, |         sigop_txs as sigopTxs, | ||||||
|         fullrbf_txs as fullrbfTxs, |         fullrbf_txs as fullrbfTxs, | ||||||
| @ -82,7 +81,6 @@ class BlocksAuditRepositories { | |||||||
|       if (rows.length) { |       if (rows.length) { | ||||||
|         rows[0].missingTxs = JSON.parse(rows[0].missingTxs); |         rows[0].missingTxs = JSON.parse(rows[0].missingTxs); | ||||||
|         rows[0].addedTxs = JSON.parse(rows[0].addedTxs); |         rows[0].addedTxs = JSON.parse(rows[0].addedTxs); | ||||||
|         rows[0].prioritizedTxs = JSON.parse(rows[0].prioritizedTxs); |  | ||||||
|         rows[0].freshTxs = JSON.parse(rows[0].freshTxs); |         rows[0].freshTxs = JSON.parse(rows[0].freshTxs); | ||||||
|         rows[0].sigopTxs = JSON.parse(rows[0].sigopTxs); |         rows[0].sigopTxs = JSON.parse(rows[0].sigopTxs); | ||||||
|         rows[0].fullrbfTxs = JSON.parse(rows[0].fullrbfTxs); |         rows[0].fullrbfTxs = JSON.parse(rows[0].fullrbfTxs); | ||||||
|  | |||||||
| @ -14,7 +14,6 @@ | |||||||
|       [blockConversion]="blockConversion" |       [blockConversion]="blockConversion" | ||||||
|       [filterFlags]="activeFilterFlags" |       [filterFlags]="activeFilterFlags" | ||||||
|       [filterMode]="filterMode" |       [filterMode]="filterMode" | ||||||
|       [relativeTime]="relativeTime" |  | ||||||
|     ></app-block-overview-tooltip> |     ></app-block-overview-tooltip> | ||||||
|     <app-block-filters *ngIf="webGlEnabled && showFilters && filtersAvailable" [excludeFilters]="excludeFilters" [cssWidth]="cssWidth" (onFilterChanged)="setFilterFlags($event)"></app-block-filters> |     <app-block-filters *ngIf="webGlEnabled && showFilters && filtersAvailable" [excludeFilters]="excludeFilters" [cssWidth]="cssWidth" (onFilterChanged)="setFilterFlags($event)"></app-block-filters> | ||||||
|     <div *ngIf="!webGlEnabled" class="placeholder"> |     <div *ngIf="!webGlEnabled" class="placeholder"> | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| import { Component, ElementRef, ViewChild, HostListener, Input, Output, EventEmitter, NgZone, AfterViewInit, OnDestroy, OnChanges } from '@angular/core'; | import { Component, ElementRef, ViewChild, HostListener, Input, Output, EventEmitter, NgZone, AfterViewInit, OnDestroy, OnChanges } from '@angular/core'; | ||||||
| import { TransactionStripped } from '../../interfaces/node-api.interface'; | import { TransactionStripped } from '../../interfaces/websocket.interface'; | ||||||
| import { FastVertexArray } from './fast-vertex-array'; | import { FastVertexArray } from './fast-vertex-array'; | ||||||
| import BlockScene from './block-scene'; | import BlockScene from './block-scene'; | ||||||
| import TxSprite from './tx-sprite'; | import TxSprite from './tx-sprite'; | ||||||
| @ -20,7 +20,7 @@ const unmatchedAuditColors = { | |||||||
|   censored: setOpacity(defaultAuditColors.censored, unmatchedOpacity), |   censored: setOpacity(defaultAuditColors.censored, unmatchedOpacity), | ||||||
|   missing: setOpacity(defaultAuditColors.missing, unmatchedOpacity), |   missing: setOpacity(defaultAuditColors.missing, unmatchedOpacity), | ||||||
|   added: setOpacity(defaultAuditColors.added, unmatchedOpacity), |   added: setOpacity(defaultAuditColors.added, unmatchedOpacity), | ||||||
|   prioritized: setOpacity(defaultAuditColors.prioritized, unmatchedOpacity), |   selected: setOpacity(defaultAuditColors.selected, unmatchedOpacity), | ||||||
|   accelerated: setOpacity(defaultAuditColors.accelerated, unmatchedOpacity), |   accelerated: setOpacity(defaultAuditColors.accelerated, unmatchedOpacity), | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| @ -46,7 +46,6 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On | |||||||
|   @Input() excludeFilters: string[] = []; |   @Input() excludeFilters: string[] = []; | ||||||
|   @Input() filterFlags: bigint | null = null; |   @Input() filterFlags: bigint | null = null; | ||||||
|   @Input() filterMode: FilterMode = 'and'; |   @Input() filterMode: FilterMode = 'and'; | ||||||
|   @Input() relativeTime: number | null; |  | ||||||
|   @Input() blockConversion: Price; |   @Input() blockConversion: Price; | ||||||
|   @Input() overrideColors: ((tx: TxView) => Color) | null = null; |   @Input() overrideColors: ((tx: TxView) => Color) | null = null; | ||||||
|   @Output() txClickEvent = new EventEmitter<{ tx: TransactionStripped, keyModifier: boolean}>(); |   @Output() txClickEvent = new EventEmitter<{ tx: TransactionStripped, keyModifier: boolean}>(); | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| import { FastVertexArray } from './fast-vertex-array'; | import { FastVertexArray } from './fast-vertex-array'; | ||||||
| import TxView from './tx-view'; | import TxView from './tx-view'; | ||||||
| import { TransactionStripped } from '../../interfaces/node-api.interface'; | import { TransactionStripped } from '../../interfaces/websocket.interface'; | ||||||
| import { Color, Position, Square, ViewUpdateParams } from './sprite-types'; | import { Color, Position, Square, ViewUpdateParams } from './sprite-types'; | ||||||
| import { defaultColorFunction } from './utils'; | import { defaultColorFunction } from './utils'; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -32,8 +32,7 @@ export default class TxView implements TransactionStripped { | |||||||
|   rate?: number; |   rate?: number; | ||||||
|   flags: number; |   flags: number; | ||||||
|   bigintFlags?: bigint | null = 0b00000100_00000000_00000000_00000000n; |   bigintFlags?: bigint | null = 0b00000100_00000000_00000000_00000000n; | ||||||
|   time?: number; |   status?: 'found' | 'missing' | 'sigop' | 'fresh' | 'freshcpfp' | 'added' | 'censored' | 'selected' | 'rbf' | 'accelerated'; | ||||||
|   status?: 'found' | 'missing' | 'sigop' | 'fresh' | 'freshcpfp' | 'added' | 'prioritized' | 'censored' | 'selected' | 'rbf' | 'accelerated'; |  | ||||||
|   context?: 'projected' | 'actual'; |   context?: 'projected' | 'actual'; | ||||||
|   scene?: BlockScene; |   scene?: BlockScene; | ||||||
| 
 | 
 | ||||||
| @ -54,7 +53,6 @@ export default class TxView implements TransactionStripped { | |||||||
|     this.scene = scene; |     this.scene = scene; | ||||||
|     this.context = tx.context; |     this.context = tx.context; | ||||||
|     this.txid = tx.txid; |     this.txid = tx.txid; | ||||||
|     this.time = tx.time || 0; |  | ||||||
|     this.fee = tx.fee; |     this.fee = tx.fee; | ||||||
|     this.vsize = tx.vsize; |     this.vsize = tx.vsize; | ||||||
|     this.value = tx.value; |     this.value = tx.value; | ||||||
|  | |||||||
| @ -45,7 +45,7 @@ export const defaultAuditColors = { | |||||||
|   censored: hexToColor('f344df'), |   censored: hexToColor('f344df'), | ||||||
|   missing: darken(desaturate(hexToColor('f344df'), 0.3), 0.7), |   missing: darken(desaturate(hexToColor('f344df'), 0.3), 0.7), | ||||||
|   added: hexToColor('0099ff'), |   added: hexToColor('0099ff'), | ||||||
|   prioritized: darken(desaturate(hexToColor('0099ff'), 0.3), 0.7), |   selected: darken(desaturate(hexToColor('0099ff'), 0.3), 0.7), | ||||||
|   accelerated: hexToColor('8F5FF6'), |   accelerated: hexToColor('8F5FF6'), | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| @ -81,8 +81,6 @@ export function defaultColorFunction( | |||||||
|       return auditColors.missing; |       return auditColors.missing; | ||||||
|     case 'added': |     case 'added': | ||||||
|       return auditColors.added; |       return auditColors.added; | ||||||
|     case 'prioritized': |  | ||||||
|       return auditColors.prioritized; |  | ||||||
|     case 'selected': |     case 'selected': | ||||||
|       return marginalFeeColors[feeLevelIndex] || marginalFeeColors[mempoolFeeColors.length - 1]; |       return marginalFeeColors[feeLevelIndex] || marginalFeeColors[mempoolFeeColors.length - 1]; | ||||||
|     case 'accelerated': |     case 'accelerated': | ||||||
|  | |||||||
| @ -14,26 +14,6 @@ | |||||||
|           <a [routerLink]="['/tx/' | relativeUrl, txid]">{{ txid | shortenString : 16}}</a> |           <a [routerLink]="['/tx/' | relativeUrl, txid]">{{ txid | shortenString : 16}}</a> | ||||||
|         </td> |         </td> | ||||||
|       </tr> |       </tr> | ||||||
|       <tr *ngIf="time"> |  | ||||||
|         <ng-container [ngSwitch]="timeMode"> |  | ||||||
|           <ng-container *ngSwitchCase="'mempool'"> |  | ||||||
|             <td class="label" i18n="transaction.first-seen|Transaction first seen">First seen</td> |  | ||||||
|             <td class="value"><i><app-time kind="since" [time]="time" [fastRender]="true"></app-time></i></td> |  | ||||||
|           </ng-container> |  | ||||||
|           <ng-container *ngSwitchCase="'missed'"> |  | ||||||
|             <td class="label" i18n="transaction.first-seen|Transaction first seen">First seen</td> |  | ||||||
|             <td class="value"><i><app-time kind="before" [time]="relativeTime - time"></app-time></i></td> |  | ||||||
|           </ng-container> |  | ||||||
|           <ng-container *ngSwitchCase="'after'"> |  | ||||||
|             <td class="label" i18n="transaction.first-seen|Transaction first seen">First seen</td> |  | ||||||
|             <td class="value"><i><app-time kind="span" [time]="time - relativeTime"></app-time></i></td> |  | ||||||
|           </ng-container> |  | ||||||
|           <ng-container *ngSwitchCase="'mined'"> |  | ||||||
|             <td class="label" i18n="transaction.confirmed-after|Transaction confirmed after">Confirmed</td> |  | ||||||
|             <td class="value"><i><app-time kind="span" [time]="relativeTime - time"></app-time></i></td> |  | ||||||
|           </ng-container> |  | ||||||
|         </ng-container> |  | ||||||
|       </tr> |  | ||||||
|       <tr> |       <tr> | ||||||
|         <td class="label" i18n="dashboard.latest-transactions.amount">Amount</td> |         <td class="label" i18n="dashboard.latest-transactions.amount">Amount</td> | ||||||
|         <td class="value"><app-amount [blockConversion]="blockConversion" [satoshis]="value" [noFiat]="true"></app-amount></td> |         <td class="value"><app-amount [blockConversion]="blockConversion" [satoshis]="value" [noFiat]="true"></app-amount></td> | ||||||
| @ -74,7 +54,6 @@ | |||||||
|             <span *ngSwitchCase="'fresh'" class="badge badge-warning" i18n="transaction.audit.recently-broadcasted">Recently broadcasted</span> |             <span *ngSwitchCase="'fresh'" class="badge badge-warning" i18n="transaction.audit.recently-broadcasted">Recently broadcasted</span> | ||||||
|             <span *ngSwitchCase="'freshcpfp'" class="badge badge-warning" i18n="transaction.audit.recently-cpfped">Recently CPFP'd</span> |             <span *ngSwitchCase="'freshcpfp'" class="badge badge-warning" i18n="transaction.audit.recently-cpfped">Recently CPFP'd</span> | ||||||
|             <span *ngSwitchCase="'added'" class="badge badge-warning" i18n="transaction.audit.added">Added</span> |             <span *ngSwitchCase="'added'" class="badge badge-warning" i18n="transaction.audit.added">Added</span> | ||||||
|             <span *ngSwitchCase="'prioritized'" class="badge badge-warning" i18n="transaction.audit.prioritized">Prioritized</span> |  | ||||||
|             <span *ngSwitchCase="'selected'" class="badge badge-warning" i18n="transaction.audit.marginal">Marginal fee rate</span> |             <span *ngSwitchCase="'selected'" class="badge badge-warning" i18n="transaction.audit.marginal">Marginal fee rate</span> | ||||||
|             <span *ngSwitchCase="'rbf'" class="badge badge-warning" i18n="transaction.audit.conflicting">Conflicting</span> |             <span *ngSwitchCase="'rbf'" class="badge badge-warning" i18n="transaction.audit.conflicting">Conflicting</span> | ||||||
|             <span *ngSwitchCase="'accelerated'" class="badge badge-accelerated" i18n="transaction.audit.accelerated">Accelerated</span> |             <span *ngSwitchCase="'accelerated'" class="badge badge-accelerated" i18n="transaction.audit.accelerated">Accelerated</span> | ||||||
|  | |||||||
| @ -3,7 +3,6 @@ import { Position } from '../../components/block-overview-graph/sprite-types.js' | |||||||
| import { Price } from '../../services/price.service'; | import { Price } from '../../services/price.service'; | ||||||
| import { TransactionStripped } from '../../interfaces/node-api.interface.js'; | import { TransactionStripped } from '../../interfaces/node-api.interface.js'; | ||||||
| import { Filter, FilterMode, TransactionFlags, toFilters } from '../../shared/filters.utils'; | import { Filter, FilterMode, TransactionFlags, toFilters } from '../../shared/filters.utils'; | ||||||
| import { Block } from '../../interfaces/electrs.interface.js'; |  | ||||||
| 
 | 
 | ||||||
| @Component({ | @Component({ | ||||||
|   selector: 'app-block-overview-tooltip', |   selector: 'app-block-overview-tooltip', | ||||||
| @ -12,7 +11,6 @@ import { Block } from '../../interfaces/electrs.interface.js'; | |||||||
| }) | }) | ||||||
| export class BlockOverviewTooltipComponent implements OnChanges { | export class BlockOverviewTooltipComponent implements OnChanges { | ||||||
|   @Input() tx: TransactionStripped | void; |   @Input() tx: TransactionStripped | void; | ||||||
|   @Input() relativeTime?: number; |  | ||||||
|   @Input() cursorPosition: Position; |   @Input() cursorPosition: Position; | ||||||
|   @Input() clickable: boolean; |   @Input() clickable: boolean; | ||||||
|   @Input() auditEnabled: boolean = false; |   @Input() auditEnabled: boolean = false; | ||||||
| @ -21,7 +19,6 @@ export class BlockOverviewTooltipComponent implements OnChanges { | |||||||
|   @Input() filterMode: FilterMode = 'and'; |   @Input() filterMode: FilterMode = 'and'; | ||||||
| 
 | 
 | ||||||
|   txid = ''; |   txid = ''; | ||||||
|   time: number = 0; |  | ||||||
|   fee = 0; |   fee = 0; | ||||||
|   value = 0; |   value = 0; | ||||||
|   vsize = 1; |   vsize = 1; | ||||||
| @ -29,7 +26,6 @@ export class BlockOverviewTooltipComponent implements OnChanges { | |||||||
|   effectiveRate; |   effectiveRate; | ||||||
|   acceleration; |   acceleration; | ||||||
|   hasEffectiveRate: boolean = false; |   hasEffectiveRate: boolean = false; | ||||||
|   timeMode: 'mempool' | 'mined' | 'missed' | 'after' = 'mempool'; |  | ||||||
|   filters: Filter[] = []; |   filters: Filter[] = []; | ||||||
|   activeFilters: { [key: string]: boolean } = {}; |   activeFilters: { [key: string]: boolean } = {}; | ||||||
| 
 | 
 | ||||||
| @ -60,7 +56,6 @@ export class BlockOverviewTooltipComponent implements OnChanges { | |||||||
| 
 | 
 | ||||||
|     if (this.tx && (changes.tx || changes.filterFlags || changes.filterMode)) { |     if (this.tx && (changes.tx || changes.filterFlags || changes.filterMode)) { | ||||||
|       this.txid = this.tx.txid || ''; |       this.txid = this.tx.txid || ''; | ||||||
|       this.time = this.tx.time || 0; |  | ||||||
|       this.fee = this.tx.fee || 0; |       this.fee = this.tx.fee || 0; | ||||||
|       this.value = this.tx.value || 0; |       this.value = this.tx.value || 0; | ||||||
|       this.vsize = this.tx.vsize || 1; |       this.vsize = this.tx.vsize || 1; | ||||||
| @ -77,22 +72,6 @@ export class BlockOverviewTooltipComponent implements OnChanges { | |||||||
|           this.activeFilters[filter.key] = true; |           this.activeFilters[filter.key] = true; | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
| 
 |  | ||||||
|       if (!this.relativeTime) { |  | ||||||
|         this.timeMode = 'mempool'; |  | ||||||
|       } else { |  | ||||||
|         if (this.tx?.context === 'actual' || this.tx?.status === 'found') { |  | ||||||
|           this.timeMode = 'mined'; |  | ||||||
|         } else { |  | ||||||
|           const time = this.relativeTime || Date.now(); |  | ||||||
|           if (this.time <= time) { |  | ||||||
|             this.timeMode = 'missed'; |  | ||||||
|           } else { |  | ||||||
|             this.timeMode = 'after'; |  | ||||||
|           } |  | ||||||
|         } |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       this.cd.markForCheck(); |       this.cd.markForCheck(); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  | |||||||
| @ -8,7 +8,6 @@ | |||||||
|       [orientation]="'top'" |       [orientation]="'top'" | ||||||
|       [flip]="false" |       [flip]="false" | ||||||
|       [disableSpinner]="true" |       [disableSpinner]="true" | ||||||
|       [relativeTime]="block?.timestamp" |  | ||||||
|       (txClickEvent)="onTxClick($event)" |       (txClickEvent)="onTxClick($event)" | ||||||
|     ></app-block-overview-graph> |     ></app-block-overview-graph> | ||||||
|   </div> |   </div> | ||||||
|  | |||||||
| @ -117,7 +117,6 @@ | |||||||
|             [blockConversion]="blockConversion" |             [blockConversion]="blockConversion" | ||||||
|             [showFilters]="true" |             [showFilters]="true" | ||||||
|             [excludeFilters]="['replacement']" |             [excludeFilters]="['replacement']" | ||||||
|             [relativeTime]="block?.timestamp" |  | ||||||
|             (txClickEvent)="onTxClick($event)" |             (txClickEvent)="onTxClick($event)" | ||||||
|           ></app-block-overview-graph> |           ></app-block-overview-graph> | ||||||
|           <ng-container *ngTemplateOutlet="emptyBlockInfo"></ng-container> |           <ng-container *ngTemplateOutlet="emptyBlockInfo"></ng-container> | ||||||
| @ -233,7 +232,7 @@ | |||||||
|           <app-block-overview-graph #blockGraphProjected [isLoading]="!stateService.isBrowser || isLoadingOverview" [resolution]="86" |           <app-block-overview-graph #blockGraphProjected [isLoading]="!stateService.isBrowser || isLoadingOverview" [resolution]="86" | ||||||
|             [blockLimit]="stateService.blockVSize" [orientation]="'top'" [flip]="false" [mirrorTxid]="hoverTx" [auditHighlighting]="showAudit" |             [blockLimit]="stateService.blockVSize" [orientation]="'top'" [flip]="false" [mirrorTxid]="hoverTx" [auditHighlighting]="showAudit" | ||||||
|             (txClickEvent)="onTxClick($event)" (txHoverEvent)="onTxHover($event)" [unavailable]="!isMobile && !showAudit" |             (txClickEvent)="onTxClick($event)" (txHoverEvent)="onTxHover($event)" [unavailable]="!isMobile && !showAudit" | ||||||
|             [showFilters]="true" [excludeFilters]="['replacement']" [relativeTime]="block?.timestamp"></app-block-overview-graph> |             [showFilters]="true" [excludeFilters]="['replacement']"></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 *ngIf="network !== 'liquid'"> | ||||||
| @ -248,7 +247,7 @@ | |||||||
|           <app-block-overview-graph #blockGraphActual [isLoading]="!stateService.isBrowser || isLoadingOverview" [resolution]="86" |           <app-block-overview-graph #blockGraphActual [isLoading]="!stateService.isBrowser || isLoadingOverview" [resolution]="86" | ||||||
|             [blockLimit]="stateService.blockVSize" [orientation]="'top'" [flip]="false" [mirrorTxid]="hoverTx" mode="mined"  [auditHighlighting]="showAudit" |             [blockLimit]="stateService.blockVSize" [orientation]="'top'" [flip]="false" [mirrorTxid]="hoverTx" mode="mined"  [auditHighlighting]="showAudit" | ||||||
|             (txClickEvent)="onTxClick($event)" (txHoverEvent)="onTxHover($event)" [unavailable]="isMobile && !showAudit" |             (txClickEvent)="onTxClick($event)" (txHoverEvent)="onTxHover($event)" [unavailable]="isMobile && !showAudit" | ||||||
|             [showFilters]="true" [excludeFilters]="['replacement']" [relativeTime]="block?.timestamp"></app-block-overview-graph> |             [showFilters]="true" [excludeFilters]="['replacement']"></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 *ngIf="network !== 'liquid'"> | ||||||
|  | |||||||
| @ -371,7 +371,6 @@ export class BlockComponent implements OnInit, OnDestroy { | |||||||
|         const inTemplate = {}; |         const inTemplate = {}; | ||||||
|         const inBlock = {}; |         const inBlock = {}; | ||||||
|         const isAdded = {}; |         const isAdded = {}; | ||||||
|         const isPrioritized = {}; |  | ||||||
|         const isCensored = {}; |         const isCensored = {}; | ||||||
|         const isMissing = {}; |         const isMissing = {}; | ||||||
|         const isSelected = {}; |         const isSelected = {}; | ||||||
| @ -395,9 +394,6 @@ export class BlockComponent implements OnInit, OnDestroy { | |||||||
|           for (const txid of blockAudit.addedTxs) { |           for (const txid of blockAudit.addedTxs) { | ||||||
|             isAdded[txid] = true; |             isAdded[txid] = true; | ||||||
|           } |           } | ||||||
|           for (const txid of blockAudit.prioritizedTxs) { |  | ||||||
|             isPrioritized[txid] = true; |  | ||||||
|           } |  | ||||||
|           for (const txid of blockAudit.missingTxs) { |           for (const txid of blockAudit.missingTxs) { | ||||||
|             isCensored[txid] = true; |             isCensored[txid] = true; | ||||||
|           } |           } | ||||||
| @ -447,8 +443,6 @@ export class BlockComponent implements OnInit, OnDestroy { | |||||||
|               tx.status = null; |               tx.status = null; | ||||||
|             } else if (isAdded[tx.txid]) { |             } else if (isAdded[tx.txid]) { | ||||||
|               tx.status = 'added'; |               tx.status = 'added'; | ||||||
|             } else if (isPrioritized[tx.txid]) { |  | ||||||
|               tx.status = 'prioritized'; |  | ||||||
|             } else if (inTemplate[tx.txid]) { |             } else if (inTemplate[tx.txid]) { | ||||||
|               tx.status = 'found'; |               tx.status = 'found'; | ||||||
|             } else if (isRbf[tx.txid]) { |             } else if (isRbf[tx.txid]) { | ||||||
|  | |||||||
| @ -12,7 +12,6 @@ | |||||||
|           [animationDuration]="animationDuration" |           [animationDuration]="animationDuration" | ||||||
|           [animationOffset]="animationOffset" |           [animationOffset]="animationOffset" | ||||||
|           [disableSpinner]="true" |           [disableSpinner]="true" | ||||||
|           [relativeTime]="blockInfo[i]?.timestamp" |  | ||||||
|           (txClickEvent)="onTxClick($event)" |           (txClickEvent)="onTxClick($event)" | ||||||
|         ></app-block-overview-graph> |         ></app-block-overview-graph> | ||||||
|         <div *ngIf="showInfo && blockInfo[i]" class="info" @infoChange> |         <div *ngIf="showInfo && blockInfo[i]" class="info" @infoChange> | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| import { HostListener, OnChanges, OnDestroy } from '@angular/core'; | import { HostListener, OnChanges, OnDestroy } from '@angular/core'; | ||||||
| import { Component, Input, OnInit, ChangeDetectionStrategy } from '@angular/core'; | import { Component, Input, OnInit, ChangeDetectionStrategy } from '@angular/core'; | ||||||
| import { TransactionStripped } from '../../interfaces/node-api.interface'; | import { TransactionStripped } from '../../interfaces/websocket.interface'; | ||||||
| import { StateService } from '../../services/state.service'; | import { StateService } from '../../services/state.service'; | ||||||
| import { VbytesPipe } from '../../shared/pipes/bytes-pipe/vbytes.pipe'; | import { VbytesPipe } from '../../shared/pipes/bytes-pipe/vbytes.pipe'; | ||||||
| import { selectPowerOfTen } from '../../bitcoin.utils'; | import { selectPowerOfTen } from '../../bitcoin.utils'; | ||||||
|  | |||||||
| @ -1,8 +1,7 @@ | |||||||
| import { Component, ComponentRef, ViewChild, HostListener, Input, Output, EventEmitter, | import { Component, ComponentRef, ViewChild, HostListener, Input, Output, EventEmitter, | ||||||
|   OnInit, OnDestroy, OnChanges, ChangeDetectionStrategy, ChangeDetectorRef, AfterViewInit } from '@angular/core'; |   OnInit, OnDestroy, OnChanges, ChangeDetectionStrategy, ChangeDetectorRef, AfterViewInit } from '@angular/core'; | ||||||
| import { StateService } from '../../services/state.service'; | import { StateService } from '../../services/state.service'; | ||||||
| import { MempoolBlockDelta } from '../../interfaces/websocket.interface'; | import { MempoolBlockDelta, TransactionStripped } from '../../interfaces/websocket.interface'; | ||||||
| import { TransactionStripped } from '../../interfaces/node-api.interface'; |  | ||||||
| import { BlockOverviewGraphComponent } from '../../components/block-overview-graph/block-overview-graph.component'; | import { BlockOverviewGraphComponent } from '../../components/block-overview-graph/block-overview-graph.component'; | ||||||
| import { Subscription, BehaviorSubject, merge, of, timer } from 'rxjs'; | import { Subscription, BehaviorSubject, merge, of, timer } from 'rxjs'; | ||||||
| import { switchMap, filter, concatMap, map } from 'rxjs/operators'; | import { switchMap, filter, concatMap, map } from 'rxjs/operators'; | ||||||
|  | |||||||
| @ -3,8 +3,7 @@ import { detectWebGL } from '../../shared/graphs.utils'; | |||||||
| import { StateService } from '../../services/state.service'; | import { StateService } from '../../services/state.service'; | ||||||
| import { ActivatedRoute, ParamMap } from '@angular/router'; | import { ActivatedRoute, ParamMap } from '@angular/router'; | ||||||
| import { switchMap, map, tap, filter } from 'rxjs/operators'; | import { switchMap, map, tap, filter } from 'rxjs/operators'; | ||||||
| import { MempoolBlock } from '../../interfaces/websocket.interface'; | import { MempoolBlock, TransactionStripped } from '../../interfaces/websocket.interface'; | ||||||
| import { TransactionStripped } from '../../interfaces/node-api.interface'; |  | ||||||
| import { Observable, BehaviorSubject } from 'rxjs'; | import { Observable, BehaviorSubject } from 'rxjs'; | ||||||
| import { SeoService } from '../../services/seo.service'; | import { SeoService } from '../../services/seo.service'; | ||||||
| import { seoDescriptionNetwork } from '../../shared/common.utils'; | import { seoDescriptionNetwork } from '../../shared/common.utils'; | ||||||
|  | |||||||
| @ -23,7 +23,7 @@ export class TimeComponent implements OnInit, OnChanges, OnDestroy { | |||||||
| 
 | 
 | ||||||
|   @Input() time: number; |   @Input() time: number; | ||||||
|   @Input() dateString: number; |   @Input() dateString: number; | ||||||
|   @Input() kind: 'plain' | 'since' | 'until' | 'span' | 'before' = 'plain'; |   @Input() kind: 'plain' | 'since' | 'until' | 'span' = 'plain'; | ||||||
|   @Input() fastRender = false; |   @Input() fastRender = false; | ||||||
|   @Input() fixedRender = false; |   @Input() fixedRender = false; | ||||||
|   @Input() relative = false; |   @Input() relative = false; | ||||||
| @ -86,9 +86,7 @@ export class TimeComponent implements OnInit, OnChanges, OnDestroy { | |||||||
|         seconds = Math.floor(this.time); |         seconds = Math.floor(this.time); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (seconds < 1 && this.kind === 'span') { |     if (seconds < 60) { | ||||||
|       return $localize`:@@date-base.immediately:Immediately`; |  | ||||||
|     } else if (seconds < 60) { |  | ||||||
|       if (this.relative || this.kind === 'since') { |       if (this.relative || this.kind === 'since') { | ||||||
|         return $localize`:@@date-base.just-now:Just now`; |         return $localize`:@@date-base.just-now:Just now`; | ||||||
|       } else if (this.kind === 'until') { |       } else if (this.kind === 'until') { | ||||||
| @ -208,29 +206,6 @@ export class TimeComponent implements OnInit, OnChanges, OnDestroy { | |||||||
|           } |           } | ||||||
|         } |         } | ||||||
|         break; |         break; | ||||||
|       case 'before': |  | ||||||
|       if (number === 1) { |  | ||||||
|         switch (unit) { // singular (1 day)
 |  | ||||||
|           case 'year': return $localize`:@@time-span:${dateStrings.i18nYear}:DATE: before`; break; |  | ||||||
|           case 'month': return $localize`:@@time-span:${dateStrings.i18nMonth}:DATE: before`; break; |  | ||||||
|           case 'week': return $localize`:@@time-span:${dateStrings.i18nWeek}:DATE: before`; break; |  | ||||||
|           case 'day': return $localize`:@@time-span:${dateStrings.i18nDay}:DATE: before`; break; |  | ||||||
|           case 'hour': return $localize`:@@time-span:${dateStrings.i18nHour}:DATE: before`; break; |  | ||||||
|           case 'minute': return $localize`:@@time-span:${dateStrings.i18nMinute}:DATE: before`; break; |  | ||||||
|           case 'second': return $localize`:@@time-span:${dateStrings.i18nSecond}:DATE: before`; break; |  | ||||||
|         } |  | ||||||
|       } else { |  | ||||||
|         switch (unit) { // plural (2 days)
 |  | ||||||
|           case 'year': return $localize`:@@time-span:${dateStrings.i18nYears}:DATE: before`; break; |  | ||||||
|           case 'month': return $localize`:@@time-span:${dateStrings.i18nMonths}:DATE: before`; break; |  | ||||||
|           case 'week': return $localize`:@@time-span:${dateStrings.i18nWeeks}:DATE: before`; break; |  | ||||||
|           case 'day': return $localize`:@@time-span:${dateStrings.i18nDays}:DATE: before`; break; |  | ||||||
|           case 'hour': return $localize`:@@time-span:${dateStrings.i18nHours}:DATE: before`; break; |  | ||||||
|           case 'minute': return $localize`:@@time-span:${dateStrings.i18nMinutes}:DATE: before`; break; |  | ||||||
|           case 'second': return $localize`:@@time-span:${dateStrings.i18nSeconds}:DATE: before`; break; |  | ||||||
|         } |  | ||||||
|       } |  | ||||||
|       break; |  | ||||||
|       default: |       default: | ||||||
|         if (number === 1) { |         if (number === 1) { | ||||||
|           switch (unit) { // singular (1 day)
 |           switch (unit) { // singular (1 day)
 | ||||||
|  | |||||||
| @ -77,9 +77,8 @@ | |||||||
|                       <span *ngIf="auditStatus.coinbase; else expected" class="badge badge-primary mr-1" i18n="tx-features.tag.coinbase|Coinbase">Coinbase</span> |                       <span *ngIf="auditStatus.coinbase; else expected" class="badge badge-primary mr-1" i18n="tx-features.tag.coinbase|Coinbase">Coinbase</span> | ||||||
|                       <ng-template #expected><span *ngIf="auditStatus.expected; else seen" class="badge badge-success mr-1" i18n-ngbTooltip="Expected in block tooltip" ngbTooltip="This transaction was projected to be included in the block" placement="bottom" i18n="tx-features.tag.expected|Expected in Block">Expected in Block</span></ng-template> |                       <ng-template #expected><span *ngIf="auditStatus.expected; else seen" class="badge badge-success mr-1" i18n-ngbTooltip="Expected in block tooltip" ngbTooltip="This transaction was projected to be included in the block" placement="bottom" i18n="tx-features.tag.expected|Expected in Block">Expected in Block</span></ng-template> | ||||||
|                       <ng-template #seen><span *ngIf="auditStatus.seen; else notSeen" class="badge badge-success mr-1" i18n-ngbTooltip="Seen in mempool tooltip" ngbTooltip="This transaction was seen in the mempool prior to mining" placement="bottom" i18n="tx-features.tag.seen|Seen in Mempool">Seen in Mempool</span></ng-template> |                       <ng-template #seen><span *ngIf="auditStatus.seen; else notSeen" class="badge badge-success mr-1" i18n-ngbTooltip="Seen in mempool tooltip" ngbTooltip="This transaction was seen in the mempool prior to mining" placement="bottom" i18n="tx-features.tag.seen|Seen in Mempool">Seen in Mempool</span></ng-template> | ||||||
|                       <ng-template #notSeen><span *ngIf="!auditStatus.conflict" class="badge badge-warning mr-1" i18n-ngbTooltip="Not seen in mempool tooltip" ngbTooltip="This transaction was missing from our mempool prior to mining" placement="bottom" i18n="tx-features.tag.not-seen|Not seen in Mempool">Not seen in Mempool</span></ng-template> |                       <ng-template #notSeen><span class="badge badge-warning mr-1" i18n-ngbTooltip="Not seen in mempool tooltip" ngbTooltip="This transaction was missing from our mempool prior to mining" placement="bottom" i18n="tx-features.tag.not-seen|Not seen in Mempool">Not seen in Mempool</span></ng-template> | ||||||
|                       <span *ngIf="auditStatus.added" class="badge badge-warning mr-1" i18n-ngbTooltip="Added transaction tooltip" ngbTooltip="This transaction may have been added out-of-band" placement="bottom" i18n="tx-features.tag.added|Added">Added</span> |                       <span *ngIf="auditStatus.added" class="badge badge-warning mr-1" i18n-ngbTooltip="Added transaction tooltip" ngbTooltip="This transaction may have been added or prioritized out-of-band" placement="bottom" i18n="tx-features.tag.added|Added">Added</span> | ||||||
|                       <span *ngIf="auditStatus.prioritized" class="badge badge-warning mr-1" i18n-ngbTooltip="Prioritized transaction tooltip" ngbTooltip="This transaction may have been prioritized out-of-band" placement="bottom" i18n="tx-features.tag.prioritized|Prioritized">Prioritized</span> |  | ||||||
|                       <span *ngIf="auditStatus.conflict" class="badge badge-warning mr-1" i18n-ngbTooltip="Conflict in mempool tooltip" ngbTooltip="This transaction conflicted with another version in our mempool" placement="bottom" i18n="tx-features.tag.conflict|Conflict">Conflict</span> |                       <span *ngIf="auditStatus.conflict" class="badge badge-warning mr-1" i18n-ngbTooltip="Conflict in mempool tooltip" ngbTooltip="This transaction conflicted with another version in our mempool" placement="bottom" i18n="tx-features.tag.conflict|Conflict">Conflict</span> | ||||||
|                     </ng-container> |                     </ng-container> | ||||||
|                   </td> |                   </td> | ||||||
|  | |||||||
| @ -42,7 +42,6 @@ interface AuditStatus { | |||||||
|   seen?: boolean; |   seen?: boolean; | ||||||
|   expected?: boolean; |   expected?: boolean; | ||||||
|   added?: boolean; |   added?: boolean; | ||||||
|   prioritized?: boolean; |  | ||||||
|   delayed?: number; |   delayed?: number; | ||||||
|   accelerated?: boolean; |   accelerated?: boolean; | ||||||
|   conflict?: boolean; |   conflict?: boolean; | ||||||
| @ -318,15 +317,13 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { | |||||||
|           fetchAudit ? this.apiService.getBlockAudit$(hash).pipe( |           fetchAudit ? this.apiService.getBlockAudit$(hash).pipe( | ||||||
|             map(audit => { |             map(audit => { | ||||||
|               const isAdded = audit.addedTxs.includes(txid); |               const isAdded = audit.addedTxs.includes(txid); | ||||||
|               const isPrioritized = audit.prioritizedTxs.includes(txid); |  | ||||||
|               const isAccelerated = audit.acceleratedTxs.includes(txid); |               const isAccelerated = audit.acceleratedTxs.includes(txid); | ||||||
|               const isConflict = audit.fullrbfTxs.includes(txid); |               const isConflict = audit.fullrbfTxs.includes(txid); | ||||||
|               const isExpected = audit.template.some(tx => tx.txid === txid); |               const isExpected = audit.template.some(tx => tx.txid === txid); | ||||||
|               return { |               return { | ||||||
|                 seen: isExpected || isPrioritized || isAccelerated, |                 seen: isExpected || !(isAdded || isConflict), | ||||||
|                 expected: isExpected, |                 expected: isExpected, | ||||||
|                 added: isAdded, |                 added: isAdded, | ||||||
|                 prioritized: isPrioritized, |  | ||||||
|                 conflict: isConflict, |                 conflict: isConflict, | ||||||
|                 accelerated: isAccelerated, |                 accelerated: isAccelerated, | ||||||
|               }; |               }; | ||||||
|  | |||||||
| @ -1,8 +1,8 @@ | |||||||
| import { AfterViewInit, ChangeDetectionStrategy, Component, HostListener, Inject, OnDestroy, OnInit, PLATFORM_ID } from '@angular/core'; | import { AfterViewInit, ChangeDetectionStrategy, Component, HostListener, Inject, OnDestroy, OnInit, PLATFORM_ID } from '@angular/core'; | ||||||
| import { combineLatest, EMPTY, fromEvent, interval, merge, Observable, of, Subject, Subscription, timer } from 'rxjs'; | import { combineLatest, EMPTY, fromEvent, interval, merge, Observable, of, Subject, Subscription, timer } from 'rxjs'; | ||||||
| import { catchError, delayWhen, distinctUntilChanged, filter, map, scan, share, shareReplay, startWith, switchMap, takeUntil, tap, throttleTime } from 'rxjs/operators'; | import { catchError, delayWhen, distinctUntilChanged, filter, map, scan, share, shareReplay, startWith, switchMap, takeUntil, tap, throttleTime } from 'rxjs/operators'; | ||||||
| import { AuditStatus, BlockExtended, CurrentPegs, FederationAddress, FederationUtxo, OptimizedMempoolStats, PegsVolume, RecentPeg, TransactionStripped } from '../interfaces/node-api.interface'; | import { AuditStatus, BlockExtended, CurrentPegs, FederationAddress, FederationUtxo, OptimizedMempoolStats, PegsVolume, RecentPeg } from '../interfaces/node-api.interface'; | ||||||
| import { MempoolInfo, ReplacementInfo } from '../interfaces/websocket.interface'; | import { MempoolInfo, TransactionStripped, ReplacementInfo } from '../interfaces/websocket.interface'; | ||||||
| import { ApiService } from '../services/api.service'; | import { ApiService } from '../services/api.service'; | ||||||
| import { StateService } from '../services/state.service'; | import { StateService } from '../services/state.service'; | ||||||
| import { WebsocketService } from '../services/websocket.service'; | import { WebsocketService } from '../services/websocket.service'; | ||||||
|  | |||||||
| @ -208,7 +208,6 @@ export interface BlockExtended extends Block { | |||||||
| export interface BlockAudit extends BlockExtended { | export interface BlockAudit extends BlockExtended { | ||||||
|   missingTxs: string[], |   missingTxs: string[], | ||||||
|   addedTxs: string[], |   addedTxs: string[], | ||||||
|   prioritizedTxs: string[], |  | ||||||
|   freshTxs: string[], |   freshTxs: string[], | ||||||
|   sigopTxs: string[], |   sigopTxs: string[], | ||||||
|   fullrbfTxs: string[], |   fullrbfTxs: string[], | ||||||
| @ -231,8 +230,7 @@ export interface TransactionStripped { | |||||||
|   rate?: number; // effective fee rate
 |   rate?: number; // effective fee rate
 | ||||||
|   acc?: boolean; |   acc?: boolean; | ||||||
|   flags?: number | null; |   flags?: number | null; | ||||||
|   time?: number; |   status?: 'found' | 'missing' | 'sigop' | 'fresh' | 'freshcpfp' | 'added' | 'censored' | 'selected' | 'rbf' | 'accelerated'; | ||||||
|   status?: 'found' | 'missing' | 'sigop' | 'fresh' | 'freshcpfp' | 'added' | 'prioritized' | 'censored' | 'selected' | 'rbf' | 'accelerated'; |  | ||||||
|   context?: 'projected' | 'actual'; |   context?: 'projected' | 'actual'; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| import { SafeResourceUrl } from '@angular/platform-browser'; | import { SafeResourceUrl } from '@angular/platform-browser'; | ||||||
| import { ILoadingIndicators } from '../services/state.service'; | import { ILoadingIndicators } from '../services/state.service'; | ||||||
| import { Transaction } from './electrs.interface'; | import { Transaction } from './electrs.interface'; | ||||||
| import { BlockExtended, DifficultyAdjustment, RbfTree, TransactionStripped } from './node-api.interface'; | import { BlockExtended, DifficultyAdjustment, RbfTree } from './node-api.interface'; | ||||||
| 
 | 
 | ||||||
| export interface WebsocketResponse { | export interface WebsocketResponse { | ||||||
|   backend?: 'esplora' | 'electrum' | 'none'; |   backend?: 'esplora' | 'electrum' | 'none'; | ||||||
| @ -93,8 +93,20 @@ export interface MempoolInfo { | |||||||
|   minrelaytxfee: number;           //  (numeric) Current minimum relay fee for transactions
 |   minrelaytxfee: number;           //  (numeric) Current minimum relay fee for transactions
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | export interface TransactionStripped { | ||||||
|  |   txid: string; | ||||||
|  |   fee: number; | ||||||
|  |   vsize: number; | ||||||
|  |   value: number; | ||||||
|  |   acc?: boolean; // is accelerated?
 | ||||||
|  |   rate?: number; // effective fee rate
 | ||||||
|  |   flags?: number; | ||||||
|  |   status?: 'found' | 'missing' | 'sigop' | 'fresh' | 'freshcpfp' | 'added' | 'censored' | 'selected' | 'rbf' | 'accelerated'; | ||||||
|  |   context?: 'projected' | 'actual'; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // [txid, fee, vsize, value, rate, flags, acceleration?]
 | // [txid, fee, vsize, value, rate, flags, acceleration?]
 | ||||||
| export type TransactionCompressed = [string, number, number, number, number, number, number, 1?]; | export type TransactionCompressed = [string, number, number, number, number, number, 1?]; | ||||||
| // [txid, rate, flags, acceleration?]
 | // [txid, rate, flags, acceleration?]
 | ||||||
| export type MempoolDeltaChange = [string, number, number, (1|0)]; | export type MempoolDeltaChange = [string, number, number, (1|0)]; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,8 +1,8 @@ | |||||||
| import { Inject, Injectable, PLATFORM_ID, LOCALE_ID } from '@angular/core'; | import { Inject, Injectable, PLATFORM_ID, LOCALE_ID } from '@angular/core'; | ||||||
| import { ReplaySubject, BehaviorSubject, Subject, fromEvent, Observable, merge } from 'rxjs'; | import { ReplaySubject, BehaviorSubject, Subject, fromEvent, Observable, merge } from 'rxjs'; | ||||||
| import { Transaction } from '../interfaces/electrs.interface'; | import { Transaction } from '../interfaces/electrs.interface'; | ||||||
| import { HealthCheckHost, IBackendInfo, MempoolBlock, MempoolBlockDelta, MempoolInfo, Recommendedfees, ReplacedTransaction, ReplacementInfo } from '../interfaces/websocket.interface'; | import { HealthCheckHost, IBackendInfo, MempoolBlock, MempoolBlockDelta, MempoolInfo, Recommendedfees, ReplacedTransaction, ReplacementInfo, TransactionStripped } from '../interfaces/websocket.interface'; | ||||||
| import { BlockExtended, CpfpInfo, DifficultyAdjustment, MempoolPosition, OptimizedMempoolStats, RbfTree, TransactionStripped } from '../interfaces/node-api.interface'; | import { BlockExtended, CpfpInfo, DifficultyAdjustment, MempoolPosition, OptimizedMempoolStats, RbfTree } from '../interfaces/node-api.interface'; | ||||||
| import { Router, NavigationStart } from '@angular/router'; | import { Router, NavigationStart } from '@angular/router'; | ||||||
| import { isPlatformBrowser } from '@angular/common'; | import { isPlatformBrowser } from '@angular/common'; | ||||||
| import { filter, map, scan, shareReplay } from 'rxjs/operators'; | import { filter, map, scan, shareReplay } from 'rxjs/operators'; | ||||||
|  | |||||||
| @ -1,5 +1,4 @@ | |||||||
| import { MempoolBlockDelta, MempoolBlockDeltaCompressed, MempoolDeltaChange, TransactionCompressed } from "../interfaces/websocket.interface"; | import { MempoolBlockDelta, MempoolBlockDeltaCompressed, MempoolDeltaChange, TransactionCompressed, TransactionStripped } from "../interfaces/websocket.interface"; | ||||||
| import { TransactionStripped } from "../interfaces/node-api.interface"; |  | ||||||
| 
 | 
 | ||||||
| export function isMobile(): boolean { | export function isMobile(): boolean { | ||||||
|   return (window.innerWidth <= 767.98); |   return (window.innerWidth <= 767.98); | ||||||
| @ -165,8 +164,7 @@ export function uncompressTx(tx: TransactionCompressed): TransactionStripped { | |||||||
|     value: tx[3], |     value: tx[3], | ||||||
|     rate: tx[4], |     rate: tx[4], | ||||||
|     flags: tx[5], |     flags: tx[5], | ||||||
|     time: tx[6], |     acc: !!tx[6], | ||||||
|     acc: !!tx[7], |  | ||||||
|   }; |   }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user