Compare commits

...

7 Commits

Author SHA1 Message Date
Mononaut
43f50b2c89 Add accelerated badge to projected block tooltip 2023-12-07 11:22:31 +00:00
wiz
4169e1053f Merge pull request #4482 from mempool/mononaut/fix-negative-accel-rate
Fix negative accelerated fee rate, simplify fee rate table
2023-12-07 01:50:12 +09:00
wiz
a46e779d0c Merge branch 'master' into mononaut/fix-negative-accel-rate 2023-12-06 23:56:12 +09:00
Mononaut
51102004bb Fix negative accelerated fee rate, simplify fee rate table 2023-12-06 14:50:26 +00:00
wiz
08e9142d57 Merge pull request #4481 from mempool/mononaut/improve-acceleration-tracking
Improve acceleration tracking
2023-12-06 22:42:00 +09:00
Mononaut
b7c4cd43fc Better error handling for accelerations update request 2023-12-06 12:09:46 +00:00
Mononaut
08a35b85a8 Fix current accelerations update race condition 2023-12-06 12:09:28 +00:00
7 changed files with 33 additions and 28 deletions

View File

@@ -9,7 +9,7 @@ import loadingIndicators from './loading-indicators';
import bitcoinClient from './bitcoin/bitcoin-client'; import bitcoinClient from './bitcoin/bitcoin-client';
import bitcoinSecondClient from './bitcoin/bitcoin-second-client'; import bitcoinSecondClient from './bitcoin/bitcoin-second-client';
import rbfCache from './rbf-cache'; import rbfCache from './rbf-cache';
import accelerationApi, { Acceleration } from './services/acceleration'; import { Acceleration } from './services/acceleration';
import redisCache from './redis-cache'; import redisCache from './redis-cache';
class Mempool { class Mempool {
@@ -185,7 +185,7 @@ class Mempool {
return txTimes; return txTimes;
} }
public async $updateMempool(transactions: string[], pollRate: number): Promise<void> { public async $updateMempool(transactions: string[], accelerations: Acceleration[] | null, pollRate: number): Promise<void> {
logger.debug(`Updating mempool...`); logger.debug(`Updating mempool...`);
// warn if this run stalls the main loop for more than 2 minutes // warn if this run stalls the main loop for more than 2 minutes
@@ -330,7 +330,7 @@ class Mempool {
const newTransactionsStripped = newTransactions.map((tx) => Common.stripTransaction(tx)); const newTransactionsStripped = newTransactions.map((tx) => Common.stripTransaction(tx));
this.latestTransactions = newTransactionsStripped.concat(this.latestTransactions).slice(0, 6); this.latestTransactions = newTransactionsStripped.concat(this.latestTransactions).slice(0, 6);
const accelerationDelta = await this.$updateAccelerations(); const accelerationDelta = accelerations != null ? await this.$updateAccelerations(accelerations) : [];
if (accelerationDelta.length) { if (accelerationDelta.length) {
hasChange = true; hasChange = true;
} }
@@ -370,14 +370,12 @@ class Mempool {
return this.accelerations; return this.accelerations;
} }
public async $updateAccelerations(): Promise<string[]> { public $updateAccelerations(newAccelerations: Acceleration[]): string[] {
if (!config.MEMPOOL_SERVICES.ACCELERATIONS) { if (!config.MEMPOOL_SERVICES.ACCELERATIONS) {
return []; return [];
} }
try { try {
const newAccelerations = await accelerationApi.$fetchAccelerations();
const changed: string[] = []; const changed: string[] = [];
const newAccelerationMap: { [txid: string]: Acceleration } = {}; const newAccelerationMap: { [txid: string]: Acceleration } = {};

View File

@@ -1,6 +1,7 @@
import { query } from '../../utils/axios-query';
import config from '../../config'; import config from '../../config';
import logger from '../../logger';
import { BlockExtended, PoolTag } from '../../mempool.interfaces'; import { BlockExtended, PoolTag } from '../../mempool.interfaces';
import axios from 'axios';
export interface Acceleration { export interface Acceleration {
txid: string, txid: string,
@@ -9,10 +10,15 @@ export interface Acceleration {
} }
class AccelerationApi { class AccelerationApi {
public async $fetchAccelerations(): Promise<Acceleration[]> { public async $fetchAccelerations(): Promise<Acceleration[] | null> {
if (config.MEMPOOL_SERVICES.ACCELERATIONS) { if (config.MEMPOOL_SERVICES.ACCELERATIONS) {
const response = await query(`${config.MEMPOOL_SERVICES.API}/accelerator/accelerations`); try {
return (response as Acceleration[]) || []; const response = await axios.get(`${config.MEMPOOL_SERVICES.API}/accelerator/accelerations`, { responseType: 'json', timeout: 10000 });
return response.data as Acceleration[];
} catch (e) {
logger.warn('Failed to fetch current accelerations from the mempool services backend: ' + (e instanceof Error ? e.message : e));
return null;
}
} else { } else {
return []; return [];
} }

View File

@@ -43,6 +43,7 @@ import { AxiosError } from 'axios';
import v8 from 'v8'; import v8 from 'v8';
import { formatBytes, getBytesUnit } from './utils/format'; import { formatBytes, getBytesUnit } from './utils/format';
import redisCache from './api/redis-cache'; import redisCache from './api/redis-cache';
import accelerationApi from './api/services/acceleration';
class Server { class Server {
private wss: WebSocket.Server | undefined; private wss: WebSocket.Server | undefined;
@@ -205,10 +206,11 @@ class Server {
} }
} }
const newMempool = await bitcoinApi.$getRawMempool(); const newMempool = await bitcoinApi.$getRawMempool();
const newAccelerations = await accelerationApi.$fetchAccelerations();
const numHandledBlocks = await blocks.$updateBlocks(); const numHandledBlocks = await blocks.$updateBlocks();
const pollRate = config.MEMPOOL.POLL_RATE_MS * (indexer.indexerIsRunning() ? 10 : 1); const pollRate = config.MEMPOOL.POLL_RATE_MS * (indexer.indexerIsRunning() ? 10 : 1);
if (numHandledBlocks === 0) { if (numHandledBlocks === 0) {
await memPool.$updateMempool(newMempool, pollRate); await memPool.$updateMempool(newMempool, newAccelerations, pollRate);
} }
indexer.$run(); indexer.$run();
priceUpdater.$run(); priceUpdater.$run();

View File

@@ -43,6 +43,10 @@
<td class="td-width" i18n="transaction.weight|Transaction Weight">Weight</td> <td class="td-width" i18n="transaction.weight|Transaction Weight">Weight</td>
<td [innerHTML]="'&lrm;' + ((vsize * 4) | wuBytes: 2)"></td> <td [innerHTML]="'&lrm;' + ((vsize * 4) | wuBytes: 2)"></td>
</tr> </tr>
<tr *ngIf="!auditEnabled && (this.acceleration || (tx && tx.acc))">
<td></td>
<td><span class="badge badge-accelerated" i18n="transaction.audit.accelerated">Accelerated</span></td>
</tr>
<tr *ngIf="auditEnabled && tx && tx.status && tx.status.length"> <tr *ngIf="auditEnabled && tx && tx.status && tx.status.length">
<td class="td-width" i18n="transaction.audit-status">Audit status</td> <td class="td-width" i18n="transaction.audit-status">Audit status</td>
<ng-container [ngSwitch]="tx?.status"> <ng-container [ngSwitch]="tx?.status">

View File

@@ -516,28 +516,20 @@
</ng-template> </ng-template>
</td> </td>
</tr> </tr>
<tr *ngIf="!hasEffectiveFeeRate && accelerationInfo"> <tr *ngIf="(cpfpInfo && hasEffectiveFeeRate) || accelerationInfo">
<td i18n="transaction.accelerated-fee-rate|Accelerated transaction fee rate">Accelerated fee rate</td>
<td>
<div class="effective-fee-container">
<app-fee-rate [fee]="accelerationInfo.effectiveFee + accelerationInfo.feePaid - accelerationInfo.baseFee - accelerationInfo.vsizeFee" [weight]="accelerationInfo.effectiveVsize * 4"></app-fee-rate>
&nbsp;
<span class="badge badge-accelerated" i18n="transaction.audit.accelerated">Accelerated</span>
</div>
</td>
</tr>
<tr *ngIf="!accelerationInfo && cpfpInfo && hasEffectiveFeeRate">
<td *ngIf="tx.acceleration || accelerationInfo" i18n="transaction.accelerated-fee-rate|Accelerated transaction fee rate">Accelerated fee rate</td> <td *ngIf="tx.acceleration || accelerationInfo" i18n="transaction.accelerated-fee-rate|Accelerated transaction fee rate">Accelerated fee rate</td>
<td *ngIf="!(tx.acceleration || accelerationInfo)" i18n="transaction.effective-fee-rate|Effective transaction fee rate">Effective fee rate</td> <td *ngIf="!(tx.acceleration || accelerationInfo)" i18n="transaction.effective-fee-rate|Effective transaction fee rate">Effective fee rate</td>
<td> <td>
<div class="effective-fee-container"> <div class="effective-fee-container">
<app-fee-rate [fee]="tx.effectiveFeePerVsize"></app-fee-rate> <app-fee-rate *ngIf="accelerationInfo" [fee]="accelerationInfo.actualFeeDelta" [weight]="accelerationInfo.effectiveVsize * 4"></app-fee-rate>
<ng-template [ngIf]="tx?.status?.confirmed"> <app-fee-rate *ngIf="!accelerationInfo" [fee]="tx.effectiveFeePerVsize"></app-fee-rate>
<app-tx-fee-rating class="ml-2 mr-2 effective-fee-rating" *ngIf="!accelerationInfo && (tx.fee || tx.effectiveFeePerVsize)" [tx]="tx"></app-tx-fee-rating>
<ng-template [ngIf]="accelerationInfo"> <ng-template [ngIf]="tx?.status?.confirmed || tx.acceleration || accelerationInfo">
<app-tx-fee-rating *ngIf="!(tx.acceleration || accelerationInfo) && (tx.fee || tx.effectiveFeePerVsize)" class="ml-2 mr-2 effective-fee-rating" [tx]="tx"></app-tx-fee-rating>
<ng-container *ngIf="accelerationInfo || tx.acceleration">
&nbsp; &nbsp;
<span class="badge badge-accelerated" i18n="transaction.audit.accelerated">Accelerated</span> <span class="badge badge-accelerated" i18n="transaction.audit.accelerated">Accelerated</span>
</ng-template> </ng-container>
</ng-template> </ng-template>
</div> </div>
<button *ngIf="cpfpInfo.bestDescendant || cpfpInfo.descendants?.length || cpfpInfo.ancestors?.length" type="button" class="btn btn-outline-info btn-sm btn-small-height float-right" (click)="showCpfpDetails = !showCpfpDetails">CPFP <fa-icon [icon]="['fas', 'info-circle']" [fixedWidth]="true"></fa-icon></button> <button *ngIf="cpfpInfo.bestDescendant || cpfpInfo.descendants?.length || cpfpInfo.ancestors?.length" type="button" class="btn btn-outline-info btn-sm btn-small-height float-right" (click)="showCpfpDetails = !showCpfpDetails">CPFP <fa-icon [icon]="['fas', 'info-circle']" [fixedWidth]="true"></fa-icon></button>

View File

@@ -253,7 +253,8 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
}) })
).subscribe((accelerationHistory) => { ).subscribe((accelerationHistory) => {
for (const acceleration of accelerationHistory) { for (const acceleration of accelerationHistory) {
if (acceleration.txid === this.txId) { if (acceleration.txid === this.txId && (acceleration.status === 'completed' || acceleration.status === 'mined') && acceleration.feePaid > 0) {
acceleration.actualFeeDelta = Math.max(acceleration.effectiveFee, acceleration.effectiveFee + acceleration.feePaid - acceleration.baseFee - acceleration.vsizeFee);
this.accelerationInfo = acceleration; this.accelerationInfo = acceleration;
} }
} }

View File

@@ -317,6 +317,8 @@ export interface Acceleration {
feeDelta: number; feeDelta: number;
blockHash: string; blockHash: string;
blockHeight: number; blockHeight: number;
actualFeeDelta?: number;
} }
export interface AccelerationHistoryParams { export interface AccelerationHistoryParams {