mirror of
https://github.com/mempool/mempool.git
synced 2025-03-29 11:12:16 +01:00
multiblock mempool page
This commit is contained in:
parent
5429d6f264
commit
50eb9b602b
@ -3,7 +3,8 @@ import * as WebSocket from 'ws';
|
||||
import {
|
||||
BlockExtended, TransactionExtended, MempoolTransactionExtended, WebsocketResponse,
|
||||
OptimizedStatistic, ILoadingIndicators, GbtCandidates, TxTrackingInfo,
|
||||
MempoolDelta, MempoolDeltaTxids
|
||||
MempoolDelta, MempoolDeltaTxids,
|
||||
TransactionCompressed
|
||||
} from '../mempool.interfaces';
|
||||
import blocks from './blocks';
|
||||
import memPool from './mempool';
|
||||
@ -317,6 +318,7 @@ class WebsocketHandler {
|
||||
|
||||
if (parsedMessage && parsedMessage['track-mempool-block'] !== undefined) {
|
||||
if (Number.isInteger(parsedMessage['track-mempool-block']) && parsedMessage['track-mempool-block'] >= 0) {
|
||||
client['track-mempool-blocks'] = undefined;
|
||||
const index = parsedMessage['track-mempool-block'];
|
||||
client['track-mempool-block'] = index;
|
||||
const mBlocksWithTransactions = mempoolBlocks.getMempoolBlocksWithTransactions();
|
||||
@ -326,7 +328,31 @@ class WebsocketHandler {
|
||||
blockTransactions: (mBlocksWithTransactions[index]?.transactions || []).map(mempoolBlocks.compressTx),
|
||||
});
|
||||
} else {
|
||||
client['track-mempool-block'] = null;
|
||||
client['track-mempool-block'] = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
if (parsedMessage && parsedMessage['track-mempool-blocks'] !== undefined) {
|
||||
if (parsedMessage['track-mempool-blocks'].length > 0) {
|
||||
client['track-mempool-block'] = undefined;
|
||||
const indices: number[] = [];
|
||||
const mBlocksWithTransactions = mempoolBlocks.getMempoolBlocksWithTransactions();
|
||||
const updates: { index: number, sequence: number, blockTransactions: TransactionCompressed[] }[] = [];
|
||||
for (const i of parsedMessage['track-mempool-blocks']) {
|
||||
const index = parseInt(i);
|
||||
if (Number.isInteger(index) && index >= 0) {
|
||||
indices.push(index);
|
||||
updates.push({
|
||||
index: index,
|
||||
sequence: this.mempoolSequence,
|
||||
blockTransactions: (mBlocksWithTransactions[index]?.transactions || []).map(mempoolBlocks.compressTx),
|
||||
});
|
||||
}
|
||||
}
|
||||
client['track-mempool-blocks'] = indices;
|
||||
response['projected-block-transactions'] = JSON.stringify(updates);
|
||||
} else {
|
||||
client['track-mempool-blocks'] = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@ -910,6 +936,19 @@ class WebsocketHandler {
|
||||
delta: mBlockDeltas[index],
|
||||
});
|
||||
}
|
||||
} else if (client['track-mempool-blocks']?.length && memPool.isInSync()) {
|
||||
const indices = client['track-mempool-blocks'];
|
||||
const updates: string[] = [];
|
||||
for (const index of indices) {
|
||||
if (mBlockDeltas[index]) {
|
||||
updates.push(getCachedResponse(`projected-block-transactions-${index}`, {
|
||||
index: index,
|
||||
sequence: this.mempoolSequence,
|
||||
delta: mBlockDeltas[index],
|
||||
}));
|
||||
}
|
||||
}
|
||||
response['projected-block-transactions'] = '[' + updates.join(',') + ']';
|
||||
}
|
||||
|
||||
if (client['track-rbf'] === 'all' && rbfReplacements) {
|
||||
@ -1306,6 +1345,27 @@ class WebsocketHandler {
|
||||
});
|
||||
}
|
||||
}
|
||||
} else if (client['track-mempool-blocks']?.length && memPool.isInSync()) {
|
||||
const indices = client['track-mempool-blocks'];
|
||||
const updates: string[] = [];
|
||||
for (const index of indices) {
|
||||
if (mBlockDeltas && mBlockDeltas[index] && mBlocksWithTransactions[index]?.transactions?.length) {
|
||||
if (mBlockDeltas[index].added.length > (mBlocksWithTransactions[index]?.transactions.length / 2)) {
|
||||
updates.push(getCachedResponse(`projected-block-transactions-full-${index}`, {
|
||||
index: index,
|
||||
sequence: this.mempoolSequence,
|
||||
blockTransactions: mBlocksWithTransactions[index].transactions.map(mempoolBlocks.compressTx),
|
||||
}));
|
||||
} else {
|
||||
updates.push(getCachedResponse(`projected-block-transactions-delta-${index}`, {
|
||||
index: index,
|
||||
sequence: this.mempoolSequence,
|
||||
delta: mBlockDeltas[index],
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
response['projected-block-transactions'] = '[' + updates.join(',') + ']';
|
||||
}
|
||||
|
||||
if (client['track-mempool-txids']) {
|
||||
|
@ -3,6 +3,7 @@ import { Routes, RouterModule } from '@angular/router';
|
||||
import { AppPreloadingStrategy } from './app.preloading-strategy'
|
||||
import { BlockViewComponent } from './components/block-view/block-view.component';
|
||||
import { EightBlocksComponent } from './components/eight-blocks/eight-blocks.component';
|
||||
import { EightMempoolComponent } from './components/eight-mempool/eight-mempool.component';
|
||||
import { MempoolBlockViewComponent } from './components/mempool-block-view/mempool-block-view.component';
|
||||
import { ClockComponent } from './components/clock/clock.component';
|
||||
import { StatusViewComponent } from './components/status-view/status-view.component';
|
||||
@ -205,6 +206,10 @@ let routes: Routes = [
|
||||
path: 'view/blocks',
|
||||
component: EightBlocksComponent,
|
||||
},
|
||||
{
|
||||
path: 'view/mempool-blocks',
|
||||
component: EightMempoolComponent,
|
||||
},
|
||||
{
|
||||
path: 'status',
|
||||
data: { networks: ['bitcoin', 'liquid'] },
|
||||
|
@ -278,7 +278,7 @@ export default class BlockScene {
|
||||
}
|
||||
|
||||
private applyTxUpdate(tx: TxView, update: ViewUpdateParams): void {
|
||||
this.animateUntil = Math.max(this.animateUntil, tx.update(update, { minX: this.x, maxY: this.y + this.height }));
|
||||
this.animateUntil = Math.max(this.animateUntil, tx.update(update, { minX: this.x - this.width, maxX: this.x + this.width + this.width, maxY: this.y + this.height }));
|
||||
}
|
||||
|
||||
private updateTxColor(tx: TxView, startTime: number, delay: number, animate: boolean = true, duration?: number): void {
|
||||
|
@ -17,10 +17,11 @@ export default class TxSprite {
|
||||
tempAttributes: OptionalAttributes;
|
||||
|
||||
minX: number;
|
||||
maxX: number;
|
||||
maxY: number;
|
||||
|
||||
|
||||
constructor(params: SpriteUpdateParams, vertexArray: FastVertexArray, minX, maxY: number) {
|
||||
constructor(params: SpriteUpdateParams, vertexArray: FastVertexArray, minX: number, maxX: number, maxY: number) {
|
||||
const offsetTime = params.start;
|
||||
this.vertexArray = vertexArray;
|
||||
this.vertexData = Array(VI.length).fill(0);
|
||||
@ -30,6 +31,7 @@ export default class TxSprite {
|
||||
};
|
||||
|
||||
this.minX = minX;
|
||||
this.maxX = maxX;
|
||||
this.maxY = maxY;
|
||||
|
||||
this.attributes = {
|
||||
@ -84,7 +86,7 @@ export default class TxSprite {
|
||||
minDuration: minimum remaining transition duration when adjust = true
|
||||
temp: if true, this update is only temporary (can be reversed with 'resume')
|
||||
*/
|
||||
update(params: SpriteUpdateParams, minX?: number, maxY?: number): void {
|
||||
update(params: SpriteUpdateParams, minX?: number, maxX?: number, maxY?: number): void {
|
||||
const offsetTime = params.start || performance.now();
|
||||
const v = params.duration > 0 ? (1 / params.duration) : 0;
|
||||
|
||||
|
@ -106,7 +106,7 @@ export default class TxView implements TransactionStripped {
|
||||
|
||||
returns minimum transition end time
|
||||
*/
|
||||
update(params: ViewUpdateParams, { minX, maxY }: { minX: number, maxY: number }): number {
|
||||
update(params: ViewUpdateParams, { minX, maxX, maxY }: { minX: number, maxX: number, maxY: number }): number {
|
||||
if (params.jitter) {
|
||||
params.delay += (Math.random() * params.jitter);
|
||||
}
|
||||
@ -117,6 +117,7 @@ export default class TxView implements TransactionStripped {
|
||||
toSpriteUpdate(params),
|
||||
this.vertexArray,
|
||||
minX,
|
||||
maxX,
|
||||
maxY
|
||||
);
|
||||
// apply any pending hover event
|
||||
@ -130,6 +131,7 @@ export default class TxView implements TransactionStripped {
|
||||
temp: true
|
||||
},
|
||||
minX,
|
||||
maxX,
|
||||
maxY
|
||||
);
|
||||
}
|
||||
@ -137,6 +139,7 @@ export default class TxView implements TransactionStripped {
|
||||
this.sprite.update(
|
||||
toSpriteUpdate(params),
|
||||
minX,
|
||||
maxX,
|
||||
maxY
|
||||
);
|
||||
}
|
||||
|
@ -40,6 +40,7 @@ export class BlockOverviewMultiComponent implements AfterViewInit, OnDestroy, On
|
||||
@Input() isLoading: boolean;
|
||||
@Input() resolution: number;
|
||||
@Input() numBlocks: number;
|
||||
@Input() padding: number = 0;
|
||||
@Input() blockWidth: number = 360;
|
||||
@Input() autofit: boolean = false;
|
||||
@Input() blockLimit: number;
|
||||
@ -285,8 +286,8 @@ export class BlockOverviewMultiComponent implements AfterViewInit, OnDestroy, On
|
||||
for (const [index, pendingUpdate] of this.pendingUpdates.entries()) {
|
||||
if (pendingUpdate.count && performance.now() > (this.lastUpdate + this.animationDuration)) {
|
||||
this.applyUpdate(index, Object.values(pendingUpdate.add), Object.values(pendingUpdate.remove), Object.values(pendingUpdate.change), pendingUpdate.direction);
|
||||
this.clearUpdateQueue(index);
|
||||
}
|
||||
this.clearUpdateQueue(index);
|
||||
}
|
||||
}
|
||||
|
||||
@ -391,8 +392,8 @@ export class BlockOverviewMultiComponent implements AfterViewInit, OnDestroy, On
|
||||
this.gl.viewport(0, 0, this.displayWidth, this.displayHeight);
|
||||
}
|
||||
for (let i = 0; i < this.scenes.length; i++) {
|
||||
const blocksPerRow = Math.floor(this.displayWidth / this.blockWidth);
|
||||
const x = (i % blocksPerRow) * this.blockWidth;
|
||||
const blocksPerRow = Math.floor((this.displayWidth + this.padding) / (this.blockWidth + this.padding));
|
||||
const x = (i % blocksPerRow) * (this.blockWidth + this.padding);
|
||||
const row = Math.floor(i / blocksPerRow);
|
||||
const y = this.displayHeight - ((row + 1) * this.blockWidth);
|
||||
if (this.scenes[i]) {
|
||||
@ -401,7 +402,7 @@ export class BlockOverviewMultiComponent implements AfterViewInit, OnDestroy, On
|
||||
} else {
|
||||
this.scenes[i] = new BlockScene({ x, y, width: this.blockWidth, height: this.blockWidth, resolution: this.resolution,
|
||||
blockLimit: this.blockLimit, orientation: this.orientation, flip: this.flip, vertexArray: this.vertexArray, theme: this.themeService,
|
||||
highlighting: this.auditHighlighting, animationDuration: this.animationDuration, animationOffset: 0,
|
||||
highlighting: this.auditHighlighting, animationDuration: this.animationDuration, animationOffset: this.animationOffset,
|
||||
colorFunction: this.getColorFunction() });
|
||||
this.start();
|
||||
}
|
||||
|
@ -114,7 +114,7 @@ export class EightBlocksComponent implements OnInit, OnDestroy {
|
||||
this.wrapBlocks = params.wrap !== 'false';
|
||||
this.stagger = Number.isInteger(Number(params.stagger)) ? Number(params.stagger) : 0;
|
||||
this.animationDuration = Number.isInteger(Number(params.animationDuration)) ? Number(params.animationDuration) : 2000;
|
||||
this.animationOffset = this.padding * 2;
|
||||
this.animationOffset = 0;
|
||||
|
||||
if (this.autofit) {
|
||||
this.resolution = bestFitResolution(76, 96, this.blockWidth - this.padding * 2);
|
||||
|
@ -0,0 +1,26 @@
|
||||
<!-- <div class="blocks" [class.wrap]="wrapBlocks">
|
||||
<ng-container *ngFor="let i of blockIndices">
|
||||
<div class="block-wrapper" [style]="wrapperStyle">
|
||||
<div class="block-container" [style]="containerStyle"> -->
|
||||
<app-block-overview-multi
|
||||
#blockGraph
|
||||
[isLoading]="false"
|
||||
[numBlocks]="numBlocks"
|
||||
[padding]="padding"
|
||||
[blockWidth]="blockWidth"
|
||||
[resolution]="resolution"
|
||||
[blockLimit]="stateService.blockVSize"
|
||||
[orientation]="'left'"
|
||||
[flip]="true"
|
||||
[animationDuration]="animationDuration"
|
||||
[animationOffset]="animationOffset"
|
||||
[disableSpinner]="true"
|
||||
></app-block-overview-multi>
|
||||
<!-- <div *ngIf="showInfo && blockInfo[i]" class="info" @infoChange>
|
||||
<h1 class="height">{{ blockInfo[i].height }}</h1>
|
||||
<h2 class="mined-by">by {{ blockInfo[i].extras.pool.name || 'Unknown' }}</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div> -->
|
@ -0,0 +1,69 @@
|
||||
.blocks {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-width: 100vw;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
justify-content: flex-start;
|
||||
align-items: flex-start;
|
||||
align-content: flex-start;
|
||||
|
||||
&.wrap {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.block-wrapper {
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
position: relative;
|
||||
--block-width: 1080px;
|
||||
|
||||
.info {
|
||||
position: absolute;
|
||||
left: 8%;
|
||||
top: 8%;
|
||||
right: 8%;
|
||||
bottom: 8%;
|
||||
height: 84%;
|
||||
width: 84%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: 700;
|
||||
font-size: calc(var(--block-width) * 0.03);
|
||||
text-shadow: 0 0 calc(var(--block-width) * 0.05) black;
|
||||
|
||||
h1 {
|
||||
font-size: 6em;
|
||||
line-height: 1;
|
||||
margin-bottom: calc(var(--block-width) * 0.03);
|
||||
}
|
||||
h2 {
|
||||
font-size: 1.8em;
|
||||
line-height: 1;
|
||||
margin-bottom: calc(var(--block-width) * 0.03);
|
||||
}
|
||||
|
||||
.hash {
|
||||
font-family: monospace;
|
||||
word-wrap: break-word;
|
||||
font-size: 1.4em;
|
||||
line-height: 1;
|
||||
margin-bottom: calc(var(--block-width) * 0.03);
|
||||
}
|
||||
|
||||
.mined-by {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
margin: auto;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.block-container {
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
@ -0,0 +1,201 @@
|
||||
import { Component, OnInit, OnDestroy, ViewChild } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { catchError } from 'rxjs/operators';
|
||||
import { Subject, Subscription, of } from 'rxjs';
|
||||
import { StateService } from '../../services/state.service';
|
||||
import { WebsocketService } from '../../services/websocket.service';
|
||||
import { BlockExtended, TransactionStripped } from '../../interfaces/node-api.interface';
|
||||
import { ApiService } from '../../services/api.service';
|
||||
import { detectWebGL } from '../../shared/graphs.utils';
|
||||
import { animate, style, transition, trigger } from '@angular/animations';
|
||||
import { BytesPipe } from '../../shared/pipes/bytes-pipe/bytes.pipe';
|
||||
import { BlockOverviewMultiComponent } from '../block-overview-multi/block-overview-multi.component';
|
||||
import { CacheService } from '../../services/cache.service';
|
||||
import { isMempoolDelta, MempoolBlockDelta } from '../../interfaces/websocket.interface';
|
||||
|
||||
function bestFitResolution(min, max, n): number {
|
||||
const target = (min + max) / 2;
|
||||
let bestScore = Infinity;
|
||||
let best = null;
|
||||
for (let i = min; i <= max; i++) {
|
||||
const remainder = (n % i);
|
||||
if (remainder < bestScore || (remainder === bestScore && (Math.abs(i - target) < Math.abs(best - target)))) {
|
||||
bestScore = remainder;
|
||||
best = i;
|
||||
}
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
interface BlockInfo extends BlockExtended {
|
||||
timeString: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-eight-mempool',
|
||||
templateUrl: './eight-mempool.component.html',
|
||||
styleUrls: ['./eight-mempool.component.scss'],
|
||||
animations: [
|
||||
trigger('infoChange', [
|
||||
transition(':enter', [
|
||||
style({ opacity: 0 }),
|
||||
animate('1000ms', style({ opacity: 1 })),
|
||||
]),
|
||||
transition(':leave', [
|
||||
animate('1000ms 500ms', style({ opacity: 0 }))
|
||||
])
|
||||
]),
|
||||
],
|
||||
})
|
||||
export class EightMempoolComponent implements OnInit, OnDestroy {
|
||||
network = '';
|
||||
strippedTransactions: { [height: number]: TransactionStripped[] } = {};
|
||||
webGlEnabled = true;
|
||||
hoverTx: string | null = null;
|
||||
|
||||
tipSubscription: Subscription;
|
||||
networkChangedSubscription: Subscription;
|
||||
queryParamsSubscription: Subscription;
|
||||
graphChangeSubscription: Subscription;
|
||||
blockSub: Subscription;
|
||||
|
||||
chainDirection: string = 'right';
|
||||
poolDirection: string = 'left';
|
||||
|
||||
lastBlockHeight: number = 0;
|
||||
lastBlockHeightUpdate: number[] = [];
|
||||
numBlocks: number = 8;
|
||||
blockIndices: number[] = [];
|
||||
autofit: boolean = false;
|
||||
padding: number = 0;
|
||||
wrapBlocks: boolean = false;
|
||||
blockWidth: number = 360;
|
||||
animationDuration: number = 2000;
|
||||
animationOffset: number = 0;
|
||||
stagger: number = 0;
|
||||
testing: boolean = true;
|
||||
testHeight: number = 800000;
|
||||
testShiftTimeout: number;
|
||||
|
||||
showInfo: boolean = true;
|
||||
blockInfo: BlockInfo[] = [];
|
||||
|
||||
wrapperStyle = {
|
||||
'--block-width': '1080px',
|
||||
width: '1080px',
|
||||
maxWidth: '1080px',
|
||||
padding: '',
|
||||
};
|
||||
containerStyle = {};
|
||||
resolution: number = 86;
|
||||
|
||||
@ViewChild('blockGraph') blockGraph: BlockOverviewMultiComponent;
|
||||
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private router: Router,
|
||||
public stateService: StateService,
|
||||
private websocketService: WebsocketService,
|
||||
private apiService: ApiService,
|
||||
private cacheService: CacheService,
|
||||
private bytesPipe: BytesPipe,
|
||||
) {
|
||||
this.webGlEnabled = this.stateService.isBrowser && detectWebGL();
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.websocketService.want(['blocks']);
|
||||
this.network = this.stateService.network;
|
||||
|
||||
this.blockSub = this.stateService.mempoolBlockUpdate$.subscribe((update) => {
|
||||
// process update
|
||||
if (isMempoolDelta(update)) {
|
||||
// delta
|
||||
this.updateBlock(update);
|
||||
} else {
|
||||
const transactionsStripped = update.transactions;
|
||||
const inOldBlock = {};
|
||||
const inNewBlock = {};
|
||||
const added: TransactionStripped[] = [];
|
||||
const changed: { txid: string, rate: number | undefined, flags: number, acc: boolean | undefined }[] = [];
|
||||
const removed: string[] = [];
|
||||
for (const tx of transactionsStripped) {
|
||||
inNewBlock[tx.txid] = true;
|
||||
}
|
||||
for (const txid of Object.keys(this.blockGraph?.scenes[update.block]?.txs || {})) {
|
||||
inOldBlock[txid] = true;
|
||||
if (!inNewBlock[txid]) {
|
||||
removed.push(txid);
|
||||
}
|
||||
}
|
||||
for (const tx of transactionsStripped) {
|
||||
if (!inOldBlock[tx.txid]) {
|
||||
added.push(tx);
|
||||
} else {
|
||||
changed.push({
|
||||
txid: tx.txid,
|
||||
rate: tx.rate,
|
||||
flags: tx.flags,
|
||||
acc: tx.acc
|
||||
});
|
||||
}
|
||||
}
|
||||
this.updateBlock({
|
||||
block: update.block,
|
||||
removed,
|
||||
changed,
|
||||
added
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
this.queryParamsSubscription = this.route.queryParams.subscribe((params) => {
|
||||
this.numBlocks = Number.isInteger(Number(params.numBlocks)) ? Number(params.numBlocks) : 8;
|
||||
this.blockIndices = [...Array(this.numBlocks).keys()];
|
||||
this.lastBlockHeightUpdate = this.blockIndices.map(() => 0);
|
||||
this.autofit = params.autofit !== 'false';
|
||||
this.blockWidth = Number.isInteger(Number(params.blockWidth)) ? Number(params.blockWidth) : 540;
|
||||
this.padding = Number.isInteger(Number(params.padding)) ? Number(params.padding) : this.blockWidth;
|
||||
this.wrapBlocks = params.wrap !== 'false';
|
||||
this.stagger = Number.isInteger(Number(params.stagger)) ? Number(params.stagger) : 0;
|
||||
this.animationDuration = Number.isInteger(Number(params.animationDuration)) ? Number(params.animationDuration) : 2000;
|
||||
this.animationOffset = 0;
|
||||
|
||||
if (this.autofit) {
|
||||
this.resolution = bestFitResolution(76, 96, this.blockWidth - this.padding * 2);
|
||||
} else {
|
||||
this.resolution = 86;
|
||||
}
|
||||
|
||||
this.wrapperStyle = {
|
||||
'--block-width': this.blockWidth + 'px',
|
||||
width: this.blockWidth + 'px',
|
||||
maxWidth: this.blockWidth + 'px',
|
||||
padding: (this.padding || 0) +'px 0px',
|
||||
};
|
||||
|
||||
this.websocketService.startTrackMempoolBlocks(this.blockIndices);
|
||||
});
|
||||
|
||||
this.networkChangedSubscription = this.stateService.networkChanged$
|
||||
.subscribe((network) => this.network = network);
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.stateService.markBlock$.next({});
|
||||
this.tipSubscription.unsubscribe();
|
||||
this.networkChangedSubscription?.unsubscribe();
|
||||
this.queryParamsSubscription?.unsubscribe();
|
||||
}
|
||||
|
||||
updateBlock(delta: MempoolBlockDelta): void {
|
||||
const blockMined = (this.stateService.latestBlockHeight > this.lastBlockHeightUpdate[delta.block]);
|
||||
if (blockMined) {
|
||||
this.blockGraph.update(this.numBlocks - delta.block - 1, delta.added, delta.removed, delta.changed || [], blockMined ? this.chainDirection : this.poolDirection, blockMined);
|
||||
} else {
|
||||
this.blockGraph.update(this.numBlocks - delta.block - 1, delta.added, delta.removed, delta.changed || [], this.poolDirection);
|
||||
}
|
||||
|
||||
this.lastBlockHeightUpdate[delta.block] = this.stateService.latestBlockHeight;
|
||||
}
|
||||
}
|
@ -33,6 +33,7 @@ export interface WebsocketResponse {
|
||||
'track-scriptpubkeys'?: string[];
|
||||
'track-asset'?: string;
|
||||
'track-mempool-block'?: number;
|
||||
'track-mempool-blocks'?: number[];
|
||||
'track-rbf'?: string;
|
||||
'track-rbf-summary'?: boolean;
|
||||
'track-accelerations'?: boolean;
|
||||
|
@ -29,12 +29,14 @@ export class WebsocketService {
|
||||
private isTrackingTx = false;
|
||||
private trackingTxId: string;
|
||||
private isTrackingMempoolBlock = false;
|
||||
private isTrackingMempoolBlocks = false;
|
||||
private isTrackingRbf: 'all' | 'fullRbf' | false = false;
|
||||
private isTrackingRbfSummary = false;
|
||||
private isTrackingAddress: string | false = false;
|
||||
private isTrackingAddresses: string[] | false = false;
|
||||
private isTrackingAccelerations: boolean = false;
|
||||
private trackingMempoolBlock: number;
|
||||
private trackingMempoolBlocks: number[];
|
||||
private stoppingTrackMempoolBlock: any | null = null;
|
||||
private latestGitCommit = '';
|
||||
private onlineCheckTimeout: number;
|
||||
@ -122,6 +124,9 @@ export class WebsocketService {
|
||||
if (this.isTrackingMempoolBlock) {
|
||||
this.startTrackMempoolBlock(this.trackingMempoolBlock, true);
|
||||
}
|
||||
if (this.isTrackingMempoolBlocks) {
|
||||
this.startTrackMempoolBlocks(this.trackingMempoolBlocks);
|
||||
}
|
||||
if (this.isTrackingRbf) {
|
||||
this.startTrackRbf(this.isTrackingRbf);
|
||||
}
|
||||
@ -218,6 +223,13 @@ export class WebsocketService {
|
||||
return false;
|
||||
}
|
||||
|
||||
startTrackMempoolBlocks(blocks: number[], force: boolean = false): boolean {
|
||||
this.websocketSubject.next({ 'track-mempool-blocks': blocks });
|
||||
this.isTrackingMempoolBlocks = true;
|
||||
this.trackingMempoolBlocks = blocks;
|
||||
return true;
|
||||
}
|
||||
|
||||
stopTrackMempoolBlock(): void {
|
||||
if (this.stoppingTrackMempoolBlock) {
|
||||
clearTimeout(this.stoppingTrackMempoolBlock);
|
||||
@ -231,6 +243,11 @@ export class WebsocketService {
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
stopTrackMempoolBlocks(): void {
|
||||
this.websocketSubject.next({ 'track-mempool-blocks': [] });
|
||||
this.isTrackingMempoolBlocks = false;
|
||||
}
|
||||
|
||||
startTrackRbf(mode: 'all' | 'fullRbf') {
|
||||
this.websocketSubject.next({ 'track-rbf': mode });
|
||||
this.isTrackingRbf = mode;
|
||||
@ -433,20 +450,25 @@ export class WebsocketService {
|
||||
}
|
||||
|
||||
if (response['projected-block-transactions']) {
|
||||
if (response['projected-block-transactions'].index == this.trackingMempoolBlock) {
|
||||
if (response['projected-block-transactions'].blockTransactions) {
|
||||
this.stateService.mempoolSequence = response['projected-block-transactions'].sequence;
|
||||
if (response['projected-block-transactions'].index != null) {
|
||||
const update = response['projected-block-transactions'];
|
||||
if (update.blockTransactions) {
|
||||
this.stateService.mempoolBlockUpdate$.next({
|
||||
block: this.trackingMempoolBlock,
|
||||
transactions: response['projected-block-transactions'].blockTransactions.map(uncompressTx),
|
||||
block: update.index,
|
||||
transactions: update.blockTransactions.map(uncompressTx),
|
||||
});
|
||||
} else if (response['projected-block-transactions'].delta) {
|
||||
if (this.stateService.mempoolSequence && response['projected-block-transactions'].sequence !== this.stateService.mempoolSequence + 1) {
|
||||
this.stateService.mempoolSequence = 0;
|
||||
this.startTrackMempoolBlock(this.trackingMempoolBlock, true);
|
||||
} else {
|
||||
this.stateService.mempoolSequence = response['projected-block-transactions'].sequence;
|
||||
this.stateService.mempoolBlockUpdate$.next(uncompressDeltaChange(this.trackingMempoolBlock, response['projected-block-transactions'].delta));
|
||||
} else if (update.delta) {
|
||||
this.stateService.mempoolBlockUpdate$.next(uncompressDeltaChange(update.index, update.delta));
|
||||
}
|
||||
} else if (response['projected-block-transactions'].length) {
|
||||
for (const update of response['projected-block-transactions']) {
|
||||
if (update.blockTransactions) {
|
||||
this.stateService.mempoolBlockUpdate$.next({
|
||||
block: update.index,
|
||||
transactions: update.blockTransactions.map(uncompressTx),
|
||||
});
|
||||
} else if (update.delta) {
|
||||
this.stateService.mempoolBlockUpdate$.next(uncompressDeltaChange(update.index, update.delta));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -107,6 +107,7 @@ import { OrdDataComponent } from '../components/ord-data/ord-data.component';
|
||||
|
||||
import { BlockViewComponent } from '../components/block-view/block-view.component';
|
||||
import { EightBlocksComponent } from '../components/eight-blocks/eight-blocks.component';
|
||||
import { EightMempoolComponent } from '../components/eight-mempool/eight-mempool.component';
|
||||
import { MempoolBlockViewComponent } from '../components/mempool-block-view/mempool-block-view.component';
|
||||
import { MempoolBlockOverviewComponent } from '../components/mempool-block-overview/mempool-block-overview.component';
|
||||
import { ClockchainComponent } from '../components/clockchain/clockchain.component';
|
||||
@ -157,6 +158,7 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-dir
|
||||
BlockchainComponent,
|
||||
BlockViewComponent,
|
||||
EightBlocksComponent,
|
||||
EightMempoolComponent,
|
||||
MempoolBlockViewComponent,
|
||||
MempoolBlocksComponent,
|
||||
BlockchainBlocksComponent,
|
||||
@ -220,6 +222,7 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-dir
|
||||
BitcoinsatoshisPipe,
|
||||
BlockViewComponent,
|
||||
EightBlocksComponent,
|
||||
EightMempoolComponent,
|
||||
MempoolBlockViewComponent,
|
||||
MempoolBlockOverviewComponent,
|
||||
ClockchainComponent,
|
||||
|
Loading…
x
Reference in New Issue
Block a user