From 1507003c19085ecb7a1f7ed31eae870f251fe040 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Thu, 21 Dec 2023 16:20:31 +0000 Subject: [PATCH 1/2] Save goggle flags in audit templates & block summaries --- .../src/api/bitcoin/bitcoin-api-abstract-factory.ts | 1 + backend/src/api/blocks.ts | 12 +++++++----- backend/src/api/common.ts | 13 ++++++++----- backend/src/api/mempool-blocks.ts | 2 +- backend/src/mempool.interfaces.ts | 6 +++--- .../src/repositories/BlocksSummariesRepository.ts | 4 ++-- 6 files changed, 22 insertions(+), 16 deletions(-) diff --git a/backend/src/api/bitcoin/bitcoin-api-abstract-factory.ts b/backend/src/api/bitcoin/bitcoin-api-abstract-factory.ts index 5bd961e23..02640efc0 100644 --- a/backend/src/api/bitcoin/bitcoin-api-abstract-factory.ts +++ b/backend/src/api/bitcoin/bitcoin-api-abstract-factory.ts @@ -1,3 +1,4 @@ +import { IBitcoinApi } from './bitcoin-api.interface'; import { IEsploraApi } from './esplora-api.interface'; export interface AbstractBitcoinApi { diff --git a/backend/src/api/blocks.ts b/backend/src/api/blocks.ts index 6b4a14a0e..646672f58 100644 --- a/backend/src/api/blocks.ts +++ b/backend/src/api/blocks.ts @@ -2,7 +2,7 @@ import config from '../config'; import bitcoinApi, { bitcoinCoreApi } from './bitcoin/bitcoin-api-factory'; import logger from '../logger'; import memPool from './mempool'; -import { BlockExtended, BlockExtension, BlockSummary, PoolTag, TransactionExtended, TransactionStripped, TransactionMinerInfo, CpfpSummary, MempoolTransactionExtended } from '../mempool.interfaces'; +import { BlockExtended, BlockExtension, BlockSummary, PoolTag, TransactionExtended, TransactionMinerInfo, CpfpSummary, MempoolTransactionExtended, TransactionClassified } from '../mempool.interfaces'; import { Common } from './common'; import diskCache from './disk-cache'; import transactionUtils from './transaction-utils'; @@ -201,7 +201,8 @@ class Blocks { txid: tx.txid, vsize: tx.weight / 4, fee: tx.fee ? Math.round(tx.fee * 100000000) : 0, - value: Math.round(tx.vout.reduce((acc, vout) => acc + (vout.value ? vout.value : 0), 0) * 100000000) + value: Math.round(tx.vout.reduce((acc, vout) => acc + (vout.value ? vout.value : 0), 0) * 100000000), + flags: 0, }; }); @@ -214,7 +215,7 @@ class Blocks { public summarizeBlockTransactions(hash: string, transactions: TransactionExtended[]): BlockSummary { return { id: hash, - transactions: Common.stripTransactions(transactions), + transactions: Common.classifyTransactions(transactions), }; } @@ -945,7 +946,7 @@ class Blocks { } public async $getStrippedBlockTransactions(hash: string, skipMemoryCache = false, - skipDBLookup = false, cpfpSummary?: CpfpSummary, blockHeight?: number): Promise + skipDBLookup = false, cpfpSummary?: CpfpSummary, blockHeight?: number): Promise { if (skipMemoryCache === false) { // Check the memory cache @@ -974,7 +975,8 @@ class Blocks { fee: tx.fee || 0, vsize: tx.vsize, value: Math.round(tx.vout.reduce((acc, vout) => acc + (vout.value ? vout.value : 0), 0)), - rate: tx.effectiveFeePerVsize + rate: tx.effectiveFeePerVsize, + flags: tx.flags || Common.getTransactionFlags(tx), }; }), }; diff --git a/backend/src/api/common.ts b/backend/src/api/common.ts index af93b9622..0dccdef84 100644 --- a/backend/src/api/common.ts +++ b/backend/src/api/common.ts @@ -1,10 +1,9 @@ import * as bitcoinjs from 'bitcoinjs-lib'; import { Request } from 'express'; -import { Ancestor, CpfpInfo, CpfpSummary, CpfpCluster, EffectiveFeeStats, MempoolBlockWithTransactions, TransactionExtended, MempoolTransactionExtended, TransactionStripped, WorkingEffectiveFeeStats, TransactionClassified, TransactionFlags } from '../mempool.interfaces'; +import { CpfpInfo, CpfpSummary, CpfpCluster, EffectiveFeeStats, MempoolBlockWithTransactions, TransactionExtended, MempoolTransactionExtended, TransactionStripped, WorkingEffectiveFeeStats, TransactionClassified, TransactionFlags } from '../mempool.interfaces'; import config from '../config'; import { NodeSocket } from '../repositories/NodesSocketsRepository'; import { isIP } from 'net'; -import rbfCache from './rbf-cache'; import transactionUtils from './transaction-utils'; import { isPoint } from '../utils/secp256k1'; export class Common { @@ -349,14 +348,18 @@ export class Common { } static classifyTransaction(tx: TransactionExtended): TransactionClassified { - const flags = this.getTransactionFlags(tx); + const flags = Common.getTransactionFlags(tx); tx.flags = flags; return { - ...this.stripTransaction(tx), + ...Common.stripTransaction(tx), flags, }; } + static classifyTransactions(txs: TransactionExtended[]): TransactionClassified[] { + return txs.map(Common.classifyTransaction); + } + static stripTransaction(tx: TransactionExtended): TransactionStripped { return { txid: tx.txid, @@ -369,7 +372,7 @@ export class Common { } static stripTransactions(txs: TransactionExtended[]): TransactionStripped[] { - return txs.map(this.stripTransaction); + return txs.map(Common.stripTransaction); } static sleep$(ms: number): Promise { diff --git a/backend/src/api/mempool-blocks.ts b/backend/src/api/mempool-blocks.ts index 0ca550f4c..58921fcfb 100644 --- a/backend/src/api/mempool-blocks.ts +++ b/backend/src/api/mempool-blocks.ts @@ -1,6 +1,6 @@ import { GbtGenerator, GbtResult, ThreadTransaction as RustThreadTransaction, ThreadAcceleration as RustThreadAcceleration } from 'rust-gbt'; import logger from '../logger'; -import { MempoolBlock, MempoolTransactionExtended, TransactionStripped, MempoolBlockWithTransactions, MempoolBlockDelta, Ancestor, CompactThreadTransaction, EffectiveFeeStats, PoolTag, TransactionClassified } from '../mempool.interfaces'; +import { MempoolBlock, MempoolTransactionExtended, MempoolBlockWithTransactions, MempoolBlockDelta, Ancestor, CompactThreadTransaction, EffectiveFeeStats, PoolTag, TransactionClassified } from '../mempool.interfaces'; import { Common, OnlineFeeStatsCalculator } from './common'; import config from '../config'; import { Worker } from 'worker_threads'; diff --git a/backend/src/mempool.interfaces.ts b/backend/src/mempool.interfaces.ts index c93372ded..09d251050 100644 --- a/backend/src/mempool.interfaces.ts +++ b/backend/src/mempool.interfaces.ts @@ -280,7 +280,7 @@ export interface BlockExtended extends IEsploraApi.Block { export interface BlockSummary { id: string; - transactions: TransactionStripped[]; + transactions: TransactionClassified[]; } export interface AuditSummary extends BlockAudit { @@ -288,8 +288,8 @@ export interface AuditSummary extends BlockAudit { size?: number, weight?: number, tx_count?: number, - transactions: TransactionStripped[]; - template?: TransactionStripped[]; + transactions: TransactionClassified[]; + template?: TransactionClassified[]; } export interface BlockPrice { diff --git a/backend/src/repositories/BlocksSummariesRepository.ts b/backend/src/repositories/BlocksSummariesRepository.ts index 09598db03..a61bf60ed 100644 --- a/backend/src/repositories/BlocksSummariesRepository.ts +++ b/backend/src/repositories/BlocksSummariesRepository.ts @@ -1,6 +1,6 @@ import DB from '../database'; import logger from '../logger'; -import { BlockSummary, TransactionStripped } from '../mempool.interfaces'; +import { BlockSummary, TransactionClassified } from '../mempool.interfaces'; class BlocksSummariesRepository { public async $getByBlockId(id: string): Promise { @@ -17,7 +17,7 @@ class BlocksSummariesRepository { return undefined; } - public async $saveTransactions(blockHeight: number, blockId: string, transactions: TransactionStripped[]): Promise { + public async $saveTransactions(blockHeight: number, blockId: string, transactions: TransactionClassified[]): Promise { try { const transactionsStr = JSON.stringify(transactions); await DB.query(` From 0c9c79c86c51776e78ad842ac88295cef42a80cb Mon Sep 17 00:00:00 2001 From: Mononaut Date: Thu, 21 Dec 2023 16:23:28 +0000 Subject: [PATCH 2/2] Enable goggles on supported mined block visualizations --- .../block-filters.component.html | 2 +- .../block-filters/block-filters.component.ts | 38 +++++++++++++++++-- .../block-overview-graph.component.html | 2 +- .../block-overview-graph.component.ts | 33 ++++++++++++---- .../app/components/block/block.component.html | 8 +++- frontend/src/app/services/state.service.ts | 2 + 6 files changed, 70 insertions(+), 15 deletions(-) diff --git a/frontend/src/app/components/block-filters/block-filters.component.html b/frontend/src/app/components/block-filters/block-filters.component.html index 7b1c2f9e5..f60b04cdd 100644 --- a/frontend/src/app/components/block-filters/block-filters.component.html +++ b/frontend/src/app/components/block-filters/block-filters.component.html @@ -18,7 +18,7 @@
{{ group.label }}
- +
diff --git a/frontend/src/app/components/block-filters/block-filters.component.ts b/frontend/src/app/components/block-filters/block-filters.component.ts index ce0dd76ab..9951984df 100644 --- a/frontend/src/app/components/block-filters/block-filters.component.ts +++ b/frontend/src/app/components/block-filters/block-filters.component.ts @@ -1,5 +1,7 @@ -import { Component, EventEmitter, Output, HostListener, Input, ChangeDetectorRef, OnChanges, SimpleChanges } from '@angular/core'; +import { Component, EventEmitter, Output, HostListener, Input, ChangeDetectorRef, OnChanges, SimpleChanges, OnInit, OnDestroy } from '@angular/core'; import { FilterGroups, TransactionFilters } from '../../shared/filters.utils'; +import { StateService } from '../../services/state.service'; +import { Subscription } from 'rxjs'; @Component({ @@ -7,24 +9,48 @@ import { FilterGroups, TransactionFilters } from '../../shared/filters.utils'; templateUrl: './block-filters.component.html', styleUrls: ['./block-filters.component.scss'], }) -export class BlockFiltersComponent implements OnChanges { +export class BlockFiltersComponent implements OnInit, OnChanges, OnDestroy { @Input() cssWidth: number = 800; + @Input() excludeFilters: string[] = []; @Output() onFilterChanged: EventEmitter = new EventEmitter(); + filterSubscription: Subscription; + filters = TransactionFilters; filterGroups = FilterGroups; + disabledFilters: { [key: string]: boolean } = {}; activeFilters: string[] = []; filterFlags: { [key: string]: boolean } = {}; menuOpen: boolean = false; constructor( + private stateService: StateService, private cd: ChangeDetectorRef, ) {} + ngOnInit(): void { + this.filterSubscription = this.stateService.activeGoggles$.subscribe((activeFilters: string[]) => { + for (const key of Object.keys(this.filterFlags)) { + this.filterFlags[key] = false; + } + for (const key of activeFilters) { + this.filterFlags[key] = !this.disabledFilters[key]; + } + this.activeFilters = [...activeFilters.filter(key => !this.disabledFilters[key])]; + this.onFilterChanged.emit(this.getBooleanFlags()); + }); + } + ngOnChanges(changes: SimpleChanges): void { if (changes.cssWidth) { this.cd.markForCheck(); } + if (changes.excludeFilters) { + this.disabledFilters = {}; + this.excludeFilters.forEach(filter => { + this.disabledFilters[filter] = true; + }); + } } toggleFilter(key): void { @@ -46,7 +72,9 @@ export class BlockFiltersComponent implements OnChanges { // remove active filter this.activeFilters = this.activeFilters.filter(f => f != key); } - this.onFilterChanged.emit(this.getBooleanFlags()); + const booleanFlags = this.getBooleanFlags(); + this.onFilterChanged.emit(booleanFlags); + this.stateService.activeGoggles$.next([...this.activeFilters]); } getBooleanFlags(): bigint | null { @@ -67,4 +95,8 @@ export class BlockFiltersComponent implements OnChanges { } return true; } + + ngOnDestroy(): void { + this.filterSubscription.unsubscribe(); + } } \ No newline at end of file diff --git a/frontend/src/app/components/block-overview-graph/block-overview-graph.component.html b/frontend/src/app/components/block-overview-graph/block-overview-graph.component.html index 9f5e7cb47..9d27d8d90 100644 --- a/frontend/src/app/components/block-overview-graph/block-overview-graph.component.html +++ b/frontend/src/app/components/block-overview-graph/block-overview-graph.component.html @@ -13,6 +13,6 @@ [auditEnabled]="auditHighlighting" [blockConversion]="blockConversion" > - + diff --git a/frontend/src/app/components/block-overview-graph/block-overview-graph.component.ts b/frontend/src/app/components/block-overview-graph/block-overview-graph.component.ts index 8a449a121..d6000e27b 100644 --- a/frontend/src/app/components/block-overview-graph/block-overview-graph.component.ts +++ b/frontend/src/app/components/block-overview-graph/block-overview-graph.component.ts @@ -40,6 +40,7 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On @Input() unavailable: boolean = false; @Input() auditHighlighting: boolean = false; @Input() showFilters: boolean = false; + @Input() excludeFilters: string[] = []; @Input() filterFlags: bigint | null = null; @Input() blockConversion: Price; @Input() overrideColors: ((tx: TxView) => Color) | null = null; @@ -71,6 +72,8 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On searchText: string; searchSubscription: Subscription; + filtersAvailable: boolean = true; + activeFilterFlags: bigint | null = null; constructor( readonly ngZone: NgZone, @@ -110,16 +113,19 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On if (changes.overrideColor && this.scene) { this.scene.setColorFunction(this.overrideColors); } - if ((changes.filterFlags || changes.showFilters) && this.scene) { - this.setFilterFlags(this.filterFlags); + if ((changes.filterFlags || changes.showFilters)) { + this.setFilterFlags(); } } - setFilterFlags(flags: bigint | null): void { - if (flags != null) { - this.scene.setColorFunction(this.getFilterColorFunction(flags)); - } else { - this.scene.setColorFunction(this.overrideColors); + setFilterFlags(flags?: bigint | null): void { + this.activeFilterFlags = this.filterFlags || flags || null; + if (this.scene) { + if (flags != null) { + this.scene.setColorFunction(this.getFilterColorFunction(flags)); + } else { + this.scene.setColorFunction(this.overrideColors); + } } this.start(); } @@ -150,6 +156,7 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On // initialize the scene without any entry transition setup(transactions: TransactionStripped[]): void { + this.filtersAvailable = transactions.reduce((flagSet, tx) => flagSet || tx.flags > 0, false); if (this.scene) { this.scene.setup(transactions); this.readyNextFrame = true; @@ -260,7 +267,7 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On this.scene = new BlockScene({ width: this.displayWidth, height: this.displayHeight, resolution: this.resolution, blockLimit: this.blockLimit, orientation: this.orientation, flip: this.flip, vertexArray: this.vertexArray, highlighting: this.auditHighlighting, animationDuration: this.animationDuration, animationOffset: this.animationOffset, - colorFunction: this.overrideColors }); + colorFunction: this.getColorFunction() }); this.start(); } } @@ -504,6 +511,16 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On this.txHoverEvent.emit(hoverId); } + getColorFunction(): ((tx: TxView) => Color) { + if (this.filterFlags) { + return this.getFilterColorFunction(this.filterFlags); + } else if (this.activeFilterFlags) { + return this.getFilterColorFunction(this.activeFilterFlags); + } else { + return this.overrideColors; + } + } + getFilterColorFunction(flags: bigint): ((tx: TxView) => Color) { return (tx: TxView) => { if ((tx.bigintFlags & flags) === flags) { diff --git a/frontend/src/app/components/block/block.component.html b/frontend/src/app/components/block/block.component.html index 1e6edd2c8..0cc7c5832 100644 --- a/frontend/src/app/components/block/block.component.html +++ b/frontend/src/app/components/block/block.component.html @@ -115,6 +115,8 @@ [orientation]="'top'" [flip]="false" [blockConversion]="blockConversion" + [showFilters]="true" + [excludeFilters]="['replacement']" (txClickEvent)="onTxClick($event)" > @@ -229,7 +231,8 @@
+ (txClickEvent)="onTxClick($event)" (txHoverEvent)="onTxHover($event)" [unavailable]="!isMobile && !showAudit" + [showFilters]="true" [excludeFilters]="['replacement']">
@@ -243,7 +246,8 @@
+ (txClickEvent)="onTxClick($event)" (txHoverEvent)="onTxHover($event)" [unavailable]="isMobile && !showAudit" + [showFilters]="true" [excludeFilters]="['replacement']">
diff --git a/frontend/src/app/services/state.service.ts b/frontend/src/app/services/state.service.ts index db1268379..f87a3dc31 100644 --- a/frontend/src/app/services/state.service.ts +++ b/frontend/src/app/services/state.service.ts @@ -150,6 +150,8 @@ export class StateService { searchFocus$: Subject = new Subject(); menuOpen$: BehaviorSubject = new BehaviorSubject(false); + activeGoggles$: BehaviorSubject = new BehaviorSubject([]); + constructor( @Inject(PLATFORM_ID) private platformId: any, @Inject(LOCALE_ID) private locale: string,