Merge branch 'master' into nymkappa/bugfix/price
This commit is contained in:
		
						commit
						fca813147d
					
				@ -6,7 +6,7 @@ import websocketHandler from '../websocket-handler';
 | 
				
			|||||||
import mempool from '../mempool';
 | 
					import mempool from '../mempool';
 | 
				
			||||||
import feeApi from '../fee-api';
 | 
					import feeApi from '../fee-api';
 | 
				
			||||||
import mempoolBlocks from '../mempool-blocks';
 | 
					import mempoolBlocks from '../mempool-blocks';
 | 
				
			||||||
import bitcoinApi from './bitcoin-api-factory';
 | 
					import bitcoinApi, { bitcoinCoreApi } from './bitcoin-api-factory';
 | 
				
			||||||
import { Common } from '../common';
 | 
					import { Common } from '../common';
 | 
				
			||||||
import backendInfo from '../backend-info';
 | 
					import backendInfo from '../backend-info';
 | 
				
			||||||
import transactionUtils from '../transaction-utils';
 | 
					import transactionUtils from '../transaction-utils';
 | 
				
			||||||
@ -469,7 +469,7 @@ class BitcoinRoutes {
 | 
				
			|||||||
          returnBlocks.push(localBlock);
 | 
					          returnBlocks.push(localBlock);
 | 
				
			||||||
          nextHash = localBlock.previousblockhash;
 | 
					          nextHash = localBlock.previousblockhash;
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
          const block = await bitcoinApi.$getBlock(nextHash);
 | 
					          const block = await bitcoinCoreApi.$getBlock(nextHash);
 | 
				
			||||||
          returnBlocks.push(block);
 | 
					          returnBlocks.push(block);
 | 
				
			||||||
          nextHash = block.previousblockhash;
 | 
					          nextHash = block.previousblockhash;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,5 @@
 | 
				
			|||||||
import config from '../config';
 | 
					import config from '../config';
 | 
				
			||||||
import bitcoinApi from './bitcoin/bitcoin-api-factory';
 | 
					import bitcoinApi, { bitcoinCoreApi } from './bitcoin/bitcoin-api-factory';
 | 
				
			||||||
import logger from '../logger';
 | 
					import logger from '../logger';
 | 
				
			||||||
import memPool from './mempool';
 | 
					import memPool from './mempool';
 | 
				
			||||||
import { BlockExtended, BlockExtension, BlockSummary, PoolTag, TransactionExtended, TransactionStripped, TransactionMinerInfo } from '../mempool.interfaces';
 | 
					import { BlockExtended, BlockExtension, BlockSummary, PoolTag, TransactionExtended, TransactionStripped, TransactionMinerInfo } from '../mempool.interfaces';
 | 
				
			||||||
@ -484,7 +484,7 @@ class Blocks {
 | 
				
			|||||||
            loadingIndicators.setProgress('block-indexing', progress, false);
 | 
					            loadingIndicators.setProgress('block-indexing', progress, false);
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
          const blockHash = await bitcoinApi.$getBlockHash(blockHeight);
 | 
					          const blockHash = await bitcoinApi.$getBlockHash(blockHeight);
 | 
				
			||||||
          const block: IEsploraApi.Block = await bitcoinApi.$getBlock(blockHash);
 | 
					          const block: IEsploraApi.Block = await bitcoinCoreApi.$getBlock(blockHash);
 | 
				
			||||||
          const transactions = await this.$getTransactionsExtended(blockHash, block.height, true, true);
 | 
					          const transactions = await this.$getTransactionsExtended(blockHash, block.height, true, true);
 | 
				
			||||||
          const blockExtended = await this.$getBlockExtended(block, transactions);
 | 
					          const blockExtended = await this.$getBlockExtended(block, transactions);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -532,13 +532,13 @@ class Blocks {
 | 
				
			|||||||
      if (blockchainInfo.blocks === blockchainInfo.headers) {
 | 
					      if (blockchainInfo.blocks === blockchainInfo.headers) {
 | 
				
			||||||
        const heightDiff = blockHeightTip % 2016;
 | 
					        const heightDiff = blockHeightTip % 2016;
 | 
				
			||||||
        const blockHash = await bitcoinApi.$getBlockHash(blockHeightTip - heightDiff);
 | 
					        const blockHash = await bitcoinApi.$getBlockHash(blockHeightTip - heightDiff);
 | 
				
			||||||
        const block: IEsploraApi.Block = await bitcoinApi.$getBlock(blockHash);
 | 
					        const block: IEsploraApi.Block = await bitcoinCoreApi.$getBlock(blockHash);
 | 
				
			||||||
        this.lastDifficultyAdjustmentTime = block.timestamp;
 | 
					        this.lastDifficultyAdjustmentTime = block.timestamp;
 | 
				
			||||||
        this.currentDifficulty = block.difficulty;
 | 
					        this.currentDifficulty = block.difficulty;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (blockHeightTip >= 2016) {
 | 
					        if (blockHeightTip >= 2016) {
 | 
				
			||||||
          const previousPeriodBlockHash = await bitcoinApi.$getBlockHash(blockHeightTip - heightDiff - 2016);
 | 
					          const previousPeriodBlockHash = await bitcoinApi.$getBlockHash(blockHeightTip - heightDiff - 2016);
 | 
				
			||||||
          const previousPeriodBlock: IEsploraApi.Block = await bitcoinApi.$getBlock(previousPeriodBlockHash);
 | 
					          const previousPeriodBlock: IEsploraApi.Block = await bitcoinCoreApi.$getBlock(previousPeriodBlockHash);
 | 
				
			||||||
          this.previousDifficultyRetarget = (block.difficulty - previousPeriodBlock.difficulty) / previousPeriodBlock.difficulty * 100;
 | 
					          this.previousDifficultyRetarget = (block.difficulty - previousPeriodBlock.difficulty) / previousPeriodBlock.difficulty * 100;
 | 
				
			||||||
          logger.debug(`Initial difficulty adjustment data set.`);
 | 
					          logger.debug(`Initial difficulty adjustment data set.`);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -662,7 +662,7 @@ class Blocks {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const blockHash = await bitcoinApi.$getBlockHash(height);
 | 
					    const blockHash = await bitcoinApi.$getBlockHash(height);
 | 
				
			||||||
    const block: IEsploraApi.Block = await bitcoinApi.$getBlock(blockHash);
 | 
					    const block: IEsploraApi.Block = await bitcoinCoreApi.$getBlock(blockHash);
 | 
				
			||||||
    const transactions = await this.$getTransactionsExtended(blockHash, block.height, true);
 | 
					    const transactions = await this.$getTransactionsExtended(blockHash, block.height, true);
 | 
				
			||||||
    const blockExtended = await this.$getBlockExtended(block, transactions);
 | 
					    const blockExtended = await this.$getBlockExtended(block, transactions);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -685,11 +685,11 @@ class Blocks {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // Not Bitcoin network, return the block as it from the bitcoin backend
 | 
					    // Not Bitcoin network, return the block as it from the bitcoin backend
 | 
				
			||||||
    if (['mainnet', 'testnet', 'signet'].includes(config.MEMPOOL.NETWORK) === false) {
 | 
					    if (['mainnet', 'testnet', 'signet'].includes(config.MEMPOOL.NETWORK) === false) {
 | 
				
			||||||
      return await bitcoinApi.$getBlock(hash);
 | 
					      return await bitcoinCoreApi.$getBlock(hash);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Bitcoin network, add our custom data on top
 | 
					    // Bitcoin network, add our custom data on top
 | 
				
			||||||
    const block: IEsploraApi.Block = await bitcoinApi.$getBlock(hash);
 | 
					    const block: IEsploraApi.Block = await bitcoinCoreApi.$getBlock(hash);
 | 
				
			||||||
    return await this.$indexBlock(block.height);
 | 
					    return await this.$indexBlock(block.height);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -7,7 +7,7 @@ import cpfpRepository from '../repositories/CpfpRepository';
 | 
				
			|||||||
import { RowDataPacket } from 'mysql2';
 | 
					import { RowDataPacket } from 'mysql2';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class DatabaseMigration {
 | 
					class DatabaseMigration {
 | 
				
			||||||
  private static currentVersion = 57;
 | 
					  private static currentVersion = 58;
 | 
				
			||||||
  private queryTimeout = 3600_000;
 | 
					  private queryTimeout = 3600_000;
 | 
				
			||||||
  private statisticsAddedIndexed = false;
 | 
					  private statisticsAddedIndexed = false;
 | 
				
			||||||
  private uniqueLogs: string[] = [];
 | 
					  private uniqueLogs: string[] = [];
 | 
				
			||||||
@ -505,6 +505,11 @@ class DatabaseMigration {
 | 
				
			|||||||
      await this.$executeQuery(`ALTER TABLE nodes MODIFY updated_at datetime NULL`);
 | 
					      await this.$executeQuery(`ALTER TABLE nodes MODIFY updated_at datetime NULL`);
 | 
				
			||||||
      await this.updateToSchemaVersion(57);
 | 
					      await this.updateToSchemaVersion(57);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (databaseSchemaVersion < 58) {
 | 
				
			||||||
 | 
					      // We only run some migration queries for this version
 | 
				
			||||||
 | 
					      await this.updateToSchemaVersion(58);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
@ -632,6 +637,11 @@ class DatabaseMigration {
 | 
				
			|||||||
      queries.push(`INSERT INTO state(name, number, string) VALUES ('last_weekly_hashrates_indexing', 0, NULL)`);
 | 
					      queries.push(`INSERT INTO state(name, number, string) VALUES ('last_weekly_hashrates_indexing', 0, NULL)`);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (version < 58) {
 | 
				
			||||||
 | 
					      queries.push(`DELETE FROM state WHERE name = 'last_hashrates_indexing'`);
 | 
				
			||||||
 | 
					      queries.push(`DELETE FROM state WHERE name = 'last_weekly_hashrates_indexing'`);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return queries;
 | 
					    return queries;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1023,6 +1033,7 @@ class DatabaseMigration {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    await this.$executeQuery(`TRUNCATE blocks`);
 | 
					    await this.$executeQuery(`TRUNCATE blocks`);
 | 
				
			||||||
    await this.$executeQuery(`TRUNCATE hashrates`);
 | 
					    await this.$executeQuery(`TRUNCATE hashrates`);
 | 
				
			||||||
 | 
					    await this.$executeQuery(`TRUNCATE difficulty_adjustments`);
 | 
				
			||||||
    await this.$executeQuery('DELETE FROM `pools`');
 | 
					    await this.$executeQuery('DELETE FROM `pools`');
 | 
				
			||||||
    await this.$executeQuery('ALTER TABLE pools AUTO_INCREMENT = 1');
 | 
					    await this.$executeQuery('ALTER TABLE pools AUTO_INCREMENT = 1');
 | 
				
			||||||
    await this.$executeQuery(`UPDATE state SET string = NULL WHERE name = 'pools_json_sha'`);
 | 
					    await this.$executeQuery(`UPDATE state SET string = NULL WHERE name = 'pools_json_sha'`);
 | 
				
			||||||
 | 
				
			|||||||
@ -97,14 +97,14 @@ class MempoolBlocks {
 | 
				
			|||||||
        blockSize += tx.size;
 | 
					        blockSize += tx.size;
 | 
				
			||||||
        transactions.push(tx);
 | 
					        transactions.push(tx);
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        mempoolBlocks.push(this.dataToMempoolBlocks(transactions, blockSize, blockWeight, mempoolBlocks.length));
 | 
					        mempoolBlocks.push(this.dataToMempoolBlocks(transactions, mempoolBlocks.length));
 | 
				
			||||||
        blockWeight = tx.weight;
 | 
					        blockWeight = tx.weight;
 | 
				
			||||||
        blockSize = tx.size;
 | 
					        blockSize = tx.size;
 | 
				
			||||||
        transactions = [tx];
 | 
					        transactions = [tx];
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    if (transactions.length) {
 | 
					    if (transactions.length) {
 | 
				
			||||||
      mempoolBlocks.push(this.dataToMempoolBlocks(transactions, blockSize, blockWeight, mempoolBlocks.length));
 | 
					      mempoolBlocks.push(this.dataToMempoolBlocks(transactions, mempoolBlocks.length));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return mempoolBlocks;
 | 
					    return mempoolBlocks;
 | 
				
			||||||
@ -281,7 +281,7 @@ class MempoolBlocks {
 | 
				
			|||||||
    const mempoolBlocks = blocks.map((transactions, blockIndex) => {
 | 
					    const mempoolBlocks = blocks.map((transactions, blockIndex) => {
 | 
				
			||||||
      return this.dataToMempoolBlocks(transactions.map(tx => {
 | 
					      return this.dataToMempoolBlocks(transactions.map(tx => {
 | 
				
			||||||
        return mempool[tx.txid] || null;
 | 
					        return mempool[tx.txid] || null;
 | 
				
			||||||
      }).filter(tx => !!tx), undefined, undefined, blockIndex);
 | 
					      }).filter(tx => !!tx), blockIndex);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (saveResults) {
 | 
					    if (saveResults) {
 | 
				
			||||||
@ -293,18 +293,17 @@ class MempoolBlocks {
 | 
				
			|||||||
    return mempoolBlocks;
 | 
					    return mempoolBlocks;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private dataToMempoolBlocks(transactions: TransactionExtended[],
 | 
					  private dataToMempoolBlocks(transactions: TransactionExtended[], blocksIndex: number): MempoolBlockWithTransactions {
 | 
				
			||||||
    blockSize: number | undefined, blockWeight: number | undefined, blocksIndex: number): MempoolBlockWithTransactions {
 | 
					    let totalSize = 0;
 | 
				
			||||||
    let totalSize = blockSize || 0;
 | 
					    let totalWeight = 0;
 | 
				
			||||||
    let totalWeight = blockWeight || 0;
 | 
					    const fitTransactions: TransactionExtended[] = [];
 | 
				
			||||||
    if (blockSize === undefined && blockWeight === undefined) {
 | 
					    transactions.forEach(tx => {
 | 
				
			||||||
      totalSize = 0;
 | 
					      totalSize += tx.size;
 | 
				
			||||||
      totalWeight = 0;
 | 
					      totalWeight += tx.weight;
 | 
				
			||||||
      transactions.forEach(tx => {
 | 
					      if ((totalWeight + tx.weight) <= config.MEMPOOL.BLOCK_WEIGHT_UNITS * 1.2) {
 | 
				
			||||||
        totalSize += tx.size;
 | 
					        fitTransactions.push(tx);
 | 
				
			||||||
        totalWeight += tx.weight;
 | 
					      }
 | 
				
			||||||
      });
 | 
					    });
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    let rangeLength = 4;
 | 
					    let rangeLength = 4;
 | 
				
			||||||
    if (blocksIndex === 0) {
 | 
					    if (blocksIndex === 0) {
 | 
				
			||||||
      rangeLength = 8;
 | 
					      rangeLength = 8;
 | 
				
			||||||
@ -322,7 +321,7 @@ class MempoolBlocks {
 | 
				
			|||||||
      medianFee: Common.percentile(transactions.map((tx) => tx.effectiveFeePerVsize), config.MEMPOOL.RECOMMENDED_FEE_PERCENTILE),
 | 
					      medianFee: Common.percentile(transactions.map((tx) => tx.effectiveFeePerVsize), config.MEMPOOL.RECOMMENDED_FEE_PERCENTILE),
 | 
				
			||||||
      feeRange: Common.getFeesInRange(transactions, rangeLength),
 | 
					      feeRange: Common.getFeesInRange(transactions, rangeLength),
 | 
				
			||||||
      transactionIds: transactions.map((tx) => tx.txid),
 | 
					      transactionIds: transactions.map((tx) => tx.txid),
 | 
				
			||||||
      transactions: transactions.map((tx) => Common.stripTransaction(tx)),
 | 
					      transactions: fitTransactions.map((tx) => Common.stripTransaction(tx)),
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -11,14 +11,13 @@ import DifficultyAdjustmentsRepository from '../../repositories/DifficultyAdjust
 | 
				
			|||||||
import config from '../../config';
 | 
					import config from '../../config';
 | 
				
			||||||
import BlocksAuditsRepository from '../../repositories/BlocksAuditsRepository';
 | 
					import BlocksAuditsRepository from '../../repositories/BlocksAuditsRepository';
 | 
				
			||||||
import PricesRepository from '../../repositories/PricesRepository';
 | 
					import PricesRepository from '../../repositories/PricesRepository';
 | 
				
			||||||
import bitcoinApiFactory from '../bitcoin/bitcoin-api-factory';
 | 
					import { bitcoinCoreApi } from '../bitcoin/bitcoin-api-factory';
 | 
				
			||||||
import { IEsploraApi } from '../bitcoin/esplora-api.interface';
 | 
					import { IEsploraApi } from '../bitcoin/esplora-api.interface';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Mining {
 | 
					class Mining {
 | 
				
			||||||
  blocksPriceIndexingRunning = false;
 | 
					  private blocksPriceIndexingRunning = false;
 | 
				
			||||||
 | 
					  public lastHashrateIndexingDate: number | null = null;
 | 
				
			||||||
  constructor() {
 | 
					  public lastWeeklyHashrateIndexingDate: number | null = null;
 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * Get historical block predictions match rate
 | 
					   * Get historical block predictions match rate
 | 
				
			||||||
@ -178,20 +177,21 @@ class Mining {
 | 
				
			|||||||
   */
 | 
					   */
 | 
				
			||||||
  public async $generatePoolHashrateHistory(): Promise<void> {
 | 
					  public async $generatePoolHashrateHistory(): Promise<void> {
 | 
				
			||||||
    const now = new Date();
 | 
					    const now = new Date();
 | 
				
			||||||
    const lastestRunDate = await HashratesRepository.$getLatestRun('last_weekly_hashrates_indexing');
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Run only if:
 | 
					    // Run only if:
 | 
				
			||||||
    // * lastestRunDate is set to 0 (node backend restart, reorg)
 | 
					    // * this.lastWeeklyHashrateIndexingDate is set to null (node backend restart, reorg)
 | 
				
			||||||
    // * we started a new week (around Monday midnight)
 | 
					    // * we started a new week (around Monday midnight)
 | 
				
			||||||
    const runIndexing = lastestRunDate === 0 || now.getUTCDay() === 1 && lastestRunDate !== now.getUTCDate();
 | 
					    const runIndexing = this.lastWeeklyHashrateIndexingDate === null ||
 | 
				
			||||||
 | 
					      now.getUTCDay() === 1 && this.lastWeeklyHashrateIndexingDate !== now.getUTCDate();
 | 
				
			||||||
    if (!runIndexing) {
 | 
					    if (!runIndexing) {
 | 
				
			||||||
 | 
					      logger.debug(`Pool hashrate history indexing is up to date, nothing to do`, logger.tags.mining);
 | 
				
			||||||
      return;
 | 
					      return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      const oldestConsecutiveBlockTimestamp = 1000 * (await BlocksRepository.$getOldestConsecutiveBlock()).timestamp;
 | 
					      const oldestConsecutiveBlockTimestamp = 1000 * (await BlocksRepository.$getOldestConsecutiveBlock()).timestamp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const genesisBlock: IEsploraApi.Block = await bitcoinApiFactory.$getBlock(await bitcoinClient.getBlockHash(0));
 | 
					      const genesisBlock: IEsploraApi.Block = await bitcoinCoreApi.$getBlock(await bitcoinClient.getBlockHash(0));
 | 
				
			||||||
      const genesisTimestamp = genesisBlock.timestamp * 1000;
 | 
					      const genesisTimestamp = genesisBlock.timestamp * 1000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const indexedTimestamp = await HashratesRepository.$getWeeklyHashrateTimestamps();
 | 
					      const indexedTimestamp = await HashratesRepository.$getWeeklyHashrateTimestamps();
 | 
				
			||||||
@ -266,7 +266,7 @@ class Mining {
 | 
				
			|||||||
        ++indexedThisRun;
 | 
					        ++indexedThisRun;
 | 
				
			||||||
        ++totalIndexed;
 | 
					        ++totalIndexed;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      await HashratesRepository.$setLatestRun('last_weekly_hashrates_indexing', new Date().getUTCDate());
 | 
					      this.lastWeeklyHashrateIndexingDate = new Date().getUTCDate();
 | 
				
			||||||
      if (newlyIndexed > 0) {
 | 
					      if (newlyIndexed > 0) {
 | 
				
			||||||
        logger.notice(`Weekly mining pools hashrates indexing completed: indexed ${newlyIndexed}`, logger.tags.mining);
 | 
					        logger.notice(`Weekly mining pools hashrates indexing completed: indexed ${newlyIndexed}`, logger.tags.mining);
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
@ -285,16 +285,16 @@ class Mining {
 | 
				
			|||||||
   */
 | 
					   */
 | 
				
			||||||
  public async $generateNetworkHashrateHistory(): Promise<void> {
 | 
					  public async $generateNetworkHashrateHistory(): Promise<void> {
 | 
				
			||||||
    // We only run this once a day around midnight
 | 
					    // We only run this once a day around midnight
 | 
				
			||||||
    const latestRunDate = await HashratesRepository.$getLatestRun('last_hashrates_indexing');
 | 
					    const today = new Date().getUTCDate();
 | 
				
			||||||
    const now = new Date().getUTCDate();
 | 
					    if (today === this.lastHashrateIndexingDate) {
 | 
				
			||||||
    if (now === latestRunDate) {
 | 
					      logger.debug(`Network hashrate history indexing is up to date, nothing to do`, logger.tags.mining);
 | 
				
			||||||
      return;
 | 
					      return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const oldestConsecutiveBlockTimestamp = 1000 * (await BlocksRepository.$getOldestConsecutiveBlock()).timestamp;
 | 
					    const oldestConsecutiveBlockTimestamp = 1000 * (await BlocksRepository.$getOldestConsecutiveBlock()).timestamp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      const genesisBlock: IEsploraApi.Block = await bitcoinApiFactory.$getBlock(await bitcoinClient.getBlockHash(0));
 | 
					      const genesisBlock: IEsploraApi.Block = await bitcoinCoreApi.$getBlock(await bitcoinClient.getBlockHash(0));
 | 
				
			||||||
      const genesisTimestamp = genesisBlock.timestamp * 1000;
 | 
					      const genesisTimestamp = genesisBlock.timestamp * 1000;
 | 
				
			||||||
      const indexedTimestamp = (await HashratesRepository.$getRawNetworkDailyHashrate(null)).map(hashrate => hashrate.timestamp);
 | 
					      const indexedTimestamp = (await HashratesRepository.$getRawNetworkDailyHashrate(null)).map(hashrate => hashrate.timestamp);
 | 
				
			||||||
      const lastMidnight = this.getDateMidnight(new Date());
 | 
					      const lastMidnight = this.getDateMidnight(new Date());
 | 
				
			||||||
@ -371,7 +371,7 @@ class Mining {
 | 
				
			|||||||
      newlyIndexed += hashrates.length;
 | 
					      newlyIndexed += hashrates.length;
 | 
				
			||||||
      await HashratesRepository.$saveHashrates(hashrates);
 | 
					      await HashratesRepository.$saveHashrates(hashrates);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      await HashratesRepository.$setLatestRun('last_hashrates_indexing', new Date().getUTCDate());
 | 
					      this.lastHashrateIndexingDate = new Date().getUTCDate();
 | 
				
			||||||
      if (newlyIndexed > 0) {
 | 
					      if (newlyIndexed > 0) {
 | 
				
			||||||
        logger.notice(`Daily network hashrate indexing completed: indexed ${newlyIndexed} days`, logger.tags.mining);
 | 
					        logger.notice(`Daily network hashrate indexing completed: indexed ${newlyIndexed} days`, logger.tags.mining);
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
@ -396,7 +396,7 @@ class Mining {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const blocks: any = await BlocksRepository.$getBlocksDifficulty();
 | 
					    const blocks: any = await BlocksRepository.$getBlocksDifficulty();
 | 
				
			||||||
    const genesisBlock: IEsploraApi.Block = await bitcoinApiFactory.$getBlock(await bitcoinClient.getBlockHash(0));
 | 
					    const genesisBlock: IEsploraApi.Block = await bitcoinCoreApi.$getBlock(await bitcoinClient.getBlockHash(0));
 | 
				
			||||||
    let currentDifficulty = genesisBlock.difficulty;
 | 
					    let currentDifficulty = genesisBlock.difficulty;
 | 
				
			||||||
    let totalIndexed = 0;
 | 
					    let totalIndexed = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -87,9 +87,6 @@ class Server {
 | 
				
			|||||||
          await databaseMigration.$blocksReindexingTruncate();
 | 
					          await databaseMigration.$blocksReindexingTruncate();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        await databaseMigration.$initializeOrMigrateDatabase();
 | 
					        await databaseMigration.$initializeOrMigrateDatabase();
 | 
				
			||||||
        if (Common.indexingEnabled()) {
 | 
					 | 
				
			||||||
          await indexer.$resetHashratesIndexingState();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      } catch (e) {
 | 
					      } catch (e) {
 | 
				
			||||||
        throw new Error(e instanceof Error ? e.message : 'Error');
 | 
					        throw new Error(e instanceof Error ? e.message : 'Error');
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
				
			|||||||
@ -3,7 +3,6 @@ import blocks from './api/blocks';
 | 
				
			|||||||
import mempool from './api/mempool';
 | 
					import mempool from './api/mempool';
 | 
				
			||||||
import mining from './api/mining/mining';
 | 
					import mining from './api/mining/mining';
 | 
				
			||||||
import logger from './logger';
 | 
					import logger from './logger';
 | 
				
			||||||
import HashratesRepository from './repositories/HashratesRepository';
 | 
					 | 
				
			||||||
import bitcoinClient from './api/bitcoin/bitcoin-client';
 | 
					import bitcoinClient from './api/bitcoin/bitcoin-client';
 | 
				
			||||||
import priceUpdater from './tasks/price-updater';
 | 
					import priceUpdater from './tasks/price-updater';
 | 
				
			||||||
import PricesRepository from './repositories/PricesRepository';
 | 
					import PricesRepository from './repositories/PricesRepository';
 | 
				
			||||||
@ -131,7 +130,6 @@ class Indexer {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
      this.runSingleTask('blocksPrices');
 | 
					      this.runSingleTask('blocksPrices');
 | 
				
			||||||
      await mining.$indexDifficultyAdjustments();
 | 
					      await mining.$indexDifficultyAdjustments();
 | 
				
			||||||
      await this.$resetHashratesIndexingState(); // TODO - Remove this as it's not efficient
 | 
					 | 
				
			||||||
      await mining.$generateNetworkHashrateHistory();
 | 
					      await mining.$generateNetworkHashrateHistory();
 | 
				
			||||||
      await mining.$generatePoolHashrateHistory();
 | 
					      await mining.$generatePoolHashrateHistory();
 | 
				
			||||||
      await blocks.$generateBlocksSummariesDatabase();
 | 
					      await blocks.$generateBlocksSummariesDatabase();
 | 
				
			||||||
@ -150,16 +148,6 @@ class Indexer {
 | 
				
			|||||||
    logger.debug(`Indexing completed. Next run planned at ${new Date(new Date().getTime() + runEvery).toUTCString()}`);
 | 
					    logger.debug(`Indexing completed. Next run planned at ${new Date(new Date().getTime() + runEvery).toUTCString()}`);
 | 
				
			||||||
    setTimeout(() => this.reindex(), runEvery);
 | 
					    setTimeout(() => this.reindex(), runEvery);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					 | 
				
			||||||
  async $resetHashratesIndexingState(): Promise<void> {
 | 
					 | 
				
			||||||
    try {
 | 
					 | 
				
			||||||
      await HashratesRepository.$setLatestRun('last_hashrates_indexing', 0);
 | 
					 | 
				
			||||||
      await HashratesRepository.$setLatestRun('last_weekly_hashrates_indexing', 0);
 | 
					 | 
				
			||||||
    } catch (e) {
 | 
					 | 
				
			||||||
      logger.err(`Cannot reset hashrate indexing timestamps. Reason: ` + (e instanceof Error ? e.message : e));
 | 
					 | 
				
			||||||
      throw e;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default new Indexer();
 | 
					export default new Indexer();
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,4 @@
 | 
				
			|||||||
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';
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,6 @@
 | 
				
			|||||||
import { escape } from 'mysql2';
 | 
					import { escape } from 'mysql2';
 | 
				
			||||||
import { Common } from '../api/common';
 | 
					import { Common } from '../api/common';
 | 
				
			||||||
 | 
					import mining from '../api/mining/mining';
 | 
				
			||||||
import DB from '../database';
 | 
					import DB from '../database';
 | 
				
			||||||
import logger from '../logger';
 | 
					import logger from '../logger';
 | 
				
			||||||
import PoolsRepository from './PoolsRepository';
 | 
					import PoolsRepository from './PoolsRepository';
 | 
				
			||||||
@ -177,20 +178,6 @@ class HashratesRepository {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					 | 
				
			||||||
   * Set latest run timestamp
 | 
					 | 
				
			||||||
   */
 | 
					 | 
				
			||||||
  public async $setLatestRun(key: string, val: number) {
 | 
					 | 
				
			||||||
    const query = `UPDATE state SET number = ? WHERE name = ?`;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    try {
 | 
					 | 
				
			||||||
      await DB.query(query, [val, key]);
 | 
					 | 
				
			||||||
    } catch (e) {
 | 
					 | 
				
			||||||
      logger.err(`Cannot set last indexing run for ${key}. Reason: ` + (e instanceof Error ? e.message : e));
 | 
					 | 
				
			||||||
      throw e;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * Get latest run timestamp
 | 
					   * Get latest run timestamp
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
@ -222,8 +209,8 @@ class HashratesRepository {
 | 
				
			|||||||
        await DB.query(`DELETE FROM hashrates WHERE hashrate_timestamp = ?`, [row.timestamp]);
 | 
					        await DB.query(`DELETE FROM hashrates WHERE hashrate_timestamp = ?`, [row.timestamp]);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      // Re-run the hashrate indexing to fill up missing data
 | 
					      // Re-run the hashrate indexing to fill up missing data
 | 
				
			||||||
      await this.$setLatestRun('last_hashrates_indexing', 0);
 | 
					      mining.lastHashrateIndexingDate = null;
 | 
				
			||||||
      await this.$setLatestRun('last_weekly_hashrates_indexing', 0);
 | 
					      mining.lastWeeklyHashrateIndexingDate = null;
 | 
				
			||||||
    } catch (e) {
 | 
					    } catch (e) {
 | 
				
			||||||
      logger.err('Cannot delete latest hashrates data points. Reason: ' + (e instanceof Error ? e.message : e));
 | 
					      logger.err('Cannot delete latest hashrates data points. Reason: ' + (e instanceof Error ? e.message : e));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -238,8 +225,8 @@ class HashratesRepository {
 | 
				
			|||||||
    try {
 | 
					    try {
 | 
				
			||||||
      await DB.query(`DELETE FROM hashrates WHERE hashrate_timestamp >= FROM_UNIXTIME(?)`, [timestamp]);
 | 
					      await DB.query(`DELETE FROM hashrates WHERE hashrate_timestamp >= FROM_UNIXTIME(?)`, [timestamp]);
 | 
				
			||||||
      // Re-run the hashrate indexing to fill up missing data
 | 
					      // Re-run the hashrate indexing to fill up missing data
 | 
				
			||||||
      await this.$setLatestRun('last_hashrates_indexing', 0);
 | 
					      mining.lastHashrateIndexingDate = null;
 | 
				
			||||||
      await this.$setLatestRun('last_weekly_hashrates_indexing', 0);
 | 
					      mining.lastWeeklyHashrateIndexingDate = null;
 | 
				
			||||||
    } catch (e) {
 | 
					    } catch (e) {
 | 
				
			||||||
      logger.err('Cannot delete latest hashrates data points. Reason: ' + (e instanceof Error ? e.message : e));
 | 
					      logger.err('Cannot delete latest hashrates data points. Reason: ' + (e instanceof Error ? e.message : e));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -16,7 +16,7 @@
 | 
				
			|||||||
      </tr>
 | 
					      </tr>
 | 
				
			||||||
      <tr>
 | 
					      <tr>
 | 
				
			||||||
        <td class="td-width" i18n="dashboard.latest-transactions.amount">Amount</td>
 | 
					        <td class="td-width" i18n="dashboard.latest-transactions.amount">Amount</td>
 | 
				
			||||||
        <td><app-amount [blockConversion]="blockConversion" [satoshis]="value"></app-amount></td>
 | 
					        <td><app-amount [blockConversion]="blockConversion" [satoshis]="value" [noFiat]="true"></app-amount></td>
 | 
				
			||||||
      </tr>
 | 
					      </tr>
 | 
				
			||||||
      <tr>
 | 
					      <tr>
 | 
				
			||||||
        <td class="td-width" i18n="transaction.fee|Transaction fee">Fee</td>
 | 
					        <td class="td-width" i18n="transaction.fee|Transaction fee">Fee</td>
 | 
				
			||||||
 | 
				
			|||||||
@ -107,7 +107,13 @@ export class SearchFormComponent implements OnInit {
 | 
				
			|||||||
          }))),
 | 
					          }))),
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
      }),
 | 
					      }),
 | 
				
			||||||
      tap((result: any[]) => {
 | 
					      map((result: any[]) => {
 | 
				
			||||||
 | 
					        if (this.network === 'bisq') {
 | 
				
			||||||
 | 
					          result[0] = result[0].map((address: string) => 'B' + address);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
 | 
					      }),
 | 
				
			||||||
 | 
					      tap(() => {
 | 
				
			||||||
        this.isTypeaheading$.next(false);
 | 
					        this.isTypeaheading$.next(false);
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
@ -126,7 +132,7 @@ export class SearchFormComponent implements OnInit {
 | 
				
			|||||||
      ]
 | 
					      ]
 | 
				
			||||||
      ).pipe(
 | 
					      ).pipe(
 | 
				
			||||||
        map((latestData) => {
 | 
					        map((latestData) => {
 | 
				
			||||||
          const searchText = latestData[0];
 | 
					          let searchText = latestData[0];
 | 
				
			||||||
          if (!searchText.length) {
 | 
					          if (!searchText.length) {
 | 
				
			||||||
            return {
 | 
					            return {
 | 
				
			||||||
              searchText: '',
 | 
					              searchText: '',
 | 
				
			||||||
@ -144,15 +150,15 @@ export class SearchFormComponent implements OnInit {
 | 
				
			|||||||
          const addressPrefixSearchResults = result[0];
 | 
					          const addressPrefixSearchResults = result[0];
 | 
				
			||||||
          const lightningResults = result[1];
 | 
					          const lightningResults = result[1];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          if (this.network === 'bisq') {
 | 
					 | 
				
			||||||
            return searchText.map((address: string) => 'B' + address);
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          const matchesBlockHeight = this.regexBlockheight.test(searchText);
 | 
					          const matchesBlockHeight = this.regexBlockheight.test(searchText);
 | 
				
			||||||
          const matchesTxId = this.regexTransaction.test(searchText) && !this.regexBlockhash.test(searchText);
 | 
					          const matchesTxId = this.regexTransaction.test(searchText) && !this.regexBlockhash.test(searchText);
 | 
				
			||||||
          const matchesBlockHash = this.regexBlockhash.test(searchText);
 | 
					          const matchesBlockHash = this.regexBlockhash.test(searchText);
 | 
				
			||||||
          const matchesAddress = this.regexAddress.test(searchText);
 | 
					          const matchesAddress = this.regexAddress.test(searchText);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          if (matchesAddress && this.network === 'bisq') {
 | 
				
			||||||
 | 
					            searchText = 'B' + searchText;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          return {
 | 
					          return {
 | 
				
			||||||
            searchText: searchText,
 | 
					            searchText: searchText,
 | 
				
			||||||
            hashQuickMatch: +(matchesBlockHeight || matchesBlockHash || matchesTxId || matchesAddress),
 | 
					            hashQuickMatch: +(matchesBlockHeight || matchesBlockHash || matchesTxId || matchesAddress),
 | 
				
			||||||
 | 
				
			|||||||
@ -38,6 +38,9 @@ export class StartComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
  pageIndex: number = 0;
 | 
					  pageIndex: number = 0;
 | 
				
			||||||
  pages: any[] = [];
 | 
					  pages: any[] = [];
 | 
				
			||||||
  pendingMark: number | void = null;
 | 
					  pendingMark: number | void = null;
 | 
				
			||||||
 | 
					  lastUpdate: number = 0;
 | 
				
			||||||
 | 
					  lastMouseX: number;
 | 
				
			||||||
 | 
					  velocity: number = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor(
 | 
					  constructor(
 | 
				
			||||||
    private stateService: StateService,
 | 
					    private stateService: StateService,
 | 
				
			||||||
@ -136,6 +139,7 @@ export class StartComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  onMouseDown(event: MouseEvent) {
 | 
					  onMouseDown(event: MouseEvent) {
 | 
				
			||||||
    this.mouseDragStartX = event.clientX;
 | 
					    this.mouseDragStartX = event.clientX;
 | 
				
			||||||
 | 
					    this.resetMomentum(event.clientX);
 | 
				
			||||||
    this.blockchainScrollLeftInit = this.blockchainContainer.nativeElement.scrollLeft;
 | 
					    this.blockchainScrollLeftInit = this.blockchainContainer.nativeElement.scrollLeft;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  onPointerDown(event: PointerEvent) {
 | 
					  onPointerDown(event: PointerEvent) {
 | 
				
			||||||
@ -159,6 +163,7 @@ export class StartComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
  @HostListener('document:mousemove', ['$event'])
 | 
					  @HostListener('document:mousemove', ['$event'])
 | 
				
			||||||
  onMouseMove(event: MouseEvent): void {
 | 
					  onMouseMove(event: MouseEvent): void {
 | 
				
			||||||
    if (this.mouseDragStartX != null) {
 | 
					    if (this.mouseDragStartX != null) {
 | 
				
			||||||
 | 
					      this.updateVelocity(event.clientX);
 | 
				
			||||||
      this.stateService.setBlockScrollingInProgress(true);
 | 
					      this.stateService.setBlockScrollingInProgress(true);
 | 
				
			||||||
      this.blockchainContainer.nativeElement.scrollLeft =
 | 
					      this.blockchainContainer.nativeElement.scrollLeft =
 | 
				
			||||||
        this.blockchainScrollLeftInit + this.mouseDragStartX - event.clientX;
 | 
					        this.blockchainScrollLeftInit + this.mouseDragStartX - event.clientX;
 | 
				
			||||||
@ -167,7 +172,7 @@ export class StartComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
  @HostListener('document:mouseup', [])
 | 
					  @HostListener('document:mouseup', [])
 | 
				
			||||||
  onMouseUp() {
 | 
					  onMouseUp() {
 | 
				
			||||||
    this.mouseDragStartX = null;
 | 
					    this.mouseDragStartX = null;
 | 
				
			||||||
    this.stateService.setBlockScrollingInProgress(false);
 | 
					    this.animateMomentum();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  @HostListener('document:pointermove', ['$event'])
 | 
					  @HostListener('document:pointermove', ['$event'])
 | 
				
			||||||
  onPointerMove(event: PointerEvent): void {
 | 
					  onPointerMove(event: PointerEvent): void {
 | 
				
			||||||
@ -183,6 +188,45 @@ export class StartComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  resetMomentum(x: number) {
 | 
				
			||||||
 | 
					    this.lastUpdate = performance.now();
 | 
				
			||||||
 | 
					    this.lastMouseX = x;
 | 
				
			||||||
 | 
					    this.velocity = 0;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  updateVelocity(x: number) {
 | 
				
			||||||
 | 
					    const now = performance.now();
 | 
				
			||||||
 | 
					    let dt = now - this.lastUpdate;
 | 
				
			||||||
 | 
					    if (dt > 0) {
 | 
				
			||||||
 | 
					      this.lastUpdate = now;
 | 
				
			||||||
 | 
					      const velocity = (x - this.lastMouseX) / dt;
 | 
				
			||||||
 | 
					      this.velocity = (0.8 * this.velocity) + (0.2 * velocity);
 | 
				
			||||||
 | 
					      this.lastMouseX = x;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  animateMomentum() {
 | 
				
			||||||
 | 
					    this.lastUpdate = performance.now();
 | 
				
			||||||
 | 
					    requestAnimationFrame(() => {
 | 
				
			||||||
 | 
					      const now = performance.now();
 | 
				
			||||||
 | 
					      const dt = now - this.lastUpdate;
 | 
				
			||||||
 | 
					      this.lastUpdate = now;
 | 
				
			||||||
 | 
					      if (Math.abs(this.velocity) < 0.005) {
 | 
				
			||||||
 | 
					        this.stateService.setBlockScrollingInProgress(false);
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        const deceleration = Math.max(0.0025, 0.001 * this.velocity * this.velocity) * (this.velocity > 0 ? -1 : 1);
 | 
				
			||||||
 | 
					        const displacement = (this.velocity * dt) - (0.5 * (deceleration * dt * dt));
 | 
				
			||||||
 | 
					        const dv = (deceleration * dt);
 | 
				
			||||||
 | 
					        if ((this.velocity < 0 && dv + this.velocity > 0) || (this.velocity > 0 && dv + this.velocity < 0)) {
 | 
				
			||||||
 | 
					          this.velocity = 0;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          this.velocity += dv;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        this.blockchainContainer.nativeElement.scrollLeft -= displacement;
 | 
				
			||||||
 | 
					        this.animateMomentum();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  onScroll(e) {
 | 
					  onScroll(e) {
 | 
				
			||||||
    const middlePage = this.pageIndex === 0 ? this.pages[0] : this.pages[1];
 | 
					    const middlePage = this.pageIndex === 0 ? this.pages[0] : this.pages[1];
 | 
				
			||||||
 | 
				
			|||||||
@ -204,6 +204,12 @@
 | 
				
			|||||||
  .txids {
 | 
					  .txids {
 | 
				
			||||||
    width: 60%;
 | 
					    width: 60%;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @media (max-width: 500px) {
 | 
				
			||||||
 | 
					    .txids {
 | 
				
			||||||
 | 
					      width: 40%;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.tx-list {
 | 
					.tx-list {
 | 
				
			||||||
 | 
				
			|||||||
@ -250,7 +250,7 @@
 | 
				
			|||||||
    </span>
 | 
					    </span>
 | 
				
			||||||
    <ng-template #inSync>
 | 
					    <ng-template #inSync>
 | 
				
			||||||
      <div class="progress inc-tx-progress-bar">
 | 
					      <div class="progress inc-tx-progress-bar">
 | 
				
			||||||
        <div class="progress-bar" role="progressbar" [ngStyle]="{'width': mempoolInfoData.value.progressWidth, 'background-color': mempoolInfoData.value.progressColor}"> </div>
 | 
					        <div class="progress-bar {{ mempoolInfoData.value.progressColor }}" role="progressbar" [ngStyle]="{'width': mempoolInfoData.value.progressWidth}"> </div>
 | 
				
			||||||
        <div class="progress-text">‎{{ mempoolInfoData.value.vBytesPerSecond | ceil | number }} <ng-container i18n="shared.vbytes-per-second|vB/s">vB/s</ng-container></div>
 | 
					        <div class="progress-text">‎{{ mempoolInfoData.value.vBytesPerSecond | ceil | number }} <ng-container i18n="shared.vbytes-per-second|vB/s">vB/s</ng-container></div>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    </ng-template>
 | 
					    </ng-template>
 | 
				
			||||||
 | 
				
			|||||||
@ -78,21 +78,12 @@ export class DashboardComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
      map(([mempoolInfo, vbytesPerSecond]) => {
 | 
					      map(([mempoolInfo, vbytesPerSecond]) => {
 | 
				
			||||||
        const percent = Math.round((Math.min(vbytesPerSecond, this.vBytesPerSecondLimit) / this.vBytesPerSecondLimit) * 100);
 | 
					        const percent = Math.round((Math.min(vbytesPerSecond, this.vBytesPerSecondLimit) / this.vBytesPerSecondLimit) * 100);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let progressColor = '#7CB342';
 | 
					        let progressColor = 'bg-success';
 | 
				
			||||||
        if (vbytesPerSecond > 1667) {
 | 
					        if (vbytesPerSecond > 1667) {
 | 
				
			||||||
          progressColor = '#FDD835';
 | 
					          progressColor = 'bg-warning';
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if (vbytesPerSecond > 2000) {
 | 
					 | 
				
			||||||
          progressColor = '#FFB300';
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if (vbytesPerSecond > 2500) {
 | 
					 | 
				
			||||||
          progressColor = '#FB8C00';
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if (vbytesPerSecond > 3000) {
 | 
					        if (vbytesPerSecond > 3000) {
 | 
				
			||||||
          progressColor = '#F4511E';
 | 
					          progressColor = 'bg-danger';
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if (vbytesPerSecond > 3500) {
 | 
					 | 
				
			||||||
          progressColor = '#D81B60';
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const mempoolSizePercentage = (mempoolInfo.usage / mempoolInfo.maxmempool * 100);
 | 
					        const mempoolSizePercentage = (mempoolInfo.usage / mempoolInfo.maxmempool * 100);
 | 
				
			||||||
 | 
				
			|||||||
@ -23,6 +23,10 @@ export class FiatCurrencyPipe implements PipeTransform {
 | 
				
			|||||||
    const digits = args[0] || 1;
 | 
					    const digits = args[0] || 1;
 | 
				
			||||||
    const currency = args[1] || this.currency || 'USD';
 | 
					    const currency = args[1] || this.currency || 'USD';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return new Intl.NumberFormat(this.locale, { style: 'currency', currency }).format(num);
 | 
					    if (num >= 1000) {
 | 
				
			||||||
 | 
					      return new Intl.NumberFormat(this.locale, { style: 'currency', currency, maximumFractionDigits: 0 }).format(num);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      return new Intl.NumberFormat(this.locale, { style: 'currency', currency }).format(num);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -9,6 +9,10 @@ location /api/tx/ {
 | 
				
			|||||||
	rewrite ^/api/(.*) /$1 break;
 | 
						rewrite ^/api/(.*) /$1 break;
 | 
				
			||||||
	try_files /dev/null @esplora-api-cache-disabled;
 | 
						try_files /dev/null @esplora-api-cache-disabled;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					location /api/address-prefix/ {
 | 
				
			||||||
 | 
						rewrite ^/api/(.*) /$1 break;
 | 
				
			||||||
 | 
						try_files /dev/null @esplora-api-cache-disabled;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# rewrite APIs to match what backend expects
 | 
					# rewrite APIs to match what backend expects
 | 
				
			||||||
location /api/currencies {
 | 
					location /api/currencies {
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user