mirror of
https://github.com/mempool/mempool.git
synced 2025-01-19 05:34:03 +01:00
Merge branch 'master' into nymkappa/menu
This commit is contained in:
commit
9427ba96a2
@ -451,6 +451,7 @@ class MempoolBlocks {
|
|||||||
private processBlockTemplates(mempool: { [txid: string]: MempoolTransactionExtended }, blocks: string[][], blockWeights: number[] | null, rates: [string, number][], clusters: string[][], accelerations, accelerationPool, saveResults): MempoolBlockWithTransactions[] {
|
private processBlockTemplates(mempool: { [txid: string]: MempoolTransactionExtended }, blocks: string[][], blockWeights: number[] | null, rates: [string, number][], clusters: string[][], accelerations, accelerationPool, saveResults): MempoolBlockWithTransactions[] {
|
||||||
for (const [txid, rate] of rates) {
|
for (const [txid, rate] of rates) {
|
||||||
if (txid in mempool) {
|
if (txid in mempool) {
|
||||||
|
mempool[txid].cpfpDirty = (rate !== mempool[txid].effectiveFeePerVsize);
|
||||||
mempool[txid].effectiveFeePerVsize = rate;
|
mempool[txid].effectiveFeePerVsize = rate;
|
||||||
mempool[txid].cpfpChecked = false;
|
mempool[txid].cpfpChecked = false;
|
||||||
}
|
}
|
||||||
@ -494,6 +495,9 @@ class MempoolBlocks {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
if (mempoolTx.ancestors?.length !== ancestors.length || mempoolTx.descendants?.length !== descendants.length) {
|
||||||
|
mempoolTx.cpfpDirty = true;
|
||||||
|
}
|
||||||
Object.assign(mempoolTx, {ancestors, descendants, bestDescendant: null, cpfpChecked: true});
|
Object.assign(mempoolTx, {ancestors, descendants, bestDescendant: null, cpfpChecked: true});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -531,12 +535,21 @@ class MempoolBlocks {
|
|||||||
|
|
||||||
const acceleration = accelerations[txid];
|
const acceleration = accelerations[txid];
|
||||||
if (isAccelerated[txid] || (acceleration && (!accelerationPool || acceleration.pools.includes(accelerationPool)))) {
|
if (isAccelerated[txid] || (acceleration && (!accelerationPool || acceleration.pools.includes(accelerationPool)))) {
|
||||||
|
if (!mempoolTx.acceleration) {
|
||||||
|
mempoolTx.cpfpDirty = true;
|
||||||
|
}
|
||||||
mempoolTx.acceleration = true;
|
mempoolTx.acceleration = true;
|
||||||
for (const ancestor of mempoolTx.ancestors || []) {
|
for (const ancestor of mempoolTx.ancestors || []) {
|
||||||
|
if (!mempool[ancestor.txid].acceleration) {
|
||||||
|
mempool[ancestor.txid].cpfpDirty = true;
|
||||||
|
}
|
||||||
mempool[ancestor.txid].acceleration = true;
|
mempool[ancestor.txid].acceleration = true;
|
||||||
isAccelerated[ancestor.txid] = true;
|
isAccelerated[ancestor.txid] = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if (mempoolTx.acceleration) {
|
||||||
|
mempoolTx.cpfpDirty = true;
|
||||||
|
}
|
||||||
delete mempoolTx.acceleration;
|
delete mempoolTx.acceleration;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -586,13 +586,25 @@ class WebsocketHandler {
|
|||||||
|
|
||||||
const mempoolTx = newMempool[trackTxid];
|
const mempoolTx = newMempool[trackTxid];
|
||||||
if (mempoolTx && mempoolTx.position) {
|
if (mempoolTx && mempoolTx.position) {
|
||||||
response['txPosition'] = JSON.stringify({
|
const positionData = {
|
||||||
txid: trackTxid,
|
txid: trackTxid,
|
||||||
position: {
|
position: {
|
||||||
...mempoolTx.position,
|
...mempoolTx.position,
|
||||||
accelerated: mempoolTx.acceleration || undefined,
|
accelerated: mempoolTx.acceleration || undefined,
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
if (mempoolTx.cpfpDirty) {
|
||||||
|
positionData['cpfp'] = {
|
||||||
|
ancestors: mempoolTx.ancestors,
|
||||||
|
bestDescendant: mempoolTx.bestDescendant || null,
|
||||||
|
descendants: mempoolTx.descendants || null,
|
||||||
|
effectiveFeePerVsize: mempoolTx.effectiveFeePerVsize || null,
|
||||||
|
sigops: mempoolTx.sigops,
|
||||||
|
adjustedVsize: mempoolTx.adjustedVsize,
|
||||||
|
acceleration: mempoolTx.acceleration
|
||||||
|
};
|
||||||
|
}
|
||||||
|
response['txPosition'] = JSON.stringify(positionData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,6 +104,7 @@ export interface MempoolTransactionExtended extends TransactionExtended {
|
|||||||
adjustedFeePerVsize: number;
|
adjustedFeePerVsize: number;
|
||||||
inputs?: number[];
|
inputs?: number[];
|
||||||
lastBoosted?: number;
|
lastBoosted?: number;
|
||||||
|
cpfpDirty?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AuditTransaction {
|
export interface AuditTransaction {
|
||||||
|
@ -174,34 +174,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
.subscribe((cpfpInfo) => {
|
.subscribe((cpfpInfo) => {
|
||||||
if (!cpfpInfo || !this.tx) {
|
this.setCpfpInfo(cpfpInfo);
|
||||||
this.cpfpInfo = null;
|
|
||||||
this.hasEffectiveFeeRate = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// merge ancestors/descendants
|
|
||||||
const relatives = [...(cpfpInfo.ancestors || []), ...(cpfpInfo.descendants || [])];
|
|
||||||
if (cpfpInfo.bestDescendant && !cpfpInfo.descendants?.length) {
|
|
||||||
relatives.push(cpfpInfo.bestDescendant);
|
|
||||||
}
|
|
||||||
const hasRelatives = !!relatives.length;
|
|
||||||
if (!cpfpInfo.effectiveFeePerVsize && hasRelatives) {
|
|
||||||
let totalWeight =
|
|
||||||
this.tx.weight +
|
|
||||||
relatives.reduce((prev, val) => prev + val.weight, 0);
|
|
||||||
let totalFees =
|
|
||||||
this.tx.fee +
|
|
||||||
relatives.reduce((prev, val) => prev + val.fee, 0);
|
|
||||||
this.tx.effectiveFeePerVsize = totalFees / (totalWeight / 4);
|
|
||||||
} else {
|
|
||||||
this.tx.effectiveFeePerVsize = cpfpInfo.effectiveFeePerVsize;
|
|
||||||
}
|
|
||||||
if (cpfpInfo.acceleration) {
|
|
||||||
this.tx.acceleration = cpfpInfo.acceleration;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.cpfpInfo = cpfpInfo;
|
|
||||||
this.hasEffectiveFeeRate = hasRelatives || (this.tx.effectiveFeePerVsize && (Math.abs(this.tx.effectiveFeePerVsize - this.tx.feePerVsize) > 0.01));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.fetchRbfSubscription = this.fetchRbfHistory$
|
this.fetchRbfSubscription = this.fetchRbfHistory$
|
||||||
@ -272,6 +245,10 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
mempoolPosition: this.mempoolPosition
|
mempoolPosition: this.mempoolPosition
|
||||||
});
|
});
|
||||||
this.txInBlockIndex = this.mempoolPosition.block;
|
this.txInBlockIndex = this.mempoolPosition.block;
|
||||||
|
|
||||||
|
if (txPosition.cpfp !== undefined) {
|
||||||
|
this.setCpfpInfo(txPosition.cpfp);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.mempoolPosition = null;
|
this.mempoolPosition = null;
|
||||||
@ -534,6 +511,37 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setCpfpInfo(cpfpInfo: CpfpInfo): void {
|
||||||
|
if (!cpfpInfo || !this.tx) {
|
||||||
|
this.cpfpInfo = null;
|
||||||
|
this.hasEffectiveFeeRate = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// merge ancestors/descendants
|
||||||
|
const relatives = [...(cpfpInfo.ancestors || []), ...(cpfpInfo.descendants || [])];
|
||||||
|
if (cpfpInfo.bestDescendant && !cpfpInfo.descendants?.length) {
|
||||||
|
relatives.push(cpfpInfo.bestDescendant);
|
||||||
|
}
|
||||||
|
const hasRelatives = !!relatives.length;
|
||||||
|
if (!cpfpInfo.effectiveFeePerVsize && hasRelatives) {
|
||||||
|
const totalWeight =
|
||||||
|
this.tx.weight +
|
||||||
|
relatives.reduce((prev, val) => prev + val.weight, 0);
|
||||||
|
const totalFees =
|
||||||
|
this.tx.fee +
|
||||||
|
relatives.reduce((prev, val) => prev + val.fee, 0);
|
||||||
|
this.tx.effectiveFeePerVsize = totalFees / (totalWeight / 4);
|
||||||
|
} else {
|
||||||
|
this.tx.effectiveFeePerVsize = cpfpInfo.effectiveFeePerVsize;
|
||||||
|
}
|
||||||
|
if (cpfpInfo.acceleration) {
|
||||||
|
this.tx.acceleration = cpfpInfo.acceleration;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.cpfpInfo = cpfpInfo;
|
||||||
|
this.hasEffectiveFeeRate = hasRelatives || (this.tx.effectiveFeePerVsize && (Math.abs(this.tx.effectiveFeePerVsize - this.tx.feePerVsize) > 0.01));
|
||||||
|
}
|
||||||
|
|
||||||
setFeatures(): void {
|
setFeatures(): void {
|
||||||
if (this.tx) {
|
if (this.tx) {
|
||||||
this.segwitEnabled = !this.tx.status.confirmed || isFeatureActive(this.stateService.network, this.tx.status.block_height, 'segwit');
|
this.segwitEnabled = !this.tx.status.confirmed || isFeatureActive(this.stateService.network, this.tx.status.block_height, 'segwit');
|
||||||
|
@ -19,7 +19,7 @@ export interface Transaction {
|
|||||||
ancestors?: Ancestor[];
|
ancestors?: Ancestor[];
|
||||||
bestDescendant?: BestDescendant | null;
|
bestDescendant?: BestDescendant | null;
|
||||||
cpfpChecked?: boolean;
|
cpfpChecked?: boolean;
|
||||||
acceleration?: number;
|
acceleration?: boolean;
|
||||||
deleteAfter?: number;
|
deleteAfter?: number;
|
||||||
_unblinded?: any;
|
_unblinded?: any;
|
||||||
_deduced?: boolean;
|
_deduced?: boolean;
|
||||||
|
@ -27,7 +27,7 @@ export interface CpfpInfo {
|
|||||||
effectiveFeePerVsize?: number;
|
effectiveFeePerVsize?: number;
|
||||||
sigops?: number;
|
sigops?: number;
|
||||||
adjustedVsize?: number;
|
adjustedVsize?: number;
|
||||||
acceleration?: number;
|
acceleration?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RbfInfo {
|
export interface RbfInfo {
|
||||||
|
@ -2,7 +2,7 @@ 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 { IBackendInfo, MempoolBlock, MempoolBlockDelta, MempoolInfo, Recommendedfees, ReplacedTransaction, ReplacementInfo, TransactionStripped } from '../interfaces/websocket.interface';
|
import { IBackendInfo, MempoolBlock, MempoolBlockDelta, MempoolInfo, Recommendedfees, ReplacedTransaction, ReplacementInfo, TransactionStripped } from '../interfaces/websocket.interface';
|
||||||
import { BlockExtended, DifficultyAdjustment, MempoolPosition, OptimizedMempoolStats, RbfTree } 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';
|
||||||
@ -116,7 +116,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}>();
|
mempoolTxPosition$ = new Subject<{ txid: string, position: MempoolPosition, cpfp: CpfpInfo | null}>();
|
||||||
blockTransactions$ = new Subject<Transaction>();
|
blockTransactions$ = new Subject<Transaction>();
|
||||||
isLoadingWebSocket$ = new ReplaySubject<boolean>(1);
|
isLoadingWebSocket$ = new ReplaySubject<boolean>(1);
|
||||||
isLoadingMempool$ = new BehaviorSubject<boolean>(true);
|
isLoadingMempool$ = new BehaviorSubject<boolean>(true);
|
||||||
|
Loading…
Reference in New Issue
Block a user