Improve stability of mempool tx position arrow
This commit is contained in:
parent
a5b764fb66
commit
3b8bcc4da5
@ -252,9 +252,17 @@ class MempoolBlocks {
|
|||||||
|
|
||||||
private processBlockTemplates(mempool, blocks, clusters, saveResults): MempoolBlockWithTransactions[] {
|
private processBlockTemplates(mempool, blocks, clusters, saveResults): MempoolBlockWithTransactions[] {
|
||||||
// update this thread's mempool with the results
|
// update this thread's mempool with the results
|
||||||
blocks.forEach(block => {
|
blocks.forEach((block, blockIndex) => {
|
||||||
|
let runningVsize = 0;
|
||||||
block.forEach(tx => {
|
block.forEach(tx => {
|
||||||
if (tx.txid && tx.txid in mempool) {
|
if (tx.txid && tx.txid in mempool) {
|
||||||
|
// save position in projected blocks
|
||||||
|
mempool[tx.txid].position = {
|
||||||
|
block: blockIndex,
|
||||||
|
vsize: runningVsize + (mempool[tx.txid].vsize / 2),
|
||||||
|
};
|
||||||
|
runningVsize += mempool[tx.txid].vsize;
|
||||||
|
|
||||||
if (tx.effectiveFeePerVsize != null) {
|
if (tx.effectiveFeePerVsize != null) {
|
||||||
mempool[tx.txid].effectiveFeePerVsize = tx.effectiveFeePerVsize;
|
mempool[tx.txid].effectiveFeePerVsize = tx.effectiveFeePerVsize;
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ import config from '../config';
|
|||||||
import logger from '../logger';
|
import logger from '../logger';
|
||||||
import { ThreadTransaction, MempoolBlockWithTransactions, AuditTransaction } from '../mempool.interfaces';
|
import { ThreadTransaction, MempoolBlockWithTransactions, AuditTransaction } from '../mempool.interfaces';
|
||||||
import { PairingHeap } from '../utils/pairing-heap';
|
import { PairingHeap } from '../utils/pairing-heap';
|
||||||
import { Common } from './common';
|
|
||||||
import { parentPort } from 'worker_threads';
|
import { parentPort } from 'worker_threads';
|
||||||
|
|
||||||
let mempool: { [txid: string]: ThreadTransaction } = {};
|
let mempool: { [txid: string]: ThreadTransaction } = {};
|
||||||
@ -72,7 +71,14 @@ function makeBlockTemplates(mempool: { [txid: string]: ThreadTransaction })
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Sort by descending ancestor score
|
// Sort by descending ancestor score
|
||||||
mempoolArray.sort((a, b) => (b.score || 0) - (a.score || 0));
|
mempoolArray.sort((a, b) => {
|
||||||
|
if (b.score === a.score) {
|
||||||
|
// tie-break by lexicographic txid order for stability
|
||||||
|
return a.txid < b.txid ? -1 : 1;
|
||||||
|
} else {
|
||||||
|
return (b.score || 0) - (a.score || 0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Build blocks by greedily choosing the highest feerate package
|
// Build blocks by greedily choosing the highest feerate package
|
||||||
// (i.e. the package rooted in the transaction with the best ancestor score)
|
// (i.e. the package rooted in the transaction with the best ancestor score)
|
||||||
@ -80,7 +86,14 @@ function makeBlockTemplates(mempool: { [txid: string]: ThreadTransaction })
|
|||||||
let blockWeight = 4000;
|
let blockWeight = 4000;
|
||||||
let blockSize = 0;
|
let blockSize = 0;
|
||||||
let transactions: AuditTransaction[] = [];
|
let transactions: AuditTransaction[] = [];
|
||||||
const modified: PairingHeap<AuditTransaction> = new PairingHeap((a, b): boolean => (a.score || 0) > (b.score || 0));
|
const modified: PairingHeap<AuditTransaction> = new PairingHeap((a, b): boolean => {
|
||||||
|
if (a.score === b.score) {
|
||||||
|
// tie-break by lexicographic txid order for stability
|
||||||
|
return a.txid > b.txid;
|
||||||
|
} else {
|
||||||
|
return (a.score || 0) > (b.score || 0);
|
||||||
|
}
|
||||||
|
});
|
||||||
let overflow: AuditTransaction[] = [];
|
let overflow: AuditTransaction[] = [];
|
||||||
let failures = 0;
|
let failures = 0;
|
||||||
let top = 0;
|
let top = 0;
|
||||||
|
@ -66,9 +66,10 @@ class WebsocketHandler {
|
|||||||
if (parsedMessage && parsedMessage['track-tx']) {
|
if (parsedMessage && parsedMessage['track-tx']) {
|
||||||
if (/^[a-fA-F0-9]{64}$/.test(parsedMessage['track-tx'])) {
|
if (/^[a-fA-F0-9]{64}$/.test(parsedMessage['track-tx'])) {
|
||||||
client['track-tx'] = parsedMessage['track-tx'];
|
client['track-tx'] = parsedMessage['track-tx'];
|
||||||
|
const trackTxid = client['track-tx'];
|
||||||
// Client is telling the transaction wasn't found
|
// Client is telling the transaction wasn't found
|
||||||
if (parsedMessage['watch-mempool']) {
|
if (parsedMessage['watch-mempool']) {
|
||||||
const rbfCacheTxid = rbfCache.getReplacedBy(client['track-tx']);
|
const rbfCacheTxid = rbfCache.getReplacedBy(trackTxid);
|
||||||
if (rbfCacheTxid) {
|
if (rbfCacheTxid) {
|
||||||
response['txReplaced'] = {
|
response['txReplaced'] = {
|
||||||
txid: rbfCacheTxid,
|
txid: rbfCacheTxid,
|
||||||
@ -76,7 +77,7 @@ class WebsocketHandler {
|
|||||||
client['track-tx'] = null;
|
client['track-tx'] = null;
|
||||||
} else {
|
} else {
|
||||||
// It might have appeared before we had the time to start watching for it
|
// It might have appeared before we had the time to start watching for it
|
||||||
const tx = memPool.getMempool()[client['track-tx']];
|
const tx = memPool.getMempool()[trackTxid];
|
||||||
if (tx) {
|
if (tx) {
|
||||||
if (config.MEMPOOL.BACKEND === 'esplora') {
|
if (config.MEMPOOL.BACKEND === 'esplora') {
|
||||||
response['tx'] = tx;
|
response['tx'] = tx;
|
||||||
@ -100,6 +101,13 @@ class WebsocketHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const tx = memPool.getMempool()[trackTxid];
|
||||||
|
if (tx && tx.position) {
|
||||||
|
response['txPosition'] = {
|
||||||
|
txid: trackTxid,
|
||||||
|
position: tx.position,
|
||||||
|
};
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
client['track-tx'] = null;
|
client['track-tx'] = null;
|
||||||
}
|
}
|
||||||
@ -401,9 +409,10 @@ class WebsocketHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (client['track-tx']) {
|
if (client['track-tx']) {
|
||||||
|
const trackTxid = client['track-tx'];
|
||||||
const outspends: object = {};
|
const outspends: object = {};
|
||||||
newTransactions.forEach((tx) => tx.vin.forEach((vin, i) => {
|
newTransactions.forEach((tx) => tx.vin.forEach((vin, i) => {
|
||||||
if (vin.txid === client['track-tx']) {
|
if (vin.txid === trackTxid) {
|
||||||
outspends[vin.vout] = {
|
outspends[vin.vout] = {
|
||||||
vin: i,
|
vin: i,
|
||||||
txid: tx.txid,
|
txid: tx.txid,
|
||||||
@ -426,6 +435,14 @@ class WebsocketHandler {
|
|||||||
if (rbfChange) {
|
if (rbfChange) {
|
||||||
response['rbfInfo'] = rbfChanges.trees[rbfChange];
|
response['rbfInfo'] = rbfChanges.trees[rbfChange];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const mempoolTx = newMempool[trackTxid];
|
||||||
|
if (mempoolTx && mempoolTx.position) {
|
||||||
|
response['txPosition'] = {
|
||||||
|
txid: trackTxid,
|
||||||
|
position: mempoolTx.position,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (client['track-mempool-block'] >= 0) {
|
if (client['track-mempool-block'] >= 0) {
|
||||||
@ -556,8 +573,19 @@ class WebsocketHandler {
|
|||||||
response['mempool-blocks'] = mBlocks;
|
response['mempool-blocks'] = mBlocks;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (client['track-tx'] && txIds.indexOf(client['track-tx']) > -1) {
|
if (client['track-tx']) {
|
||||||
response['txConfirmed'] = true;
|
const trackTxid = client['track-tx'];
|
||||||
|
if (txIds.indexOf(trackTxid) > -1) {
|
||||||
|
response['txConfirmed'] = true;
|
||||||
|
} else {
|
||||||
|
const mempoolTx = _memPool[trackTxid];
|
||||||
|
if (mempoolTx && mempoolTx.position) {
|
||||||
|
response['txPosition'] = {
|
||||||
|
txid: trackTxid,
|
||||||
|
position: mempoolTx.position,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (client['track-address']) {
|
if (client['track-address']) {
|
||||||
|
@ -80,6 +80,10 @@ export interface TransactionExtended extends IEsploraApi.Transaction {
|
|||||||
bestDescendant?: BestDescendant | null;
|
bestDescendant?: BestDescendant | null;
|
||||||
cpfpChecked?: boolean;
|
cpfpChecked?: boolean;
|
||||||
deleteAfter?: number;
|
deleteAfter?: number;
|
||||||
|
position?: {
|
||||||
|
block: number,
|
||||||
|
vsize: number,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AuditTransaction {
|
export interface AuditTransaction {
|
||||||
|
@ -8,7 +8,7 @@ import { feeLevels, mempoolFeeColors } from '../../app.constants';
|
|||||||
import { specialBlocks } from '../../app.constants';
|
import { specialBlocks } from '../../app.constants';
|
||||||
import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe';
|
import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe';
|
||||||
import { Location } from '@angular/common';
|
import { Location } from '@angular/common';
|
||||||
import { DifficultyAdjustment } from '../../interfaces/node-api.interface';
|
import { DifficultyAdjustment, MempoolPosition } from '../../interfaces/node-api.interface';
|
||||||
import { animate, style, transition, trigger } from '@angular/animations';
|
import { animate, style, transition, trigger } from '@angular/animations';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -58,6 +58,7 @@ export class MempoolBlocksComponent implements OnInit, OnDestroy {
|
|||||||
transition = 'background 2s, right 2s, transform 1s';
|
transition = 'background 2s, right 2s, transform 1s';
|
||||||
|
|
||||||
markIndex: number;
|
markIndex: number;
|
||||||
|
txPosition: MempoolPosition;
|
||||||
txFeePerVSize: number;
|
txFeePerVSize: number;
|
||||||
|
|
||||||
resetTransitionTimeout: number;
|
resetTransitionTimeout: number;
|
||||||
@ -156,6 +157,9 @@ export class MempoolBlocksComponent implements OnInit, OnDestroy {
|
|||||||
if (state.mempoolBlockIndex !== undefined) {
|
if (state.mempoolBlockIndex !== undefined) {
|
||||||
this.markIndex = state.mempoolBlockIndex;
|
this.markIndex = state.mempoolBlockIndex;
|
||||||
}
|
}
|
||||||
|
if (state.mempoolPosition) {
|
||||||
|
this.txPosition = state.mempoolPosition;
|
||||||
|
}
|
||||||
if (state.txFeePerVSize) {
|
if (state.txFeePerVSize) {
|
||||||
this.txFeePerVSize = state.txFeePerVSize;
|
this.txFeePerVSize = state.txFeePerVSize;
|
||||||
}
|
}
|
||||||
@ -302,7 +306,7 @@ export class MempoolBlocksComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
calculateTransactionPosition() {
|
calculateTransactionPosition() {
|
||||||
if ((!this.txFeePerVSize && (this.markIndex === undefined || this.markIndex === -1)) || !this.mempoolBlocks) {
|
if ((!this.txPosition && !this.txFeePerVSize && (this.markIndex === undefined || this.markIndex === -1)) || !this.mempoolBlocks) {
|
||||||
this.arrowVisible = false;
|
this.arrowVisible = false;
|
||||||
return;
|
return;
|
||||||
} else if (this.markIndex > -1) {
|
} else if (this.markIndex > -1) {
|
||||||
@ -320,33 +324,43 @@ export class MempoolBlocksComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
this.arrowVisible = true;
|
this.arrowVisible = true;
|
||||||
|
|
||||||
let found = false;
|
if (this.txPosition) {
|
||||||
for (let txInBlockIndex = 0; txInBlockIndex < this.mempoolBlocks.length && !found; txInBlockIndex++) {
|
if (this.txPosition.block >= this.mempoolBlocks.length) {
|
||||||
const block = this.mempoolBlocks[txInBlockIndex];
|
this.rightPosition = ((this.mempoolBlocks.length - 1) * (this.blockWidth + this.blockPadding)) + this.blockWidth;
|
||||||
for (let i = 0; i < block.feeRange.length - 1 && !found; i++) {
|
} else {
|
||||||
if (this.txFeePerVSize < block.feeRange[i + 1] && this.txFeePerVSize >= block.feeRange[i]) {
|
const positionInBlock = Math.min(1, this.txPosition.vsize / this.stateService.blockVSize) * this.blockWidth;
|
||||||
const feeRangeIndex = i;
|
const positionOfBlock = this.txPosition.block * (this.blockWidth + this.blockPadding);
|
||||||
const feeRangeChunkSize = 1 / (block.feeRange.length - 1);
|
this.rightPosition = positionOfBlock + positionInBlock;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let found = false;
|
||||||
|
for (let txInBlockIndex = 0; txInBlockIndex < this.mempoolBlocks.length && !found; txInBlockIndex++) {
|
||||||
|
const block = this.mempoolBlocks[txInBlockIndex];
|
||||||
|
for (let i = 0; i < block.feeRange.length - 1 && !found; i++) {
|
||||||
|
if (this.txFeePerVSize < block.feeRange[i + 1] && this.txFeePerVSize >= block.feeRange[i]) {
|
||||||
|
const feeRangeIndex = i;
|
||||||
|
const feeRangeChunkSize = 1 / (block.feeRange.length - 1);
|
||||||
|
|
||||||
const txFee = this.txFeePerVSize - block.feeRange[i];
|
const txFee = this.txFeePerVSize - block.feeRange[i];
|
||||||
const max = block.feeRange[i + 1] - block.feeRange[i];
|
const max = block.feeRange[i + 1] - block.feeRange[i];
|
||||||
const blockLocation = txFee / max;
|
const blockLocation = txFee / max;
|
||||||
|
|
||||||
const chunkPositionOffset = blockLocation * feeRangeChunkSize;
|
const chunkPositionOffset = blockLocation * feeRangeChunkSize;
|
||||||
const feePosition = feeRangeChunkSize * feeRangeIndex + chunkPositionOffset;
|
const feePosition = feeRangeChunkSize * feeRangeIndex + chunkPositionOffset;
|
||||||
|
|
||||||
const blockedFilledPercentage = (block.blockVSize > this.stateService.blockVSize ? this.stateService.blockVSize : block.blockVSize) / this.stateService.blockVSize;
|
const blockedFilledPercentage = (block.blockVSize > this.stateService.blockVSize ? this.stateService.blockVSize : block.blockVSize) / this.stateService.blockVSize;
|
||||||
const arrowRightPosition = txInBlockIndex * (this.blockWidth + this.blockPadding)
|
const arrowRightPosition = txInBlockIndex * (this.blockWidth + this.blockPadding)
|
||||||
+ ((1 - feePosition) * blockedFilledPercentage * this.blockWidth);
|
+ ((1 - feePosition) * blockedFilledPercentage * this.blockWidth);
|
||||||
|
|
||||||
this.rightPosition = arrowRightPosition;
|
this.rightPosition = arrowRightPosition;
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.txFeePerVSize >= block.feeRange[block.feeRange.length - 1]) {
|
||||||
|
this.rightPosition = txInBlockIndex * (this.blockWidth + this.blockPadding);
|
||||||
found = true;
|
found = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this.txFeePerVSize >= block.feeRange[block.feeRange.length - 1]) {
|
|
||||||
this.rightPosition = txInBlockIndex * (this.blockWidth + this.blockPadding);
|
|
||||||
found = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ import { WebsocketService } from '../../services/websocket.service';
|
|||||||
import { AudioService } from '../../services/audio.service';
|
import { AudioService } from '../../services/audio.service';
|
||||||
import { ApiService } from '../../services/api.service';
|
import { ApiService } from '../../services/api.service';
|
||||||
import { SeoService } from '../../services/seo.service';
|
import { SeoService } from '../../services/seo.service';
|
||||||
import { BlockExtended, CpfpInfo, RbfTree } from '../../interfaces/node-api.interface';
|
import { BlockExtended, CpfpInfo, RbfTree, MempoolPosition } from '../../interfaces/node-api.interface';
|
||||||
import { LiquidUnblinding } from './liquid-ublinding';
|
import { LiquidUnblinding } from './liquid-ublinding';
|
||||||
import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe';
|
import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe';
|
||||||
import { Price, PriceService } from '../../services/price.service';
|
import { Price, PriceService } from '../../services/price.service';
|
||||||
@ -35,6 +35,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
tx: Transaction;
|
tx: Transaction;
|
||||||
txId: string;
|
txId: string;
|
||||||
txInBlockIndex: number;
|
txInBlockIndex: number;
|
||||||
|
mempoolPosition: MempoolPosition;
|
||||||
isLoadingTx = true;
|
isLoadingTx = true;
|
||||||
error: any = undefined;
|
error: any = undefined;
|
||||||
errorUnblinded: any = undefined;
|
errorUnblinded: any = undefined;
|
||||||
@ -47,6 +48,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
fetchCachedTxSubscription: Subscription;
|
fetchCachedTxSubscription: Subscription;
|
||||||
txReplacedSubscription: Subscription;
|
txReplacedSubscription: Subscription;
|
||||||
txRbfInfoSubscription: Subscription;
|
txRbfInfoSubscription: Subscription;
|
||||||
|
mempoolPositionSubscription: Subscription;
|
||||||
blocksSubscription: Subscription;
|
blocksSubscription: Subscription;
|
||||||
queryParamsSubscription: Subscription;
|
queryParamsSubscription: Subscription;
|
||||||
urlFragmentSubscription: Subscription;
|
urlFragmentSubscription: Subscription;
|
||||||
@ -174,6 +176,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
if (!this.tx?.status?.confirmed) {
|
if (!this.tx?.status?.confirmed) {
|
||||||
this.stateService.markBlock$.next({
|
this.stateService.markBlock$.next({
|
||||||
txFeePerVSize: this.tx.effectiveFeePerVsize,
|
txFeePerVSize: this.tx.effectiveFeePerVsize,
|
||||||
|
mempoolPosition: this.mempoolPosition,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.cpfpInfo = cpfpInfo;
|
this.cpfpInfo = cpfpInfo;
|
||||||
@ -231,6 +234,19 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.mempoolPositionSubscription = this.stateService.mempoolTxPosition$.subscribe(txPosition => {
|
||||||
|
if (txPosition && txPosition.txid === this.txId && txPosition.position) {
|
||||||
|
this.mempoolPosition = txPosition.position;
|
||||||
|
if (this.tx && !this.tx.status.confirmed) {
|
||||||
|
this.stateService.markBlock$.next({
|
||||||
|
mempoolPosition: this.mempoolPosition
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.mempoolPosition = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
this.subscription = this.route.paramMap
|
this.subscription = this.route.paramMap
|
||||||
.pipe(
|
.pipe(
|
||||||
switchMap((params: ParamMap) => {
|
switchMap((params: ParamMap) => {
|
||||||
@ -342,6 +358,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
if (tx.cpfpChecked) {
|
if (tx.cpfpChecked) {
|
||||||
this.stateService.markBlock$.next({
|
this.stateService.markBlock$.next({
|
||||||
txFeePerVSize: tx.effectiveFeePerVsize,
|
txFeePerVSize: tx.effectiveFeePerVsize,
|
||||||
|
mempoolPosition: this.mempoolPosition,
|
||||||
});
|
});
|
||||||
this.cpfpInfo = {
|
this.cpfpInfo = {
|
||||||
ancestors: tx.ancestors,
|
ancestors: tx.ancestors,
|
||||||
@ -569,6 +586,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
this.flowPrefSubscription.unsubscribe();
|
this.flowPrefSubscription.unsubscribe();
|
||||||
this.urlFragmentSubscription.unsubscribe();
|
this.urlFragmentSubscription.unsubscribe();
|
||||||
this.mempoolBlocksSubscription.unsubscribe();
|
this.mempoolBlocksSubscription.unsubscribe();
|
||||||
|
this.mempoolPositionSubscription.unsubscribe();
|
||||||
this.leaveTransaction();
|
this.leaveTransaction();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -162,6 +162,10 @@ interface RbfTransaction extends TransactionStripped {
|
|||||||
rbf?: boolean;
|
rbf?: boolean;
|
||||||
mined?: boolean,
|
mined?: boolean,
|
||||||
}
|
}
|
||||||
|
export interface MempoolPosition {
|
||||||
|
block: number,
|
||||||
|
vsize: number,
|
||||||
|
}
|
||||||
|
|
||||||
export interface RewardStats {
|
export interface RewardStats {
|
||||||
startBlock: number;
|
startBlock: number;
|
||||||
|
@ -2,7 +2,7 @@ import { Inject, Injectable, PLATFORM_ID, LOCALE_ID } from '@angular/core';
|
|||||||
import { ReplaySubject, BehaviorSubject, Subject, fromEvent, Observable } from 'rxjs';
|
import { ReplaySubject, BehaviorSubject, Subject, fromEvent, Observable } from 'rxjs';
|
||||||
import { Transaction } from '../interfaces/electrs.interface';
|
import { Transaction } from '../interfaces/electrs.interface';
|
||||||
import { IBackendInfo, MempoolBlock, MempoolBlockWithTransactions, MempoolBlockDelta, MempoolInfo, Recommendedfees, ReplacedTransaction, TransactionStripped } from '../interfaces/websocket.interface';
|
import { IBackendInfo, MempoolBlock, MempoolBlockWithTransactions, MempoolBlockDelta, MempoolInfo, Recommendedfees, ReplacedTransaction, TransactionStripped } from '../interfaces/websocket.interface';
|
||||||
import { BlockExtended, DifficultyAdjustment, OptimizedMempoolStats, RbfTree } from '../interfaces/node-api.interface';
|
import { BlockExtended, 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 { map, shareReplay } from 'rxjs/operators';
|
import { map, shareReplay } from 'rxjs/operators';
|
||||||
@ -12,6 +12,7 @@ interface MarkBlockState {
|
|||||||
blockHeight?: number;
|
blockHeight?: number;
|
||||||
mempoolBlockIndex?: number;
|
mempoolBlockIndex?: number;
|
||||||
txFeePerVSize?: number;
|
txFeePerVSize?: number;
|
||||||
|
mempoolPosition?: MempoolPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ILoadingIndicators { [name: string]: number; }
|
export interface ILoadingIndicators { [name: string]: number; }
|
||||||
@ -105,6 +106,7 @@ export class StateService {
|
|||||||
utxoSpent$ = new Subject<object>();
|
utxoSpent$ = new Subject<object>();
|
||||||
difficultyAdjustment$ = new ReplaySubject<DifficultyAdjustment>(1);
|
difficultyAdjustment$ = new ReplaySubject<DifficultyAdjustment>(1);
|
||||||
mempoolTransactions$ = new Subject<Transaction>();
|
mempoolTransactions$ = new Subject<Transaction>();
|
||||||
|
mempoolTxPosition$ = new Subject<{ txid: string, position: MempoolPosition}>();
|
||||||
blockTransactions$ = new Subject<Transaction>();
|
blockTransactions$ = new Subject<Transaction>();
|
||||||
isLoadingWebSocket$ = new ReplaySubject<boolean>(1);
|
isLoadingWebSocket$ = new ReplaySubject<boolean>(1);
|
||||||
vbytesPerSecond$ = new ReplaySubject<number>(1);
|
vbytesPerSecond$ = new ReplaySubject<number>(1);
|
||||||
|
@ -249,6 +249,10 @@ export class WebsocketService {
|
|||||||
this.stateService.mempoolTransactions$.next(response.tx);
|
this.stateService.mempoolTransactions$.next(response.tx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (response['txPosition']) {
|
||||||
|
this.stateService.mempoolTxPosition$.next(response['txPosition']);
|
||||||
|
}
|
||||||
|
|
||||||
if (response.block) {
|
if (response.block) {
|
||||||
if (response.block.height > this.stateService.latestBlockHeight) {
|
if (response.block.height > this.stateService.latestBlockHeight) {
|
||||||
this.stateService.updateChainTip(response.block.height);
|
this.stateService.updateChainTip(response.block.height);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user