Revert "Better audits"
This commit is contained in:
		
							parent
							
								
									10d4e5a600
								
							
						
					
					
						commit
						102cb96483
					
				@ -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