Merge branch 'master' into nymkappa/feature/hashrate-moving-average
This commit is contained in:
		
						commit
						f9a1f10b99
					
				@ -13,6 +13,7 @@
 | 
				
			|||||||
    "INITIAL_BLOCKS_AMOUNT": 8,
 | 
					    "INITIAL_BLOCKS_AMOUNT": 8,
 | 
				
			||||||
    "MEMPOOL_BLOCKS_AMOUNT": 8,
 | 
					    "MEMPOOL_BLOCKS_AMOUNT": 8,
 | 
				
			||||||
    "INDEXING_BLOCKS_AMOUNT": 11000,
 | 
					    "INDEXING_BLOCKS_AMOUNT": 11000,
 | 
				
			||||||
 | 
					    "BLOCKS_SUMMARIES_INDEXING": false,
 | 
				
			||||||
    "PRICE_FEED_UPDATE_INTERVAL": 600,
 | 
					    "PRICE_FEED_UPDATE_INTERVAL": 600,
 | 
				
			||||||
    "USE_SECOND_NODE_FOR_MINFEE": false,
 | 
					    "USE_SECOND_NODE_FOR_MINFEE": false,
 | 
				
			||||||
    "EXTERNAL_ASSETS": [],
 | 
					    "EXTERNAL_ASSETS": [],
 | 
				
			||||||
 | 
				
			|||||||
@ -20,6 +20,7 @@ import indexer from '../indexer';
 | 
				
			|||||||
import fiatConversion from './fiat-conversion';
 | 
					import fiatConversion from './fiat-conversion';
 | 
				
			||||||
import RatesRepository from '../repositories/RatesRepository';
 | 
					import RatesRepository from '../repositories/RatesRepository';
 | 
				
			||||||
import poolsParser from './pools-parser';
 | 
					import poolsParser from './pools-parser';
 | 
				
			||||||
 | 
					import BlocksSummariesRepository from '../repositories/BlocksSummariesRepository';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Blocks {
 | 
					class Blocks {
 | 
				
			||||||
  private blocks: BlockExtended[] = [];
 | 
					  private blocks: BlockExtended[] = [];
 | 
				
			||||||
@ -242,13 +243,68 @@ class Blocks {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * [INDEXING] Index all blocks summaries for the block txs visualization
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  public async $generateBlocksSummariesDatabase() {
 | 
				
			||||||
 | 
					    if (Common.blocksSummariesIndexingEnabled() === false) {
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      // Get all indexed block hash
 | 
				
			||||||
 | 
					      const indexedBlocks = await blocksRepository.$getIndexedBlocks();
 | 
				
			||||||
 | 
					      const indexedBlockSummariesHashesArray = await BlocksSummariesRepository.$getIndexedSummariesId();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const indexedBlockSummariesHashes = {}; // Use a map for faster seek during the indexing loop
 | 
				
			||||||
 | 
					      for (const hash of indexedBlockSummariesHashesArray) {
 | 
				
			||||||
 | 
					        indexedBlockSummariesHashes[hash] = true;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // Logging
 | 
				
			||||||
 | 
					      let newlyIndexed = 0;
 | 
				
			||||||
 | 
					      let totalIndexed = indexedBlockSummariesHashesArray.length;
 | 
				
			||||||
 | 
					      let indexedThisRun = 0;
 | 
				
			||||||
 | 
					      let timer = new Date().getTime() / 1000;
 | 
				
			||||||
 | 
					      const startedAt = new Date().getTime() / 1000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      for (const block of indexedBlocks) {
 | 
				
			||||||
 | 
					        if (indexedBlockSummariesHashes[block.hash] === true) {
 | 
				
			||||||
 | 
					          continue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Logging
 | 
				
			||||||
 | 
					        const elapsedSeconds = Math.max(1, Math.round((new Date().getTime() / 1000) - timer));
 | 
				
			||||||
 | 
					        if (elapsedSeconds > 5) {
 | 
				
			||||||
 | 
					          const runningFor = Math.max(1, Math.round((new Date().getTime() / 1000) - startedAt));
 | 
				
			||||||
 | 
					          const blockPerSeconds = Math.max(1, indexedThisRun / elapsedSeconds);
 | 
				
			||||||
 | 
					          const progress = Math.round(totalIndexed / indexedBlocks.length * 10000) / 100;
 | 
				
			||||||
 | 
					          const timeLeft = Math.round((indexedBlocks.length - totalIndexed) / blockPerSeconds);
 | 
				
			||||||
 | 
					          logger.debug(`Indexing block summary for #${block.height} | ~${blockPerSeconds.toFixed(2)} blocks/sec | total: ${totalIndexed}/${indexedBlocks.length} (${progress}%) | elapsed: ${runningFor} seconds | left: ~${timeLeft} seconds`);
 | 
				
			||||||
 | 
					          timer = new Date().getTime() / 1000;
 | 
				
			||||||
 | 
					          indexedThisRun = 0;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        await this.$getStrippedBlockTransactions(block.hash, true, true); // This will index the block summary
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Logging
 | 
				
			||||||
 | 
					        indexedThisRun++;
 | 
				
			||||||
 | 
					        totalIndexed++;
 | 
				
			||||||
 | 
					        newlyIndexed++;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      logger.notice(`Blocks summaries indexing completed: indexed ${newlyIndexed} blocks`);
 | 
				
			||||||
 | 
					    } catch (e) {
 | 
				
			||||||
 | 
					      logger.err(`Blocks summaries indexing failed. Reason: ${(e instanceof Error ? e.message : e)}`);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * [INDEXING] Index all blocks metadata for the mining dashboard
 | 
					   * [INDEXING] Index all blocks metadata for the mining dashboard
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  public async $generateBlockDatabase() {
 | 
					  public async $generateBlockDatabase(): Promise<boolean> {
 | 
				
			||||||
    const blockchainInfo = await bitcoinClient.getBlockchainInfo();
 | 
					    const blockchainInfo = await bitcoinClient.getBlockchainInfo();
 | 
				
			||||||
    if (blockchainInfo.blocks !== blockchainInfo.headers) { // Wait for node to sync
 | 
					    if (blockchainInfo.blocks !== blockchainInfo.headers) { // Wait for node to sync
 | 
				
			||||||
      return;
 | 
					      return false;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
@ -292,7 +348,7 @@ class Blocks {
 | 
				
			|||||||
          const elapsedSeconds = Math.max(1, Math.round((new Date().getTime() / 1000) - timer));
 | 
					          const elapsedSeconds = Math.max(1, Math.round((new Date().getTime() / 1000) - timer));
 | 
				
			||||||
          if (elapsedSeconds > 5 || blockHeight === lastBlockToIndex) {
 | 
					          if (elapsedSeconds > 5 || blockHeight === lastBlockToIndex) {
 | 
				
			||||||
            const runningFor = Math.max(1, Math.round((new Date().getTime() / 1000) - startedAt));
 | 
					            const runningFor = Math.max(1, Math.round((new Date().getTime() / 1000) - startedAt));
 | 
				
			||||||
            const blockPerSeconds = Math.max(1, Math.round(indexedThisRun / elapsedSeconds));
 | 
					            const blockPerSeconds = Math.max(1, indexedThisRun / elapsedSeconds);
 | 
				
			||||||
            const progress = Math.round(totalIndexed / indexingBlockAmount * 10000) / 100;
 | 
					            const progress = Math.round(totalIndexed / indexingBlockAmount * 10000) / 100;
 | 
				
			||||||
            const timeLeft = Math.round((indexingBlockAmount - totalIndexed) / blockPerSeconds);
 | 
					            const timeLeft = Math.round((indexingBlockAmount - totalIndexed) / blockPerSeconds);
 | 
				
			||||||
            logger.debug(`Indexing block #${blockHeight} | ~${blockPerSeconds.toFixed(2)} blocks/sec | total: ${totalIndexed}/${indexingBlockAmount} (${progress}%) | elapsed: ${runningFor} seconds | left: ~${timeLeft} seconds`);
 | 
					            logger.debug(`Indexing block #${blockHeight} | ~${blockPerSeconds.toFixed(2)} blocks/sec | total: ${totalIndexed}/${indexingBlockAmount} (${progress}%) | elapsed: ${runningFor} seconds | left: ~${timeLeft} seconds`);
 | 
				
			||||||
@ -316,13 +372,16 @@ class Blocks {
 | 
				
			|||||||
    } catch (e) {
 | 
					    } catch (e) {
 | 
				
			||||||
      logger.err('Block indexing failed. Trying again later. Reason: ' + (e instanceof Error ? e.message : e));
 | 
					      logger.err('Block indexing failed. Trying again later. Reason: ' + (e instanceof Error ? e.message : e));
 | 
				
			||||||
      loadingIndicators.setProgress('block-indexing', 100);
 | 
					      loadingIndicators.setProgress('block-indexing', 100);
 | 
				
			||||||
      return;
 | 
					      return false;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const chainValid = await BlocksRepository.$validateChain();
 | 
					    const chainValid = await BlocksRepository.$validateChain();
 | 
				
			||||||
    if (!chainValid) {
 | 
					    if (!chainValid) {
 | 
				
			||||||
      indexer.reindex();
 | 
					      indexer.reindex();
 | 
				
			||||||
 | 
					      return false;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public async $updateBlocks() {
 | 
					  public async $updateBlocks() {
 | 
				
			||||||
@ -387,11 +446,19 @@ class Blocks {
 | 
				
			|||||||
            // We assume there won't be a reorg with more than 10 block depth
 | 
					            // We assume there won't be a reorg with more than 10 block depth
 | 
				
			||||||
            await BlocksRepository.$deleteBlocksFrom(lastBlock['height'] - 10);
 | 
					            await BlocksRepository.$deleteBlocksFrom(lastBlock['height'] - 10);
 | 
				
			||||||
            await HashratesRepository.$deleteLastEntries();
 | 
					            await HashratesRepository.$deleteLastEntries();
 | 
				
			||||||
 | 
					            await BlocksSummariesRepository.$deleteBlocksFrom(lastBlock['height'] - 10);
 | 
				
			||||||
            for (let i = 10; i >= 0; --i) {
 | 
					            for (let i = 10; i >= 0; --i) {
 | 
				
			||||||
              await this.$indexBlock(lastBlock['height'] - i);
 | 
					              const newBlock = await this.$indexBlock(lastBlock['height'] - i);
 | 
				
			||||||
 | 
					              await this.$getStrippedBlockTransactions(newBlock.id, true, true);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            logger.info(`Re-indexed 10 blocks and summaries`);
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
          await blocksRepository.$saveBlockInDatabase(blockExtended);
 | 
					          await blocksRepository.$saveBlockInDatabase(blockExtended);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          // Save blocks summary for visualization if it's enabled
 | 
				
			||||||
 | 
					          if (Common.blocksSummariesIndexingEnabled() === true) {
 | 
				
			||||||
 | 
					            await this.$getStrippedBlockTransactions(blockExtended.id, true);
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      if (fiatConversion.ratesInitialized === true && config.DATABASE.ENABLED === true) {
 | 
					      if (fiatConversion.ratesInitialized === true && config.DATABASE.ENABLED === true) {
 | 
				
			||||||
@ -477,14 +544,34 @@ class Blocks {
 | 
				
			|||||||
    return blockExtended;
 | 
					    return blockExtended;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public async $getStrippedBlockTransactions(hash: string): Promise<TransactionStripped[]> {
 | 
					  public async $getStrippedBlockTransactions(hash: string, skipMemoryCache: boolean = false,
 | 
				
			||||||
 | 
					    skipDBLookup: boolean = false): Promise<TransactionStripped[]>
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    if (skipMemoryCache === false) {
 | 
				
			||||||
      // Check the memory cache
 | 
					      // Check the memory cache
 | 
				
			||||||
      const cachedSummary = this.getBlockSummaries().find((b) => b.id === hash);
 | 
					      const cachedSummary = this.getBlockSummaries().find((b) => b.id === hash);
 | 
				
			||||||
      if (cachedSummary) {
 | 
					      if (cachedSummary) {
 | 
				
			||||||
        return cachedSummary.transactions;
 | 
					        return cachedSummary.transactions;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Check if it's indexed in db
 | 
				
			||||||
 | 
					    if (skipDBLookup === false && Common.blocksSummariesIndexingEnabled() === true) {
 | 
				
			||||||
 | 
					      const indexedSummary = await BlocksSummariesRepository.$getByBlockId(hash);
 | 
				
			||||||
 | 
					      if (indexedSummary !== undefined) {
 | 
				
			||||||
 | 
					        return indexedSummary.transactions;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Call Core RPC
 | 
				
			||||||
    const block = await bitcoinClient.getBlock(hash, 2);
 | 
					    const block = await bitcoinClient.getBlock(hash, 2);
 | 
				
			||||||
    const summary = this.summarizeBlock(block);
 | 
					    const summary = this.summarizeBlock(block);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Index the response if needed
 | 
				
			||||||
 | 
					    if (Common.blocksSummariesIndexingEnabled() === true) {
 | 
				
			||||||
 | 
					      await BlocksSummariesRepository.$saveSummary(block.height, summary);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return summary.transactions;
 | 
					    return summary.transactions;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -177,4 +177,11 @@ export class Common {
 | 
				
			|||||||
      config.MEMPOOL.INDEXING_BLOCKS_AMOUNT !== 0
 | 
					      config.MEMPOOL.INDEXING_BLOCKS_AMOUNT !== 0
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  static blocksSummariesIndexingEnabled(): boolean {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      Common.indexingEnabled() &&
 | 
				
			||||||
 | 
					      config.MEMPOOL.BLOCKS_SUMMARIES_INDEXING === true
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -4,7 +4,7 @@ import logger from '../logger';
 | 
				
			|||||||
import { Common } from './common';
 | 
					import { Common } from './common';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class DatabaseMigration {
 | 
					class DatabaseMigration {
 | 
				
			||||||
  private static currentVersion = 19;
 | 
					  private static currentVersion = 20;
 | 
				
			||||||
  private queryTimeout = 120000;
 | 
					  private queryTimeout = 120000;
 | 
				
			||||||
  private statisticsAddedIndexed = false;
 | 
					  private statisticsAddedIndexed = false;
 | 
				
			||||||
  private uniqueLogs: string[] = [];
 | 
					  private uniqueLogs: string[] = [];
 | 
				
			||||||
@ -217,6 +217,10 @@ class DatabaseMigration {
 | 
				
			|||||||
      if (databaseSchemaVersion < 19) {
 | 
					      if (databaseSchemaVersion < 19) {
 | 
				
			||||||
        await this.$executeQuery(this.getCreateRatesTableQuery(), await this.$checkIfTableExists('rates'));
 | 
					        await this.$executeQuery(this.getCreateRatesTableQuery(), await this.$checkIfTableExists('rates'));
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (databaseSchemaVersion < 20 && isBitcoin === true) {
 | 
				
			||||||
 | 
					        await this.$executeQuery(this.getCreateBlocksSummariesTableQuery(), await this.$checkIfTableExists('blocks_summaries'));
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
    } catch (e) {
 | 
					    } catch (e) {
 | 
				
			||||||
      throw e;
 | 
					      throw e;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -512,6 +516,16 @@ class DatabaseMigration {
 | 
				
			|||||||
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;`;
 | 
					    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;`;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private getCreateBlocksSummariesTableQuery(): string {
 | 
				
			||||||
 | 
					    return `CREATE TABLE IF NOT EXISTS blocks_summaries (
 | 
				
			||||||
 | 
					      height int(10) unsigned NOT NULL,
 | 
				
			||||||
 | 
					      id varchar(65) NOT NULL,
 | 
				
			||||||
 | 
					      transactions JSON NOT NULL,
 | 
				
			||||||
 | 
					      PRIMARY KEY (id),
 | 
				
			||||||
 | 
					      INDEX (height)
 | 
				
			||||||
 | 
					    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;`;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public async $truncateIndexedData(tables: string[]) {
 | 
					  public async $truncateIndexedData(tables: string[]) {
 | 
				
			||||||
    const allowedTables = ['blocks', 'hashrates'];
 | 
					    const allowedTables = ['blocks', 'hashrates'];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -15,6 +15,7 @@ interface IConfig {
 | 
				
			|||||||
    INITIAL_BLOCKS_AMOUNT: number;
 | 
					    INITIAL_BLOCKS_AMOUNT: number;
 | 
				
			||||||
    MEMPOOL_BLOCKS_AMOUNT: number;
 | 
					    MEMPOOL_BLOCKS_AMOUNT: number;
 | 
				
			||||||
    INDEXING_BLOCKS_AMOUNT: number;
 | 
					    INDEXING_BLOCKS_AMOUNT: number;
 | 
				
			||||||
 | 
					    BLOCKS_SUMMARIES_INDEXING: boolean;
 | 
				
			||||||
    PRICE_FEED_UPDATE_INTERVAL: number;
 | 
					    PRICE_FEED_UPDATE_INTERVAL: number;
 | 
				
			||||||
    USE_SECOND_NODE_FOR_MINFEE: boolean;
 | 
					    USE_SECOND_NODE_FOR_MINFEE: boolean;
 | 
				
			||||||
    EXTERNAL_ASSETS: string[];
 | 
					    EXTERNAL_ASSETS: string[];
 | 
				
			||||||
@ -104,6 +105,7 @@ const defaults: IConfig = {
 | 
				
			|||||||
    'INITIAL_BLOCKS_AMOUNT': 8,
 | 
					    'INITIAL_BLOCKS_AMOUNT': 8,
 | 
				
			||||||
    'MEMPOOL_BLOCKS_AMOUNT': 8,
 | 
					    'MEMPOOL_BLOCKS_AMOUNT': 8,
 | 
				
			||||||
    'INDEXING_BLOCKS_AMOUNT': 11000, // 0 = disable indexing, -1 = index all blocks
 | 
					    'INDEXING_BLOCKS_AMOUNT': 11000, // 0 = disable indexing, -1 = index all blocks
 | 
				
			||||||
 | 
					    'BLOCKS_SUMMARIES_INDEXING': false,
 | 
				
			||||||
    'PRICE_FEED_UPDATE_INTERVAL': 600,
 | 
					    'PRICE_FEED_UPDATE_INTERVAL': 600,
 | 
				
			||||||
    'USE_SECOND_NODE_FOR_MINFEE': false,
 | 
					    'USE_SECOND_NODE_FOR_MINFEE': false,
 | 
				
			||||||
    'EXTERNAL_ASSETS': [],
 | 
					    'EXTERNAL_ASSETS': [],
 | 
				
			||||||
 | 
				
			|||||||
@ -29,10 +29,17 @@ class Indexer {
 | 
				
			|||||||
    this.indexerRunning = true;
 | 
					    this.indexerRunning = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      await blocks.$generateBlockDatabase();
 | 
					      const chainValid = await blocks.$generateBlockDatabase();
 | 
				
			||||||
 | 
					      if (chainValid === false) {
 | 
				
			||||||
 | 
					        // Chain of block hash was invalid, so we need to reindex. Stop here and continue at the next iteration
 | 
				
			||||||
 | 
					        this.indexerRunning = false;
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      await this.$resetHashratesIndexingState();
 | 
					      await this.$resetHashratesIndexingState();
 | 
				
			||||||
      await mining.$generateNetworkHashrateHistory();
 | 
					      await mining.$generateNetworkHashrateHistory();
 | 
				
			||||||
      await mining.$generatePoolHashrateHistory();
 | 
					      await mining.$generatePoolHashrateHistory();
 | 
				
			||||||
 | 
					      await blocks.$generateBlocksSummariesDatabase();
 | 
				
			||||||
    } catch (e) {
 | 
					    } catch (e) {
 | 
				
			||||||
      this.reindex();
 | 
					      this.reindex();
 | 
				
			||||||
      logger.err(`Indexer failed, trying again later. Reason: ` + (e instanceof Error ? e.message : e));
 | 
					      logger.err(`Indexer failed, trying again later. Reason: ` + (e instanceof Error ? e.message : e));
 | 
				
			||||||
 | 
				
			|||||||
@ -6,6 +6,7 @@ import { prepareBlock } from '../utils/blocks-utils';
 | 
				
			|||||||
import PoolsRepository from './PoolsRepository';
 | 
					import PoolsRepository from './PoolsRepository';
 | 
				
			||||||
import HashratesRepository from './HashratesRepository';
 | 
					import HashratesRepository from './HashratesRepository';
 | 
				
			||||||
import { escape } from 'mysql2';
 | 
					import { escape } from 'mysql2';
 | 
				
			||||||
 | 
					import BlocksSummariesRepository from './BlocksSummariesRepository';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class BlocksRepository {
 | 
					class BlocksRepository {
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
@ -495,6 +496,7 @@ class BlocksRepository {
 | 
				
			|||||||
        if (blocks[idx].previous_block_hash !== blocks[idx - 1].hash) {
 | 
					        if (blocks[idx].previous_block_hash !== blocks[idx - 1].hash) {
 | 
				
			||||||
          logger.warn(`Chain divergence detected at block ${blocks[idx - 1].height}, re-indexing newer blocks and hashrates`);
 | 
					          logger.warn(`Chain divergence detected at block ${blocks[idx - 1].height}, re-indexing newer blocks and hashrates`);
 | 
				
			||||||
          await this.$deleteBlocksFrom(blocks[idx - 1].height);
 | 
					          await this.$deleteBlocksFrom(blocks[idx - 1].height);
 | 
				
			||||||
 | 
					          await BlocksSummariesRepository.$deleteBlocksFrom(blocks[idx - 1].height);
 | 
				
			||||||
          await HashratesRepository.$deleteHashratesFromTimestamp(blocks[idx - 1].timestamp - 604800);
 | 
					          await HashratesRepository.$deleteHashratesFromTimestamp(blocks[idx - 1].timestamp - 604800);
 | 
				
			||||||
          return false;
 | 
					          return false;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -652,6 +654,19 @@ class BlocksRepository {
 | 
				
			|||||||
      throw e;
 | 
					      throw e;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Get a list of blocks that have been indexed
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  public async $getIndexedBlocks(): Promise<any[]> {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      const [rows]: any = await DB.query(`SELECT height, hash FROM blocks ORDER BY height DESC`);
 | 
				
			||||||
 | 
					      return rows;
 | 
				
			||||||
 | 
					    } catch (e) {
 | 
				
			||||||
 | 
					      logger.err('Cannot generate block size and weight history. Reason: ' + (e instanceof Error ? e.message : e));
 | 
				
			||||||
 | 
					      throw e;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default new BlocksRepository();
 | 
					export default new BlocksRepository();
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										59
									
								
								backend/src/repositories/BlocksSummariesRepository.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								backend/src/repositories/BlocksSummariesRepository.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,59 @@
 | 
				
			|||||||
 | 
					import DB from '../database';
 | 
				
			||||||
 | 
					import logger from '../logger';
 | 
				
			||||||
 | 
					import { BlockSummary } from '../mempool.interfaces';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class BlocksSummariesRepository {
 | 
				
			||||||
 | 
					  public async $getByBlockId(id: string): Promise<BlockSummary | undefined> {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      const [summary]: any[] = await DB.query(`SELECT * from blocks_summaries WHERE id = ?`, [id]);
 | 
				
			||||||
 | 
					      if (summary.length > 0) {
 | 
				
			||||||
 | 
					        summary[0].transactions = JSON.parse(summary[0].transactions);
 | 
				
			||||||
 | 
					        return summary[0];
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    } catch (e) {
 | 
				
			||||||
 | 
					      logger.err(`Cannot get block summary for block id ${id}. Reason: ` + (e instanceof Error ? e.message : e));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return undefined;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public async $saveSummary(height: number, summary: BlockSummary) {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      await DB.query(`INSERT INTO blocks_summaries VALUE (?, ?, ?)`, [height, summary.id, JSON.stringify(summary.transactions)]);
 | 
				
			||||||
 | 
					    } catch (e: any) {
 | 
				
			||||||
 | 
					      if (e.errno === 1062) { // ER_DUP_ENTRY - This scenario is possible upon node backend restart
 | 
				
			||||||
 | 
					        logger.debug(`Cannot save block summary for ${summary.id} because it has already been indexed, ignoring`);
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        logger.debug(`Cannot save block summary for ${summary.id}. Reason: ${e instanceof Error ? e.message : e}`);
 | 
				
			||||||
 | 
					        throw e;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public async $getIndexedSummariesId(): Promise<string[]> {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      const [rows]: any[] = await DB.query(`SELECT id from blocks_summaries`);
 | 
				
			||||||
 | 
					      return rows.map(row => row.id);
 | 
				
			||||||
 | 
					    } catch (e) {
 | 
				
			||||||
 | 
					      logger.err(`Cannot get block summaries id list. Reason: ` + (e instanceof Error ? e.message : e));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return [];
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Delete blocks from the database from blockHeight
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					   public async $deleteBlocksFrom(blockHeight: number) {
 | 
				
			||||||
 | 
					    logger.info(`Delete newer blocks summary from height ${blockHeight} from the database`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      await DB.query(`DELETE FROM blocks_summaries where height >= ${blockHeight}`);
 | 
				
			||||||
 | 
					    } catch (e) {
 | 
				
			||||||
 | 
					      logger.err('Cannot delete indexed blocks summaries. Reason: ' + (e instanceof Error ? e.message : e));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default new BlocksSummariesRepository();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -729,7 +729,7 @@ class Routes {
 | 
				
			|||||||
  public async getStrippedBlockTransactions(req: Request, res: Response) {
 | 
					  public async getStrippedBlockTransactions(req: Request, res: Response) {
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      const transactions = await blocks.$getStrippedBlockTransactions(req.params.hash);
 | 
					      const transactions = await blocks.$getStrippedBlockTransactions(req.params.hash);
 | 
				
			||||||
      res.setHeader('Expires', new Date(Date.now() + 1000 * 600).toUTCString());
 | 
					      res.setHeader('Expires', new Date(Date.now() + 1000 * 3600 * 24 * 30).toUTCString());
 | 
				
			||||||
      res.json(transactions);
 | 
					      res.json(transactions);
 | 
				
			||||||
    } catch (e) {
 | 
					    } catch (e) {
 | 
				
			||||||
      res.status(500).send(e instanceof Error ? e.message : e);
 | 
					      res.status(500).send(e instanceof Error ? e.message : e);
 | 
				
			||||||
 | 
				
			|||||||
@ -19,7 +19,8 @@
 | 
				
			|||||||
    "EXTERNAL_RETRY_INTERVAL": __MEMPOOL_EXTERNAL_RETRY_INTERVAL__,
 | 
					    "EXTERNAL_RETRY_INTERVAL": __MEMPOOL_EXTERNAL_RETRY_INTERVAL__,
 | 
				
			||||||
    "USER_AGENT": "__MEMPOOL_USER_AGENT__",
 | 
					    "USER_AGENT": "__MEMPOOL_USER_AGENT__",
 | 
				
			||||||
    "STDOUT_LOG_MIN_PRIORITY": "__MEMPOOL_STDOUT_LOG_MIN_PRIORITY__",
 | 
					    "STDOUT_LOG_MIN_PRIORITY": "__MEMPOOL_STDOUT_LOG_MIN_PRIORITY__",
 | 
				
			||||||
    "INDEXING_BLOCKS_AMOUNT": __MEMPOOL_INDEXING_BLOCKS_AMOUNT__
 | 
					    "INDEXING_BLOCKS_AMOUNT": __MEMPOOL_INDEXING_BLOCKS_AMOUNT__,
 | 
				
			||||||
 | 
					    "BLOCKS_SUMMARIES_INDEXING": __MEMPOOL_BLOCKS_SUMMARIES_INDEXING__
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "CORE_RPC": {
 | 
					  "CORE_RPC": {
 | 
				
			||||||
    "HOST": "__CORE_RPC_HOST__",
 | 
					    "HOST": "__CORE_RPC_HOST__",
 | 
				
			||||||
 | 
				
			|||||||
@ -14,6 +14,7 @@ __MEMPOOL_BLOCK_WEIGHT_UNITS__=${MEMPOOL_BLOCK_WEIGHT_UNITS:=4000000}
 | 
				
			|||||||
__MEMPOOL_INITIAL_BLOCKS_AMOUNT__=${MEMPOOL_INITIAL_BLOCKS_AMOUNT:=8}
 | 
					__MEMPOOL_INITIAL_BLOCKS_AMOUNT__=${MEMPOOL_INITIAL_BLOCKS_AMOUNT:=8}
 | 
				
			||||||
__MEMPOOL_MEMPOOL_BLOCKS_AMOUNT__=${MEMPOOL_MEMPOOL_BLOCKS_AMOUNT:=8}
 | 
					__MEMPOOL_MEMPOOL_BLOCKS_AMOUNT__=${MEMPOOL_MEMPOOL_BLOCKS_AMOUNT:=8}
 | 
				
			||||||
__MEMPOOL_INDEXING_BLOCKS_AMOUNT__=${MEMPOOL_INDEXING_BLOCKS_AMOUNT:=11000}
 | 
					__MEMPOOL_INDEXING_BLOCKS_AMOUNT__=${MEMPOOL_INDEXING_BLOCKS_AMOUNT:=11000}
 | 
				
			||||||
 | 
					__MEMPOOL_BLOCKS_SUMMARIES_INDEXING__=${MEMPOOL_BLOCKS_SUMMARIES_INDEXING:=false}
 | 
				
			||||||
__MEMPOOL_PRICE_FEED_UPDATE_INTERVAL__=${MEMPOOL_PRICE_FEED_UPDATE_INTERVAL:=600}
 | 
					__MEMPOOL_PRICE_FEED_UPDATE_INTERVAL__=${MEMPOOL_PRICE_FEED_UPDATE_INTERVAL:=600}
 | 
				
			||||||
__MEMPOOL_USE_SECOND_NODE_FOR_MINFEE__=${MEMPOOL_USE_SECOND_NODE_FOR_MINFEE:=false}
 | 
					__MEMPOOL_USE_SECOND_NODE_FOR_MINFEE__=${MEMPOOL_USE_SECOND_NODE_FOR_MINFEE:=false}
 | 
				
			||||||
__MEMPOOL_EXTERNAL_ASSETS__=${MEMPOOL_EXTERNAL_ASSETS:=[]}
 | 
					__MEMPOOL_EXTERNAL_ASSETS__=${MEMPOOL_EXTERNAL_ASSETS:=[]}
 | 
				
			||||||
@ -101,6 +102,7 @@ sed -i "s/__MEMPOOL_BLOCK_WEIGHT_UNITS__/${__MEMPOOL_BLOCK_WEIGHT_UNITS__}/g" me
 | 
				
			|||||||
sed -i "s/__MEMPOOL_INITIAL_BLOCKS_AMOUNT__/${__MEMPOOL_INITIAL_BLOCKS_AMOUNT__}/g" mempool-config.json
 | 
					sed -i "s/__MEMPOOL_INITIAL_BLOCKS_AMOUNT__/${__MEMPOOL_INITIAL_BLOCKS_AMOUNT__}/g" mempool-config.json
 | 
				
			||||||
sed -i "s/__MEMPOOL_MEMPOOL_BLOCKS_AMOUNT__/${__MEMPOOL_MEMPOOL_BLOCKS_AMOUNT__}/g" mempool-config.json
 | 
					sed -i "s/__MEMPOOL_MEMPOOL_BLOCKS_AMOUNT__/${__MEMPOOL_MEMPOOL_BLOCKS_AMOUNT__}/g" mempool-config.json
 | 
				
			||||||
sed -i "s/__MEMPOOL_INDEXING_BLOCKS_AMOUNT__/${__MEMPOOL_INDEXING_BLOCKS_AMOUNT__}/g" mempool-config.json
 | 
					sed -i "s/__MEMPOOL_INDEXING_BLOCKS_AMOUNT__/${__MEMPOOL_INDEXING_BLOCKS_AMOUNT__}/g" mempool-config.json
 | 
				
			||||||
 | 
					sed -i "s/__MEMPOOL_BLOCKS_SUMMARIES_INDEXING__/${__MEMPOOL_BLOCKS_SUMMARIES_INDEXING__}/g" mempool-config.json
 | 
				
			||||||
sed -i "s/__MEMPOOL_PRICE_FEED_UPDATE_INTERVAL__/${__MEMPOOL_PRICE_FEED_UPDATE_INTERVAL__}/g" mempool-config.json
 | 
					sed -i "s/__MEMPOOL_PRICE_FEED_UPDATE_INTERVAL__/${__MEMPOOL_PRICE_FEED_UPDATE_INTERVAL__}/g" mempool-config.json
 | 
				
			||||||
sed -i "s/__MEMPOOL_USE_SECOND_NODE_FOR_MINFEE__/${__MEMPOOL_USE_SECOND_NODE_FOR_MINFEE__}/g" mempool-config.json
 | 
					sed -i "s/__MEMPOOL_USE_SECOND_NODE_FOR_MINFEE__/${__MEMPOOL_USE_SECOND_NODE_FOR_MINFEE__}/g" mempool-config.json
 | 
				
			||||||
sed -i "s!__MEMPOOL_EXTERNAL_ASSETS__!${__MEMPOOL_EXTERNAL_ASSETS__}!g" mempool-config.json
 | 
					sed -i "s!__MEMPOOL_EXTERNAL_ASSETS__!${__MEMPOOL_EXTERNAL_ASSETS__}!g" mempool-config.json
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
<div [formGroup]="languageForm" class="text-small text-center mt-4">
 | 
					<div [formGroup]="languageForm" class="text-small text-center">
 | 
				
			||||||
    <select formControlName="language" class="custom-select custom-select-sm form-control-secondary form-control mx-auto" style="width: 130px;" (change)="changeLanguage()">
 | 
					    <select formControlName="language" class="custom-select custom-select-sm form-control-secondary form-control mx-auto" style="width: 130px;" (change)="changeLanguage()">
 | 
				
			||||||
        <option *ngFor="let lang of languages" [value]="lang.code">{{ lang.name }}</option>
 | 
					        <option *ngFor="let lang of languages" [value]="lang.code">{{ lang.name }}</option>
 | 
				
			||||||
    </select>
 | 
					    </select>
 | 
				
			||||||
 | 
				
			|||||||
@ -1,34 +1,6 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
<div class="container-xl dashboard-container">
 | 
					<div class="container-xl dashboard-container">
 | 
				
			||||||
  <div class="row row-cols-1 row-cols-md-2" *ngIf="{ value: (mempoolInfoData$ | async) } as mempoolInfoData">
 | 
					  <div class="row row-cols-1 row-cols-md-2" *ngIf="{ value: (mempoolInfoData$ | async) } as mempoolInfoData">
 | 
				
			||||||
    <ng-template [ngIf]="collapseLevel === 'three'" [ngIfElse]="expanded">
 | 
					 | 
				
			||||||
      <div class="col card-wrapper" *ngIf="(network$ | async) !== 'liquid' && (network$ | async) !== 'liquidtestnet'">
 | 
					 | 
				
			||||||
        <div class="main-title" i18n="fees-box.transaction-fees">Transaction Fees</div>
 | 
					 | 
				
			||||||
        <div class="card">
 | 
					 | 
				
			||||||
          <div class="card-body less-padding">
 | 
					 | 
				
			||||||
            <app-fees-box class="d-block"></app-fees-box>
 | 
					 | 
				
			||||||
          </div>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
      </div>
 | 
					 | 
				
			||||||
      <div class="col" *ngIf="(network$ | async) !== 'liquid' && (network$ | async) !== 'liquidtestnet'">
 | 
					 | 
				
			||||||
        <app-difficulty></app-difficulty>
 | 
					 | 
				
			||||||
      </div>
 | 
					 | 
				
			||||||
      <div class="col">
 | 
					 | 
				
			||||||
        <div class="card">
 | 
					 | 
				
			||||||
          <div class="card-body">
 | 
					 | 
				
			||||||
            <ng-container *ngTemplateOutlet="stateService.network === 'liquid' ? lbtcPegs : mempoolTable; context: { $implicit: mempoolInfoData }"></ng-container>
 | 
					 | 
				
			||||||
          </div>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
      </div>
 | 
					 | 
				
			||||||
      <div class="col">
 | 
					 | 
				
			||||||
        <div class="card">
 | 
					 | 
				
			||||||
          <div class="card-body">
 | 
					 | 
				
			||||||
            <ng-container *ngTemplateOutlet="stateService.network === 'liquid' ? mempoolTable : txPerSecond; context: { $implicit: mempoolInfoData }"></ng-container>
 | 
					 | 
				
			||||||
          </div>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
      </div>
 | 
					 | 
				
			||||||
    </ng-template>
 | 
					 | 
				
			||||||
    <ng-template #expanded>
 | 
					 | 
				
			||||||
    <ng-container *ngIf="(network$ | async) !== 'liquid' && (network$ | async) !== 'liquidtestnet'">
 | 
					    <ng-container *ngIf="(network$ | async) !== 'liquid' && (network$ | async) !== 'liquidtestnet'">
 | 
				
			||||||
      <div class="col card-wrapper">
 | 
					      <div class="col card-wrapper">
 | 
				
			||||||
        <div class="main-title" i18n="fees-box.transaction-fees">Transaction Fees</div>
 | 
					        <div class="main-title" i18n="fees-box.transaction-fees">Transaction Fees</div>
 | 
				
			||||||
@ -102,7 +74,6 @@
 | 
				
			|||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
      <ng-template [ngIf]="collapseLevel === 'one'">
 | 
					 | 
				
			||||||
    <div class="col" style="max-height: 410px">
 | 
					    <div class="col" style="max-height: 410px">
 | 
				
			||||||
      <div class="card">
 | 
					      <div class="card">
 | 
				
			||||||
        <div class="card-body">
 | 
					        <div class="card-body">
 | 
				
			||||||
@ -163,20 +134,12 @@
 | 
				
			|||||||
              </tr>
 | 
					              </tr>
 | 
				
			||||||
            </tbody>
 | 
					            </tbody>
 | 
				
			||||||
          </table>
 | 
					          </table>
 | 
				
			||||||
 | 
					          <div class=""> </div>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
    </ng-template>
 | 
					 | 
				
			||||||
    </ng-template>
 | 
					 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  <button type="button" class="btn btn-secondary btn-sm d-block mx-auto" (click)="toggleCollapsed()">
 | 
					 | 
				
			||||||
    <div [ngSwitch]="collapseLevel">
 | 
					 | 
				
			||||||
      <fa-icon *ngSwitchCase="'three'" [icon]="['fas', 'angle-down']" [fixedWidth]="true" i18n-title="dashboard.expand" title="Expand"></fa-icon>
 | 
					 | 
				
			||||||
      <fa-icon *ngSwitchDefault [icon]="['fas', 'angle-up']" [fixedWidth]="true" i18n-title="dashboard.collapse" title="Collapse"></fa-icon>
 | 
					 | 
				
			||||||
    </div>
 | 
					 | 
				
			||||||
  </button>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  <app-language-selector></app-language-selector>
 | 
					  <app-language-selector></app-language-selector>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  <div class="terms-of-service">
 | 
					  <div class="terms-of-service">
 | 
				
			||||||
 | 
				
			|||||||
@ -33,7 +33,6 @@ interface MempoolStatsData {
 | 
				
			|||||||
  changeDetection: ChangeDetectionStrategy.OnPush
 | 
					  changeDetection: ChangeDetectionStrategy.OnPush
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class DashboardComponent implements OnInit {
 | 
					export class DashboardComponent implements OnInit {
 | 
				
			||||||
  collapseLevel: string;
 | 
					 | 
				
			||||||
  featuredAssets$: Observable<any>;
 | 
					  featuredAssets$: Observable<any>;
 | 
				
			||||||
  network$: Observable<string>;
 | 
					  network$: Observable<string>;
 | 
				
			||||||
  mempoolBlocksData$: Observable<MempoolBlocksData>;
 | 
					  mempoolBlocksData$: Observable<MempoolBlocksData>;
 | 
				
			||||||
@ -63,7 +62,6 @@ export class DashboardComponent implements OnInit {
 | 
				
			|||||||
    this.seoService.resetTitle();
 | 
					    this.seoService.resetTitle();
 | 
				
			||||||
    this.websocketService.want(['blocks', 'stats', 'mempool-blocks', 'live-2h-chart']);
 | 
					    this.websocketService.want(['blocks', 'stats', 'mempool-blocks', 'live-2h-chart']);
 | 
				
			||||||
    this.network$ = merge(of(''), this.stateService.networkChanged$);
 | 
					    this.network$ = merge(of(''), this.stateService.networkChanged$);
 | 
				
			||||||
    this.collapseLevel = this.storageService.getValue('dashboard-collapsed') || 'one';
 | 
					 | 
				
			||||||
    this.mempoolLoadingStatus$ = this.stateService.loadingIndicators$
 | 
					    this.mempoolLoadingStatus$ = this.stateService.loadingIndicators$
 | 
				
			||||||
      .pipe(
 | 
					      .pipe(
 | 
				
			||||||
        map((indicators) => indicators.mempool !== undefined ? indicators.mempool : 100)
 | 
					        map((indicators) => indicators.mempool !== undefined ? indicators.mempool : 100)
 | 
				
			||||||
@ -230,15 +228,4 @@ export class DashboardComponent implements OnInit {
 | 
				
			|||||||
  trackByBlock(index: number, block: BlockExtended) {
 | 
					  trackByBlock(index: number, block: BlockExtended) {
 | 
				
			||||||
    return block.height;
 | 
					    return block.height;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					 | 
				
			||||||
  toggleCollapsed() {
 | 
					 | 
				
			||||||
    if (this.collapseLevel === 'one') {
 | 
					 | 
				
			||||||
      this.collapseLevel = 'two';
 | 
					 | 
				
			||||||
    } else if (this.collapseLevel === 'two') {
 | 
					 | 
				
			||||||
      this.collapseLevel = 'three';
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
      this.collapseLevel = 'one';
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    this.storageService.setValue('dashboard-collapsed', this.collapseLevel);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,7 @@
 | 
				
			|||||||
import { Injectable } from '@angular/core';
 | 
					import { Injectable } from '@angular/core';
 | 
				
			||||||
import { HttpClient, HttpParams } from '@angular/common/http';
 | 
					import { HttpClient, HttpParams } from '@angular/common/http';
 | 
				
			||||||
import { CpfpInfo, OptimizedMempoolStats, AddressInformation, LiquidPegs, ITranslators,
 | 
					import { CpfpInfo, OptimizedMempoolStats, AddressInformation, LiquidPegs, ITranslators,
 | 
				
			||||||
         PoolsStats, PoolStat, BlockExtended, TransactionStripped, RewardStats } from '../interfaces/node-api.interface';
 | 
					  PoolStat, BlockExtended, TransactionStripped, RewardStats } from '../interfaces/node-api.interface';
 | 
				
			||||||
import { Observable } from 'rxjs';
 | 
					import { Observable } from 'rxjs';
 | 
				
			||||||
import { StateService } from './state.service';
 | 
					import { StateService } from './state.service';
 | 
				
			||||||
import { WebsocketResponse } from '../interfaces/websocket.interface';
 | 
					import { WebsocketResponse } from '../interfaces/websocket.interface';
 | 
				
			||||||
 | 
				
			|||||||
@ -9,6 +9,7 @@
 | 
				
			|||||||
    "CLEAR_PROTECTION_MINUTES": 5,
 | 
					    "CLEAR_PROTECTION_MINUTES": 5,
 | 
				
			||||||
    "POLL_RATE_MS": 1000,
 | 
					    "POLL_RATE_MS": 1000,
 | 
				
			||||||
    "INDEXING_BLOCKS_AMOUNT": -1,
 | 
					    "INDEXING_BLOCKS_AMOUNT": -1,
 | 
				
			||||||
 | 
					    "BLOCKS_SUMMARIES_INDEXING": true,
 | 
				
			||||||
    "USE_SECOND_NODE_FOR_MINFEE": true
 | 
					    "USE_SECOND_NODE_FOR_MINFEE": true
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "SYSLOG" : {
 | 
					  "SYSLOG" : {
 | 
				
			||||||
 | 
				
			|||||||
@ -4,14 +4,42 @@ location /api/v1/statistics {
 | 
				
			|||||||
location /api/v1/mining {
 | 
					location /api/v1/mining {
 | 
				
			||||||
	try_files /dev/null @mempool-api-v1-warmcache;
 | 
						try_files /dev/null @mempool-api-v1-warmcache;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					location /api/v1/block {
 | 
				
			||||||
 | 
						try_files /dev/null @mempool-api-v1-forevercache;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
location /api/v1 {
 | 
					location /api/v1 {
 | 
				
			||||||
	try_files /dev/null @mempool-api-v1-coldcache;
 | 
						try_files /dev/null @mempool-api-v1-coldcache;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					location /api/block {
 | 
				
			||||||
 | 
						rewrite ^/api/(.*) /$1 break;
 | 
				
			||||||
 | 
						try_files /dev/null @electrs-api-forevercache;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
location /api/ {
 | 
					location /api/ {
 | 
				
			||||||
	rewrite ^/api/(.*) /$1 break;
 | 
						rewrite ^/api/(.*) /$1 break;
 | 
				
			||||||
	try_files /dev/null @electrs-api-nocache;
 | 
						try_files /dev/null @electrs-api-nocache;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					location @mempool-api-v1-forevercache {
 | 
				
			||||||
 | 
						proxy_pass $mempoolBackend;
 | 
				
			||||||
 | 
						proxy_http_version 1.1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						proxy_set_header Host $http_host;
 | 
				
			||||||
 | 
						proxy_set_header X-Real-IP $remote_addr;
 | 
				
			||||||
 | 
						proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
 | 
				
			||||||
 | 
						proxy_set_header Upgrade $http_upgrade;
 | 
				
			||||||
 | 
						proxy_set_header Connection "upgrade";
 | 
				
			||||||
 | 
						proxy_set_header X-Forwarded-Proto $scheme;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						proxy_cache_bypass $http_upgrade;
 | 
				
			||||||
 | 
						proxy_cache_background_update on;
 | 
				
			||||||
 | 
						proxy_cache_use_stale updating;
 | 
				
			||||||
 | 
						proxy_cache api;
 | 
				
			||||||
 | 
						proxy_cache_valid 200 30d;
 | 
				
			||||||
 | 
						proxy_redirect off;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						expires 30d;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
location @mempool-api-v1-warmcache {
 | 
					location @mempool-api-v1-warmcache {
 | 
				
			||||||
	proxy_pass $mempoolBackend;
 | 
						proxy_pass $mempoolBackend;
 | 
				
			||||||
	proxy_http_version 1.1;
 | 
						proxy_http_version 1.1;
 | 
				
			||||||
@ -46,6 +74,7 @@ location @mempool-api-v1-coldcache {
 | 
				
			|||||||
	proxy_cache api;
 | 
						proxy_cache api;
 | 
				
			||||||
	proxy_cache_valid 200 10s;
 | 
						proxy_cache_valid 200 10s;
 | 
				
			||||||
	proxy_redirect off;
 | 
						proxy_redirect off;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	expires 10s;
 | 
						expires 10s;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -81,4 +110,27 @@ location @electrs-api-nocache {
 | 
				
			|||||||
	proxy_cache_bypass $http_upgrade;
 | 
						proxy_cache_bypass $http_upgrade;
 | 
				
			||||||
	proxy_redirect off;
 | 
						proxy_redirect off;
 | 
				
			||||||
	proxy_buffering off;
 | 
						proxy_buffering off;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						expires -1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					location @electrs-api-forevercache {
 | 
				
			||||||
 | 
						proxy_pass $electrsBackend;
 | 
				
			||||||
 | 
						proxy_http_version 1.1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						proxy_set_header Host $http_host;
 | 
				
			||||||
 | 
						proxy_set_header X-Real-IP $remote_addr;
 | 
				
			||||||
 | 
						proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
 | 
				
			||||||
 | 
						proxy_set_header Upgrade $http_upgrade;
 | 
				
			||||||
 | 
						proxy_set_header Connection "upgrade";
 | 
				
			||||||
 | 
						proxy_set_header X-Forwarded-Proto $scheme;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						proxy_cache_bypass $http_upgrade;
 | 
				
			||||||
 | 
						proxy_cache_background_update on;
 | 
				
			||||||
 | 
						proxy_cache_use_stale updating;
 | 
				
			||||||
 | 
						proxy_cache api;
 | 
				
			||||||
 | 
						proxy_cache_valid 200 30d;
 | 
				
			||||||
 | 
						proxy_redirect off;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						expires 30d;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user