Add hashrate & difficulty chart resolution scaling
This commit is contained in:
		
							parent
							
								
									4390d520e7
								
							
						
					
					
						commit
						efb5eba092
					
				@ -8,6 +8,7 @@ import { Common } from './common';
 | 
			
		||||
import loadingIndicators from './loading-indicators';
 | 
			
		||||
import { escape } from 'mysql2';
 | 
			
		||||
import DifficultyAdjustmentsRepository from '../repositories/DifficultyAdjustmentsRepository';
 | 
			
		||||
import config from '../config';
 | 
			
		||||
 | 
			
		||||
class Mining {
 | 
			
		||||
  constructor() {
 | 
			
		||||
@ -302,7 +303,7 @@ class Mining {
 | 
			
		||||
      while (toTimestamp > genesisTimestamp) {
 | 
			
		||||
        const fromTimestamp = toTimestamp - 86400000;
 | 
			
		||||
 | 
			
		||||
        // Skip already indexed weeks
 | 
			
		||||
        // Skip already indexed days
 | 
			
		||||
        if (indexedTimestamp.includes(toTimestamp / 1000)) {
 | 
			
		||||
          toTimestamp -= 86400000;
 | 
			
		||||
          ++totalIndexed;
 | 
			
		||||
@ -313,7 +314,7 @@ class Mining {
 | 
			
		||||
        // we are currently indexing has complete data)
 | 
			
		||||
        const blockStatsPreviousDay: any = await BlocksRepository.$blockCountBetweenTimestamp(
 | 
			
		||||
          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;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -357,9 +358,10 @@ class Mining {
 | 
			
		||||
      // Add genesis block manually
 | 
			
		||||
      if (toTimestamp <= genesisTimestamp && !indexedTimestamp.includes(genesisTimestamp)) {
 | 
			
		||||
        hashrates.push({
 | 
			
		||||
          hashrateTimestamp: genesisTimestamp,
 | 
			
		||||
          hashrateTimestamp: genesisTimestamp / 1000,
 | 
			
		||||
          avgHashrate: await bitcoinClient.getNetworkHashPs(1, 1),
 | 
			
		||||
          poolId: null,
 | 
			
		||||
          poolId: 0,
 | 
			
		||||
          share: 1,
 | 
			
		||||
          type: 'daily',
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
@ -393,6 +395,15 @@ class Mining {
 | 
			
		||||
    let currentDifficulty = 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) {
 | 
			
		||||
      if (block.difficulty !== currentDifficulty) {
 | 
			
		||||
        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/hashrate/pools/:interval', routes.$getPoolsHistoricalHashrate)
 | 
			
		||||
        .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/blocks/fees/:interval', routes.$getHistoricalBlockFees)
 | 
			
		||||
        .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) {
 | 
			
		||||
          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 BlocksSummariesRepository.$deleteBlocksFrom(blocks[idx - 1].height);
 | 
			
		||||
          await HashratesRepository.$deleteHashratesFromTimestamp(blocks[idx - 1].timestamp - 604800);
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,5 @@
 | 
			
		||||
import { Common } from '../api/common';
 | 
			
		||||
import config from '../config';
 | 
			
		||||
import DB from '../database';
 | 
			
		||||
import logger from '../logger';
 | 
			
		||||
import { IndexedDifficultyAdjustment } from '../mempool.interfaces';
 | 
			
		||||
@ -31,13 +32,19 @@ class DifficultyAdjustmentsRepository {
 | 
			
		||||
  public async $getAdjustments(interval: string | null, descOrder: boolean = false): Promise<IndexedDifficultyAdjustment[]> {
 | 
			
		||||
    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`;
 | 
			
		||||
 | 
			
		||||
    if (interval) {
 | 
			
		||||
      query += ` WHERE time BETWEEN DATE_SUB(NOW(), INTERVAL ${interval}) AND NOW()`;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    query += ` GROUP BY UNIX_TIMESTAMP(time) DIV ${86400}`;
 | 
			
		||||
 | 
			
		||||
    if (descOrder === true) {
 | 
			
		||||
      query += ` ORDER BY time DESC`;
 | 
			
		||||
    } else {
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,6 @@
 | 
			
		||||
import { escape } from 'mysql2';
 | 
			
		||||
import { Common } from '../api/common';
 | 
			
		||||
import config from '../config';
 | 
			
		||||
import DB from '../database';
 | 
			
		||||
import logger from '../logger';
 | 
			
		||||
import PoolsRepository from './PoolsRepository';
 | 
			
		||||
@ -32,7 +33,9 @@ class HashratesRepository {
 | 
			
		||||
  public async $getNetworkDailyHashrate(interval: string | null): Promise<any[]> {
 | 
			
		||||
    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`;
 | 
			
		||||
 | 
			
		||||
    if (interval) {
 | 
			
		||||
@ -42,6 +45,7 @@ class HashratesRepository {
 | 
			
		||||
      query += ` WHERE hashrates.type = 'daily'`;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    query += ` GROUP BY UNIX_TIMESTAMP(hashrate_timestamp) DIV ${86400}`;
 | 
			
		||||
    query += ` ORDER by hashrate_timestamp`;
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
 | 
			
		||||
@ -11,6 +11,7 @@ import { StorageService } from 'src/app/services/storage.service';
 | 
			
		||||
import { MiningService } from 'src/app/services/mining.service';
 | 
			
		||||
import { download } from 'src/app/shared/graphs.utils';
 | 
			
		||||
import { ActivatedRoute } from '@angular/router';
 | 
			
		||||
import { StateService } from 'src/app/services/state.service';
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
  selector: 'app-hashrate-chart',
 | 
			
		||||
@ -47,7 +48,7 @@ export class HashrateChartComponent implements OnInit {
 | 
			
		||||
  formatNumber = formatNumber;
 | 
			
		||||
  timespan = '';
 | 
			
		||||
  chartInstance: any = undefined;
 | 
			
		||||
  maResolution: number =  30;
 | 
			
		||||
  network = '';
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    @Inject(LOCALE_ID) public locale: string,
 | 
			
		||||
@ -57,10 +58,13 @@ export class HashrateChartComponent implements OnInit {
 | 
			
		||||
    private storageService: StorageService,
 | 
			
		||||
    private miningService: MiningService,
 | 
			
		||||
    private route: ActivatedRoute,
 | 
			
		||||
    private stateService: StateService
 | 
			
		||||
  ) {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ngOnInit(): void {
 | 
			
		||||
    this.stateService.networkChanged$.subscribe((network) => this.network = network);
 | 
			
		||||
 | 
			
		||||
    let firstRun = true;
 | 
			
		||||
 | 
			
		||||
    if (this.widget) {
 | 
			
		||||
@ -124,17 +128,14 @@ export class HashrateChartComponent implements OnInit {
 | 
			
		||||
                  ++diffIndex;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                this.maResolution = 30;
 | 
			
		||||
                if (["3m", "6m"].includes(this.timespan)) {
 | 
			
		||||
                  this.maResolution = 7;
 | 
			
		||||
                }
 | 
			
		||||
                let maResolution = 15;
 | 
			
		||||
                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;
 | 
			
		||||
                  for (let y = this.maResolution - 1; y >= 0; --y) {
 | 
			
		||||
                  for (let y = maResolution - 1; y >= 0; --y) {
 | 
			
		||||
                    avg += data.hashrates[i - y].avgHashrate;
 | 
			
		||||
                  }
 | 
			
		||||
                  avg /= this.maResolution;
 | 
			
		||||
                  avg /= maResolution;
 | 
			
		||||
                  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)',
 | 
			
		||||
            textStyle: {  
 | 
			
		||||
            textStyle: {
 | 
			
		||||
              color: 'white',
 | 
			
		||||
            },
 | 
			
		||||
            icon: 'roundRect',
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            name: $localize`Hashrate` + ` (MA${this.maResolution})`,
 | 
			
		||||
            name: $localize`Hashrate (MA)`,
 | 
			
		||||
            inactiveColor: 'rgb(110, 112, 121)',
 | 
			
		||||
            textStyle: {  
 | 
			
		||||
            textStyle: {
 | 
			
		||||
              color: 'white',
 | 
			
		||||
            },
 | 
			
		||||
            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 : [
 | 
			
		||||
        {
 | 
			
		||||
          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',
 | 
			
		||||
          axisLabel: {
 | 
			
		||||
@ -363,7 +372,7 @@ export class HashrateChartComponent implements OnInit {
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          zlevel: 2,
 | 
			
		||||
          name: $localize`Hashrate` + ` (MA${this.maResolution})`,
 | 
			
		||||
          name: $localize`Hashrate (MA)`,
 | 
			
		||||
          showSymbol: false,
 | 
			
		||||
          symbol: 'none',
 | 
			
		||||
          data: data.hashrateMa,
 | 
			
		||||
@ -404,6 +413,10 @@ export class HashrateChartComponent implements OnInit {
 | 
			
		||||
 | 
			
		||||
  onChartInit(ec) {
 | 
			
		||||
    this.chartInstance = ec;
 | 
			
		||||
 | 
			
		||||
    this.chartInstance.on('legendselectchanged', (e) => {
 | 
			
		||||
      this.storageService.setValue('hashrate_difficulty_legend', JSON.stringify(e.selected));
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  isMobile() {
 | 
			
		||||
 | 
			
		||||
@ -40,7 +40,7 @@
 | 
			
		||||
      <div class="card">
 | 
			
		||||
        <div class="card-body pl-lg-3 pr-lg-3 pl-2 pr-2">
 | 
			
		||||
          <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>
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user