[Indexing] Link blocks to their closest known price
This commit is contained in:
		
							parent
							
								
									bda8b4612b
								
							
						
					
					
						commit
						e29a4e0a16
					
				@ -18,13 +18,10 @@ import BlocksRepository from '../repositories/BlocksRepository';
 | 
				
			|||||||
import HashratesRepository from '../repositories/HashratesRepository';
 | 
					import HashratesRepository from '../repositories/HashratesRepository';
 | 
				
			||||||
import indexer from '../indexer';
 | 
					import indexer from '../indexer';
 | 
				
			||||||
import fiatConversion from './fiat-conversion';
 | 
					import fiatConversion from './fiat-conversion';
 | 
				
			||||||
import RatesRepository from '../repositories/RatesRepository';
 | 
					 | 
				
			||||||
import database from '../database';
 | 
					 | 
				
			||||||
import poolsParser from './pools-parser';
 | 
					import poolsParser from './pools-parser';
 | 
				
			||||||
import BlocksSummariesRepository from '../repositories/BlocksSummariesRepository';
 | 
					import BlocksSummariesRepository from '../repositories/BlocksSummariesRepository';
 | 
				
			||||||
import mining from './mining/mining';
 | 
					import mining from './mining/mining';
 | 
				
			||||||
import DifficultyAdjustmentsRepository from '../repositories/DifficultyAdjustmentsRepository';
 | 
					import DifficultyAdjustmentsRepository from '../repositories/DifficultyAdjustmentsRepository';
 | 
				
			||||||
import difficultyAdjustment from './difficulty-adjustment';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Blocks {
 | 
					class Blocks {
 | 
				
			||||||
  private blocks: BlockExtended[] = [];
 | 
					  private blocks: BlockExtended[] = [];
 | 
				
			||||||
 | 
				
			|||||||
@ -4,7 +4,7 @@ import logger from '../logger';
 | 
				
			|||||||
import { Common } from './common';
 | 
					import { Common } from './common';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class DatabaseMigration {
 | 
					class DatabaseMigration {
 | 
				
			||||||
  private static currentVersion = 29;
 | 
					  private static currentVersion = 30;
 | 
				
			||||||
  private queryTimeout = 120000;
 | 
					  private queryTimeout = 120000;
 | 
				
			||||||
  private statisticsAddedIndexed = false;
 | 
					  private statisticsAddedIndexed = false;
 | 
				
			||||||
  private uniqueLogs: string[] = [];
 | 
					  private uniqueLogs: string[] = [];
 | 
				
			||||||
@ -12,8 +12,6 @@ class DatabaseMigration {
 | 
				
			|||||||
  private blocksTruncatedMessage = `'blocks' table has been truncated. Re-indexing from scratch.`;
 | 
					  private blocksTruncatedMessage = `'blocks' table has been truncated. Re-indexing from scratch.`;
 | 
				
			||||||
  private hashratesTruncatedMessage = `'hashrates' table has been truncated. Re-indexing from scratch.`;
 | 
					  private hashratesTruncatedMessage = `'hashrates' table has been truncated. Re-indexing from scratch.`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor() { }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * Avoid printing multiple time the same message
 | 
					   * Avoid printing multiple time the same message
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
@ -104,195 +102,187 @@ class DatabaseMigration {
 | 
				
			|||||||
    await this.$setStatisticsAddedIndexedFlag(databaseSchemaVersion);
 | 
					    await this.$setStatisticsAddedIndexedFlag(databaseSchemaVersion);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const isBitcoin = ['mainnet', 'testnet', 'signet'].includes(config.MEMPOOL.NETWORK);
 | 
					    const isBitcoin = ['mainnet', 'testnet', 'signet'].includes(config.MEMPOOL.NETWORK);
 | 
				
			||||||
    try {
 | 
					 | 
				
			||||||
      await this.$executeQuery(this.getCreateElementsTableQuery(), await this.$checkIfTableExists('elements_pegs'));
 | 
					 | 
				
			||||||
      await this.$executeQuery(this.getCreateStatisticsQuery(), await this.$checkIfTableExists('statistics'));
 | 
					 | 
				
			||||||
      if (databaseSchemaVersion < 2 && this.statisticsAddedIndexed === false) {
 | 
					 | 
				
			||||||
        await this.$executeQuery(`CREATE INDEX added ON statistics (added);`);
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      if (databaseSchemaVersion < 3) {
 | 
					 | 
				
			||||||
        await this.$executeQuery(this.getCreatePoolsTableQuery(), await this.$checkIfTableExists('pools'));
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      if (databaseSchemaVersion < 4) {
 | 
					 | 
				
			||||||
        await this.$executeQuery('DROP table IF EXISTS blocks;');
 | 
					 | 
				
			||||||
        await this.$executeQuery(this.getCreateBlocksTableQuery(), await this.$checkIfTableExists('blocks'));
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      if (databaseSchemaVersion < 5 && isBitcoin === true) {
 | 
					 | 
				
			||||||
        this.uniqueLog(logger.notice, this.blocksTruncatedMessage);
 | 
					 | 
				
			||||||
        await this.$executeQuery('TRUNCATE blocks;'); // Need to re-index
 | 
					 | 
				
			||||||
        await this.$executeQuery('ALTER TABLE blocks ADD `reward` double unsigned NOT NULL DEFAULT "0"');
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (databaseSchemaVersion < 6 && isBitcoin === true) {
 | 
					    await this.$executeQuery(this.getCreateElementsTableQuery(), await this.$checkIfTableExists('elements_pegs'));
 | 
				
			||||||
        this.uniqueLog(logger.notice, this.blocksTruncatedMessage);
 | 
					    await this.$executeQuery(this.getCreateStatisticsQuery(), await this.$checkIfTableExists('statistics'));
 | 
				
			||||||
        await this.$executeQuery('TRUNCATE blocks;');  // Need to re-index
 | 
					    if (databaseSchemaVersion < 2 && this.statisticsAddedIndexed === false) {
 | 
				
			||||||
        // Cleanup original blocks fields type
 | 
					      await this.$executeQuery(`CREATE INDEX added ON statistics (added);`);
 | 
				
			||||||
        await this.$executeQuery('ALTER TABLE blocks MODIFY `height` integer unsigned NOT NULL DEFAULT "0"');
 | 
					    }
 | 
				
			||||||
        await this.$executeQuery('ALTER TABLE blocks MODIFY `tx_count` smallint unsigned NOT NULL DEFAULT "0"');
 | 
					    if (databaseSchemaVersion < 3) {
 | 
				
			||||||
        await this.$executeQuery('ALTER TABLE blocks MODIFY `size` integer unsigned NOT NULL DEFAULT "0"');
 | 
					      await this.$executeQuery(this.getCreatePoolsTableQuery(), await this.$checkIfTableExists('pools'));
 | 
				
			||||||
        await this.$executeQuery('ALTER TABLE blocks MODIFY `weight` integer unsigned NOT NULL DEFAULT "0"');
 | 
					    }
 | 
				
			||||||
        await this.$executeQuery('ALTER TABLE blocks MODIFY `difficulty` double NOT NULL DEFAULT "0"');
 | 
					    if (databaseSchemaVersion < 4) {
 | 
				
			||||||
        // We also fix the pools.id type so we need to drop/re-create the foreign key
 | 
					      await this.$executeQuery('DROP table IF EXISTS blocks;');
 | 
				
			||||||
        await this.$executeQuery('ALTER TABLE blocks DROP FOREIGN KEY IF EXISTS `blocks_ibfk_1`');
 | 
					      await this.$executeQuery(this.getCreateBlocksTableQuery(), await this.$checkIfTableExists('blocks'));
 | 
				
			||||||
        await this.$executeQuery('ALTER TABLE pools MODIFY `id` smallint unsigned AUTO_INCREMENT');
 | 
					    }
 | 
				
			||||||
        await this.$executeQuery('ALTER TABLE blocks MODIFY `pool_id` smallint unsigned NULL');
 | 
					    if (databaseSchemaVersion < 5 && isBitcoin === true) {
 | 
				
			||||||
        await this.$executeQuery('ALTER TABLE blocks ADD FOREIGN KEY (`pool_id`) REFERENCES `pools` (`id`)');
 | 
					      this.uniqueLog(logger.notice, this.blocksTruncatedMessage);
 | 
				
			||||||
        // Add new block indexing fields
 | 
					      await this.$executeQuery('TRUNCATE blocks;'); // Need to re-index
 | 
				
			||||||
        await this.$executeQuery('ALTER TABLE blocks ADD `version` integer unsigned NOT NULL DEFAULT "0"');
 | 
					      await this.$executeQuery('ALTER TABLE blocks ADD `reward` double unsigned NOT NULL DEFAULT "0"');
 | 
				
			||||||
        await this.$executeQuery('ALTER TABLE blocks ADD `bits` integer unsigned NOT NULL DEFAULT "0"');
 | 
					    }
 | 
				
			||||||
        await this.$executeQuery('ALTER TABLE blocks ADD `nonce` bigint unsigned NOT NULL DEFAULT "0"');
 | 
					 | 
				
			||||||
        await this.$executeQuery('ALTER TABLE blocks ADD `merkle_root` varchar(65) NOT NULL DEFAULT ""');
 | 
					 | 
				
			||||||
        await this.$executeQuery('ALTER TABLE blocks ADD `previous_block_hash` varchar(65) NULL');
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (databaseSchemaVersion < 7 && isBitcoin === true) {
 | 
					    if (databaseSchemaVersion < 6 && isBitcoin === true) {
 | 
				
			||||||
        await this.$executeQuery('DROP table IF EXISTS hashrates;');
 | 
					      this.uniqueLog(logger.notice, this.blocksTruncatedMessage);
 | 
				
			||||||
        await this.$executeQuery(this.getCreateDailyStatsTableQuery(), await this.$checkIfTableExists('hashrates'));
 | 
					      await this.$executeQuery('TRUNCATE blocks;');  // Need to re-index
 | 
				
			||||||
      }
 | 
					      // Cleanup original blocks fields type
 | 
				
			||||||
 | 
					      await this.$executeQuery('ALTER TABLE blocks MODIFY `height` integer unsigned NOT NULL DEFAULT "0"');
 | 
				
			||||||
 | 
					      await this.$executeQuery('ALTER TABLE blocks MODIFY `tx_count` smallint unsigned NOT NULL DEFAULT "0"');
 | 
				
			||||||
 | 
					      await this.$executeQuery('ALTER TABLE blocks MODIFY `size` integer unsigned NOT NULL DEFAULT "0"');
 | 
				
			||||||
 | 
					      await this.$executeQuery('ALTER TABLE blocks MODIFY `weight` integer unsigned NOT NULL DEFAULT "0"');
 | 
				
			||||||
 | 
					      await this.$executeQuery('ALTER TABLE blocks MODIFY `difficulty` double NOT NULL DEFAULT "0"');
 | 
				
			||||||
 | 
					      // We also fix the pools.id type so we need to drop/re-create the foreign key
 | 
				
			||||||
 | 
					      await this.$executeQuery('ALTER TABLE blocks DROP FOREIGN KEY IF EXISTS `blocks_ibfk_1`');
 | 
				
			||||||
 | 
					      await this.$executeQuery('ALTER TABLE pools MODIFY `id` smallint unsigned AUTO_INCREMENT');
 | 
				
			||||||
 | 
					      await this.$executeQuery('ALTER TABLE blocks MODIFY `pool_id` smallint unsigned NULL');
 | 
				
			||||||
 | 
					      await this.$executeQuery('ALTER TABLE blocks ADD FOREIGN KEY (`pool_id`) REFERENCES `pools` (`id`)');
 | 
				
			||||||
 | 
					      // Add new block indexing fields
 | 
				
			||||||
 | 
					      await this.$executeQuery('ALTER TABLE blocks ADD `version` integer unsigned NOT NULL DEFAULT "0"');
 | 
				
			||||||
 | 
					      await this.$executeQuery('ALTER TABLE blocks ADD `bits` integer unsigned NOT NULL DEFAULT "0"');
 | 
				
			||||||
 | 
					      await this.$executeQuery('ALTER TABLE blocks ADD `nonce` bigint unsigned NOT NULL DEFAULT "0"');
 | 
				
			||||||
 | 
					      await this.$executeQuery('ALTER TABLE blocks ADD `merkle_root` varchar(65) NOT NULL DEFAULT ""');
 | 
				
			||||||
 | 
					      await this.$executeQuery('ALTER TABLE blocks ADD `previous_block_hash` varchar(65) NULL');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (databaseSchemaVersion < 8 && isBitcoin === true) {
 | 
					    if (databaseSchemaVersion < 7 && isBitcoin === true) {
 | 
				
			||||||
        this.uniqueLog(logger.notice, this.blocksTruncatedMessage);
 | 
					      await this.$executeQuery('DROP table IF EXISTS hashrates;');
 | 
				
			||||||
        await this.$executeQuery('TRUNCATE hashrates;'); // Need to re-index
 | 
					      await this.$executeQuery(this.getCreateDailyStatsTableQuery(), await this.$checkIfTableExists('hashrates'));
 | 
				
			||||||
        await this.$executeQuery('ALTER TABLE `hashrates` DROP INDEX `PRIMARY`');
 | 
					    }
 | 
				
			||||||
        await this.$executeQuery('ALTER TABLE `hashrates` ADD `id` int NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST');
 | 
					 | 
				
			||||||
        await this.$executeQuery('ALTER TABLE `hashrates` ADD `share` float NOT NULL DEFAULT "0"');
 | 
					 | 
				
			||||||
        await this.$executeQuery('ALTER TABLE `hashrates` ADD `type` enum("daily", "weekly") DEFAULT "daily"');
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (databaseSchemaVersion < 9 && isBitcoin === true) {
 | 
					    if (databaseSchemaVersion < 8 && isBitcoin === true) {
 | 
				
			||||||
        this.uniqueLog(logger.notice, this.hashratesTruncatedMessage);
 | 
					      this.uniqueLog(logger.notice, this.blocksTruncatedMessage);
 | 
				
			||||||
        await this.$executeQuery('TRUNCATE hashrates;'); // Need to re-index
 | 
					      await this.$executeQuery('TRUNCATE hashrates;'); // Need to re-index
 | 
				
			||||||
        await this.$executeQuery('ALTER TABLE `state` CHANGE `name` `name` varchar(100)');
 | 
					      await this.$executeQuery('ALTER TABLE `hashrates` DROP INDEX `PRIMARY`');
 | 
				
			||||||
        await this.$executeQuery('ALTER TABLE `hashrates` ADD UNIQUE `hashrate_timestamp_pool_id` (`hashrate_timestamp`, `pool_id`)');
 | 
					      await this.$executeQuery('ALTER TABLE `hashrates` ADD `id` int NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST');
 | 
				
			||||||
      }
 | 
					      await this.$executeQuery('ALTER TABLE `hashrates` ADD `share` float NOT NULL DEFAULT "0"');
 | 
				
			||||||
 | 
					      await this.$executeQuery('ALTER TABLE `hashrates` ADD `type` enum("daily", "weekly") DEFAULT "daily"');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (databaseSchemaVersion < 10 && isBitcoin === true) {
 | 
					    if (databaseSchemaVersion < 9 && isBitcoin === true) {
 | 
				
			||||||
        await this.$executeQuery('ALTER TABLE `blocks` ADD INDEX `blockTimestamp` (`blockTimestamp`)');
 | 
					      this.uniqueLog(logger.notice, this.hashratesTruncatedMessage);
 | 
				
			||||||
      }
 | 
					      await this.$executeQuery('TRUNCATE hashrates;'); // Need to re-index
 | 
				
			||||||
 | 
					      await this.$executeQuery('ALTER TABLE `state` CHANGE `name` `name` varchar(100)');
 | 
				
			||||||
 | 
					      await this.$executeQuery('ALTER TABLE `hashrates` ADD UNIQUE `hashrate_timestamp_pool_id` (`hashrate_timestamp`, `pool_id`)');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (databaseSchemaVersion < 11 && isBitcoin === true) {
 | 
					    if (databaseSchemaVersion < 10 && isBitcoin === true) {
 | 
				
			||||||
        this.uniqueLog(logger.notice, this.blocksTruncatedMessage);
 | 
					      await this.$executeQuery('ALTER TABLE `blocks` ADD INDEX `blockTimestamp` (`blockTimestamp`)');
 | 
				
			||||||
        await this.$executeQuery('TRUNCATE blocks;'); // Need to re-index
 | 
					    }
 | 
				
			||||||
        await this.$executeQuery(`ALTER TABLE blocks
 | 
					 | 
				
			||||||
          ADD avg_fee INT UNSIGNED NULL,
 | 
					 | 
				
			||||||
          ADD avg_fee_rate INT UNSIGNED NULL
 | 
					 | 
				
			||||||
        `);
 | 
					 | 
				
			||||||
        await this.$executeQuery('ALTER TABLE blocks MODIFY `reward` BIGINT UNSIGNED NOT NULL DEFAULT "0"');
 | 
					 | 
				
			||||||
        await this.$executeQuery('ALTER TABLE blocks MODIFY `median_fee` INT UNSIGNED NOT NULL DEFAULT "0"');
 | 
					 | 
				
			||||||
        await this.$executeQuery('ALTER TABLE blocks MODIFY `fees` INT UNSIGNED NOT NULL DEFAULT "0"');
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (databaseSchemaVersion < 12 && isBitcoin === true) {
 | 
					    if (databaseSchemaVersion < 11 && isBitcoin === true) {
 | 
				
			||||||
        // No need to re-index because the new data type can contain larger values
 | 
					      this.uniqueLog(logger.notice, this.blocksTruncatedMessage);
 | 
				
			||||||
        await this.$executeQuery('ALTER TABLE blocks MODIFY `fees` BIGINT UNSIGNED NOT NULL DEFAULT "0"');
 | 
					      await this.$executeQuery('TRUNCATE blocks;'); // Need to re-index
 | 
				
			||||||
      }
 | 
					      await this.$executeQuery(`ALTER TABLE blocks
 | 
				
			||||||
 | 
					        ADD avg_fee INT UNSIGNED NULL,
 | 
				
			||||||
 | 
					        ADD avg_fee_rate INT UNSIGNED NULL
 | 
				
			||||||
 | 
					      `);
 | 
				
			||||||
 | 
					      await this.$executeQuery('ALTER TABLE blocks MODIFY `reward` BIGINT UNSIGNED NOT NULL DEFAULT "0"');
 | 
				
			||||||
 | 
					      await this.$executeQuery('ALTER TABLE blocks MODIFY `median_fee` INT UNSIGNED NOT NULL DEFAULT "0"');
 | 
				
			||||||
 | 
					      await this.$executeQuery('ALTER TABLE blocks MODIFY `fees` INT UNSIGNED NOT NULL DEFAULT "0"');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (databaseSchemaVersion < 13 && isBitcoin === true) {
 | 
					    if (databaseSchemaVersion < 12 && isBitcoin === true) {
 | 
				
			||||||
        await this.$executeQuery('ALTER TABLE blocks MODIFY `difficulty` DOUBLE UNSIGNED NOT NULL DEFAULT "0"');
 | 
					      // No need to re-index because the new data type can contain larger values
 | 
				
			||||||
        await this.$executeQuery('ALTER TABLE blocks MODIFY `median_fee` BIGINT UNSIGNED NOT NULL DEFAULT "0"');
 | 
					      await this.$executeQuery('ALTER TABLE blocks MODIFY `fees` BIGINT UNSIGNED NOT NULL DEFAULT "0"');
 | 
				
			||||||
        await this.$executeQuery('ALTER TABLE blocks MODIFY `avg_fee` BIGINT UNSIGNED NOT NULL DEFAULT "0"');
 | 
					    }
 | 
				
			||||||
        await this.$executeQuery('ALTER TABLE blocks MODIFY `avg_fee_rate` BIGINT UNSIGNED NOT NULL DEFAULT "0"');
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (databaseSchemaVersion < 14 && isBitcoin === true) {
 | 
					    if (databaseSchemaVersion < 13 && isBitcoin === true) {
 | 
				
			||||||
        this.uniqueLog(logger.notice, this.hashratesTruncatedMessage);
 | 
					      await this.$executeQuery('ALTER TABLE blocks MODIFY `difficulty` DOUBLE UNSIGNED NOT NULL DEFAULT "0"');
 | 
				
			||||||
        await this.$executeQuery('TRUNCATE hashrates;'); // Need to re-index
 | 
					      await this.$executeQuery('ALTER TABLE blocks MODIFY `median_fee` BIGINT UNSIGNED NOT NULL DEFAULT "0"');
 | 
				
			||||||
        await this.$executeQuery('ALTER TABLE `hashrates` DROP FOREIGN KEY `hashrates_ibfk_1`');
 | 
					      await this.$executeQuery('ALTER TABLE blocks MODIFY `avg_fee` BIGINT UNSIGNED NOT NULL DEFAULT "0"');
 | 
				
			||||||
        await this.$executeQuery('ALTER TABLE `hashrates` MODIFY `pool_id` SMALLINT UNSIGNED NOT NULL DEFAULT "0"');
 | 
					      await this.$executeQuery('ALTER TABLE blocks MODIFY `avg_fee_rate` BIGINT UNSIGNED NOT NULL DEFAULT "0"');
 | 
				
			||||||
      }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (databaseSchemaVersion < 16 && isBitcoin === true) {
 | 
					    if (databaseSchemaVersion < 14 && isBitcoin === true) {
 | 
				
			||||||
        this.uniqueLog(logger.notice, this.hashratesTruncatedMessage);
 | 
					      this.uniqueLog(logger.notice, this.hashratesTruncatedMessage);
 | 
				
			||||||
        await this.$executeQuery('TRUNCATE hashrates;'); // Need to re-index because we changed timestamps
 | 
					      await this.$executeQuery('TRUNCATE hashrates;'); // Need to re-index
 | 
				
			||||||
      }
 | 
					      await this.$executeQuery('ALTER TABLE `hashrates` DROP FOREIGN KEY `hashrates_ibfk_1`');
 | 
				
			||||||
 | 
					      await this.$executeQuery('ALTER TABLE `hashrates` MODIFY `pool_id` SMALLINT UNSIGNED NOT NULL DEFAULT "0"');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (databaseSchemaVersion < 17 && isBitcoin === true) {
 | 
					    if (databaseSchemaVersion < 16 && isBitcoin === true) {
 | 
				
			||||||
        await this.$executeQuery('ALTER TABLE `pools` ADD `slug` CHAR(50) NULL');
 | 
					      this.uniqueLog(logger.notice, this.hashratesTruncatedMessage);
 | 
				
			||||||
      }
 | 
					      await this.$executeQuery('TRUNCATE hashrates;'); // Need to re-index because we changed timestamps
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (databaseSchemaVersion < 18 && isBitcoin === true) {
 | 
					    if (databaseSchemaVersion < 17 && isBitcoin === true) {
 | 
				
			||||||
        await this.$executeQuery('ALTER TABLE `blocks` ADD INDEX `hash` (`hash`);');
 | 
					      await this.$executeQuery('ALTER TABLE `pools` ADD `slug` CHAR(50) NULL');
 | 
				
			||||||
      }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (databaseSchemaVersion < 19) {
 | 
					    if (databaseSchemaVersion < 18 && isBitcoin === true) {
 | 
				
			||||||
        await this.$executeQuery(this.getCreateRatesTableQuery(), await this.$checkIfTableExists('rates'));
 | 
					      await this.$executeQuery('ALTER TABLE `blocks` ADD INDEX `hash` (`hash`);');
 | 
				
			||||||
      }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (databaseSchemaVersion < 20 && isBitcoin === true) {
 | 
					    if (databaseSchemaVersion < 19) {
 | 
				
			||||||
        await this.$executeQuery(this.getCreateBlocksSummariesTableQuery(), await this.$checkIfTableExists('blocks_summaries'));
 | 
					      await this.$executeQuery(this.getCreateRatesTableQuery(), await this.$checkIfTableExists('rates'));
 | 
				
			||||||
      }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (databaseSchemaVersion < 21) {
 | 
					    if (databaseSchemaVersion < 20 && isBitcoin === true) {
 | 
				
			||||||
        await this.$executeQuery('DROP TABLE IF EXISTS `rates`');
 | 
					      await this.$executeQuery(this.getCreateBlocksSummariesTableQuery(), await this.$checkIfTableExists('blocks_summaries'));
 | 
				
			||||||
        await this.$executeQuery(this.getCreatePricesTableQuery(), await this.$checkIfTableExists('prices'));
 | 
					    }
 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (databaseSchemaVersion < 22 && isBitcoin === true) {
 | 
					    if (databaseSchemaVersion < 21) {
 | 
				
			||||||
        await this.$executeQuery('DROP TABLE IF EXISTS `difficulty_adjustments`');
 | 
					      await this.$executeQuery('DROP TABLE IF EXISTS `rates`');
 | 
				
			||||||
        await this.$executeQuery(this.getCreateDifficultyAdjustmentsTableQuery(), await this.$checkIfTableExists('difficulty_adjustments'));
 | 
					      await this.$executeQuery(this.getCreatePricesTableQuery(), await this.$checkIfTableExists('prices'));
 | 
				
			||||||
      }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (databaseSchemaVersion < 23) {
 | 
					    if (databaseSchemaVersion < 22 && isBitcoin === true) {
 | 
				
			||||||
        await this.$executeQuery('TRUNCATE `prices`');
 | 
					      await this.$executeQuery('DROP TABLE IF EXISTS `difficulty_adjustments`');
 | 
				
			||||||
        await this.$executeQuery('ALTER TABLE `prices` DROP `avg_prices`');
 | 
					      await this.$executeQuery(this.getCreateDifficultyAdjustmentsTableQuery(), await this.$checkIfTableExists('difficulty_adjustments'));
 | 
				
			||||||
        await this.$executeQuery('ALTER TABLE `prices` ADD `USD` float DEFAULT "0"');
 | 
					    }
 | 
				
			||||||
        await this.$executeQuery('ALTER TABLE `prices` ADD `EUR` float DEFAULT "0"');
 | 
					 | 
				
			||||||
        await this.$executeQuery('ALTER TABLE `prices` ADD `GBP` float DEFAULT "0"');
 | 
					 | 
				
			||||||
        await this.$executeQuery('ALTER TABLE `prices` ADD `CAD` float DEFAULT "0"');
 | 
					 | 
				
			||||||
        await this.$executeQuery('ALTER TABLE `prices` ADD `CHF` float DEFAULT "0"');
 | 
					 | 
				
			||||||
        await this.$executeQuery('ALTER TABLE `prices` ADD `AUD` float DEFAULT "0"');
 | 
					 | 
				
			||||||
        await this.$executeQuery('ALTER TABLE `prices` ADD `JPY` float DEFAULT "0"');
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (databaseSchemaVersion < 24 && isBitcoin == true) {
 | 
					    if (databaseSchemaVersion < 23) {
 | 
				
			||||||
        await this.$executeQuery('DROP TABLE IF EXISTS `blocks_audits`');
 | 
					      await this.$executeQuery('TRUNCATE `prices`');
 | 
				
			||||||
        await this.$executeQuery(this.getCreateBlocksAuditsTableQuery(), await this.$checkIfTableExists('blocks_audits'));
 | 
					      await this.$executeQuery('ALTER TABLE `prices` DROP `avg_prices`');
 | 
				
			||||||
      }
 | 
					      await this.$executeQuery('ALTER TABLE `prices` ADD `USD` float DEFAULT "0"');
 | 
				
			||||||
 | 
					      await this.$executeQuery('ALTER TABLE `prices` ADD `EUR` float DEFAULT "0"');
 | 
				
			||||||
 | 
					      await this.$executeQuery('ALTER TABLE `prices` ADD `GBP` float DEFAULT "0"');
 | 
				
			||||||
 | 
					      await this.$executeQuery('ALTER TABLE `prices` ADD `CAD` float DEFAULT "0"');
 | 
				
			||||||
 | 
					      await this.$executeQuery('ALTER TABLE `prices` ADD `CHF` float DEFAULT "0"');
 | 
				
			||||||
 | 
					      await this.$executeQuery('ALTER TABLE `prices` ADD `AUD` float DEFAULT "0"');
 | 
				
			||||||
 | 
					      await this.$executeQuery('ALTER TABLE `prices` ADD `JPY` float DEFAULT "0"');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (databaseSchemaVersion < 25 && isBitcoin === true) {
 | 
					    if (databaseSchemaVersion < 24 && isBitcoin == true) {
 | 
				
			||||||
        await this.$executeQuery(`INSERT INTO state VALUES('last_node_stats', 0, '1970-01-01');`);
 | 
					      await this.$executeQuery('DROP TABLE IF EXISTS `blocks_audits`');
 | 
				
			||||||
        await this.$executeQuery(this.getCreateLightningStatisticsQuery(), await this.$checkIfTableExists('lightning_stats'));
 | 
					      await this.$executeQuery(this.getCreateBlocksAuditsTableQuery(), await this.$checkIfTableExists('blocks_audits'));
 | 
				
			||||||
        await this.$executeQuery(this.getCreateNodesQuery(), await this.$checkIfTableExists('nodes'));
 | 
					    }
 | 
				
			||||||
        await this.$executeQuery(this.getCreateChannelsQuery(), await this.$checkIfTableExists('channels'));
 | 
					 | 
				
			||||||
        await this.$executeQuery(this.getCreateNodesStatsQuery(), await this.$checkIfTableExists('node_stats'));
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (databaseSchemaVersion < 26 && isBitcoin === true) {
 | 
					    if (databaseSchemaVersion < 25 && isBitcoin === true) {
 | 
				
			||||||
        this.uniqueLog(logger.notice, `'lightning_stats' table has been truncated. Will re-generate historical data from scratch.`);
 | 
					      await this.$executeQuery(`INSERT INTO state VALUES('last_node_stats', 0, '1970-01-01');`);
 | 
				
			||||||
        await this.$executeQuery(`TRUNCATE lightning_stats`);
 | 
					      await this.$executeQuery(this.getCreateLightningStatisticsQuery(), await this.$checkIfTableExists('lightning_stats'));
 | 
				
			||||||
        await this.$executeQuery('ALTER TABLE `lightning_stats` ADD tor_nodes int(11) NOT NULL DEFAULT "0"');
 | 
					      await this.$executeQuery(this.getCreateNodesQuery(), await this.$checkIfTableExists('nodes'));
 | 
				
			||||||
        await this.$executeQuery('ALTER TABLE `lightning_stats` ADD clearnet_nodes int(11) NOT NULL DEFAULT "0"');
 | 
					      await this.$executeQuery(this.getCreateChannelsQuery(), await this.$checkIfTableExists('channels'));
 | 
				
			||||||
        await this.$executeQuery('ALTER TABLE `lightning_stats` ADD unannounced_nodes int(11) NOT NULL DEFAULT "0"');
 | 
					      await this.$executeQuery(this.getCreateNodesStatsQuery(), await this.$checkIfTableExists('node_stats'));
 | 
				
			||||||
      }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (databaseSchemaVersion < 27 && isBitcoin === true) {
 | 
					    if (databaseSchemaVersion < 26 && isBitcoin === true) {
 | 
				
			||||||
        await this.$executeQuery('ALTER TABLE `lightning_stats` ADD avg_capacity bigint(20) unsigned NOT NULL DEFAULT "0"');
 | 
					      this.uniqueLog(logger.notice, `'lightning_stats' table has been truncated. Will re-generate historical data from scratch.`);
 | 
				
			||||||
        await this.$executeQuery('ALTER TABLE `lightning_stats` ADD avg_fee_rate int(11) unsigned NOT NULL DEFAULT "0"');
 | 
					      await this.$executeQuery(`TRUNCATE lightning_stats`);
 | 
				
			||||||
        await this.$executeQuery('ALTER TABLE `lightning_stats` ADD avg_base_fee_mtokens bigint(20) unsigned NOT NULL DEFAULT "0"');
 | 
					      await this.$executeQuery('ALTER TABLE `lightning_stats` ADD tor_nodes int(11) NOT NULL DEFAULT "0"');
 | 
				
			||||||
        await this.$executeQuery('ALTER TABLE `lightning_stats` ADD med_capacity bigint(20) unsigned NOT NULL DEFAULT "0"');
 | 
					      await this.$executeQuery('ALTER TABLE `lightning_stats` ADD clearnet_nodes int(11) NOT NULL DEFAULT "0"');
 | 
				
			||||||
        await this.$executeQuery('ALTER TABLE `lightning_stats` ADD med_fee_rate int(11) unsigned NOT NULL DEFAULT "0"');
 | 
					      await this.$executeQuery('ALTER TABLE `lightning_stats` ADD unannounced_nodes int(11) NOT NULL DEFAULT "0"');
 | 
				
			||||||
        await this.$executeQuery('ALTER TABLE `lightning_stats` ADD med_base_fee_mtokens bigint(20) unsigned NOT NULL DEFAULT "0"');
 | 
					    }
 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (databaseSchemaVersion < 28 && isBitcoin === true) {
 | 
					    if (databaseSchemaVersion < 28 && isBitcoin === true) {
 | 
				
			||||||
        await this.$executeQuery(`TRUNCATE lightning_stats`);
 | 
					      await this.$executeQuery(`TRUNCATE lightning_stats`);
 | 
				
			||||||
        await this.$executeQuery(`TRUNCATE node_stats`);
 | 
					      await this.$executeQuery(`TRUNCATE node_stats`);
 | 
				
			||||||
        await this.$executeQuery(`ALTER TABLE lightning_stats MODIFY added DATE`);
 | 
					      await this.$executeQuery(`ALTER TABLE lightning_stats MODIFY added DATE`);
 | 
				
			||||||
      }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (databaseSchemaVersion < 29 && isBitcoin === true) {
 | 
					    if (databaseSchemaVersion < 29 && isBitcoin === true) {
 | 
				
			||||||
        await this.$executeQuery(this.getCreateGeoNamesTableQuery(), await this.$checkIfTableExists('geo_names'));
 | 
					      await this.$executeQuery(this.getCreateGeoNamesTableQuery(), await this.$checkIfTableExists('geo_names'));
 | 
				
			||||||
        await this.$executeQuery('ALTER TABLE `nodes` ADD as_number int(11) unsigned NULL DEFAULT NULL');
 | 
					      await this.$executeQuery('ALTER TABLE `nodes` ADD as_number int(11) unsigned NULL DEFAULT NULL');
 | 
				
			||||||
        await this.$executeQuery('ALTER TABLE `nodes` ADD city_id int(11) unsigned NULL DEFAULT NULL');
 | 
					      await this.$executeQuery('ALTER TABLE `nodes` ADD city_id int(11) unsigned NULL DEFAULT NULL');
 | 
				
			||||||
        await this.$executeQuery('ALTER TABLE `nodes` ADD country_id int(11) unsigned NULL DEFAULT NULL');
 | 
					      await this.$executeQuery('ALTER TABLE `nodes` ADD country_id int(11) unsigned NULL DEFAULT NULL');
 | 
				
			||||||
        await this.$executeQuery('ALTER TABLE `nodes` ADD accuracy_radius int(11) unsigned NULL DEFAULT NULL');
 | 
					      await this.$executeQuery('ALTER TABLE `nodes` ADD accuracy_radius int(11) unsigned NULL DEFAULT NULL');
 | 
				
			||||||
        await this.$executeQuery('ALTER TABLE `nodes` ADD subdivision_id int(11) unsigned NULL DEFAULT NULL');
 | 
					      await this.$executeQuery('ALTER TABLE `nodes` ADD subdivision_id int(11) unsigned NULL DEFAULT NULL');
 | 
				
			||||||
        await this.$executeQuery('ALTER TABLE `nodes` ADD longitude double NULL DEFAULT NULL');
 | 
					      await this.$executeQuery('ALTER TABLE `nodes` ADD longitude double NULL DEFAULT NULL');
 | 
				
			||||||
        await this.$executeQuery('ALTER TABLE `nodes` ADD latitude double NULL DEFAULT NULL');
 | 
					      await this.$executeQuery('ALTER TABLE `nodes` ADD latitude double NULL DEFAULT NULL');
 | 
				
			||||||
      }
 | 
					    }
 | 
				
			||||||
 | 
					    if (databaseSchemaVersion < 25 && isBitcoin == true) { // Link blocks to prices
 | 
				
			||||||
    } catch (e) {
 | 
					      await this.$executeQuery('ALTER TABLE `prices` ADD `id` int NULL AUTO_INCREMENT UNIQUE');
 | 
				
			||||||
      throw e;
 | 
					      await this.$executeQuery('DROP TABLE IF EXISTS `blocks_prices`');
 | 
				
			||||||
 | 
					      await this.$executeQuery(this.getCreateBlocksPricesTableQuery(), await this.$checkIfTableExists('blocks_prices'));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -331,7 +321,7 @@ class DatabaseMigration {
 | 
				
			|||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * Small query execution wrapper to log all executed queries
 | 
					   * Small query execution wrapper to log all executed queries
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  private async $executeQuery(query: string, silent: boolean = false): Promise<any> {
 | 
					  private async $executeQuery(query: string, silent = false): Promise<any> {
 | 
				
			||||||
    if (!silent) {
 | 
					    if (!silent) {
 | 
				
			||||||
      logger.debug('MIGRATIONS: Execute query:\n' + query);
 | 
					      logger.debug('MIGRATIONS: Execute query:\n' + query);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -360,21 +350,17 @@ class DatabaseMigration {
 | 
				
			|||||||
   * Create the `state` table
 | 
					   * Create the `state` table
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  private async $createMigrationStateTable(): Promise<void> {
 | 
					  private async $createMigrationStateTable(): Promise<void> {
 | 
				
			||||||
    try {
 | 
					    const query = `CREATE TABLE IF NOT EXISTS state (
 | 
				
			||||||
      const query = `CREATE TABLE IF NOT EXISTS state (
 | 
					      name varchar(25) NOT NULL,
 | 
				
			||||||
        name varchar(25) NOT NULL,
 | 
					      number int(11) NULL,
 | 
				
			||||||
        number int(11) NULL,
 | 
					      string varchar(100) NULL,
 | 
				
			||||||
        string varchar(100) NULL,
 | 
					      CONSTRAINT name_unique UNIQUE (name)
 | 
				
			||||||
        CONSTRAINT name_unique UNIQUE (name)
 | 
					    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;`;
 | 
				
			||||||
      ) ENGINE=InnoDB DEFAULT CHARSET=utf8;`;
 | 
					    await this.$executeQuery(query);
 | 
				
			||||||
      await this.$executeQuery(query);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      // Set initial values
 | 
					    // Set initial values
 | 
				
			||||||
      await this.$executeQuery(`INSERT INTO state VALUES('schema_version', 0, NULL);`);
 | 
					    await this.$executeQuery(`INSERT INTO state VALUES('schema_version', 0, NULL);`);
 | 
				
			||||||
      await this.$executeQuery(`INSERT INTO state VALUES('last_elements_block', 0, NULL);`);
 | 
					    await this.$executeQuery(`INSERT INTO state VALUES('last_elements_block', 0, NULL);`);
 | 
				
			||||||
    } catch (e) {
 | 
					 | 
				
			||||||
      throw e;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
@ -714,6 +700,15 @@ class DatabaseMigration {
 | 
				
			|||||||
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;`
 | 
					    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;`
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private getCreateBlocksPricesTableQuery(): string {
 | 
				
			||||||
 | 
					    return `CREATE TABLE IF NOT EXISTS blocks_prices (
 | 
				
			||||||
 | 
					      height int(10) unsigned NOT NULL,
 | 
				
			||||||
 | 
					      price_id int(10) unsigned NOT NULL,
 | 
				
			||||||
 | 
					      PRIMARY KEY (height),
 | 
				
			||||||
 | 
					      INDEX (price_id)
 | 
				
			||||||
 | 
					    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;`;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public async $truncateIndexedData(tables: string[]) {
 | 
					  public async $truncateIndexedData(tables: string[]) {
 | 
				
			||||||
    const allowedTables = ['blocks', 'hashrates', 'prices'];
 | 
					    const allowedTables = ['blocks', 'hashrates', 'prices'];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -11,8 +11,11 @@ import indexer from '../../indexer';
 | 
				
			|||||||
import DifficultyAdjustmentsRepository from '../../repositories/DifficultyAdjustmentsRepository';
 | 
					import DifficultyAdjustmentsRepository from '../../repositories/DifficultyAdjustmentsRepository';
 | 
				
			||||||
import config from '../../config';
 | 
					import config from '../../config';
 | 
				
			||||||
import BlocksAuditsRepository from '../../repositories/BlocksAuditsRepository';
 | 
					import BlocksAuditsRepository from '../../repositories/BlocksAuditsRepository';
 | 
				
			||||||
 | 
					import PricesRepository from '../repositories/PricesRepository';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Mining {
 | 
					class Mining {
 | 
				
			||||||
 | 
					  blocksPriceIndexingRunning = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor() {
 | 
					  constructor() {
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -453,6 +456,70 @@ class Mining {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Create a link between blocks and the latest price at when they were mined
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  public async $indexBlockPrices() {
 | 
				
			||||||
 | 
					    if (this.blocksPriceIndexingRunning === true) {
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    this.blocksPriceIndexingRunning = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      const prices: any[] = await PricesRepository.$getPricesTimesAndId();    
 | 
				
			||||||
 | 
					      const blocksWithoutPrices: any[] = await BlocksRepository.$getBlocksWithoutPrice();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      let totalInserted = 0;
 | 
				
			||||||
 | 
					      const blocksPrices: BlockPrice[] = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      for (const block of blocksWithoutPrices) {
 | 
				
			||||||
 | 
					        // Quick optimisation, out mtgox feed only goes back to 2010-07-19 02:00:00, so skip the first 68951 blocks
 | 
				
			||||||
 | 
					        if (block.height < 68951) {
 | 
				
			||||||
 | 
					          blocksPrices.push({
 | 
				
			||||||
 | 
					            height: block.height,
 | 
				
			||||||
 | 
					            priceId: prices[0].id,
 | 
				
			||||||
 | 
					          });
 | 
				
			||||||
 | 
					          continue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        for (const price of prices) {
 | 
				
			||||||
 | 
					          if (block.timestamp < price.time) {
 | 
				
			||||||
 | 
					            blocksPrices.push({
 | 
				
			||||||
 | 
					              height: block.height,
 | 
				
			||||||
 | 
					              priceId: price.id,
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					          };
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (blocksPrices.length >= 100000) {
 | 
				
			||||||
 | 
					          totalInserted += blocksPrices.length;
 | 
				
			||||||
 | 
					          if (blocksWithoutPrices.length > 200000) {
 | 
				
			||||||
 | 
					            logger.debug(`Linking ${blocksPrices.length} newly indexed blocks to their closest price | Progress ${Math.round(totalInserted / blocksWithoutPrices.length * 100)}%`);
 | 
				
			||||||
 | 
					          } else {
 | 
				
			||||||
 | 
					            logger.debug(`Linking ${blocksPrices.length} newly indexed blocks to their closest price`);
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          await BlocksRepository.$saveBlockPrices(blocksPrices);
 | 
				
			||||||
 | 
					          blocksPrices.length = 0;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (blocksPrices.length > 0) {
 | 
				
			||||||
 | 
					        totalInserted += blocksPrices.length;
 | 
				
			||||||
 | 
					        if (blocksWithoutPrices.length > 200000) {
 | 
				
			||||||
 | 
					          logger.debug(`Linking ${blocksPrices.length} newly indexed blocks to their closest price | Progress ${Math.round(totalInserted / blocksWithoutPrices.length * 100)}%`);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          logger.debug(`Linking ${blocksPrices.length} newly indexed blocks to their closest price`);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        await BlocksRepository.$saveBlockPrices(blocksPrices);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    } catch (e) {
 | 
				
			||||||
 | 
					      this.blocksPriceIndexingRunning = false;
 | 
				
			||||||
 | 
					      throw e;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.blocksPriceIndexingRunning = false;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private getDateMidnight(date: Date): Date {
 | 
					  private getDateMidnight(date: Date): Date {
 | 
				
			||||||
    date.setUTCHours(0);
 | 
					    date.setUTCHours(0);
 | 
				
			||||||
    date.setUTCMinutes(0);
 | 
					    date.setUTCMinutes(0);
 | 
				
			||||||
 | 
				
			|||||||
@ -35,6 +35,8 @@ import miningRoutes from "./api/mining/mining-routes";
 | 
				
			|||||||
import bisqRoutes from "./api/bisq/bisq.routes";
 | 
					import bisqRoutes from "./api/bisq/bisq.routes";
 | 
				
			||||||
import liquidRoutes from "./api/liquid/liquid.routes";
 | 
					import liquidRoutes from "./api/liquid/liquid.routes";
 | 
				
			||||||
import bitcoinRoutes from "./api/bitcoin/bitcoin.routes";
 | 
					import bitcoinRoutes from "./api/bitcoin/bitcoin.routes";
 | 
				
			||||||
 | 
					import BlocksAuditsRepository from './repositories/BlocksAuditsRepository';
 | 
				
			||||||
 | 
					import mining from "./api/mining";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Server {
 | 
					class Server {
 | 
				
			||||||
  private wss: WebSocket.Server | undefined;
 | 
					  private wss: WebSocket.Server | undefined;
 | 
				
			||||||
@ -166,7 +168,7 @@ class Server {
 | 
				
			|||||||
      await blocks.$updateBlocks();
 | 
					      await blocks.$updateBlocks();
 | 
				
			||||||
      await memPool.$updateMempool();
 | 
					      await memPool.$updateMempool();
 | 
				
			||||||
      indexer.$run();
 | 
					      indexer.$run();
 | 
				
			||||||
      priceUpdater.$run();
 | 
					      priceUpdater.$run().then(mining.$indexBlockPrices.bind(this));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      setTimeout(this.runMainUpdateLoop.bind(this), config.MEMPOOL.POLL_RATE_MS);
 | 
					      setTimeout(this.runMainUpdateLoop.bind(this), config.MEMPOOL.POLL_RATE_MS);
 | 
				
			||||||
      this.currentBackendRetryInterval = 5;
 | 
					      this.currentBackendRetryInterval = 5;
 | 
				
			||||||
 | 
				
			|||||||
@ -48,7 +48,7 @@ class Indexer {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      await mining.$indexDifficultyAdjustments();
 | 
					      await mining.$indexDifficultyAdjustments();
 | 
				
			||||||
      await this.$resetHashratesIndexingState();
 | 
					      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();
 | 
				
			||||||
 | 
				
			|||||||
@ -121,6 +121,11 @@ export interface BlockSummary {
 | 
				
			|||||||
  transactions: TransactionStripped[];
 | 
					  transactions: TransactionStripped[];
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface BlockPrice {
 | 
				
			||||||
 | 
					  height: number;
 | 
				
			||||||
 | 
					  priceId: number;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface TransactionMinerInfo {
 | 
					export interface TransactionMinerInfo {
 | 
				
			||||||
  vin: VinStrippedToScriptsig[];
 | 
					  vin: VinStrippedToScriptsig[];
 | 
				
			||||||
  vout: VoutStrippedToScriptPubkey[];
 | 
					  vout: VoutStrippedToScriptPubkey[];
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
import { BlockExtended } from '../mempool.interfaces';
 | 
					import { BlockExtended, BlockPrice } from '../mempool.interfaces';
 | 
				
			||||||
import DB from '../database';
 | 
					import DB from '../database';
 | 
				
			||||||
import logger from '../logger';
 | 
					import logger from '../logger';
 | 
				
			||||||
import { Common } from '../api/common';
 | 
					import { Common } from '../api/common';
 | 
				
			||||||
@ -275,9 +275,7 @@ class BlocksRepository {
 | 
				
			|||||||
      previous_block_hash as previousblockhash,
 | 
					      previous_block_hash as previousblockhash,
 | 
				
			||||||
      avg_fee,
 | 
					      avg_fee,
 | 
				
			||||||
      avg_fee_rate,
 | 
					      avg_fee_rate,
 | 
				
			||||||
      IFNULL(JSON_EXTRACT(rates.bisq_rates, '$.USD'), null) as usd
 | 
					 | 
				
			||||||
      FROM blocks
 | 
					      FROM blocks
 | 
				
			||||||
      LEFT JOIN rates on rates.height = blocks.height
 | 
					 | 
				
			||||||
      WHERE pool_id = ?`;
 | 
					      WHERE pool_id = ?`;
 | 
				
			||||||
    params.push(pool.id);
 | 
					    params.push(pool.id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -335,12 +333,10 @@ class BlocksRepository {
 | 
				
			|||||||
        merkle_root,
 | 
					        merkle_root,
 | 
				
			||||||
        previous_block_hash as previousblockhash,
 | 
					        previous_block_hash as previousblockhash,
 | 
				
			||||||
        avg_fee,
 | 
					        avg_fee,
 | 
				
			||||||
        avg_fee_rate,
 | 
					        avg_fee_rate
 | 
				
			||||||
        IFNULL(JSON_EXTRACT(rates.bisq_rates, '$.USD'), null) as usd
 | 
					 | 
				
			||||||
        FROM blocks
 | 
					        FROM blocks
 | 
				
			||||||
        JOIN pools ON blocks.pool_id = pools.id
 | 
					        JOIN pools ON blocks.pool_id = pools.id
 | 
				
			||||||
        LEFT JOIN rates on rates.height = blocks.height
 | 
					        WHERE blocks.height = ${height}
 | 
				
			||||||
        WHERE blocks.height = ${height};
 | 
					 | 
				
			||||||
      `);
 | 
					      `);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (rows.length <= 0) {
 | 
					      if (rows.length <= 0) {
 | 
				
			||||||
@ -365,10 +361,8 @@ class BlocksRepository {
 | 
				
			|||||||
        pools.id as pool_id, pools.name as pool_name, pools.link as pool_link, pools.slug as pool_slug,
 | 
					        pools.id as pool_id, pools.name as pool_name, pools.link as pool_link, pools.slug as pool_slug,
 | 
				
			||||||
        pools.addresses as pool_addresses, pools.regexes as pool_regexes,
 | 
					        pools.addresses as pool_addresses, pools.regexes as pool_regexes,
 | 
				
			||||||
        previous_block_hash as previousblockhash,
 | 
					        previous_block_hash as previousblockhash,
 | 
				
			||||||
        IFNULL(JSON_EXTRACT(rates.bisq_rates, '$.USD'), null) as usd
 | 
					 | 
				
			||||||
        FROM blocks
 | 
					        FROM blocks
 | 
				
			||||||
        JOIN pools ON blocks.pool_id = pools.id
 | 
					        JOIN pools ON blocks.pool_id = pools.id
 | 
				
			||||||
        LEFT JOIN rates on rates.height = blocks.height
 | 
					 | 
				
			||||||
        WHERE hash = '${hash}';
 | 
					        WHERE hash = '${hash}';
 | 
				
			||||||
      `;
 | 
					      `;
 | 
				
			||||||
      const [rows]: any[] = await DB.query(query);
 | 
					      const [rows]: any[] = await DB.query(query);
 | 
				
			||||||
@ -393,7 +387,20 @@ class BlocksRepository {
 | 
				
			|||||||
      const [rows]: any[] = await DB.query(`SELECT UNIX_TIMESTAMP(blockTimestamp) as time, height, difficulty FROM blocks`);
 | 
					      const [rows]: any[] = await DB.query(`SELECT UNIX_TIMESTAMP(blockTimestamp) as time, height, difficulty FROM blocks`);
 | 
				
			||||||
      return rows;
 | 
					      return rows;
 | 
				
			||||||
    } catch (e) {
 | 
					    } catch (e) {
 | 
				
			||||||
      logger.err('Cannot generate difficulty history. Reason: ' + (e instanceof Error ? e.message : e));
 | 
					      logger.err('Cannot get blocks difficulty list from the db. Reason: ' + (e instanceof Error ? e.message : e));
 | 
				
			||||||
 | 
					      throw e;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Return blocks height
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					   public async $getBlocksHeightsAndTimestamp(): Promise<object[]> {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      const [rows]: any[] = await DB.query(`SELECT height, blockTimestamp as timestamp FROM blocks`);
 | 
				
			||||||
 | 
					      return rows;
 | 
				
			||||||
 | 
					    } catch (e) {
 | 
				
			||||||
 | 
					      logger.err('Cannot get blocks height and timestamp from the db. Reason: ' + (e instanceof Error ? e.message : e));
 | 
				
			||||||
      throw e;
 | 
					      throw e;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@ -481,10 +488,9 @@ class BlocksRepository {
 | 
				
			|||||||
      let query = `SELECT
 | 
					      let query = `SELECT
 | 
				
			||||||
        CAST(AVG(blocks.height) as INT) as avgHeight,
 | 
					        CAST(AVG(blocks.height) as INT) as avgHeight,
 | 
				
			||||||
        CAST(AVG(UNIX_TIMESTAMP(blockTimestamp)) as INT) as timestamp,
 | 
					        CAST(AVG(UNIX_TIMESTAMP(blockTimestamp)) as INT) as timestamp,
 | 
				
			||||||
        CAST(AVG(fees) as INT) as avgFees,
 | 
					        CAST(AVG(fees) as INT) as avgFees
 | 
				
			||||||
        IFNULL(JSON_EXTRACT(rates.bisq_rates, '$.USD'), null) as usd
 | 
					 | 
				
			||||||
        FROM blocks
 | 
					        FROM blocks
 | 
				
			||||||
        LEFT JOIN rates on rates.height = blocks.height`;
 | 
					      `;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (interval !== null) {
 | 
					      if (interval !== null) {
 | 
				
			||||||
        query += ` WHERE blockTimestamp BETWEEN DATE_SUB(NOW(), INTERVAL ${interval}) AND NOW()`;
 | 
					        query += ` WHERE blockTimestamp BETWEEN DATE_SUB(NOW(), INTERVAL ${interval}) AND NOW()`;
 | 
				
			||||||
@ -508,10 +514,9 @@ class BlocksRepository {
 | 
				
			|||||||
      let query = `SELECT
 | 
					      let query = `SELECT
 | 
				
			||||||
        CAST(AVG(blocks.height) as INT) as avgHeight,
 | 
					        CAST(AVG(blocks.height) as INT) as avgHeight,
 | 
				
			||||||
        CAST(AVG(UNIX_TIMESTAMP(blockTimestamp)) as INT) as timestamp,
 | 
					        CAST(AVG(UNIX_TIMESTAMP(blockTimestamp)) as INT) as timestamp,
 | 
				
			||||||
        CAST(AVG(reward) as INT) as avgRewards,
 | 
					        CAST(AVG(reward) as INT) as avgRewards
 | 
				
			||||||
        IFNULL(JSON_EXTRACT(rates.bisq_rates, '$.USD'), null) as usd
 | 
					 | 
				
			||||||
        FROM blocks
 | 
					        FROM blocks
 | 
				
			||||||
        LEFT JOIN rates on rates.height = blocks.height`;
 | 
					      `;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (interval !== null) {
 | 
					      if (interval !== null) {
 | 
				
			||||||
        query += ` WHERE blockTimestamp BETWEEN DATE_SUB(NOW(), INTERVAL ${interval}) AND NOW()`;
 | 
					        query += ` WHERE blockTimestamp BETWEEN DATE_SUB(NOW(), INTERVAL ${interval}) AND NOW()`;
 | 
				
			||||||
@ -638,6 +643,62 @@ class BlocksRepository {
 | 
				
			|||||||
      throw e;
 | 
					      throw e;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Get all blocks which have not be linked to a price yet
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					   public async $getBlocksWithoutPrice(): Promise<object[]> {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      const [rows]: any[] = await DB.query(`
 | 
				
			||||||
 | 
					        SELECT UNIX_TIMESTAMP(blocks.blockTimestamp) as timestamp, blocks.height
 | 
				
			||||||
 | 
					        FROM blocks
 | 
				
			||||||
 | 
					        LEFT JOIN blocks_prices ON blocks.height = blocks_prices.height
 | 
				
			||||||
 | 
					        WHERE blocks_prices.height IS NULL
 | 
				
			||||||
 | 
					        ORDER BY blocks.height
 | 
				
			||||||
 | 
					      `);
 | 
				
			||||||
 | 
					      return rows;
 | 
				
			||||||
 | 
					    } catch (e) {
 | 
				
			||||||
 | 
					      logger.err('Cannot get blocks height and timestamp from the db. Reason: ' + (e instanceof Error ? e.message : e));
 | 
				
			||||||
 | 
					      throw e;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Save block price
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					   public async $saveBlockPrice(blockPrice: BlockPrice): Promise<void> {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      await DB.query(`INSERT INTO blocks_prices(height, price_id) VALUE (?, ?)`, [blockPrice.height, blockPrice.priceId]);
 | 
				
			||||||
 | 
					    } catch (e: any) {
 | 
				
			||||||
 | 
					      if (e.errno === 1062) { // ER_DUP_ENTRY - This scenario is possible upon node backend restart
 | 
				
			||||||
 | 
					        logger.debug(`Cannot save block price for block ${blockPrice.height} because it has already been indexed, ignoring`);
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        logger.err(`Cannot save block price into db. Reason: ` + (e instanceof Error ? e.message : e));
 | 
				
			||||||
 | 
					        throw e;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Save block price by batch
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					   public async $saveBlockPrices(blockPrices: BlockPrice[]): Promise<void> {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      let query = `INSERT INTO blocks_prices(height, price_id) VALUES`;
 | 
				
			||||||
 | 
					      for (const price of blockPrices) {
 | 
				
			||||||
 | 
					        query += ` (${price.height}, ${price.priceId}),`
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      query = query.slice(0, -1);
 | 
				
			||||||
 | 
					      await DB.query(query);
 | 
				
			||||||
 | 
					    } catch (e: any) {
 | 
				
			||||||
 | 
					      if (e.errno === 1062) { // ER_DUP_ENTRY - This scenario is possible upon node backend restart
 | 
				
			||||||
 | 
					        logger.debug(`Cannot save blocks prices for blocks [${blockPrices[0].height} to ${blockPrices[blockPrices.length - 1].height}] because it has already been indexed, ignoring`);
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        logger.err(`Cannot save blocks prices for blocks [${blockPrices[0].height} to ${blockPrices[blockPrices.length - 1].height}] into db. Reason: ` + (e instanceof Error ? e.message : e));
 | 
				
			||||||
 | 
					        throw e;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default new BlocksRepository();
 | 
					export default new BlocksRepository();
 | 
				
			||||||
 | 
				
			|||||||
@ -33,9 +33,14 @@ class PricesRepository {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public async $getPricesTimes(): Promise<number[]> {
 | 
					  public async $getPricesTimes(): Promise<number[]> {
 | 
				
			||||||
    const [times]: any[] = await DB.query(`SELECT UNIX_TIMESTAMP(time) as time from prices WHERE USD != -1`);
 | 
					    const [times]: any[] = await DB.query(`SELECT UNIX_TIMESTAMP(time) as time from prices WHERE USD != -1 ORDER BY time`);
 | 
				
			||||||
    return times.map(time => time.time);
 | 
					    return times.map(time => time.time);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public async $getPricesTimesAndId(): Promise<number[]> {
 | 
				
			||||||
 | 
					    const [times]: any[] = await DB.query(`SELECT UNIX_TIMESTAMP(time) as time, id, USD from prices ORDER BY time`);
 | 
				
			||||||
 | 
					    return times;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default new PricesRepository();
 | 
					export default new PricesRepository();
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user