mirror of
https://github.com/mempool/mempool.git
synced 2026-08-13 12:33:11 +02:00
Merge branch 'master' into knorrium/db_tests
This commit is contained in:
commit
165eae6eba
7 changed files with 131 additions and 53 deletions
|
|
@ -11,6 +11,7 @@ class AccelerationRoutes {
|
|||
public initRoutes(app: Application): void {
|
||||
app
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'services/accelerator/accelerations', this.$getAcceleratorAccelerations.bind(this))
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'services/accelerator/accelerations/:txid', this.$getAcceleratorAcceleration.bind(this))
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'services/accelerator/accelerations/history', this.$getAcceleratorAccelerationsHistory.bind(this))
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'services/accelerator/accelerations/history/aggregated', this.$getAcceleratorAccelerationsHistoryAggregated.bind(this))
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'services/accelerator/accelerations/stats', this.$getAcceleratorAccelerationsStats.bind(this))
|
||||
|
|
@ -23,6 +24,19 @@ class AccelerationRoutes {
|
|||
res.status(200).send(Object.values(accelerations));
|
||||
}
|
||||
|
||||
private async $getAcceleratorAcceleration(req: Request, res: Response): Promise<void> {
|
||||
if (req.params.txid) {
|
||||
const acceleration = await AccelerationRepository.$getAccelerationInfoForTxid(req.params.txid);
|
||||
if (acceleration) {
|
||||
res.status(200).send(acceleration);
|
||||
} else {
|
||||
res.status(404).send('Acceleration not found');
|
||||
}
|
||||
} else {
|
||||
res.status(400).send('txid is required');
|
||||
}
|
||||
}
|
||||
|
||||
private async $getAcceleratorAccelerationsHistory(req: Request, res: Response): Promise<void> {
|
||||
const history = await AccelerationRepository.$getAccelerationInfo(null, req.query.blockHeight ? parseInt(req.query.blockHeight as string, 10) : null);
|
||||
res.status(200).send(history.map(accel => ({
|
||||
|
|
|
|||
|
|
@ -60,6 +60,32 @@ class AccelerationRepository {
|
|||
}
|
||||
}
|
||||
|
||||
public async $getAccelerationInfoForTxid(txid: string): Promise<PublicAcceleration | null> {
|
||||
const [rows] = await DB.query(`
|
||||
SELECT *, UNIX_TIMESTAMP(requested) as requested_timestamp, UNIX_TIMESTAMP(added) as block_timestamp FROM accelerations
|
||||
JOIN pools on pools.unique_id = accelerations.pool
|
||||
WHERE txid = ?
|
||||
`, [txid]) as RowDataPacket[][];
|
||||
if (rows?.length) {
|
||||
const row = rows[0];
|
||||
return {
|
||||
txid: row.txid,
|
||||
height: row.height,
|
||||
added: row.requested_timestamp || row.block_timestamp,
|
||||
pool: {
|
||||
id: row.id,
|
||||
slug: row.slug,
|
||||
name: row.name,
|
||||
},
|
||||
effective_vsize: row.effective_vsize,
|
||||
effective_fee: row.effective_fee,
|
||||
boost_rate: row.boost_rate,
|
||||
boost_cost: row.boost_cost,
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public async $getAccelerationInfo(poolSlug: string | null = null, height: number | null = null, interval: string | null = null): Promise<PublicAcceleration[]> {
|
||||
if (!interval || !['24h', '3d', '1w', '1m'].includes(interval)) {
|
||||
interval = '1m';
|
||||
|
|
|
|||
|
|
@ -138,7 +138,7 @@ export class BlockPreviewComponent implements OnInit, OnDestroy {
|
|||
return of(transactions);
|
||||
})
|
||||
),
|
||||
this.stateService.env.ACCELERATOR === true && block.height > 819500
|
||||
this.stateService.env.ACCELERATOR === true && block.height > 819500 && this.stateService.network === ''
|
||||
? this.servicesApiService.getAllAccelerationHistory$({ blockHeight: block.height })
|
||||
.pipe(
|
||||
catchError(() => {
|
||||
|
|
|
|||
|
|
@ -384,7 +384,7 @@ export class BlockComponent implements OnInit, OnDestroy {
|
|||
|
||||
this.accelerationsSubscription = this.block$.pipe(
|
||||
switchMap((block) => {
|
||||
return this.stateService.env.ACCELERATOR === true && block.height > 819500
|
||||
return this.stateService.env.ACCELERATOR === true && block.height > 819500 && this.stateService.network === ''
|
||||
? this.servicesApiService.getAllAccelerationHistory$({ blockHeight: block.height })
|
||||
.pipe(catchError(() => {
|
||||
return of([]);
|
||||
|
|
|
|||
|
|
@ -10,10 +10,11 @@ import {
|
|||
mergeMap,
|
||||
tap,
|
||||
map,
|
||||
startWith
|
||||
startWith,
|
||||
retry
|
||||
} from 'rxjs/operators';
|
||||
import { Transaction } from '@interfaces/electrs.interface';
|
||||
import { of, merge, Subscription, Observable, Subject, throwError, combineLatest, BehaviorSubject } from 'rxjs';
|
||||
import { of, merge, Subscription, Observable, Subject, throwError, combineLatest, BehaviorSubject, timer } from 'rxjs';
|
||||
import { StateService } from '@app/services/state.service';
|
||||
import { CacheService } from '@app/services/cache.service';
|
||||
import { WebsocketService } from '@app/services/websocket.service';
|
||||
|
|
@ -101,7 +102,7 @@ export class TrackerComponent implements OnInit, OnDestroy {
|
|||
fetchCpfp$ = new Subject<string>();
|
||||
fetchRbfHistory$ = new Subject<string>();
|
||||
fetchCachedTx$ = new Subject<string>();
|
||||
fetchAcceleration$ = new Subject<string>();
|
||||
fetchAcceleration$ = new Subject<number>();
|
||||
fetchMiningInfo$ = new Subject<{ hash: string, height: number, txid: string }>();
|
||||
txChanged$ = new BehaviorSubject<boolean>(false); // triggered whenever this.tx changes (long term, we should refactor to make this.tx an observable itself)
|
||||
isAccelerated$ = new BehaviorSubject<boolean>(false); // refactor this to make isAccelerated an observable itself
|
||||
|
|
@ -284,24 +285,48 @@ export class TrackerComponent implements OnInit, OnDestroy {
|
|||
filter(() => this.stateService.env.ACCELERATOR === true),
|
||||
tap(() => {
|
||||
this.accelerationInfo = null;
|
||||
this.setIsAccelerated();
|
||||
}),
|
||||
switchMap((blockHash: string) => {
|
||||
return this.servicesApiService.getAllAccelerationHistory$({ blockHash }, null, this.txId);
|
||||
}),
|
||||
catchError(() => {
|
||||
return of(null);
|
||||
})
|
||||
).subscribe((accelerationHistory) => {
|
||||
for (const acceleration of accelerationHistory) {
|
||||
if (acceleration.txid === this.txId && (acceleration.status === 'completed' || acceleration.status === 'completed_provisional') && acceleration.pools.includes(acceleration.minedByPoolUniqueId)) {
|
||||
const boostCost = acceleration.boostCost || acceleration.bidBoost;
|
||||
acceleration.acceleratedFeeRate = Math.max(acceleration.effectiveFee, acceleration.effectiveFee + boostCost) / acceleration.effectiveVsize;
|
||||
acceleration.boost = boostCost;
|
||||
|
||||
this.accelerationInfo = acceleration;
|
||||
this.setIsAccelerated();
|
||||
switchMap((blockHeight: number) => {
|
||||
if (this.stateService.network === '' && this.stateService.env.ACCELERATOR && blockHeight >= 819500 ) {
|
||||
return this.servicesApiService.getAccelerationDataForTxid$(this.txId).pipe(
|
||||
switchMap((accelerationData: Acceleration) => {
|
||||
if (this.tx.acceleration && !accelerationData) { // If the just mined transaction was accelerated, but services backend did not return any acceleration data, retry
|
||||
return throwError(() => 'retry');
|
||||
}
|
||||
return of(accelerationData);
|
||||
}),
|
||||
retry({
|
||||
count: 3,
|
||||
delay: (error) => {
|
||||
if (error === 'retry') {
|
||||
return timer(2000);
|
||||
}
|
||||
return throwError(() => error); // Don't retry, just rethrow
|
||||
}
|
||||
}),
|
||||
catchError(() => {
|
||||
return of(null);
|
||||
})
|
||||
);
|
||||
} else {
|
||||
return of(null);
|
||||
}
|
||||
}),
|
||||
filter((acceleration: Acceleration) => !!acceleration),
|
||||
).subscribe((acceleration: Acceleration) => {
|
||||
if (acceleration.txid === this.txId && (acceleration.status === 'completed' || acceleration.status === 'completed_provisional') && acceleration.pools.includes(acceleration.minedByPoolUniqueId)) {
|
||||
const boostCost = acceleration.boostCost || acceleration.bidBoost;
|
||||
acceleration.acceleratedFeeRate = Math.max(acceleration.effectiveFee, acceleration.effectiveFee + boostCost) / acceleration.effectiveVsize;
|
||||
acceleration.boost = boostCost;
|
||||
this.tx.acceleratedAt = acceleration.added;
|
||||
this.accelerationInfo = acceleration;
|
||||
}
|
||||
if (acceleration.txid === this.txId && (acceleration.status === 'failed' || acceleration.status === 'failed_provisional')) {
|
||||
this.tx.acceleratedAt = acceleration.added;
|
||||
this.accelerationInfo = acceleration;
|
||||
}
|
||||
this.setIsAccelerated();
|
||||
});
|
||||
|
||||
this.miningSubscription = this.fetchMiningInfo$.pipe(
|
||||
|
|
@ -476,7 +501,7 @@ export class TrackerComponent implements OnInit, OnDestroy {
|
|||
} else {
|
||||
this.trackerStage = 'confirmed';
|
||||
this.loadingPosition = false;
|
||||
this.fetchAcceleration$.next(tx.status.block_hash);
|
||||
this.fetchAcceleration$.next(tx.status.block_height);
|
||||
this.fetchMiningInfo$.next({ hash: tx.status.block_hash, height: tx.status.block_height, txid: tx.txid });
|
||||
this.transactionTime = 0;
|
||||
}
|
||||
|
|
@ -540,7 +565,7 @@ export class TrackerComponent implements OnInit, OnDestroy {
|
|||
} else {
|
||||
this.audioService.playSound('magic');
|
||||
}
|
||||
this.fetchAcceleration$.next(block.id);
|
||||
this.fetchAcceleration$.next(block.height);
|
||||
this.fetchMiningInfo$.next({ hash: block.id, height: block.height, txid: this.tx.txid });
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import {
|
|||
take
|
||||
} from 'rxjs/operators';
|
||||
import { Transaction } from '@interfaces/electrs.interface';
|
||||
import { of, merge, Subscription, Observable, Subject, from, throwError, combineLatest, BehaviorSubject } from 'rxjs';
|
||||
import { of, merge, Subscription, Observable, Subject, from, throwError, combineLatest, BehaviorSubject, timer } from 'rxjs';
|
||||
import { StateService } from '@app/services/state.service';
|
||||
import { CacheService } from '@app/services/cache.service';
|
||||
import { WebsocketService } from '@app/services/websocket.service';
|
||||
|
|
@ -343,38 +343,47 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
this.setIsAccelerated();
|
||||
}),
|
||||
switchMap((blockHeight: number) => {
|
||||
return this.servicesApiService.getAllAccelerationHistory$({ blockHeight }, null, this.txId).pipe(
|
||||
switchMap((accelerationHistory: Acceleration[]) => {
|
||||
if (this.tx.acceleration && !accelerationHistory.length) { // If the just mined transaction was accelerated, but services backend did not return any acceleration data, retry
|
||||
return throwError('retry');
|
||||
}
|
||||
return of(accelerationHistory);
|
||||
}),
|
||||
retry({ count: 3, delay: 2000 }),
|
||||
catchError(() => {
|
||||
return of([]);
|
||||
})
|
||||
);
|
||||
}),
|
||||
).subscribe((accelerationHistory) => {
|
||||
for (const acceleration of accelerationHistory) {
|
||||
if (acceleration.txid === this.txId) {
|
||||
if ((acceleration.status === 'completed' || acceleration.status === 'completed_provisional') && acceleration.pools.includes(acceleration.minedByPoolUniqueId)) {
|
||||
const boostCost = acceleration.boostCost || acceleration.bidBoost;
|
||||
acceleration.acceleratedFeeRate = Math.max(acceleration.effectiveFee, acceleration.effectiveFee + boostCost) / acceleration.effectiveVsize;
|
||||
acceleration.boost = boostCost;
|
||||
this.tx.acceleratedAt = acceleration.added;
|
||||
this.accelerationInfo = acceleration;
|
||||
}
|
||||
if (acceleration.status === 'failed' || acceleration.status === 'failed_provisional') {
|
||||
this.accelerationCanceled = true;
|
||||
this.tx.acceleratedAt = acceleration.added;
|
||||
this.accelerationInfo = acceleration;
|
||||
}
|
||||
this.waitingForAccelerationInfo = false;
|
||||
this.setIsAccelerated();
|
||||
if (this.stateService.network === '' && this.stateService.env.ACCELERATOR && blockHeight >= 819500 ) {
|
||||
return this.servicesApiService.getAccelerationDataForTxid$(this.txId).pipe(
|
||||
switchMap((accelerationData: Acceleration) => {
|
||||
if (this.tx.acceleration && !accelerationData) { // If the just mined transaction was accelerated, but services backend did not return any acceleration data, retry
|
||||
return throwError(() => 'retry');
|
||||
}
|
||||
return of(accelerationData);
|
||||
}),
|
||||
retry({
|
||||
count: 3,
|
||||
delay: (error) => {
|
||||
if (error === 'retry') {
|
||||
return timer(2000);
|
||||
}
|
||||
return throwError(() => error);
|
||||
}
|
||||
}),
|
||||
catchError(() => {
|
||||
return of(null);
|
||||
})
|
||||
);
|
||||
} else {
|
||||
return of(null);
|
||||
}
|
||||
}),
|
||||
filter((acceleration: Acceleration) => !!acceleration),
|
||||
).subscribe((acceleration: Acceleration) => {
|
||||
if (acceleration.txid === this.txId && (acceleration.status === 'completed' || acceleration.status === 'completed_provisional') && acceleration.pools.includes(acceleration.minedByPoolUniqueId)) {
|
||||
const boostCost = acceleration.boostCost || acceleration.bidBoost;
|
||||
acceleration.acceleratedFeeRate = Math.max(acceleration.effectiveFee, acceleration.effectiveFee + boostCost) / acceleration.effectiveVsize;
|
||||
acceleration.boost = boostCost;
|
||||
this.tx.acceleratedAt = acceleration.added;
|
||||
this.accelerationInfo = acceleration;
|
||||
}
|
||||
if (acceleration.txid === this.txId && (acceleration.status === 'failed' || acceleration.status === 'failed_provisional')) {
|
||||
this.accelerationCanceled = true;
|
||||
this.tx.acceleratedAt = acceleration.added;
|
||||
this.accelerationInfo = acceleration;
|
||||
}
|
||||
this.waitingForAccelerationInfo = false;
|
||||
this.setIsAccelerated();
|
||||
});
|
||||
|
||||
this.miningSubscription = this.fetchMiningInfo$.pipe(
|
||||
|
|
|
|||
|
|
@ -163,6 +163,10 @@ export class ServicesApiServices {
|
|||
return this.httpClient.get<Acceleration[]>(`${this.stateService.env.SERVICES_API}/accelerator/accelerations/history`, { params: { ...params } });
|
||||
}
|
||||
|
||||
getAccelerationDataForTxid$(txid: string) {
|
||||
return this.httpClient.get<Acceleration>(`${this.stateService.env.SERVICES_API}/accelerator/accelerations/${txid}`);
|
||||
}
|
||||
|
||||
getAllAccelerationHistory$(params: AccelerationHistoryParams, limit?: number, findTxid?: string): Observable<Acceleration[]> {
|
||||
const getPage$ = (page: number, accelerations: Acceleration[] = []): Observable<{ page: number, total: number, accelerations: Acceleration[] }> => {
|
||||
return this.getAccelerationHistoryObserveResponse$({...params, page}).pipe(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue