parent
8d18a143cb
commit
8637059119
71
backend/src/api/difficulty-adjustment.ts
Normal file
71
backend/src/api/difficulty-adjustment.ts
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
import config from '../config';
|
||||||
|
import { IDifficultyAdjustment } from '../mempool.interfaces';
|
||||||
|
import blocks from './blocks';
|
||||||
|
|
||||||
|
class DifficultyAdjustmentApi {
|
||||||
|
constructor() { }
|
||||||
|
|
||||||
|
public getDifficultyAdjustment(): IDifficultyAdjustment {
|
||||||
|
const DATime = blocks.getLastDifficultyAdjustmentTime();
|
||||||
|
const previousRetarget = blocks.getPreviousDifficultyRetarget();
|
||||||
|
const blockHeight = blocks.getCurrentBlockHeight();
|
||||||
|
const blocksCache = blocks.getBlocks();
|
||||||
|
const latestBlock = blocksCache[blocksCache.length - 1];
|
||||||
|
|
||||||
|
const now = new Date().getTime() / 1000;
|
||||||
|
const diff = now - DATime;
|
||||||
|
const blocksInEpoch = blockHeight % 2016;
|
||||||
|
const progressPercent = (blocksInEpoch >= 0) ? blocksInEpoch / 2016 * 100 : 100;
|
||||||
|
const remainingBlocks = 2016 - blocksInEpoch;
|
||||||
|
const nextRetargetHeight = blockHeight + remainingBlocks;
|
||||||
|
|
||||||
|
let difficultyChange = 0;
|
||||||
|
if (remainingBlocks < 1870) {
|
||||||
|
if (blocksInEpoch > 0) {
|
||||||
|
difficultyChange = (600 / (diff / blocksInEpoch ) - 1) * 100;
|
||||||
|
}
|
||||||
|
if (difficultyChange > 300) {
|
||||||
|
difficultyChange = 300;
|
||||||
|
}
|
||||||
|
if (difficultyChange < -75) {
|
||||||
|
difficultyChange = -75;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeAvgDiff = difficultyChange * 0.1;
|
||||||
|
|
||||||
|
let timeAvgMins = 10;
|
||||||
|
|
||||||
|
if (timeAvgDiff > 0) {
|
||||||
|
timeAvgMins -= Math.abs(timeAvgDiff);
|
||||||
|
} else {
|
||||||
|
timeAvgMins += Math.abs(timeAvgDiff);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Testnet difficulty is set to 1 after 20 minutes of no blockSize,
|
||||||
|
// therefore the time between blocks will always be below 20 minutes (1200s).
|
||||||
|
let timeOffset = 0;
|
||||||
|
if (config.MEMPOOL.NETWORK === 'testnet' && now - latestBlock.timestamp + timeAvgMins * 60 > 1200) {
|
||||||
|
timeOffset = -Math.min(now - latestBlock.timestamp, 1200) * 1000;
|
||||||
|
timeAvgMins = 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeAvg = timeAvgMins * 60 * 1000 ;
|
||||||
|
const remainingTime = (remainingBlocks * timeAvg) + (now * 1000);
|
||||||
|
const estimatedRetargetDate = remainingTime + now;
|
||||||
|
|
||||||
|
return {
|
||||||
|
progressPercent,
|
||||||
|
difficultyChange,
|
||||||
|
estimatedRetargetDate,
|
||||||
|
remainingBlocks,
|
||||||
|
remainingTime,
|
||||||
|
previousRetarget,
|
||||||
|
nextRetargetHeight,
|
||||||
|
timeAvg,
|
||||||
|
timeOffset,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new DifficultyAdjustmentApi();
|
@ -12,6 +12,7 @@ import loadingIndicators from './loading-indicators';
|
|||||||
import config from '../config';
|
import config from '../config';
|
||||||
import transactionUtils from './transaction-utils';
|
import transactionUtils from './transaction-utils';
|
||||||
import rbfCache from './rbf-cache';
|
import rbfCache from './rbf-cache';
|
||||||
|
import difficultyAdjustment from './difficulty-adjustment';
|
||||||
|
|
||||||
class WebsocketHandler {
|
class WebsocketHandler {
|
||||||
private wss: WebSocket.Server | undefined;
|
private wss: WebSocket.Server | undefined;
|
||||||
@ -191,14 +192,13 @@ class WebsocketHandler {
|
|||||||
return {
|
return {
|
||||||
'mempoolInfo': memPool.getMempoolInfo(),
|
'mempoolInfo': memPool.getMempoolInfo(),
|
||||||
'vBytesPerSecond': memPool.getVBytesPerSecond(),
|
'vBytesPerSecond': memPool.getVBytesPerSecond(),
|
||||||
'lastDifficultyAdjustment': blocks.getLastDifficultyAdjustmentTime(),
|
|
||||||
'previousRetarget': blocks.getPreviousDifficultyRetarget(),
|
|
||||||
'blocks': _blocks,
|
'blocks': _blocks,
|
||||||
'conversions': fiatConversion.getConversionRates(),
|
'conversions': fiatConversion.getConversionRates(),
|
||||||
'mempool-blocks': mempoolBlocks.getMempoolBlocks(),
|
'mempool-blocks': mempoolBlocks.getMempoolBlocks(),
|
||||||
'transactions': memPool.getLatestTransactions(),
|
'transactions': memPool.getLatestTransactions(),
|
||||||
'backendInfo': backendInfo.getBackendInfo(),
|
'backendInfo': backendInfo.getBackendInfo(),
|
||||||
'loadingIndicators': loadingIndicators.getLoadingIndicators(),
|
'loadingIndicators': loadingIndicators.getLoadingIndicators(),
|
||||||
|
'da': difficultyAdjustment.getDifficultyAdjustment(),
|
||||||
...this.extraInitProperties
|
...this.extraInitProperties
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -234,6 +234,7 @@ class WebsocketHandler {
|
|||||||
const mempoolInfo = memPool.getMempoolInfo();
|
const mempoolInfo = memPool.getMempoolInfo();
|
||||||
const vBytesPerSecond = memPool.getVBytesPerSecond();
|
const vBytesPerSecond = memPool.getVBytesPerSecond();
|
||||||
const rbfTransactions = Common.findRbfTransactions(newTransactions, deletedTransactions);
|
const rbfTransactions = Common.findRbfTransactions(newTransactions, deletedTransactions);
|
||||||
|
const da = difficultyAdjustment.getDifficultyAdjustment();
|
||||||
memPool.handleRbfTransactions(rbfTransactions);
|
memPool.handleRbfTransactions(rbfTransactions);
|
||||||
|
|
||||||
this.wss.clients.forEach(async (client: WebSocket) => {
|
this.wss.clients.forEach(async (client: WebSocket) => {
|
||||||
@ -247,6 +248,7 @@ class WebsocketHandler {
|
|||||||
response['mempoolInfo'] = mempoolInfo;
|
response['mempoolInfo'] = mempoolInfo;
|
||||||
response['vBytesPerSecond'] = vBytesPerSecond;
|
response['vBytesPerSecond'] = vBytesPerSecond;
|
||||||
response['transactions'] = newTransactions.slice(0, 6).map((tx) => Common.stripTransaction(tx));
|
response['transactions'] = newTransactions.slice(0, 6).map((tx) => Common.stripTransaction(tx));
|
||||||
|
response['da'] = da;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (client['want-mempool-blocks']) {
|
if (client['want-mempool-blocks']) {
|
||||||
@ -410,8 +412,7 @@ class WebsocketHandler {
|
|||||||
const response = {
|
const response = {
|
||||||
'block': block,
|
'block': block,
|
||||||
'mempoolInfo': memPool.getMempoolInfo(),
|
'mempoolInfo': memPool.getMempoolInfo(),
|
||||||
'lastDifficultyAdjustment': blocks.getLastDifficultyAdjustmentTime(),
|
'da': difficultyAdjustment.getDifficultyAdjustment(),
|
||||||
'previousRetarget': blocks.getPreviousDifficultyRetarget(),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (mBlocks && client['want-mempool-blocks']) {
|
if (mBlocks && client['want-mempool-blocks']) {
|
||||||
|
@ -197,3 +197,15 @@ export interface IBackendInfo {
|
|||||||
gitCommit: string;
|
gitCommit: string;
|
||||||
version: string;
|
version: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IDifficultyAdjustment {
|
||||||
|
progressPercent: number;
|
||||||
|
difficultyChange: number;
|
||||||
|
estimatedRetargetDate: number;
|
||||||
|
remainingBlocks: number;
|
||||||
|
remainingTime: number;
|
||||||
|
previousRetarget: number;
|
||||||
|
nextRetargetHeight: number;
|
||||||
|
timeAvg: number;
|
||||||
|
timeOffset: number;
|
||||||
|
}
|
||||||
|
@ -25,6 +25,7 @@ import axios from 'axios';
|
|||||||
import mining from './api/mining';
|
import mining from './api/mining';
|
||||||
import BlocksRepository from './repositories/BlocksRepository';
|
import BlocksRepository from './repositories/BlocksRepository';
|
||||||
import HashratesRepository from './repositories/HashratesRepository';
|
import HashratesRepository from './repositories/HashratesRepository';
|
||||||
|
import difficultyAdjustment from './api/difficulty-adjustment';
|
||||||
|
|
||||||
class Routes {
|
class Routes {
|
||||||
constructor() {}
|
constructor() {}
|
||||||
@ -847,55 +848,7 @@ class Routes {
|
|||||||
|
|
||||||
public getDifficultyChange(req: Request, res: Response) {
|
public getDifficultyChange(req: Request, res: Response) {
|
||||||
try {
|
try {
|
||||||
const DATime = blocks.getLastDifficultyAdjustmentTime();
|
res.json(difficultyAdjustment.getDifficultyAdjustment());
|
||||||
const previousRetarget = blocks.getPreviousDifficultyRetarget();
|
|
||||||
const blockHeight = blocks.getCurrentBlockHeight();
|
|
||||||
|
|
||||||
const now = new Date().getTime() / 1000;
|
|
||||||
const diff = now - DATime;
|
|
||||||
const blocksInEpoch = blockHeight % 2016;
|
|
||||||
const progressPercent = (blocksInEpoch >= 0) ? blocksInEpoch / 2016 * 100 : 100;
|
|
||||||
const remainingBlocks = 2016 - blocksInEpoch;
|
|
||||||
const nextRetargetHeight = blockHeight + remainingBlocks;
|
|
||||||
|
|
||||||
let difficultyChange = 0;
|
|
||||||
if (remainingBlocks < 1870) {
|
|
||||||
if (blocksInEpoch > 0) {
|
|
||||||
difficultyChange = (600 / (diff / blocksInEpoch ) - 1) * 100;
|
|
||||||
}
|
|
||||||
if (difficultyChange > 300) {
|
|
||||||
difficultyChange = 300;
|
|
||||||
}
|
|
||||||
if (difficultyChange < -75) {
|
|
||||||
difficultyChange = -75;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const timeAvgDiff = difficultyChange * 0.1;
|
|
||||||
|
|
||||||
let timeAvgMins = 10;
|
|
||||||
if (timeAvgDiff > 0) {
|
|
||||||
timeAvgMins -= Math.abs(timeAvgDiff);
|
|
||||||
} else {
|
|
||||||
timeAvgMins += Math.abs(timeAvgDiff);
|
|
||||||
}
|
|
||||||
|
|
||||||
const timeAvg = timeAvgMins * 60;
|
|
||||||
const remainingTime = remainingBlocks * timeAvg;
|
|
||||||
const estimatedRetargetDate = remainingTime + now;
|
|
||||||
|
|
||||||
const result = {
|
|
||||||
progressPercent,
|
|
||||||
difficultyChange,
|
|
||||||
estimatedRetargetDate,
|
|
||||||
remainingBlocks,
|
|
||||||
remainingTime,
|
|
||||||
previousRetarget,
|
|
||||||
nextRetargetHeight,
|
|
||||||
timeAvg,
|
|
||||||
};
|
|
||||||
res.json(result);
|
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
res.status(500).send(e instanceof Error ? e.message : e);
|
res.status(500).send(e instanceof Error ? e.message : e);
|
||||||
}
|
}
|
||||||
|
@ -6,12 +6,11 @@ import { StateService } from '../..//services/state.service';
|
|||||||
interface EpochProgress {
|
interface EpochProgress {
|
||||||
base: string;
|
base: string;
|
||||||
change: number;
|
change: number;
|
||||||
progress: string;
|
progress: number;
|
||||||
remainingBlocks: number;
|
remainingBlocks: number;
|
||||||
newDifficultyHeight: number;
|
newDifficultyHeight: number;
|
||||||
colorAdjustments: string;
|
colorAdjustments: string;
|
||||||
colorPreviousAdjustments: string;
|
colorPreviousAdjustments: string;
|
||||||
timeAvg: string;
|
|
||||||
remainingTime: number;
|
remainingTime: number;
|
||||||
previousRetarget: number;
|
previousRetarget: number;
|
||||||
blocksUntilHalving: number;
|
blocksUntilHalving: number;
|
||||||
@ -38,85 +37,52 @@ export class DifficultyComponent implements OnInit {
|
|||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.isLoadingWebSocket$ = this.stateService.isLoadingWebSocket$;
|
this.isLoadingWebSocket$ = this.stateService.isLoadingWebSocket$;
|
||||||
this.difficultyEpoch$ = timer(0, 1000)
|
this.difficultyEpoch$ = combineLatest([
|
||||||
.pipe(
|
this.stateService.blocks$.pipe(map(([block]) => block)),
|
||||||
switchMap(() => combineLatest([
|
this.stateService.difficultyAdjustment$,
|
||||||
this.stateService.blocks$.pipe(map(([block]) => block)),
|
])
|
||||||
this.stateService.lastDifficultyAdjustment$,
|
.pipe(
|
||||||
this.stateService.previousRetarget$
|
map(([block, da]) => {
|
||||||
])),
|
let colorAdjustments = '#ffffff66';
|
||||||
map(([block, DATime, previousRetarget]) => {
|
if (da.difficultyChange > 0) {
|
||||||
const now = new Date().getTime() / 1000;
|
colorAdjustments = '#3bcc49';
|
||||||
const diff = now - DATime;
|
}
|
||||||
const blocksInEpoch = block.height % 2016;
|
if (da.difficultyChange < 0) {
|
||||||
const progress = (blocksInEpoch >= 0) ? (blocksInEpoch / 2016 * 100).toFixed(2) : `100`;
|
colorAdjustments = '#dc3545';
|
||||||
const remainingBlocks = 2016 - blocksInEpoch;
|
}
|
||||||
const newDifficultyHeight = block.height + remainingBlocks;
|
|
||||||
|
|
||||||
let change = 0;
|
let colorPreviousAdjustments = '#dc3545';
|
||||||
if (remainingBlocks < 1870) {
|
if (da.previousRetarget) {
|
||||||
if (blocksInEpoch > 0) {
|
if (da.previousRetarget >= 0) {
|
||||||
change = (600 / (diff / blocksInEpoch ) - 1) * 100;
|
colorPreviousAdjustments = '#3bcc49';
|
||||||
}
|
|
||||||
if (change > 300) {
|
|
||||||
change = 300;
|
|
||||||
}
|
|
||||||
if (change < -75) {
|
|
||||||
change = -75;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (da.previousRetarget === 0) {
|
||||||
const timeAvgDiff = change * 0.1;
|
|
||||||
|
|
||||||
let timeAvgMins = 10;
|
|
||||||
if (timeAvgDiff > 0) {
|
|
||||||
timeAvgMins -= Math.abs(timeAvgDiff);
|
|
||||||
} else {
|
|
||||||
timeAvgMins += Math.abs(timeAvgDiff);
|
|
||||||
}
|
|
||||||
|
|
||||||
const timeAvg = timeAvgMins.toFixed(0);
|
|
||||||
const remainingTime = (remainingBlocks * timeAvgMins * 60 * 1000) + (now * 1000);
|
|
||||||
|
|
||||||
let colorAdjustments = '#ffffff66';
|
|
||||||
if (change > 0) {
|
|
||||||
colorAdjustments = '#3bcc49';
|
|
||||||
}
|
|
||||||
if (change < 0) {
|
|
||||||
colorAdjustments = '#dc3545';
|
|
||||||
}
|
|
||||||
|
|
||||||
let colorPreviousAdjustments = '#dc3545';
|
|
||||||
if (previousRetarget) {
|
|
||||||
if (previousRetarget >= 0) {
|
|
||||||
colorPreviousAdjustments = '#3bcc49';
|
|
||||||
}
|
|
||||||
if (previousRetarget === 0) {
|
|
||||||
colorPreviousAdjustments = '#ffffff66';
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
colorPreviousAdjustments = '#ffffff66';
|
colorPreviousAdjustments = '#ffffff66';
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
colorPreviousAdjustments = '#ffffff66';
|
||||||
|
}
|
||||||
|
|
||||||
const blocksUntilHalving = 210000 - (block.height % 210000);
|
const timeAvgMins = da.timeAvg;
|
||||||
const timeUntilHalving = (blocksUntilHalving * timeAvgMins * 60 * 1000) + (now * 1000);
|
const now = new Date().getTime() / 1000;
|
||||||
|
const blocksUntilHalving = 210000 - (block.height % 210000);
|
||||||
|
const timeUntilHalving = (blocksUntilHalving * timeAvgMins * 60 * 1000) + (now * 1000);
|
||||||
|
|
||||||
return {
|
const data = {
|
||||||
base: `${progress}%`,
|
base: `${da.progressPercent.toFixed(2)}%`,
|
||||||
change,
|
change: da.difficultyChange,
|
||||||
progress,
|
progress: da.progressPercent,
|
||||||
remainingBlocks,
|
remainingBlocks: da.remainingBlocks,
|
||||||
timeAvg,
|
colorAdjustments,
|
||||||
colorAdjustments,
|
colorPreviousAdjustments,
|
||||||
colorPreviousAdjustments,
|
newDifficultyHeight: da.nextRetargetHeight,
|
||||||
blocksInEpoch,
|
remainingTime: da.remainingTime,
|
||||||
newDifficultyHeight,
|
previousRetarget: da.previousRetarget,
|
||||||
remainingTime,
|
blocksUntilHalving,
|
||||||
previousRetarget,
|
timeUntilHalving,
|
||||||
blocksUntilHalving,
|
};
|
||||||
timeUntilHalving,
|
return data;
|
||||||
};
|
})
|
||||||
})
|
);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<ng-container *ngIf="(loadingBlocks$ | async) === false; else loadingBlocks">
|
<ng-container *ngIf="(loadingBlocks$ | async) === false; else loadingBlocks">
|
||||||
<div class="mempool-blocks-container" *ngIf="(timeAvg$ | async) as timeAvg;">
|
<div class="mempool-blocks-container" *ngIf="(difficultyAdjustments$ | async) as da;">
|
||||||
<div class="flashing">
|
<div class="flashing">
|
||||||
<ng-template ngFor let-projectedBlock [ngForOf]="mempoolBlocks$ | async" let-i="index" [ngForTrackBy]="trackByFn">
|
<ng-template ngFor let-projectedBlock [ngForOf]="mempoolBlocks$ | async" let-i="index" [ngForTrackBy]="trackByFn">
|
||||||
<div class="bitcoin-block text-center mempool-block" id="mempool-block-{{ i }}" [ngStyle]="mempoolBlockStyles[i]" [class.blink-bg]="projectedBlock.blink">
|
<div class="bitcoin-block text-center mempool-block" id="mempool-block-{{ i }}" [ngStyle]="mempoolBlockStyles[i]" [class.blink-bg]="projectedBlock.blink">
|
||||||
@ -26,7 +26,7 @@
|
|||||||
<app-time-until [time]="(1 * i) + now + 61000" [fastRender]="false" [fixedRender]="true"></app-time-until>
|
<app-time-until [time]="(1 * i) + now + 61000" [fastRender]="false" [fixedRender]="true"></app-time-until>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
<ng-template #timeDiffMainnet>
|
<ng-template #timeDiffMainnet>
|
||||||
<app-time-until [time]="(timeAvg * i) + now + timeAvg + timeOffset" [fastRender]="false" [fixedRender]="true" [forceFloorOnTimeIntervals]="['hour']"></app-time-until>
|
<app-time-until [time]="da.timeAvg * (i + 1) + now + da.timeOffset" [fastRender]="false" [fixedRender]="true" [forceFloorOnTimeIntervals]="['hour']"></app-time-until>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</div>
|
</div>
|
||||||
<ng-template #mergedBlock>
|
<ng-template #mergedBlock>
|
||||||
|
@ -8,6 +8,7 @@ import { feeLevels, mempoolFeeColors } from 'src/app/app.constants';
|
|||||||
import { specialBlocks } from 'src/app/app.constants';
|
import { specialBlocks } from 'src/app/app.constants';
|
||||||
import { RelativeUrlPipe } from 'src/app/shared/pipes/relative-url/relative-url.pipe';
|
import { RelativeUrlPipe } from 'src/app/shared/pipes/relative-url/relative-url.pipe';
|
||||||
import { Location } from '@angular/common';
|
import { Location } from '@angular/common';
|
||||||
|
import { DifficultyAdjustment } from 'src/app/interfaces/node-api.interface';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-mempool-blocks',
|
selector: 'app-mempool-blocks',
|
||||||
@ -20,7 +21,7 @@ export class MempoolBlocksComponent implements OnInit, OnDestroy {
|
|||||||
mempoolBlocks: MempoolBlock[] = [];
|
mempoolBlocks: MempoolBlock[] = [];
|
||||||
mempoolEmptyBlocks: MempoolBlock[] = this.mountEmptyBlocks();
|
mempoolEmptyBlocks: MempoolBlock[] = this.mountEmptyBlocks();
|
||||||
mempoolBlocks$: Observable<MempoolBlock[]>;
|
mempoolBlocks$: Observable<MempoolBlock[]>;
|
||||||
timeAvg$: Observable<number>;
|
difficultyAdjustments$: Observable<DifficultyAdjustment>;
|
||||||
loadingBlocks$: Observable<boolean>;
|
loadingBlocks$: Observable<boolean>;
|
||||||
blocksSubscription: Subscription;
|
blocksSubscription: Subscription;
|
||||||
|
|
||||||
@ -123,40 +124,11 @@ export class MempoolBlocksComponent implements OnInit, OnDestroy {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
this.timeAvg$ = timer(0, 1000)
|
this.difficultyAdjustments$ = this.stateService.difficultyAdjustment$
|
||||||
.pipe(
|
.pipe(
|
||||||
switchMap(() => combineLatest([
|
map((da) => {
|
||||||
this.stateService.blocks$.pipe(map(([block]) => block)),
|
|
||||||
this.stateService.lastDifficultyAdjustment$
|
|
||||||
])),
|
|
||||||
map(([block, DATime]) => {
|
|
||||||
this.now = new Date().getTime();
|
this.now = new Date().getTime();
|
||||||
const now = new Date().getTime() / 1000;
|
return da;
|
||||||
const diff = now - DATime;
|
|
||||||
const blocksInEpoch = block.height % 2016;
|
|
||||||
let difficultyChange = 0;
|
|
||||||
if (blocksInEpoch > 0) {
|
|
||||||
difficultyChange = (600 / (diff / blocksInEpoch ) - 1) * 100;
|
|
||||||
}
|
|
||||||
const timeAvgDiff = difficultyChange * 0.1;
|
|
||||||
|
|
||||||
let timeAvgMins = 10;
|
|
||||||
if (timeAvgDiff > 0 ){
|
|
||||||
timeAvgMins -= Math.abs(timeAvgDiff);
|
|
||||||
} else {
|
|
||||||
timeAvgMins += Math.abs(timeAvgDiff);
|
|
||||||
}
|
|
||||||
|
|
||||||
// testnet difficulty is set to 1 after 20 minutes of no blockSize
|
|
||||||
// therefore the time between blocks will always be below 20 minutes (1200s)
|
|
||||||
if (this.stateService.network === 'testnet' && now - block.timestamp + timeAvgMins * 60 > 1200) {
|
|
||||||
this.timeOffset = -Math.min(now - block.timestamp, 1200) * 1000;
|
|
||||||
timeAvgMins = 20;
|
|
||||||
} else {
|
|
||||||
this.timeOffset = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return timeAvgMins * 60 * 1000;
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -66,29 +66,8 @@ export class TransactionComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
this.timeAvg$ = timer(0, 1000)
|
this.timeAvg$ = timer(0, 1000)
|
||||||
.pipe(
|
.pipe(
|
||||||
switchMap(() => combineLatest([
|
switchMap(() => this.stateService.difficultyAdjustment$),
|
||||||
this.stateService.blocks$.pipe(map(([block]) => block)),
|
map((da) => da.timeAvg)
|
||||||
this.stateService.lastDifficultyAdjustment$
|
|
||||||
])),
|
|
||||||
map(([block, DATime]) => {
|
|
||||||
this.now = new Date().getTime();
|
|
||||||
const now = new Date().getTime() / 1000;
|
|
||||||
const diff = now - DATime;
|
|
||||||
const blocksInEpoch = block.height % 2016;
|
|
||||||
let difficultyChange = 0;
|
|
||||||
if (blocksInEpoch > 0) {
|
|
||||||
difficultyChange = (600 / (diff / blocksInEpoch ) - 1) * 100;
|
|
||||||
}
|
|
||||||
const timeAvgDiff = difficultyChange * 0.1;
|
|
||||||
|
|
||||||
let timeAvgMins = 10;
|
|
||||||
if (timeAvgDiff > 0 ){
|
|
||||||
timeAvgMins -= Math.abs(timeAvgDiff);
|
|
||||||
} else {
|
|
||||||
timeAvgMins += Math.abs(timeAvgDiff);
|
|
||||||
}
|
|
||||||
return timeAvgMins * 60 * 1000;
|
|
||||||
})
|
|
||||||
);
|
);
|
||||||
|
|
||||||
this.fetchCpfpSubscription = this.fetchCpfp$
|
this.fetchCpfpSubscription = this.fetchCpfp$
|
||||||
|
@ -26,12 +26,15 @@ export interface CpfpInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface DifficultyAdjustment {
|
export interface DifficultyAdjustment {
|
||||||
|
progressPercent: number;
|
||||||
difficultyChange: number;
|
difficultyChange: number;
|
||||||
estimatedRetargetDate: number;
|
estimatedRetargetDate: number;
|
||||||
previousRetarget: number;
|
|
||||||
progressPercent: number;
|
|
||||||
remainingBlocks: number;
|
remainingBlocks: number;
|
||||||
remainingTime: number;
|
remainingTime: number;
|
||||||
|
previousRetarget: number;
|
||||||
|
nextRetargetHeight: number;
|
||||||
|
timeAvg: number;
|
||||||
|
timeOffset: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AddressInformation {
|
export interface AddressInformation {
|
||||||
@ -111,4 +114,3 @@ export interface BlockExtension {
|
|||||||
export interface BlockExtended extends Block {
|
export interface BlockExtended extends Block {
|
||||||
extras?: BlockExtension;
|
extras?: BlockExtension;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { ILoadingIndicators } from '../services/state.service';
|
import { ILoadingIndicators } from '../services/state.service';
|
||||||
import { Transaction } from './electrs.interface';
|
import { Transaction } from './electrs.interface';
|
||||||
import { BlockExtended } from './node-api.interface';
|
import { BlockExtended, DifficultyAdjustment } from './node-api.interface';
|
||||||
|
|
||||||
export interface WebsocketResponse {
|
export interface WebsocketResponse {
|
||||||
block?: BlockExtended;
|
block?: BlockExtended;
|
||||||
@ -10,7 +10,6 @@ export interface WebsocketResponse {
|
|||||||
historicalDate?: string;
|
historicalDate?: string;
|
||||||
mempoolInfo?: MempoolInfo;
|
mempoolInfo?: MempoolInfo;
|
||||||
vBytesPerSecond?: number;
|
vBytesPerSecond?: number;
|
||||||
lastDifficultyAdjustment?: number;
|
|
||||||
previousRetarget?: number;
|
previousRetarget?: number;
|
||||||
action?: string;
|
action?: string;
|
||||||
data?: string[];
|
data?: string[];
|
||||||
@ -21,6 +20,7 @@ export interface WebsocketResponse {
|
|||||||
transactions?: TransactionStripped[];
|
transactions?: TransactionStripped[];
|
||||||
loadingIndicators?: ILoadingIndicators;
|
loadingIndicators?: ILoadingIndicators;
|
||||||
backendInfo?: IBackendInfo;
|
backendInfo?: IBackendInfo;
|
||||||
|
da?: DifficultyAdjustment;
|
||||||
'track-tx'?: string;
|
'track-tx'?: string;
|
||||||
'track-address'?: string;
|
'track-address'?: string;
|
||||||
'track-asset'?: string;
|
'track-asset'?: string;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { HttpClient, HttpParams } from '@angular/common/http';
|
import { HttpClient, HttpParams } from '@angular/common/http';
|
||||||
import { CpfpInfo, OptimizedMempoolStats, DifficultyAdjustment, AddressInformation, LiquidPegs, ITranslators, PoolsStats, PoolStat, BlockExtended, BlockExtension } from '../interfaces/node-api.interface';
|
import { CpfpInfo, OptimizedMempoolStats, AddressInformation, LiquidPegs, ITranslators, PoolsStats, PoolStat, BlockExtended } from '../interfaces/node-api.interface';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { StateService } from './state.service';
|
import { StateService } from './state.service';
|
||||||
import { WebsocketResponse } from '../interfaces/websocket.interface';
|
import { WebsocketResponse } from '../interfaces/websocket.interface';
|
||||||
@ -105,10 +105,6 @@ export class ApiService {
|
|||||||
return this.httpClient.get<CpfpInfo>(this.apiBaseUrl + this.apiBasePath + '/api/v1/cpfp/' + txid);
|
return this.httpClient.get<CpfpInfo>(this.apiBaseUrl + this.apiBasePath + '/api/v1/cpfp/' + txid);
|
||||||
}
|
}
|
||||||
|
|
||||||
getDifficultyAdjustment$(): Observable<DifficultyAdjustment> {
|
|
||||||
return this.httpClient.get<DifficultyAdjustment>(this.apiBaseUrl + this.apiBasePath + '/api/v1/difficulty-adjustment');
|
|
||||||
}
|
|
||||||
|
|
||||||
validateAddress$(address: string): Observable<AddressInformation> {
|
validateAddress$(address: string): Observable<AddressInformation> {
|
||||||
return this.httpClient.get<AddressInformation>(this.apiBaseUrl + this.apiBasePath + '/api/v1/validate-address/' + address);
|
return this.httpClient.get<AddressInformation>(this.apiBaseUrl + this.apiBasePath + '/api/v1/validate-address/' + address);
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ import { Inject, Injectable, PLATFORM_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, MempoolInfo, ReplacedTransaction, TransactionStripped } from '../interfaces/websocket.interface';
|
import { IBackendInfo, MempoolBlock, MempoolInfo, ReplacedTransaction, TransactionStripped } from '../interfaces/websocket.interface';
|
||||||
import { BlockExtended, OptimizedMempoolStats } from '../interfaces/node-api.interface';
|
import { BlockExtended, DifficultyAdjustment, OptimizedMempoolStats } 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';
|
||||||
@ -82,11 +82,11 @@ export class StateService {
|
|||||||
mempoolBlocks$ = new ReplaySubject<MempoolBlock[]>(1);
|
mempoolBlocks$ = new ReplaySubject<MempoolBlock[]>(1);
|
||||||
txReplaced$ = new Subject<ReplacedTransaction>();
|
txReplaced$ = new Subject<ReplacedTransaction>();
|
||||||
utxoSpent$ = new Subject<object>();
|
utxoSpent$ = new Subject<object>();
|
||||||
|
difficultyAdjustment$ = new ReplaySubject<DifficultyAdjustment>(1);
|
||||||
mempoolTransactions$ = new Subject<Transaction>();
|
mempoolTransactions$ = new Subject<Transaction>();
|
||||||
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);
|
||||||
lastDifficultyAdjustment$ = new ReplaySubject<number>(1);
|
|
||||||
previousRetarget$ = new ReplaySubject<number>(1);
|
previousRetarget$ = new ReplaySubject<number>(1);
|
||||||
backendInfo$ = new ReplaySubject<IBackendInfo>(1);
|
backendInfo$ = new ReplaySubject<IBackendInfo>(1);
|
||||||
loadingIndicators$ = new ReplaySubject<ILoadingIndicators>(1);
|
loadingIndicators$ = new ReplaySubject<ILoadingIndicators>(1);
|
||||||
|
@ -259,6 +259,10 @@ export class WebsocketService {
|
|||||||
this.stateService.utxoSpent$.next(response.utxoSpent);
|
this.stateService.utxoSpent$.next(response.utxoSpent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (response.da) {
|
||||||
|
this.stateService.difficultyAdjustment$.next(response.da);
|
||||||
|
}
|
||||||
|
|
||||||
if (response.backendInfo) {
|
if (response.backendInfo) {
|
||||||
this.stateService.backendInfo$.next(response.backendInfo);
|
this.stateService.backendInfo$.next(response.backendInfo);
|
||||||
|
|
||||||
@ -301,10 +305,6 @@ export class WebsocketService {
|
|||||||
this.stateService.vbytesPerSecond$.next(response.vBytesPerSecond);
|
this.stateService.vbytesPerSecond$.next(response.vBytesPerSecond);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.lastDifficultyAdjustment !== undefined) {
|
|
||||||
this.stateService.lastDifficultyAdjustment$.next(response.lastDifficultyAdjustment);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (response.previousRetarget !== undefined) {
|
if (response.previousRetarget !== undefined) {
|
||||||
this.stateService.previousRetarget$.next(response.previousRetarget);
|
this.stateService.previousRetarget$.next(response.previousRetarget);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user