Add hashrate & difficulty chart resolution scaling
This commit is contained in:
parent
81c68620a1
commit
9ed7b2aad3
@ -8,6 +8,7 @@ import { Common } from './common';
|
|||||||
import loadingIndicators from './loading-indicators';
|
import loadingIndicators from './loading-indicators';
|
||||||
import { escape } from 'mysql2';
|
import { escape } from 'mysql2';
|
||||||
import DifficultyAdjustmentsRepository from '../repositories/DifficultyAdjustmentsRepository';
|
import DifficultyAdjustmentsRepository from '../repositories/DifficultyAdjustmentsRepository';
|
||||||
|
import config from '../config';
|
||||||
|
|
||||||
class Mining {
|
class Mining {
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -302,7 +303,7 @@ class Mining {
|
|||||||
while (toTimestamp > genesisTimestamp) {
|
while (toTimestamp > genesisTimestamp) {
|
||||||
const fromTimestamp = toTimestamp - 86400000;
|
const fromTimestamp = toTimestamp - 86400000;
|
||||||
|
|
||||||
// Skip already indexed weeks
|
// Skip already indexed days
|
||||||
if (indexedTimestamp.includes(toTimestamp / 1000)) {
|
if (indexedTimestamp.includes(toTimestamp / 1000)) {
|
||||||
toTimestamp -= 86400000;
|
toTimestamp -= 86400000;
|
||||||
++totalIndexed;
|
++totalIndexed;
|
||||||
@ -313,7 +314,7 @@ class Mining {
|
|||||||
// we are currently indexing has complete data)
|
// we are currently indexing has complete data)
|
||||||
const blockStatsPreviousDay: any = await BlocksRepository.$blockCountBetweenTimestamp(
|
const blockStatsPreviousDay: any = await BlocksRepository.$blockCountBetweenTimestamp(
|
||||||
null, (fromTimestamp - 86400000) / 1000, (toTimestamp - 86400000) / 1000);
|
null, (fromTimestamp - 86400000) / 1000, (toTimestamp - 86400000) / 1000);
|
||||||
if (blockStatsPreviousDay.blockCount === 0) { // We are done indexing
|
if (blockStatsPreviousDay.blockCount === 0 && config.MEMPOOL.NETWORK === 'mainnet') { // We are done indexing
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -357,9 +358,10 @@ class Mining {
|
|||||||
// Add genesis block manually
|
// Add genesis block manually
|
||||||
if (toTimestamp <= genesisTimestamp && !indexedTimestamp.includes(genesisTimestamp)) {
|
if (toTimestamp <= genesisTimestamp && !indexedTimestamp.includes(genesisTimestamp)) {
|
||||||
hashrates.push({
|
hashrates.push({
|
||||||
hashrateTimestamp: genesisTimestamp,
|
hashrateTimestamp: genesisTimestamp / 1000,
|
||||||
avgHashrate: await bitcoinClient.getNetworkHashPs(1, 1),
|
avgHashrate: await bitcoinClient.getNetworkHashPs(1, 1),
|
||||||
poolId: null,
|
poolId: 0,
|
||||||
|
share: 1,
|
||||||
type: 'daily',
|
type: 'daily',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -393,6 +395,15 @@ class Mining {
|
|||||||
let currentDifficulty = 0;
|
let currentDifficulty = 0;
|
||||||
let totalIndexed = 0;
|
let totalIndexed = 0;
|
||||||
|
|
||||||
|
if (indexedHeights[0] === false) {
|
||||||
|
await DifficultyAdjustmentsRepository.$saveAdjustments({
|
||||||
|
time: 1231006505,
|
||||||
|
height: 0,
|
||||||
|
difficulty: 1.0,
|
||||||
|
adjustment: 0.0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
for (const block of blocks) {
|
for (const block of blocks) {
|
||||||
if (block.difficulty !== currentDifficulty) {
|
if (block.difficulty !== currentDifficulty) {
|
||||||
if (block.height === 0 || indexedHeights[block.height] === true) { // Already indexed
|
if (block.height === 0 || indexedHeights[block.height] === true) { // Already indexed
|
||||||
|
@ -285,6 +285,7 @@ class Server {
|
|||||||
.get(config.MEMPOOL.API_URL_PREFIX + 'mining/pool/:slug', routes.$getPool)
|
.get(config.MEMPOOL.API_URL_PREFIX + 'mining/pool/:slug', routes.$getPool)
|
||||||
.get(config.MEMPOOL.API_URL_PREFIX + 'mining/hashrate/pools/:interval', routes.$getPoolsHistoricalHashrate)
|
.get(config.MEMPOOL.API_URL_PREFIX + 'mining/hashrate/pools/:interval', routes.$getPoolsHistoricalHashrate)
|
||||||
.get(config.MEMPOOL.API_URL_PREFIX + 'mining/hashrate/:interval', routes.$getHistoricalHashrate)
|
.get(config.MEMPOOL.API_URL_PREFIX + 'mining/hashrate/:interval', routes.$getHistoricalHashrate)
|
||||||
|
.get(config.MEMPOOL.API_URL_PREFIX + 'mining/difficulty-adjustments', routes.$getDifficultyAdjustments)
|
||||||
.get(config.MEMPOOL.API_URL_PREFIX + 'mining/reward-stats/:blockCount', routes.$getRewardStats)
|
.get(config.MEMPOOL.API_URL_PREFIX + 'mining/reward-stats/:blockCount', routes.$getRewardStats)
|
||||||
.get(config.MEMPOOL.API_URL_PREFIX + 'mining/blocks/fees/:interval', routes.$getHistoricalBlockFees)
|
.get(config.MEMPOOL.API_URL_PREFIX + 'mining/blocks/fees/:interval', routes.$getHistoricalBlockFees)
|
||||||
.get(config.MEMPOOL.API_URL_PREFIX + 'mining/blocks/rewards/:interval', routes.$getHistoricalBlockRewards)
|
.get(config.MEMPOOL.API_URL_PREFIX + 'mining/blocks/rewards/:interval', routes.$getHistoricalBlockRewards)
|
||||||
|
@ -436,7 +436,7 @@ class BlocksRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (blocks[idx].previous_block_hash !== blocks[idx - 1].hash) {
|
if (blocks[idx].previous_block_hash !== blocks[idx - 1].hash) {
|
||||||
logger.warn(`Chain divergence detected at block ${blocks[idx - 1].height}, re-indexing newer blocks and hashrates`);
|
logger.warn(`Chain divergence detected at block ${blocks[idx - 1].height}`);
|
||||||
await this.$deleteBlocksFrom(blocks[idx - 1].height);
|
await this.$deleteBlocksFrom(blocks[idx - 1].height);
|
||||||
await BlocksSummariesRepository.$deleteBlocksFrom(blocks[idx - 1].height);
|
await BlocksSummariesRepository.$deleteBlocksFrom(blocks[idx - 1].height);
|
||||||
await HashratesRepository.$deleteHashratesFromTimestamp(blocks[idx - 1].timestamp - 604800);
|
await HashratesRepository.$deleteHashratesFromTimestamp(blocks[idx - 1].timestamp - 604800);
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { Common } from '../api/common';
|
import { Common } from '../api/common';
|
||||||
|
import config from '../config';
|
||||||
import DB from '../database';
|
import DB from '../database';
|
||||||
import logger from '../logger';
|
import logger from '../logger';
|
||||||
import { IndexedDifficultyAdjustment } from '../mempool.interfaces';
|
import { IndexedDifficultyAdjustment } from '../mempool.interfaces';
|
||||||
@ -31,13 +32,19 @@ class DifficultyAdjustmentsRepository {
|
|||||||
public async $getAdjustments(interval: string | null, descOrder: boolean = false): Promise<IndexedDifficultyAdjustment[]> {
|
public async $getAdjustments(interval: string | null, descOrder: boolean = false): Promise<IndexedDifficultyAdjustment[]> {
|
||||||
interval = Common.getSqlInterval(interval);
|
interval = Common.getSqlInterval(interval);
|
||||||
|
|
||||||
let query = `SELECT UNIX_TIMESTAMP(time) as time, height, difficulty, adjustment
|
let query = `SELECT
|
||||||
|
CAST(AVG(UNIX_TIMESTAMP(time)) as INT) as time,
|
||||||
|
CAST(AVG(height) AS INT) as height,
|
||||||
|
CAST(AVG(difficulty) as DOUBLE) as difficulty,
|
||||||
|
CAST(AVG(adjustment) as DOUBLE) as adjustment
|
||||||
FROM difficulty_adjustments`;
|
FROM difficulty_adjustments`;
|
||||||
|
|
||||||
if (interval) {
|
if (interval) {
|
||||||
query += ` WHERE time BETWEEN DATE_SUB(NOW(), INTERVAL ${interval}) AND NOW()`;
|
query += ` WHERE time BETWEEN DATE_SUB(NOW(), INTERVAL ${interval}) AND NOW()`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
query += ` GROUP BY UNIX_TIMESTAMP(time) DIV ${86400}`;
|
||||||
|
|
||||||
if (descOrder === true) {
|
if (descOrder === true) {
|
||||||
query += ` ORDER BY time DESC`;
|
query += ` ORDER BY time DESC`;
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { escape } from 'mysql2';
|
import { escape } from 'mysql2';
|
||||||
import { Common } from '../api/common';
|
import { Common } from '../api/common';
|
||||||
|
import config from '../config';
|
||||||
import DB from '../database';
|
import DB from '../database';
|
||||||
import logger from '../logger';
|
import logger from '../logger';
|
||||||
import PoolsRepository from './PoolsRepository';
|
import PoolsRepository from './PoolsRepository';
|
||||||
@ -32,7 +33,9 @@ class HashratesRepository {
|
|||||||
public async $getNetworkDailyHashrate(interval: string | null): Promise<any[]> {
|
public async $getNetworkDailyHashrate(interval: string | null): Promise<any[]> {
|
||||||
interval = Common.getSqlInterval(interval);
|
interval = Common.getSqlInterval(interval);
|
||||||
|
|
||||||
let query = `SELECT UNIX_TIMESTAMP(hashrate_timestamp) as timestamp, avg_hashrate as avgHashrate
|
let query = `SELECT
|
||||||
|
CAST(AVG(UNIX_TIMESTAMP(hashrate_timestamp)) as INT) as timestamp,
|
||||||
|
CAST(AVG(avg_hashrate) as DOUBLE) as avgHashrate
|
||||||
FROM hashrates`;
|
FROM hashrates`;
|
||||||
|
|
||||||
if (interval) {
|
if (interval) {
|
||||||
@ -42,6 +45,7 @@ class HashratesRepository {
|
|||||||
query += ` WHERE hashrates.type = 'daily'`;
|
query += ` WHERE hashrates.type = 'daily'`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
query += ` GROUP BY UNIX_TIMESTAMP(hashrate_timestamp) DIV ${86400}`;
|
||||||
query += ` ORDER by hashrate_timestamp`;
|
query += ` ORDER by hashrate_timestamp`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -11,6 +11,7 @@ import { StorageService } from 'src/app/services/storage.service';
|
|||||||
import { MiningService } from 'src/app/services/mining.service';
|
import { MiningService } from 'src/app/services/mining.service';
|
||||||
import { download } from 'src/app/shared/graphs.utils';
|
import { download } from 'src/app/shared/graphs.utils';
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from '@angular/router';
|
||||||
|
import { StateService } from 'src/app/services/state.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-hashrate-chart',
|
selector: 'app-hashrate-chart',
|
||||||
@ -47,7 +48,7 @@ export class HashrateChartComponent implements OnInit {
|
|||||||
formatNumber = formatNumber;
|
formatNumber = formatNumber;
|
||||||
timespan = '';
|
timespan = '';
|
||||||
chartInstance: any = undefined;
|
chartInstance: any = undefined;
|
||||||
maResolution: number = 30;
|
network = '';
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(LOCALE_ID) public locale: string,
|
@Inject(LOCALE_ID) public locale: string,
|
||||||
@ -57,10 +58,13 @@ export class HashrateChartComponent implements OnInit {
|
|||||||
private storageService: StorageService,
|
private storageService: StorageService,
|
||||||
private miningService: MiningService,
|
private miningService: MiningService,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
|
private stateService: StateService
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
this.stateService.networkChanged$.subscribe((network) => this.network = network);
|
||||||
|
|
||||||
let firstRun = true;
|
let firstRun = true;
|
||||||
|
|
||||||
if (this.widget) {
|
if (this.widget) {
|
||||||
@ -124,17 +128,14 @@ export class HashrateChartComponent implements OnInit {
|
|||||||
++diffIndex;
|
++diffIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.maResolution = 30;
|
let maResolution = 15;
|
||||||
if (["3m", "6m"].includes(this.timespan)) {
|
|
||||||
this.maResolution = 7;
|
|
||||||
}
|
|
||||||
const hashrateMa = [];
|
const hashrateMa = [];
|
||||||
for (let i = this.maResolution - 1; i < data.hashrates.length; ++i) {
|
for (let i = maResolution - 1; i < data.hashrates.length; ++i) {
|
||||||
let avg = 0;
|
let avg = 0;
|
||||||
for (let y = this.maResolution - 1; y >= 0; --y) {
|
for (let y = maResolution - 1; y >= 0; --y) {
|
||||||
avg += data.hashrates[i - y].avgHashrate;
|
avg += data.hashrates[i - y].avgHashrate;
|
||||||
}
|
}
|
||||||
avg /= this.maResolution;
|
avg /= maResolution;
|
||||||
hashrateMa.push([data.hashrates[i].timestamp * 1000, avg]);
|
hashrateMa.push([data.hashrates[i].timestamp * 1000, avg]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -276,17 +277,17 @@ export class HashrateChartComponent implements OnInit {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: $localize`::Difficulty`,
|
name: $localize`:@@25148835d92465353fc5fe8897c27d5369978e5a:Difficulty`,
|
||||||
inactiveColor: 'rgb(110, 112, 121)',
|
inactiveColor: 'rgb(110, 112, 121)',
|
||||||
textStyle: {
|
textStyle: {
|
||||||
color: 'white',
|
color: 'white',
|
||||||
},
|
},
|
||||||
icon: 'roundRect',
|
icon: 'roundRect',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: $localize`Hashrate` + ` (MA${this.maResolution})`,
|
name: $localize`Hashrate (MA)`,
|
||||||
inactiveColor: 'rgb(110, 112, 121)',
|
inactiveColor: 'rgb(110, 112, 121)',
|
||||||
textStyle: {
|
textStyle: {
|
||||||
color: 'white',
|
color: 'white',
|
||||||
},
|
},
|
||||||
icon: 'roundRect',
|
icon: 'roundRect',
|
||||||
@ -295,11 +296,19 @@ export class HashrateChartComponent implements OnInit {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
selected: JSON.parse(this.storageService.getValue('hashrate_difficulty_legend')) ?? {
|
||||||
|
'$localize`:@@79a9dc5b1caca3cbeb1733a19515edacc5fc7920:Hashrate`': true,
|
||||||
|
'$localize`::Difficulty`': this.network === '',
|
||||||
|
'$localize`Hashrate (MA)`': true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
yAxis: data.hashrates.length === 0 ? undefined : [
|
yAxis: data.hashrates.length === 0 ? undefined : [
|
||||||
{
|
{
|
||||||
min: (value) => {
|
min: (value) => {
|
||||||
return value.min * 0.9;
|
const selectedPowerOfTen: any = selectPowerOfTen(value.min);
|
||||||
|
const newMin = Math.floor(value.min / selectedPowerOfTen.divider / 10);
|
||||||
|
console.log(newMin);
|
||||||
|
return newMin * selectedPowerOfTen.divider * 10;
|
||||||
},
|
},
|
||||||
type: 'value',
|
type: 'value',
|
||||||
axisLabel: {
|
axisLabel: {
|
||||||
@ -363,7 +372,7 @@ export class HashrateChartComponent implements OnInit {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
zlevel: 2,
|
zlevel: 2,
|
||||||
name: $localize`Hashrate` + ` (MA${this.maResolution})`,
|
name: $localize`Hashrate (MA)`,
|
||||||
showSymbol: false,
|
showSymbol: false,
|
||||||
symbol: 'none',
|
symbol: 'none',
|
||||||
data: data.hashrateMa,
|
data: data.hashrateMa,
|
||||||
@ -404,6 +413,10 @@ export class HashrateChartComponent implements OnInit {
|
|||||||
|
|
||||||
onChartInit(ec) {
|
onChartInit(ec) {
|
||||||
this.chartInstance = ec;
|
this.chartInstance = ec;
|
||||||
|
|
||||||
|
this.chartInstance.on('legendselectchanged', (e) => {
|
||||||
|
this.storageService.setValue('hashrate_difficulty_legend', JSON.stringify(e.selected));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
isMobile() {
|
isMobile() {
|
||||||
|
@ -40,7 +40,7 @@
|
|||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-body pl-lg-3 pr-lg-3 pl-2 pr-2">
|
<div class="card-body pl-lg-3 pr-lg-3 pl-2 pr-2">
|
||||||
<app-hashrate-chart [widget]="true"></app-hashrate-chart>
|
<app-hashrate-chart [widget]="true"></app-hashrate-chart>
|
||||||
<div class="mt-1"><a [routerLink]="['/graphs/mining/hashrate-difficulty' | relativeUrl]" i18n="dashboard.view-more">View more »</a></div>
|
<div class="mt-1"><a [routerLink]="['/graphs/mining/hashrate-difficulty' | relativeUrl]" fragment="1y" i18n="dashboard.view-more">View more »</a></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user