Skip to content

Commit dc13592

Browse files
committed
Difficulty adjustment refactor
fixes #1157
1 parent 0dbee14 commit dc13592

13 files changed

+155
-203
lines changed
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import config from '../config';
2+
import { IDifficultyAdjustment } from '../mempool.interfaces';
3+
import blocks from './blocks';
4+
5+
class DifficultyAdjustmentApi {
6+
constructor() { }
7+
8+
public getDifficultyAdjustment(): IDifficultyAdjustment {
9+
const DATime = blocks.getLastDifficultyAdjustmentTime();
10+
const previousRetarget = blocks.getPreviousDifficultyRetarget();
11+
const blockHeight = blocks.getCurrentBlockHeight();
12+
const blocksCache = blocks.getBlocks();
13+
const latestBlock = blocksCache[blocksCache.length - 1];
14+
15+
const now = new Date().getTime() / 1000;
16+
const diff = now - DATime;
17+
const blocksInEpoch = blockHeight % 2016;
18+
const progressPercent = (blocksInEpoch >= 0) ? blocksInEpoch / 2016 * 100 : 100;
19+
const remainingBlocks = 2016 - blocksInEpoch;
20+
const nextRetargetHeight = blockHeight + remainingBlocks;
21+
22+
let difficultyChange = 0;
23+
if (remainingBlocks < 1870) {
24+
if (blocksInEpoch > 0) {
25+
difficultyChange = (600 / (diff / blocksInEpoch ) - 1) * 100;
26+
}
27+
if (difficultyChange > 300) {
28+
difficultyChange = 300;
29+
}
30+
if (difficultyChange < -75) {
31+
difficultyChange = -75;
32+
}
33+
}
34+
35+
const timeAvgDiff = difficultyChange * 0.1;
36+
37+
let timeAvgMins = 10;
38+
39+
if (timeAvgDiff > 0) {
40+
timeAvgMins -= Math.abs(timeAvgDiff);
41+
} else {
42+
timeAvgMins += Math.abs(timeAvgDiff);
43+
}
44+
45+
// Testnet difficulty is set to 1 after 20 minutes of no blockSize,
46+
// therefore the time between blocks will always be below 20 minutes (1200s).
47+
let timeOffset = 0;
48+
if (config.MEMPOOL.NETWORK === 'testnet' && now - latestBlock.timestamp + timeAvgMins * 60 > 1200) {
49+
timeOffset = -Math.min(now - latestBlock.timestamp, 1200) * 1000;
50+
timeAvgMins = 20;
51+
}
52+
53+
const timeAvg = timeAvgMins * 60 * 1000 ;
54+
const remainingTime = (remainingBlocks * timeAvg) + (now * 1000);
55+
const estimatedRetargetDate = remainingTime + now;
56+
57+
return {
58+
progressPercent,
59+
difficultyChange,
60+
estimatedRetargetDate,
61+
remainingBlocks,
62+
remainingTime,
63+
previousRetarget,
64+
nextRetargetHeight,
65+
timeAvg,
66+
timeOffset,
67+
};
68+
}
69+
}
70+
71+
export default new DifficultyAdjustmentApi();

backend/src/api/websocket-handler.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import loadingIndicators from './loading-indicators';
1212
import config from '../config';
1313
import transactionUtils from './transaction-utils';
1414
import rbfCache from './rbf-cache';
15+
import difficultyAdjustment from './difficulty-adjustment';
1516

1617
class WebsocketHandler {
1718
private wss: WebSocket.Server | undefined;
@@ -191,14 +192,13 @@ class WebsocketHandler {
191192
return {
192193
'mempoolInfo': memPool.getMempoolInfo(),
193194
'vBytesPerSecond': memPool.getVBytesPerSecond(),
194-
'lastDifficultyAdjustment': blocks.getLastDifficultyAdjustmentTime(),
195-
'previousRetarget': blocks.getPreviousDifficultyRetarget(),
196195
'blocks': _blocks,
197196
'conversions': fiatConversion.getConversionRates(),
198197
'mempool-blocks': mempoolBlocks.getMempoolBlocks(),
199198
'transactions': memPool.getLatestTransactions(),
200199
'backendInfo': backendInfo.getBackendInfo(),
201200
'loadingIndicators': loadingIndicators.getLoadingIndicators(),
201+
'da': difficultyAdjustment.getDifficultyAdjustment(),
202202
...this.extraInitProperties
203203
};
204204
}
@@ -234,6 +234,7 @@ class WebsocketHandler {
234234
const mempoolInfo = memPool.getMempoolInfo();
235235
const vBytesPerSecond = memPool.getVBytesPerSecond();
236236
const rbfTransactions = Common.findRbfTransactions(newTransactions, deletedTransactions);
237+
const da = difficultyAdjustment.getDifficultyAdjustment();
237238
memPool.handleRbfTransactions(rbfTransactions);
238239

239240
this.wss.clients.forEach(async (client: WebSocket) => {
@@ -247,6 +248,7 @@ class WebsocketHandler {
247248
response['mempoolInfo'] = mempoolInfo;
248249
response['vBytesPerSecond'] = vBytesPerSecond;
249250
response['transactions'] = newTransactions.slice(0, 6).map((tx) => Common.stripTransaction(tx));
251+
response['da'] = da;
250252
}
251253

252254
if (client['want-mempool-blocks']) {
@@ -410,8 +412,7 @@ class WebsocketHandler {
410412
const response = {
411413
'block': block,
412414
'mempoolInfo': memPool.getMempoolInfo(),
413-
'lastDifficultyAdjustment': blocks.getLastDifficultyAdjustmentTime(),
414-
'previousRetarget': blocks.getPreviousDifficultyRetarget(),
415+
'da': difficultyAdjustment.getDifficultyAdjustment(),
415416
};
416417

417418
if (mBlocks && client['want-mempool-blocks']) {

backend/src/mempool.interfaces.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,3 +197,15 @@ export interface IBackendInfo {
197197
gitCommit: string;
198198
version: string;
199199
}
200+
201+
export interface IDifficultyAdjustment {
202+
progressPercent: number;
203+
difficultyChange: number;
204+
estimatedRetargetDate: number;
205+
remainingBlocks: number;
206+
remainingTime: number;
207+
previousRetarget: number;
208+
nextRetargetHeight: number;
209+
timeAvg: number;
210+
timeOffset: number;
211+
}

backend/src/routes.ts

Lines changed: 2 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import axios from 'axios';
2525
import mining from './api/mining';
2626
import BlocksRepository from './repositories/BlocksRepository';
2727
import HashratesRepository from './repositories/HashratesRepository';
28+
import difficultyAdjustment from './api/difficulty-adjustment';
2829

2930
class Routes {
3031
constructor() {}
@@ -847,55 +848,7 @@ class Routes {
847848

848849
public getDifficultyChange(req: Request, res: Response) {
849850
try {
850-
const DATime = blocks.getLastDifficultyAdjustmentTime();
851-
const previousRetarget = blocks.getPreviousDifficultyRetarget();
852-
const blockHeight = blocks.getCurrentBlockHeight();
853-
854-
const now = new Date().getTime() / 1000;
855-
const diff = now - DATime;
856-
const blocksInEpoch = blockHeight % 2016;
857-
const progressPercent = (blocksInEpoch >= 0) ? blocksInEpoch / 2016 * 100 : 100;
858-
const remainingBlocks = 2016 - blocksInEpoch;
859-
const nextRetargetHeight = blockHeight + remainingBlocks;
860-
861-
let difficultyChange = 0;
862-
if (remainingBlocks < 1870) {
863-
if (blocksInEpoch > 0) {
864-
difficultyChange = (600 / (diff / blocksInEpoch ) - 1) * 100;
865-
}
866-
if (difficultyChange > 300) {
867-
difficultyChange = 300;
868-
}
869-
if (difficultyChange < -75) {
870-
difficultyChange = -75;
871-
}
872-
}
873-
874-
const timeAvgDiff = difficultyChange * 0.1;
875-
876-
let timeAvgMins = 10;
877-
if (timeAvgDiff > 0) {
878-
timeAvgMins -= Math.abs(timeAvgDiff);
879-
} else {
880-
timeAvgMins += Math.abs(timeAvgDiff);
881-
}
882-
883-
const timeAvg = timeAvgMins * 60;
884-
const remainingTime = remainingBlocks * timeAvg;
885-
const estimatedRetargetDate = remainingTime + now;
886-
887-
const result = {
888-
progressPercent,
889-
difficultyChange,
890-
estimatedRetargetDate,
891-
remainingBlocks,
892-
remainingTime,
893-
previousRetarget,
894-
nextRetargetHeight,
895-
timeAvg,
896-
};
897-
res.json(result);
898-
851+
res.json(difficultyAdjustment.getDifficultyAdjustment());
899852
} catch (e) {
900853
res.status(500).send(e instanceof Error ? e.message : e);
901854
}

frontend/src/app/components/difficulty/difficulty.component.ts

Lines changed: 42 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,11 @@ import { StateService } from '../..//services/state.service';
66
interface EpochProgress {
77
base: string;
88
change: number;
9-
progress: string;
9+
progress: number;
1010
remainingBlocks: number;
1111
newDifficultyHeight: number;
1212
colorAdjustments: string;
1313
colorPreviousAdjustments: string;
14-
timeAvg: string;
1514
remainingTime: number;
1615
previousRetarget: number;
1716
blocksUntilHalving: number;
@@ -38,85 +37,52 @@ export class DifficultyComponent implements OnInit {
3837

3938
ngOnInit(): void {
4039
this.isLoadingWebSocket$ = this.stateService.isLoadingWebSocket$;
41-
this.difficultyEpoch$ = timer(0, 1000)
42-
.pipe(
43-
switchMap(() => combineLatest([
44-
this.stateService.blocks$.pipe(map(([block]) => block)),
45-
this.stateService.lastDifficultyAdjustment$,
46-
this.stateService.previousRetarget$
47-
])),
48-
map(([block, DATime, previousRetarget]) => {
49-
const now = new Date().getTime() / 1000;
50-
const diff = now - DATime;
51-
const blocksInEpoch = block.height % 2016;
52-
const progress = (blocksInEpoch >= 0) ? (blocksInEpoch / 2016 * 100).toFixed(2) : `100`;
53-
const remainingBlocks = 2016 - blocksInEpoch;
54-
const newDifficultyHeight = block.height + remainingBlocks;
40+
this.difficultyEpoch$ = combineLatest([
41+
this.stateService.blocks$.pipe(map(([block]) => block)),
42+
this.stateService.difficultyAdjustment$,
43+
])
44+
.pipe(
45+
map(([block, da]) => {
46+
let colorAdjustments = '#ffffff66';
47+
if (da.difficultyChange > 0) {
48+
colorAdjustments = '#3bcc49';
49+
}
50+
if (da.difficultyChange < 0) {
51+
colorAdjustments = '#dc3545';
52+
}
5553

56-
let change = 0;
57-
if (remainingBlocks < 1870) {
58-
if (blocksInEpoch > 0) {
59-
change = (600 / (diff / blocksInEpoch ) - 1) * 100;
60-
}
61-
if (change > 300) {
62-
change = 300;
63-
}
64-
if (change < -75) {
65-
change = -75;
66-
}
54+
let colorPreviousAdjustments = '#dc3545';
55+
if (da.previousRetarget) {
56+
if (da.previousRetarget >= 0) {
57+
colorPreviousAdjustments = '#3bcc49';
6758
}
68-
69-
const timeAvgDiff = change * 0.1;
70-
71-
let timeAvgMins = 10;
72-
if (timeAvgDiff > 0) {
73-
timeAvgMins -= Math.abs(timeAvgDiff);
74-
} else {
75-
timeAvgMins += Math.abs(timeAvgDiff);
76-
}
77-
78-
const timeAvg = timeAvgMins.toFixed(0);
79-
const remainingTime = (remainingBlocks * timeAvgMins * 60 * 1000) + (now * 1000);
80-
81-
let colorAdjustments = '#ffffff66';
82-
if (change > 0) {
83-
colorAdjustments = '#3bcc49';
84-
}
85-
if (change < 0) {
86-
colorAdjustments = '#dc3545';
87-
}
88-
89-
let colorPreviousAdjustments = '#dc3545';
90-
if (previousRetarget) {
91-
if (previousRetarget >= 0) {
92-
colorPreviousAdjustments = '#3bcc49';
93-
}
94-
if (previousRetarget === 0) {
95-
colorPreviousAdjustments = '#ffffff66';
96-
}
97-
} else {
59+
if (da.previousRetarget === 0) {
9860
colorPreviousAdjustments = '#ffffff66';
9961
}
62+
} else {
63+
colorPreviousAdjustments = '#ffffff66';
64+
}
10065

101-
const blocksUntilHalving = 210000 - (block.height % 210000);
102-
const timeUntilHalving = (blocksUntilHalving * timeAvgMins * 60 * 1000) + (now * 1000);
66+
const timeAvgMins = da.timeAvg;
67+
const now = new Date().getTime() / 1000;
68+
const blocksUntilHalving = 210000 - (block.height % 210000);
69+
const timeUntilHalving = (blocksUntilHalving * timeAvgMins * 60 * 1000) + (now * 1000);
10370

104-
return {
105-
base: `${progress}%`,
106-
change,
107-
progress,
108-
remainingBlocks,
109-
timeAvg,
110-
colorAdjustments,
111-
colorPreviousAdjustments,
112-
blocksInEpoch,
113-
newDifficultyHeight,
114-
remainingTime,
115-
previousRetarget,
116-
blocksUntilHalving,
117-
timeUntilHalving,
118-
};
119-
})
120-
);
71+
const data = {
72+
base: `${da.progressPercent.toFixed(2)}%`,
73+
change: da.difficultyChange,
74+
progress: da.progressPercent,
75+
remainingBlocks: da.remainingBlocks,
76+
colorAdjustments,
77+
colorPreviousAdjustments,
78+
newDifficultyHeight: da.nextRetargetHeight,
79+
remainingTime: da.remainingTime,
80+
previousRetarget: da.previousRetarget,
81+
blocksUntilHalving,
82+
timeUntilHalving,
83+
};
84+
return data;
85+
})
86+
);
12187
}
12288
}

frontend/src/app/components/mempool-blocks/mempool-blocks.component.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<ng-container *ngIf="(loadingBlocks$ | async) === false; else loadingBlocks">
2-
<div class="mempool-blocks-container" *ngIf="(timeAvg$ | async) as timeAvg;">
2+
<div class="mempool-blocks-container" *ngIf="(difficultyAdjustments$ | async) as da;">
33
<div class="flashing">
44
<ng-template ngFor let-projectedBlock [ngForOf]="mempoolBlocks$ | async" let-i="index" [ngForTrackBy]="trackByFn">
55
<div class="bitcoin-block text-center mempool-block" id="mempool-block-{{ i }}" [ngStyle]="mempoolBlockStyles[i]" [class.blink-bg]="projectedBlock.blink">
@@ -26,7 +26,7 @@
2626
<app-time-until [time]="(1 * i) + now + 61000" [fastRender]="false" [fixedRender]="true"></app-time-until>
2727
</ng-template>
2828
<ng-template #timeDiffMainnet>
29-
<app-time-until [time]="(timeAvg * i) + now + timeAvg + timeOffset" [fastRender]="false" [fixedRender]="true" [forceFloorOnTimeIntervals]="['hour']"></app-time-until>
29+
<app-time-until [time]="da.timeAvg * (i + 1) + now + da.timeOffset" [fastRender]="false" [fixedRender]="true" [forceFloorOnTimeIntervals]="['hour']"></app-time-until>
3030
</ng-template>
3131
</div>
3232
<ng-template #mergedBlock>

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy