From 7f8696c88dcb7d3279d0787cdc29edbf82465d5b Mon Sep 17 00:00:00 2001 From: nymkappa Date: Thu, 12 May 2022 10:12:14 +0200 Subject: [PATCH 01/15] Make sure to re-index skipped block when backend is offline for too long --- backend/src/api/blocks.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/api/blocks.ts b/backend/src/api/blocks.ts index 293862e93..1fe0ba1b9 100644 --- a/backend/src/api/blocks.ts +++ b/backend/src/api/blocks.ts @@ -201,13 +201,12 @@ class Blocks { return; } - this.reindexFlag = false; - const blockchainInfo = await bitcoinClient.getBlockchainInfo(); if (blockchainInfo.blocks !== blockchainInfo.headers) { // Wait for node to sync return; } + this.reindexFlag = false; this.blockIndexingStarted = true; this.blockIndexingCompleted = false; @@ -299,6 +298,7 @@ class Blocks { logger.info(`${blockHeightTip - this.currentBlockHeight} blocks since tip. Fast forwarding to the ${config.MEMPOOL.INITIAL_BLOCKS_AMOUNT} recent blocks`); this.currentBlockHeight = blockHeightTip - config.MEMPOOL.INITIAL_BLOCKS_AMOUNT; fastForwarded = true; + this.reindexFlag = true; // Make sure to index the skipped blocks #1619 } if (!this.lastDifficultyAdjustmentTime) { From 2a8314efc5306a46af39c09af66936f442b81d35 Mon Sep 17 00:00:00 2001 From: nymkappa Date: Thu, 19 May 2022 16:40:38 +0200 Subject: [PATCH 02/15] Move indexing logic into `Indexer` class --- backend/src/api/blocks.ts | 23 ++++++----------- backend/src/api/mining.ts | 25 ++----------------- backend/src/index.ts | 28 +++------------------ backend/src/indexer.ts | 52 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 64 insertions(+), 64 deletions(-) create mode 100644 backend/src/indexer.ts diff --git a/backend/src/api/blocks.ts b/backend/src/api/blocks.ts index 1fe0ba1b9..ba6fdff22 100644 --- a/backend/src/api/blocks.ts +++ b/backend/src/api/blocks.ts @@ -15,6 +15,7 @@ import BitcoinApi from './bitcoin/bitcoin-api'; import { prepareBlock } from '../utils/blocks-utils'; import BlocksRepository from '../repositories/BlocksRepository'; import HashratesRepository from '../repositories/HashratesRepository'; +import indexer from '../indexer'; class Blocks { private blocks: BlockExtended[] = []; @@ -23,9 +24,6 @@ class Blocks { private lastDifficultyAdjustmentTime = 0; private previousDifficultyRetarget = 0; private newBlockCallbacks: ((block: BlockExtended, txIds: string[], transactions: TransactionExtended[]) => void)[] = []; - private blockIndexingStarted = false; - public blockIndexingCompleted = false; - public reindexFlag = false; constructor() { } @@ -197,23 +195,15 @@ class Blocks { * [INDEXING] Index all blocks metadata for the mining dashboard */ public async $generateBlockDatabase() { - if (this.blockIndexingStarted && !this.reindexFlag) { - return; - } - const blockchainInfo = await bitcoinClient.getBlockchainInfo(); if (blockchainInfo.blocks !== blockchainInfo.headers) { // Wait for node to sync return; } - this.reindexFlag = false; - this.blockIndexingStarted = true; - this.blockIndexingCompleted = false; - try { let currentBlockHeight = blockchainInfo.blocks; - let indexingBlockAmount = config.MEMPOOL.INDEXING_BLOCKS_AMOUNT; + let indexingBlockAmount = Math.min(config.MEMPOOL.INDEXING_BLOCKS_AMOUNT, blockchainInfo.blocks); if (indexingBlockAmount <= -1) { indexingBlockAmount = currentBlockHeight + 1; } @@ -274,14 +264,14 @@ class Blocks { loadingIndicators.setProgress('block-indexing', 100); } catch (e) { logger.err('Block indexing failed. Trying again later. Reason: ' + (e instanceof Error ? e.message : e)); - this.blockIndexingStarted = false; loadingIndicators.setProgress('block-indexing', 100); return; } const chainValid = await BlocksRepository.$validateChain(); - this.reindexFlag = !chainValid; - this.blockIndexingCompleted = chainValid; + if (!chainValid) { + indexer.reindex(); + } } public async $updateBlocks() { @@ -298,7 +288,8 @@ class Blocks { logger.info(`${blockHeightTip - this.currentBlockHeight} blocks since tip. Fast forwarding to the ${config.MEMPOOL.INITIAL_BLOCKS_AMOUNT} recent blocks`); this.currentBlockHeight = blockHeightTip - config.MEMPOOL.INITIAL_BLOCKS_AMOUNT; fastForwarded = true; - this.reindexFlag = true; // Make sure to index the skipped blocks #1619 + logger.info(`Re-indexing skipped blocks and corresponding hashrates data`); + indexer.reindex(); // Make sure to index the skipped blocks #1619 } if (!this.lastDifficultyAdjustmentTime) { diff --git a/backend/src/api/mining.ts b/backend/src/api/mining.ts index cbd412068..592ac34f1 100644 --- a/backend/src/api/mining.ts +++ b/backend/src/api/mining.ts @@ -4,14 +4,10 @@ import PoolsRepository from '../repositories/PoolsRepository'; import HashratesRepository from '../repositories/HashratesRepository'; import bitcoinClient from './bitcoin/bitcoin-client'; import logger from '../logger'; -import blocks from './blocks'; import { Common } from './common'; import loadingIndicators from './loading-indicators'; class Mining { - hashrateIndexingStarted = false; - weeklyHashrateIndexingStarted = false; - constructor() { } @@ -152,14 +148,9 @@ class Mining { * [INDEXING] Generate weekly mining pool hashrate history */ public async $generatePoolHashrateHistory(): Promise { - if (!blocks.blockIndexingCompleted || this.hashrateIndexingStarted || this.weeklyHashrateIndexingStarted) { - return; - } - const now = new Date(); try { - this.weeklyHashrateIndexingStarted = true; const lastestRunDate = await HashratesRepository.$getLatestRun('last_weekly_hashrates_indexing'); // Run only if: @@ -167,11 +158,9 @@ class Mining { // * we started a new week (around Monday midnight) const runIndexing = lastestRunDate === 0 || now.getUTCDay() === 1 && lastestRunDate !== now.getUTCDate(); if (!runIndexing) { - this.weeklyHashrateIndexingStarted = false; return; } } catch (e) { - this.weeklyHashrateIndexingStarted = false; throw e; } @@ -191,6 +180,7 @@ class Mining { const startedAt = new Date().getTime() / 1000; let timer = new Date().getTime() / 1000; + logger.debug(`Indexing weekly mining pool hashrate`); loadingIndicators.setProgress('weekly-hashrate-indexing', 0); while (toTimestamp > genesisTimestamp) { @@ -255,7 +245,6 @@ class Mining { ++indexedThisRun; ++totalIndexed; } - this.weeklyHashrateIndexingStarted = false; await HashratesRepository.$setLatestRun('last_weekly_hashrates_indexing', new Date().getUTCDate()); if (newlyIndexed > 0) { logger.info(`Indexed ${newlyIndexed} pools weekly hashrate`); @@ -263,7 +252,6 @@ class Mining { loadingIndicators.setProgress('weekly-hashrate-indexing', 100); } catch (e) { loadingIndicators.setProgress('weekly-hashrate-indexing', 100); - this.weeklyHashrateIndexingStarted = false; throw e; } } @@ -272,22 +260,14 @@ class Mining { * [INDEXING] Generate daily hashrate data */ public async $generateNetworkHashrateHistory(): Promise { - if (!blocks.blockIndexingCompleted || this.hashrateIndexingStarted) { - return; - } - try { - this.hashrateIndexingStarted = true; - // We only run this once a day around midnight const latestRunDate = await HashratesRepository.$getLatestRun('last_hashrates_indexing'); const now = new Date().getUTCDate(); if (now === latestRunDate) { - this.hashrateIndexingStarted = false; return; } } catch (e) { - this.hashrateIndexingStarted = false; throw e; } @@ -305,6 +285,7 @@ class Mining { const startedAt = new Date().getTime() / 1000; let timer = new Date().getTime() / 1000; + logger.debug(`Indexing daily network hashrate`); loadingIndicators.setProgress('daily-hashrate-indexing', 0); while (toTimestamp > genesisTimestamp) { @@ -376,14 +357,12 @@ class Mining { await HashratesRepository.$saveHashrates(hashrates); await HashratesRepository.$setLatestRun('last_hashrates_indexing', new Date().getUTCDate()); - this.hashrateIndexingStarted = false; if (newlyIndexed > 0) { logger.info(`Indexed ${newlyIndexed} day of network hashrate`); } loadingIndicators.setProgress('daily-hashrate-indexing', 100); } catch (e) { loadingIndicators.setProgress('daily-hashrate-indexing', 100); - this.hashrateIndexingStarted = false; throw e; } } diff --git a/backend/src/index.ts b/backend/src/index.ts index c6eab6c17..4b7881730 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -29,6 +29,7 @@ import mining from './api/mining'; import HashratesRepository from './repositories/HashratesRepository'; import BlocksRepository from './repositories/BlocksRepository'; import poolsUpdater from './tasks/pools-updater'; +import indexer from './indexer'; class Server { private wss: WebSocket.Server | undefined; @@ -99,7 +100,7 @@ class Server { } await databaseMigration.$initializeOrMigrateDatabase(); if (Common.indexingEnabled()) { - await this.$resetHashratesIndexingState(); + await indexer.$resetHashratesIndexingState(); } } catch (e) { throw new Error(e instanceof Error ? e.message : 'Error'); @@ -154,7 +155,7 @@ class Server { await poolsUpdater.updatePoolsJson(); await blocks.$updateBlocks(); await memPool.$updateMempool(); - this.$runIndexingWhenReady(); + indexer.$run(); setTimeout(this.runMainUpdateLoop.bind(this), config.MEMPOOL.POLL_RATE_MS); this.currentBackendRetryInterval = 5; @@ -173,29 +174,6 @@ class Server { } } - async $resetHashratesIndexingState() { - try { - await HashratesRepository.$setLatestRun('last_hashrates_indexing', 0); - await HashratesRepository.$setLatestRun('last_weekly_hashrates_indexing', 0); - } catch (e) { - logger.err(`Cannot reset hashrate indexing timestamps. Reason: ` + (e instanceof Error ? e.message : e)); - } - } - - async $runIndexingWhenReady() { - if (!Common.indexingEnabled() || mempool.hasPriority()) { - return; - } - - try { - await blocks.$generateBlockDatabase(); - await mining.$generateNetworkHashrateHistory(); - await mining.$generatePoolHashrateHistory(); - } catch (e) { - logger.err(`Indexing failed, trying again later. Reason: ` + (e instanceof Error ? e.message : e)); - } - } - setUpWebsocketHandling() { if (this.wss) { websocketHandler.setWebsocketServer(this.wss); diff --git a/backend/src/indexer.ts b/backend/src/indexer.ts new file mode 100644 index 000000000..7ddc2a47f --- /dev/null +++ b/backend/src/indexer.ts @@ -0,0 +1,52 @@ +import { Common } from './api/common'; +import blocks from './api/blocks'; +import mempool from './api/mempool'; +import mining from './api/mining'; +import logger from './logger'; +import HashratesRepository from './repositories/HashratesRepository'; + +class Indexer { + runIndexer = true; + indexerRunning = false; + + constructor() { + } + + public reindex() { + this.runIndexer = true; + } + + public async $run() { + if (!Common.indexingEnabled() || this.runIndexer === false || + this.indexerRunning === true || mempool.hasPriority() + ) { + return; + } + + this.runIndexer = false; + this.indexerRunning = true; + + try { + await blocks.$generateBlockDatabase(); + await this.$resetHashratesIndexingState(); + await mining.$generateNetworkHashrateHistory(); + await mining.$generatePoolHashrateHistory(); + } catch (e) { + this.reindex(); + logger.err(`Indexer failed, trying again later. Reason: ` + (e instanceof Error ? e.message : e)); + } + + this.indexerRunning = false; + } + + async $resetHashratesIndexingState() { + try { + await HashratesRepository.$setLatestRun('last_hashrates_indexing', 0); + await HashratesRepository.$setLatestRun('last_weekly_hashrates_indexing', 0); + } catch (e) { + logger.err(`Cannot reset hashrate indexing timestamps. Reason: ` + (e instanceof Error ? e.message : e)); + } + } +} + +export default new Indexer(); From e101c4e218049552c43b557a9fa2a071b25c072d Mon Sep 17 00:00:00 2001 From: nymkappa Date: Fri, 20 May 2022 09:44:29 +0200 Subject: [PATCH 03/15] Replace all `avg_XXX` with `avgXXX` for consistency --- backend/src/repositories/BlocksRepository.ts | 32 +++++++++---------- .../block-fee-rates-graph.component.ts | 14 ++++---- .../block-fees-graph.component.ts | 2 +- .../block-rewards-graph.component.ts | 2 +- .../block-sizes-weights-graph.component.ts | 4 +-- 5 files changed, 27 insertions(+), 27 deletions(-) diff --git a/backend/src/repositories/BlocksRepository.ts b/backend/src/repositories/BlocksRepository.ts index 47a18fc9b..c22491839 100644 --- a/backend/src/repositories/BlocksRepository.ts +++ b/backend/src/repositories/BlocksRepository.ts @@ -474,9 +474,9 @@ class BlocksRepository { public async $getHistoricalBlockFees(div: number, interval: string | null): Promise { try { let query = `SELECT - CAST(AVG(height) as INT) as avg_height, + CAST(AVG(height) as INT) as avgHeight, CAST(AVG(UNIX_TIMESTAMP(blockTimestamp)) as INT) as timestamp, - CAST(AVG(fees) as INT) as avg_fees + CAST(AVG(fees) as INT) as avgFees FROM blocks`; if (interval !== null) { @@ -499,9 +499,9 @@ class BlocksRepository { public async $getHistoricalBlockRewards(div: number, interval: string | null): Promise { try { let query = `SELECT - CAST(AVG(height) as INT) as avg_height, + CAST(AVG(height) as INT) as avgHeight, CAST(AVG(UNIX_TIMESTAMP(blockTimestamp)) as INT) as timestamp, - CAST(AVG(reward) as INT) as avg_rewards + CAST(AVG(reward) as INT) as avgRewards FROM blocks`; if (interval !== null) { @@ -524,15 +524,15 @@ class BlocksRepository { public async $getHistoricalBlockFeeRates(div: number, interval: string | null): Promise { try { let query = `SELECT - CAST(AVG(height) as INT) as avg_height, + CAST(AVG(height) as INT) as avgHeight, CAST(AVG(UNIX_TIMESTAMP(blockTimestamp)) as INT) as timestamp, - CAST(AVG(JSON_EXTRACT(fee_span, '$[0]')) as INT) as avg_fee_0, - CAST(AVG(JSON_EXTRACT(fee_span, '$[1]')) as INT) as avg_fee_10, - CAST(AVG(JSON_EXTRACT(fee_span, '$[2]')) as INT) as avg_fee_25, - CAST(AVG(JSON_EXTRACT(fee_span, '$[3]')) as INT) as avg_fee_50, - CAST(AVG(JSON_EXTRACT(fee_span, '$[4]')) as INT) as avg_fee_75, - CAST(AVG(JSON_EXTRACT(fee_span, '$[5]')) as INT) as avg_fee_90, - CAST(AVG(JSON_EXTRACT(fee_span, '$[6]')) as INT) as avg_fee_100 + CAST(AVG(JSON_EXTRACT(fee_span, '$[0]')) as INT) as avgFee_0, + CAST(AVG(JSON_EXTRACT(fee_span, '$[1]')) as INT) as avgFee_10, + CAST(AVG(JSON_EXTRACT(fee_span, '$[2]')) as INT) as avgFee_25, + CAST(AVG(JSON_EXTRACT(fee_span, '$[3]')) as INT) as avgFee_50, + CAST(AVG(JSON_EXTRACT(fee_span, '$[4]')) as INT) as avgFee_75, + CAST(AVG(JSON_EXTRACT(fee_span, '$[5]')) as INT) as avgFee_90, + CAST(AVG(JSON_EXTRACT(fee_span, '$[6]')) as INT) as avgFee_100 FROM blocks`; if (interval !== null) { @@ -555,9 +555,9 @@ class BlocksRepository { public async $getHistoricalBlockSizes(div: number, interval: string | null): Promise { try { let query = `SELECT - CAST(AVG(height) as INT) as avg_height, + CAST(AVG(height) as INT) as avgHeight, CAST(AVG(UNIX_TIMESTAMP(blockTimestamp)) as INT) as timestamp, - CAST(AVG(size) as INT) as avg_size + CAST(AVG(size) as INT) as avgSize FROM blocks`; if (interval !== null) { @@ -580,9 +580,9 @@ class BlocksRepository { public async $getHistoricalBlockWeights(div: number, interval: string | null): Promise { try { let query = `SELECT - CAST(AVG(height) as INT) as avg_height, + CAST(AVG(height) as INT) as avgHeight, CAST(AVG(UNIX_TIMESTAMP(blockTimestamp)) as INT) as timestamp, - CAST(AVG(weight) as INT) as avg_weight + CAST(AVG(weight) as INT) as avgWeight FROM blocks`; if (interval !== null) { diff --git a/frontend/src/app/components/block-fee-rates-graph/block-fee-rates-graph.component.ts b/frontend/src/app/components/block-fee-rates-graph/block-fee-rates-graph.component.ts index 656a00d45..2cd421f26 100644 --- a/frontend/src/app/components/block-fee-rates-graph/block-fee-rates-graph.component.ts +++ b/frontend/src/app/components/block-fee-rates-graph/block-fee-rates-graph.component.ts @@ -89,13 +89,13 @@ export class BlockFeeRatesGraphComponent implements OnInit { }; for (const rate of data.blockFeeRates) { const timestamp = rate.timestamp * 1000; - seriesData['Min'].push([timestamp, rate.avg_fee_0, rate.avg_height]); - seriesData['10th'].push([timestamp, rate.avg_fee_10, rate.avg_height]); - seriesData['25th'].push([timestamp, rate.avg_fee_25, rate.avg_height]); - seriesData['Median'].push([timestamp, rate.avg_fee_50, rate.avg_height]); - seriesData['75th'].push([timestamp, rate.avg_fee_75, rate.avg_height]); - seriesData['90th'].push([timestamp, rate.avg_fee_90, rate.avg_height]); - seriesData['Max'].push([timestamp, rate.avg_fee_100, rate.avg_height]); + seriesData['Min'].push([timestamp, rate.avgFee_0, rate.avgHeight]); + seriesData['10th'].push([timestamp, rate.avgFee_10, rate.avgHeight]); + seriesData['25th'].push([timestamp, rate.avgFee_25, rate.avgHeight]); + seriesData['Median'].push([timestamp, rate.avgFee_50, rate.avgHeight]); + seriesData['75th'].push([timestamp, rate.avgFee_75, rate.avgHeight]); + seriesData['90th'].push([timestamp, rate.avgFee_90, rate.avgHeight]); + seriesData['Max'].push([timestamp, rate.avgFee_100, rate.avgHeight]); } // Prepare chart diff --git a/frontend/src/app/components/block-fees-graph/block-fees-graph.component.ts b/frontend/src/app/components/block-fees-graph/block-fees-graph.component.ts index c938b351f..185938d5d 100644 --- a/frontend/src/app/components/block-fees-graph/block-fees-graph.component.ts +++ b/frontend/src/app/components/block-fees-graph/block-fees-graph.component.ts @@ -71,7 +71,7 @@ export class BlockFeesGraphComponent implements OnInit { .pipe( tap((response) => { this.prepareChartOptions({ - blockFees: response.body.map(val => [val.timestamp * 1000, val.avg_fees / 100000000]), + blockFees: response.body.map(val => [val.timestamp * 1000, val.avgFees / 100000000]), }); this.isLoading = false; }), diff --git a/frontend/src/app/components/block-rewards-graph/block-rewards-graph.component.ts b/frontend/src/app/components/block-rewards-graph/block-rewards-graph.component.ts index 48d7ec10c..24614903c 100644 --- a/frontend/src/app/components/block-rewards-graph/block-rewards-graph.component.ts +++ b/frontend/src/app/components/block-rewards-graph/block-rewards-graph.component.ts @@ -69,7 +69,7 @@ export class BlockRewardsGraphComponent implements OnInit { .pipe( tap((response) => { this.prepareChartOptions({ - blockRewards: response.body.map(val => [val.timestamp * 1000, val.avg_rewards / 100000000]), + blockRewards: response.body.map(val => [val.timestamp * 1000, val.avgRewards / 100000000]), }); this.isLoading = false; }), diff --git a/frontend/src/app/components/block-sizes-weights-graph/block-sizes-weights-graph.component.ts b/frontend/src/app/components/block-sizes-weights-graph/block-sizes-weights-graph.component.ts index 7c72b42ef..3255fc4af 100644 --- a/frontend/src/app/components/block-sizes-weights-graph/block-sizes-weights-graph.component.ts +++ b/frontend/src/app/components/block-sizes-weights-graph/block-sizes-weights-graph.component.ts @@ -83,8 +83,8 @@ export class BlockSizesWeightsGraphComponent implements OnInit { tap((response) => { const data = response.body; this.prepareChartOptions({ - sizes: data.sizes.map(val => [val.timestamp * 1000, val.avg_size / 1000000, val.avg_height]), - weights: data.weights.map(val => [val.timestamp * 1000, val.avg_weight / 1000000, val.avg_height]), + sizes: data.sizes.map(val => [val.timestamp * 1000, val.avgSize / 1000000, val.avgHeight]), + weights: data.weights.map(val => [val.timestamp * 1000, val.avgWeight / 1000000, val.avgHeight]), }); this.isLoading = false; }), From 897126d56f60228c08c27a645957c95fec03bbb1 Mon Sep 17 00:00:00 2001 From: nymkappa Date: Fri, 20 May 2022 09:49:13 +0200 Subject: [PATCH 04/15] Update API documentation for e101c4e2 --- .../src/app/docs/api-docs/api-docs-data.ts | 252 +++++++++--------- 1 file changed, 126 insertions(+), 126 deletions(-) diff --git a/frontend/src/app/docs/api-docs/api-docs-data.ts b/frontend/src/app/docs/api-docs/api-docs-data.ts index ff57aeb82..4737b27c7 100644 --- a/frontend/src/app/docs/api-docs/api-docs-data.ts +++ b/frontend/src/app/docs/api-docs/api-docs-data.ts @@ -3833,34 +3833,34 @@ export const restApiDocsData = [ curl: [`1w`], response: `[ { - "avg_height": 735644, + "avgHeight": 735644, "timestamp": 1652119111, - "avg_fees": 24212890 + "avgFees": 24212890 }, { - "avg_height": 735646, + "avgHeight": 735646, "timestamp": 1652120252, - "avg_fees": 21655996 + "avgFees": 21655996 }, { - "avg_height": 735648, + "avgHeight": 735648, "timestamp": 1652121214, - "avg_fees": 20678859 + "avgFees": 20678859 }, { - "avg_height": 735649, + "avgHeight": 735649, "timestamp": 1652121757, - "avg_fees": 21020140 + "avgFees": 21020140 }, { - "avg_height": 735650, + "avgHeight": 735650, "timestamp": 1652122367, - "avg_fees": 23064200 + "avgFees": 23064200 }, { - "avg_height": 735652, + "avgHeight": 735652, "timestamp": 1652122893, - "avg_fees": 17620723 + "avgFees": 17620723 }, ... ]` @@ -3871,14 +3871,14 @@ export const restApiDocsData = [ curl: [`1w`], response: `[ { - "avg_height": 2224253, + "avgHeight": 2224253, "timestamp": 1652346420, - "avg_fees": 211686 + "avgFees": 211686 }, { - "avg_height": 2224254, + "avgHeight": 2224254, "timestamp": 1652346850, - "avg_fees": 2565952 + "avgFees": 2565952 }, ... ]` @@ -3889,14 +3889,14 @@ export const restApiDocsData = [ curl: [`1w`], response: `[ { - "avg_height": 89978, + "avgHeight": 89978, "timestamp": 1652346573, - "avg_fees": 1071 + "avgFees": 1071 }, { - "avg_height": 89979, + "avgHeight": 89979, "timestamp": 1652346970, - "avg_fees": 1224 + "avgFees": 1224 }, ... ]` @@ -3932,29 +3932,29 @@ export const restApiDocsData = [ curl: [`1d`], response: `[ { - "avg_height": 599992, + "avgHeight": 599992, "timestamp": 1571438412, - "avg_rewards": 1260530933 + "avgRewards": 1260530933 }, { - "avg_height": 600000, + "avgHeight": 600000, "timestamp": 1571443398, - "avg_rewards": 1264314538 + "avgRewards": 1264314538 }, { - "avg_height": 725441, + "avgHeight": 725441, "timestamp": 1646139035, - "avg_rewards": 637067563 + "avgRewards": 637067563 }, { - "avg_height": 725585, + "avgHeight": 725585, "timestamp": 1646222444, - "avg_rewards": 646519104 + "avgRewards": 646519104 }, { - "avg_height": 725727, + "avgHeight": 725727, "timestamp": 1646308374, - "avg_rewards": 638709605 + "avgRewards": 638709605 }, ... ]` @@ -3965,14 +3965,14 @@ export const restApiDocsData = [ curl: [`1d`], response: `[ { - "avg_height": 12, + "avgHeight": 12, "timestamp": 1296689648, - "avg_rewards": 5000000000 + "avgRewards": 5000000000 }, { - "avg_height": 269, + "avgHeight": 269, "timestamp": 1296717674, - "avg_rewards": 5000091820 + "avgRewards": 5000091820 }, ... ]` @@ -3983,14 +3983,14 @@ export const restApiDocsData = [ curl: [`1d`], response: `[ { - "avg_height": 183, + "avgHeight": 183, "timestamp": 1598962247, - "avg_rewards": 5000000000 + "avgRewards": 5000000000 }, { - "avg_height": 576, + "avgHeight": 576, "timestamp": 1599047892, - "avg_rewards": 5000000000 + "avgRewards": 5000000000 }, ... ]` @@ -4028,37 +4028,37 @@ export const restApiDocsData = [ "oldestIndexedBlockTimestamp": 1571434851, "blockFeeRates": [ { - "avg_height": 732152, + "avgHeight": 732152, "timestamp": 1650132959, - "avg_fee_0": 1, - "avg_fee_10": 2, - "avg_fee_25": 2, - "avg_fee_50": 3, - "avg_fee_75": 4, - "avg_fee_90": 8, - "avg_fee_100": 393 + "avgFee_0": 1, + "avgFee_10": 2, + "avgFee_25": 2, + "avgFee_50": 3, + "avgFee_75": 4, + "avgFee_90": 8, + "avgFee_100": 393 }, { - "avg_height": 732158, + "avgHeight": 732158, "timestamp": 1650134432, - "avg_fee_0": 1, - "avg_fee_10": 1, - "avg_fee_25": 2, - "avg_fee_50": 4, - "avg_fee_75": 6, - "avg_fee_90": 10, - "avg_fee_100": 240 + "avgFee_0": 1, + "avgFee_10": 1, + "avgFee_25": 2, + "avgFee_50": 4, + "avgFee_75": 6, + "avgFee_90": 10, + "avgFee_100": 240 }, { - "avg_height": 732161, + "avgHeight": 732161, "timestamp": 1650135818, - "avg_fee_0": 1, - "avg_fee_10": 1, - "avg_fee_25": 1, - "avg_fee_50": 2, - "avg_fee_75": 5, - "avg_fee_90": 8, - "avg_fee_100": 251 + "avgFee_0": 1, + "avgFee_10": 1, + "avgFee_25": 1, + "avgFee_50": 2, + "avgFee_75": 5, + "avgFee_90": 8, + "avgFee_100": 251 }, ... ] @@ -4072,26 +4072,26 @@ export const restApiDocsData = [ "oldestIndexedBlockTimestamp": 1296688602, "blockFeeRates": [ { - "avg_height": 2196306, + "avgHeight": 2196306, "timestamp": 1650360168, - "avg_fee_0": 1, - "avg_fee_10": 1, - "avg_fee_25": 1, - "avg_fee_50": 1, - "avg_fee_75": 2, - "avg_fee_90": 28, - "avg_fee_100": 2644 + "avgFee_0": 1, + "avgFee_10": 1, + "avgFee_25": 1, + "avgFee_50": 1, + "avgFee_75": 2, + "avgFee_90": 28, + "avgFee_100": 2644 }, { - "avg_height": 2196308, + "avgHeight": 2196308, "timestamp": 1650361209, - "avg_fee_0": 1, - "avg_fee_10": 1, - "avg_fee_25": 1, - "avg_fee_50": 4, - "avg_fee_75": 12, - "avg_fee_90": 65, - "avg_fee_100": 102 + "avgFee_0": 1, + "avgFee_10": 1, + "avgFee_25": 1, + "avgFee_50": 4, + "avgFee_75": 12, + "avgFee_90": 65, + "avgFee_100": 102 }, ... ] @@ -4105,26 +4105,26 @@ export const restApiDocsData = [ "oldestIndexedBlockTimestamp": 1598918400, "blockFeeRates": [ { - "avg_height": 86620, + "avgHeight": 86620, "timestamp": 1650360010, - "avg_fee_0": 1, - "avg_fee_10": 1, - "avg_fee_25": 1, - "avg_fee_50": 1, - "avg_fee_75": 1, - "avg_fee_90": 1, - "avg_fee_100": 1 + "avgFee_0": 1, + "avgFee_10": 1, + "avgFee_25": 1, + "avgFee_50": 1, + "avgFee_75": 1, + "avgFee_90": 1, + "avgFee_100": 1 }, { - "avg_height": 86623, + "avgHeight": 86623, "timestamp": 1650361330, - "avg_fee_0": 1, - "avg_fee_10": 1, - "avg_fee_25": 1, - "avg_fee_50": 1, - "avg_fee_75": 1, - "avg_fee_90": 1, - "avg_fee_100": 1 + "avgFee_0": 1, + "avgFee_10": 1, + "avgFee_25": 1, + "avgFee_50": 1, + "avgFee_75": 1, + "avgFee_90": 1, + "avgFee_100": 1 }, ... ] @@ -4162,47 +4162,47 @@ export const restApiDocsData = [ response: `{ "sizes": [ { - "avg_height": 576650, + "avgHeight": 576650, "timestamp": 1558212081, - "avg_size": 1271404 + "avgSize": 1271404 }, { - "avg_height": 576715, + "avgHeight": 576715, "timestamp": 1558246272, - "avg_size": 1105893 + "avgSize": 1105893 }, { - "avg_height": 576797, + "avgHeight": 576797, "timestamp": 1558289379, - "avg_size": 1141071 + "avgSize": 1141071 }, { - "avg_height": 576885, + "avgHeight": 576885, "timestamp": 1558330184, - "avg_size": 1108166 + "avgSize": 1108166 }, ... ], "weights": [ { - "avg_height": 576650, + "avgHeight": 576650, "timestamp": 1558212081, - "avg_weight": 3994002 + "avgWeight": 3994002 }, { - "avg_height": 576715, + "avgHeight": 576715, "timestamp": 1558246272, - "avg_weight": 3756312 + "avgWeight": 3756312 }, { - "avg_height": 576797, + "avgHeight": 576797, "timestamp": 1558289379, - "avg_weight": 3719625 + "avgWeight": 3719625 }, { - "avg_height": 576885, + "avgHeight": 576885, "timestamp": 1558330184, - "avg_weight": 3631381 + "avgWeight": 3631381 }, ... ] @@ -4215,27 +4215,27 @@ export const restApiDocsData = [ response: `{ "sizes": [ { - "avg_height": 1517188, + "avgHeight": 1517188, "timestamp": 1558262730, - "avg_size": 25089 + "avgSize": 25089 }, { - "avg_height": 1517275, + "avgHeight": 1517275, "timestamp": 1558290933, - "avg_size": 21679 + "avgSize": 21679 }, ... ], "weights": [ { - "avg_height": 1517188, + "avgHeight": 1517188, "timestamp": 1558262730, - "avg_weight": 74921 + "avgWeight": 74921 }, { - "avg_height": 1517275, + "avgHeight": 1517275, "timestamp": 1558290933, - "avg_weight": 65164 + "avgWeight": 65164 }, ... ] @@ -4248,27 +4248,27 @@ export const restApiDocsData = [ response: `{ "sizes": [ { - "avg_height": 83, + "avgHeight": 83, "timestamp": 1598937527, - "avg_size": 329 + "avgSize": 329 }, { - "avg_height": 266, + "avgHeight": 266, "timestamp": 1598982991, - "avg_size": 330 + "avgSize": 330 }, ... ], "weights": [ { - "avg_height": 83, + "avgHeight": 83, "timestamp": 1598937527, - "avg_weight": 1209 + "avgWeight": 1209 }, { - "avg_height": 266, + "avgHeight": 266, "timestamp": 1598982991, - "avg_weight": 1212 + "avgWeight": 1212 }, ... ] From a1fb89963c5afd8b86a9d14cde2bfb52d11ef501 Mon Sep 17 00:00:00 2001 From: softsimon Date: Sat, 21 May 2022 02:30:38 +0400 Subject: [PATCH 05/15] Block transactions list error handling --- .../app/components/block/block.component.html | 17 ++++++++++++++--- .../src/app/components/block/block.component.ts | 11 ++++++++++- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/frontend/src/app/components/block/block.component.html b/frontend/src/app/components/block/block.component.html index c0ff29889..07ac76c21 100644 --- a/frontend/src/app/components/block/block.component.html +++ b/frontend/src/app/components/block/block.component.html @@ -197,7 +197,18 @@ - + +
+
+ Error loading data. +

+ {{ transactionsError.status }}: {{ transactionsError.error }} +
+
+
+
+ +
@@ -271,9 +282,9 @@
- Error loading block data. + Error loading data.

- {{ error.error }} + {{ error.code }}: {{ error.error }}
diff --git a/frontend/src/app/components/block/block.component.ts b/frontend/src/app/components/block/block.component.ts index 57417a5c3..bd70e8628 100644 --- a/frontend/src/app/components/block/block.component.ts +++ b/frontend/src/app/components/block/block.component.ts @@ -38,6 +38,7 @@ export class BlockComponent implements OnInit, OnDestroy { showDetails = false; showPreviousBlocklink = true; showNextBlocklink = true; + transactionsError: any = null; subscription: Subscription; keyNavigationSubscription: Subscription; @@ -152,12 +153,13 @@ export class BlockComponent implements OnInit, OnDestroy { this.stateService.markBlock$.next({ blockHeight: this.blockHeight }); this.isLoadingTransactions = true; this.transactions = null; + this.transactionsError = null; }), debounceTime(300), switchMap((block) => this.electrsApiService.getBlockTransactions$(block.id) .pipe( catchError((err) => { - console.log(err); + this.transactionsError = err; return of([]); })) ), @@ -218,9 +220,16 @@ export class BlockComponent implements OnInit, OnDestroy { const start = (page - 1) * this.itemsPerPage; this.isLoadingTransactions = true; this.transactions = null; + this.transactionsError = null; target.scrollIntoView(); // works for chrome this.electrsApiService.getBlockTransactions$(this.block.id, start) + .pipe( + catchError((err) => { + this.transactionsError = err; + return of([]); + }) + ) .subscribe((transactions) => { this.transactions = transactions; this.isLoadingTransactions = false; From 37b7ea6702b27358d319ec8d202f9aec61426505 Mon Sep 17 00:00:00 2001 From: nymkappa Date: Fri, 20 May 2022 18:11:02 +0200 Subject: [PATCH 06/15] Merge legacy and mining /blocks components and APIs --- backend/src/api/blocks.ts | 5 +- backend/src/index.ts | 7 +- backend/src/routes.ts | 41 +---- frontend/src/app/app-routing.module.ts | 31 +--- .../blocks-list/blocks-list.component.html | 31 ++-- .../blocks-list/blocks-list.component.scss | 25 ++++ .../blocks-list/blocks-list.component.ts | 16 +- .../latest-blocks.component.html | 57 ------- .../latest-blocks.component.scss | 14 -- .../latest-blocks/latest-blocks.component.ts | 140 ------------------ frontend/src/app/services/api.service.ts | 2 +- frontend/src/app/shared/shared.module.ts | 3 - 12 files changed, 63 insertions(+), 309 deletions(-) delete mode 100644 frontend/src/app/components/latest-blocks/latest-blocks.component.html delete mode 100644 frontend/src/app/components/latest-blocks/latest-blocks.component.scss delete mode 100644 frontend/src/app/components/latest-blocks/latest-blocks.component.ts diff --git a/backend/src/api/blocks.ts b/backend/src/api/blocks.ts index 293862e93..91db74faa 100644 --- a/backend/src/api/blocks.ts +++ b/backend/src/api/blocks.ts @@ -426,10 +426,7 @@ class Blocks { return blockExtended; } - public async $getBlocksExtras(fromHeight?: number, limit: number = 15): Promise { - // Note - This API is breaking if indexing is not available. For now it is okay because we only - // use it for the mining pages, and mining pages should not be available if indexing is turned off. - // I'll need to fix it before we refactor the block(s) related pages + public async $getBlocks(fromHeight?: number, limit: number = 15): Promise { try { let currentHeight = fromHeight !== undefined ? fromHeight : this.getCurrentBlockHeight(); const returnBlocks: BlockExtended[] = []; diff --git a/backend/src/index.ts b/backend/src/index.ts index c6eab6c17..d195ff157 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -27,7 +27,6 @@ import icons from './api/liquid/icons'; import { Common } from './api/common'; import mining from './api/mining'; import HashratesRepository from './repositories/HashratesRepository'; -import BlocksRepository from './repositories/BlocksRepository'; import poolsUpdater from './tasks/pools-updater'; class Server { @@ -337,8 +336,8 @@ class Server { } this.app - .get(config.MEMPOOL.API_URL_PREFIX + 'blocks-extras', routes.getBlocksExtras) - .get(config.MEMPOOL.API_URL_PREFIX + 'blocks-extras/:height', routes.getBlocksExtras) + .get(config.MEMPOOL.API_URL_PREFIX + 'blocks', routes.getBlocks) + .get(config.MEMPOOL.API_URL_PREFIX + 'blocks/:height', routes.getBlocks) .get(config.MEMPOOL.API_URL_PREFIX + 'block/:hash', routes.getBlock); if (config.MEMPOOL.BACKEND !== 'esplora') { @@ -352,8 +351,6 @@ class Server { .get(config.MEMPOOL.API_URL_PREFIX + 'tx/:txId/status', routes.getTransactionStatus) .get(config.MEMPOOL.API_URL_PREFIX + 'tx/:txId/outspends', routes.getTransactionOutspends) .get(config.MEMPOOL.API_URL_PREFIX + 'block/:hash/header', routes.getBlockHeader) - .get(config.MEMPOOL.API_URL_PREFIX + 'blocks', routes.getBlocks) - .get(config.MEMPOOL.API_URL_PREFIX + 'blocks/:height', routes.getBlocks) .get(config.MEMPOOL.API_URL_PREFIX + 'blocks/tip/height', routes.getBlockTipHeight) .get(config.MEMPOOL.API_URL_PREFIX + 'block/:hash/txs', routes.getBlockTransactions) .get(config.MEMPOOL.API_URL_PREFIX + 'block/:hash/txs/:index', routes.getBlockTransactions) diff --git a/backend/src/routes.ts b/backend/src/routes.ts index cf28dd71d..5b762ff27 100644 --- a/backend/src/routes.ts +++ b/backend/src/routes.ts @@ -720,53 +720,16 @@ class Routes { } } - public async getBlocksExtras(req: Request, res: Response) { + public async getBlocks(req: Request, res: Response) { try { const height = req.params.height === undefined ? undefined : parseInt(req.params.height, 10); res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString()); - res.json(await blocks.$getBlocksExtras(height, 15)); + res.json(await blocks.$getBlocks(height, 15)); } catch (e) { res.status(500).send(e instanceof Error ? e.message : e); } } - public async getBlocks(req: Request, res: Response) { - try { - loadingIndicators.setProgress('blocks', 0); - - const returnBlocks: IEsploraApi.Block[] = []; - const fromHeight = parseInt(req.params.height, 10) || blocks.getCurrentBlockHeight(); - - // Check if block height exist in local cache to skip the hash lookup - const blockByHeight = blocks.getBlocks().find((b) => b.height === fromHeight); - let startFromHash: string | null = null; - if (blockByHeight) { - startFromHash = blockByHeight.id; - } else { - startFromHash = await bitcoinApi.$getBlockHash(fromHeight); - } - - let nextHash = startFromHash; - for (let i = 0; i < 10 && nextHash; i++) { - const localBlock = blocks.getBlocks().find((b) => b.id === nextHash); - if (localBlock) { - returnBlocks.push(localBlock); - nextHash = localBlock.previousblockhash; - } else { - const block = await bitcoinApi.$getBlock(nextHash); - returnBlocks.push(block); - nextHash = block.previousblockhash; - } - loadingIndicators.setProgress('blocks', i / 10 * 100); - } - - res.json(returnBlocks); - } catch (e) { - loadingIndicators.setProgress('blocks', 100); - res.status(500).send(e instanceof Error ? e.message : e); - } - } - public async getBlockTransactions(req: Request, res: Response) { try { loadingIndicators.setProgress('blocktxs-' + req.params.hash, 0); diff --git a/frontend/src/app/app-routing.module.ts b/frontend/src/app/app-routing.module.ts index 672d68686..5f1124031 100644 --- a/frontend/src/app/app-routing.module.ts +++ b/frontend/src/app/app-routing.module.ts @@ -7,7 +7,6 @@ import { AddressComponent } from './components/address/address.component'; import { MasterPageComponent } from './components/master-page/master-page.component'; import { AboutComponent } from './components/about/about.component'; import { StatusViewComponent } from './components/status-view/status-view.component'; -import { LatestBlocksComponent } from './components/latest-blocks/latest-blocks.component'; import { TermsOfServiceComponent } from './components/terms-of-service/terms-of-service.component'; import { PrivacyPolicyComponent } from './components/privacy-policy/privacy-policy.component'; import { TrademarkPolicyComponent } from './components/trademark-policy/trademark-policy.component'; @@ -39,16 +38,12 @@ let routes: Routes = [ path: 'tx/push', component: PushTransactionComponent, }, - { - path: 'blocks', - component: LatestBlocksComponent, - }, { path: 'about', component: AboutComponent, }, { - path: 'mining/blocks', + path: 'blocks', component: BlocksList, }, { @@ -128,16 +123,12 @@ let routes: Routes = [ path: 'tx/push', component: PushTransactionComponent, }, - { - path: 'blocks', - component: LatestBlocksComponent, - }, { path: 'about', component: AboutComponent, }, { - path: 'mining/blocks', + path: 'blocks', component: BlocksList, }, { @@ -214,16 +205,12 @@ let routes: Routes = [ path: 'tx/push', component: PushTransactionComponent, }, - { - path: 'blocks', - component: LatestBlocksComponent, - }, { path: 'about', component: AboutComponent, }, { - path: 'mining/blocks', + path: 'blocks', component: BlocksList, }, { @@ -321,16 +308,12 @@ if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') { path: 'tx/push', component: PushTransactionComponent, }, - { - path: 'blocks', - component: LatestBlocksComponent, - }, { path: 'about', component: AboutComponent, }, { - path: 'mining/blocks', + path: 'blocks', component: BlocksList, }, { @@ -429,16 +412,12 @@ if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') { path: 'tx/push', component: PushTransactionComponent, }, - { - path: 'blocks', - component: LatestBlocksComponent, - }, { path: 'about', component: AboutComponent, }, { - path: 'mining/blocks', + path: 'blocks', component: BlocksList, }, { diff --git a/frontend/src/app/components/blocks-list/blocks-list.component.html b/frontend/src/app/components/blocks-list/blocks-list.component.html index face9452b..d5c8c2ef9 100644 --- a/frontend/src/app/components/blocks-list/blocks-list.component.html +++ b/frontend/src/app/components/blocks-list/blocks-list.component.html @@ -1,6 +1,6 @@ -
+

Blocks

@@ -8,14 +8,15 @@
- - - - - - - - + + + + + + + + + @@ -23,7 +24,7 @@ {{ block.height }} - - - - - -
HeightPoolTimestampMinedRewardFeesTXsSizeHeightPoolTimestampMinedRewardFeesTXsTransactionsSize
+ + + + + {{ block.tx_count | number }} +
diff --git a/frontend/src/app/components/blocks-list/blocks-list.component.scss b/frontend/src/app/components/blocks-list/blocks-list.component.scss index ead712be0..cea30e126 100644 --- a/frontend/src/app/components/blocks-list/blocks-list.component.scss +++ b/frontend/src/app/components/blocks-list/blocks-list.component.scss @@ -6,6 +6,9 @@ padding-left: 0px; padding-bottom: 0px; } +.container-xl.legacy { + max-width: 1140px; +} .container { max-width: 100%; @@ -58,6 +61,9 @@ tr, td, th { width: 10%; } } +.height.legacy { + width: 15%; +} .timestamp { width: 18%; @@ -65,6 +71,9 @@ tr, td, th { display: none; } } +.timestamp.legacy { + width: 20%; +} .mined { width: 13%; @@ -72,6 +81,12 @@ tr, td, th { display: none; } } +.mined.legacy { + width: 15%; + @media (max-width: 576px) { + display: table-cell; + } +} .txs { padding-right: 40px; @@ -88,6 +103,10 @@ tr, td, th { display: none; } } +.txs.legacy { + padding-right: 80px; + width: 10%; +} .fees { width: 10%; @@ -126,6 +145,12 @@ tr, td, th { display: none; } } +.size.legacy { + width: 20%; + @media (max-width: 576px) { + display: table-cell; + } +} /* Tooltip text */ .tooltip-custom { diff --git a/frontend/src/app/components/blocks-list/blocks-list.component.ts b/frontend/src/app/components/blocks-list/blocks-list.component.ts index b5b66b22b..f1f81d321 100644 --- a/frontend/src/app/components/blocks-list/blocks-list.component.ts +++ b/frontend/src/app/components/blocks-list/blocks-list.component.ts @@ -17,6 +17,7 @@ export class BlocksList implements OnInit { blocks$: Observable = undefined; + indexingAvailable = false; isLoading = true; fromBlockHeight = undefined; paginationMaxSize: number; @@ -35,6 +36,9 @@ export class BlocksList implements OnInit { } ngOnInit(): void { + this.indexingAvailable = (this.stateService.env.BASE_MODULE === 'mempool' && + this.stateService.env.MINING_DASHBOARD === true); + if (!this.widget) { this.websocketService.want(['blocks']); } @@ -55,17 +59,19 @@ export class BlocksList implements OnInit { this.isLoading = false; }), map(blocks => { - for (const block of blocks) { - // @ts-ignore: Need to add an extra field for the template - block.extras.pool.logo = `./resources/mining-pools/` + - block.extras.pool.name.toLowerCase().replace(' ', '').replace('.', '') + '.svg'; + if (this.indexingAvailable) { + for (const block of blocks) { + // @ts-ignore: Need to add an extra field for the template + block.extras.pool.logo = `./resources/mining-pools/` + + block.extras.pool.name.toLowerCase().replace(' ', '').replace('.', '') + '.svg'; + } } if (this.widget) { return blocks.slice(0, 6); } return blocks; }), - retryWhen(errors => errors.pipe(delayWhen(() => timer(1000)))) + retryWhen(errors => errors.pipe(delayWhen(() => timer(10000)))) ) }) ), diff --git a/frontend/src/app/components/latest-blocks/latest-blocks.component.html b/frontend/src/app/components/latest-blocks/latest-blocks.component.html deleted file mode 100644 index 297d62f48..000000000 --- a/frontend/src/app/components/latest-blocks/latest-blocks.component.html +++ /dev/null @@ -1,57 +0,0 @@ -
-

Blocks

-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HeightTimestampMinedTransactionsSize
{{ block.height }}‎{{ block.timestamp * 1000 | date:'yyyy-MM-dd HH:mm' }}{{ block.tx_count | number }} -
-
-
-
-
-
-
-
-
- - -
- Error loading blocks -
- {{ error.error }} -
-
- -
diff --git a/frontend/src/app/components/latest-blocks/latest-blocks.component.scss b/frontend/src/app/components/latest-blocks/latest-blocks.component.scss deleted file mode 100644 index 0f2246c99..000000000 --- a/frontend/src/app/components/latest-blocks/latest-blocks.component.scss +++ /dev/null @@ -1,14 +0,0 @@ -.progress { - background-color: #2d3348; -} - -@media (min-width: 768px) { - .d-md-block { - display: table-cell !important; - } -} -@media (min-width: 992px) { - .d-lg-block { - display: table-cell !important; - } -} diff --git a/frontend/src/app/components/latest-blocks/latest-blocks.component.ts b/frontend/src/app/components/latest-blocks/latest-blocks.component.ts deleted file mode 100644 index 3d4ae91ad..000000000 --- a/frontend/src/app/components/latest-blocks/latest-blocks.component.ts +++ /dev/null @@ -1,140 +0,0 @@ -import { Component, OnInit, OnDestroy, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core'; -import { ElectrsApiService } from '../../services/electrs-api.service'; -import { StateService } from '../../services/state.service'; -import { Block } from '../../interfaces/electrs.interface'; -import { Subscription, Observable, merge, of } from 'rxjs'; -import { SeoService } from '../../services/seo.service'; -import { WebsocketService } from 'src/app/services/websocket.service'; -import { map } from 'rxjs/operators'; - -@Component({ - selector: 'app-latest-blocks', - templateUrl: './latest-blocks.component.html', - styleUrls: ['./latest-blocks.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush -}) -export class LatestBlocksComponent implements OnInit, OnDestroy { - network$: Observable; - error: any; - blocks: any[] = []; - blockSubscription: Subscription; - isLoading = true; - interval: any; - blocksLoadingStatus$: Observable; - - latestBlockHeight: number; - - heightOfPageUntilBlocks = 150; - heightOfBlocksTableChunk = 470; - - constructor( - private electrsApiService: ElectrsApiService, - public stateService: StateService, - private seoService: SeoService, - private websocketService: WebsocketService, - private cd: ChangeDetectorRef, - ) { } - - ngOnInit() { - this.seoService.setTitle($localize`:@@8a7b4bd44c0ac71b2e72de0398b303257f7d2f54:Blocks`); - this.websocketService.want(['blocks']); - - this.network$ = merge(of(''), this.stateService.networkChanged$); - - this.blocksLoadingStatus$ = this.stateService.loadingIndicators$ - .pipe( - map((indicators) => indicators['blocks'] !== undefined ? indicators['blocks'] : 0) - ); - - this.blockSubscription = this.stateService.blocks$ - .subscribe(([block]) => { - if (block === null || !this.blocks.length) { - return; - } - - this.latestBlockHeight = block.height; - - if (block.height === this.blocks[0].height) { - return; - } - - // If we are out of sync, reload the blocks instead - if (block.height > this.blocks[0].height + 1) { - this.loadInitialBlocks(); - return; - } - - if (block.height <= this.blocks[0].height) { - return; - } - - this.blocks.pop(); - this.blocks.unshift(block); - this.cd.markForCheck(); - }); - - this.loadInitialBlocks(); - } - - ngOnDestroy() { - clearInterval(this.interval); - this.blockSubscription.unsubscribe(); - } - - loadInitialBlocks() { - this.electrsApiService.listBlocks$() - .subscribe((blocks) => { - this.blocks = blocks; - this.isLoading = false; - this.error = undefined; - - this.latestBlockHeight = blocks[0].height; - - const spaceForBlocks = window.innerHeight - this.heightOfPageUntilBlocks; - const chunks = Math.ceil(spaceForBlocks / this.heightOfBlocksTableChunk) - 1; - if (chunks > 0) { - this.loadMore(chunks); - } - this.cd.markForCheck(); - }, - (error) => { - console.log(error); - this.error = error; - this.isLoading = false; - this.cd.markForCheck(); - }); - } - - loadMore(chunks = 0) { - if (this.isLoading) { - return; - } - const height = this.blocks[this.blocks.length - 1].height - 1; - if (height < 0) { - return; - } - this.isLoading = true; - this.electrsApiService.listBlocks$(height) - .subscribe((blocks) => { - this.blocks = this.blocks.concat(blocks); - this.isLoading = false; - this.error = undefined; - - const chunksLeft = chunks - 1; - if (chunksLeft > 0) { - this.loadMore(chunksLeft); - } - this.cd.markForCheck(); - }, - (error) => { - console.log(error); - this.error = error; - this.isLoading = false; - this.cd.markForCheck(); - }); - } - - trackByBlock(index: number, block: Block) { - return block.height; - } -} diff --git a/frontend/src/app/services/api.service.ts b/frontend/src/app/services/api.service.ts index 8ef656013..7a523a7b3 100644 --- a/frontend/src/app/services/api.service.ts +++ b/frontend/src/app/services/api.service.ts @@ -149,7 +149,7 @@ export class ApiService { getBlocks$(from: number): Observable { return this.httpClient.get( - this.apiBaseUrl + this.apiBasePath + `/api/v1/blocks-extras` + + this.apiBaseUrl + this.apiBasePath + `/api/v1/blocks` + (from !== undefined ? `/${from}` : ``) ); } diff --git a/frontend/src/app/shared/shared.module.ts b/frontend/src/app/shared/shared.module.ts index 488131d84..eb0bf5b87 100644 --- a/frontend/src/app/shared/shared.module.ts +++ b/frontend/src/app/shared/shared.module.ts @@ -48,7 +48,6 @@ import { TransactionsListComponent } from '../components/transactions-list/trans import { BlockComponent } from '../components/block/block.component'; import { AddressComponent } from '../components/address/address.component'; import { SearchFormComponent } from '../components/search-form/search-form.component'; -import { LatestBlocksComponent } from '../components/latest-blocks/latest-blocks.component'; import { AddressLabelsComponent } from '../components/address-labels/address-labels.component'; import { FooterComponent } from '../components/footer/footer.component'; import { TimeSpanComponent } from '../components/time-span/time-span.component'; @@ -113,7 +112,6 @@ import { IndexingProgressComponent } from '../components/indexing-progress/index BlockComponent, TransactionsListComponent, AddressComponent, - LatestBlocksComponent, SearchFormComponent, TimeSpanComponent, AddressLabelsComponent, @@ -208,7 +206,6 @@ import { IndexingProgressComponent } from '../components/indexing-progress/index BlockComponent, TransactionsListComponent, AddressComponent, - LatestBlocksComponent, SearchFormComponent, TimeSpanComponent, AddressLabelsComponent, From 8db440f164572247a36898af9a94b3a8b8cbff61 Mon Sep 17 00:00:00 2001 From: nymkappa Date: Fri, 20 May 2022 20:24:55 +0200 Subject: [PATCH 07/15] Update cache warmer --- production/nginx-cache-warmer | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/production/nginx-cache-warmer b/production/nginx-cache-warmer index 165a97860..366a07345 100755 --- a/production/nginx-cache-warmer +++ b/production/nginx-cache-warmer @@ -4,6 +4,7 @@ slugs=(`curl -sSL https://raw.githubusercontent.com/mempool/mining-pools/master/ while true do for url in / \ + '/api/v1/blocks' \ '/api/v1/statistics/2h' \ '/api/v1/statistics/24h' \ '/api/v1/statistics/1w' \ @@ -36,7 +37,6 @@ do for url in / \ '/api/v1/mining/hashrate/pools/3y' \ '/api/v1/mining/hashrate/pools/all' \ '/api/v1/mining/reward-stats/144' \ - '/api/v1/mining/blocks-extras' \ '/api/v1/mining/blocks/fees/24h' \ '/api/v1/mining/blocks/fees/3d' \ '/api/v1/mining/blocks/fees/1w' \ From ffcfa4a659fc41b961d2ed4f31ad9d50245bfb83 Mon Sep 17 00:00:00 2001 From: nymkappa Date: Sat, 21 May 2022 08:47:49 +0200 Subject: [PATCH 08/15] Setup redirect from /mining/blocks to /blocks - Update router links --- frontend/src/app/app-routing.module.ts | 15 +++++++++++++++ .../mining-dashboard.component.html | 2 +- .../src/app/dashboard/dashboard.component.html | 2 +- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/app-routing.module.ts b/frontend/src/app/app-routing.module.ts index 5f1124031..6951accb2 100644 --- a/frontend/src/app/app-routing.module.ts +++ b/frontend/src/app/app-routing.module.ts @@ -34,6 +34,11 @@ let routes: Routes = [ path: '', component: MasterPageComponent, children: [ + { + path: 'mining/blocks', + redirectTo: 'blocks', + pathMatch: 'full' + }, { path: 'tx/push', component: PushTransactionComponent, @@ -110,6 +115,11 @@ let routes: Routes = [ { path: 'signet', children: [ + { + path: 'mining/blocks', + redirectTo: 'blocks', + pathMatch: 'full' + }, { path: '', pathMatch: 'full', @@ -201,6 +211,11 @@ let routes: Routes = [ path: '', component: MasterPageComponent, children: [ + { + path: 'mining/blocks', + redirectTo: 'blocks', + pathMatch: 'full' + }, { path: 'tx/push', component: PushTransactionComponent, diff --git a/frontend/src/app/components/mining-dashboard/mining-dashboard.component.html b/frontend/src/app/components/mining-dashboard/mining-dashboard.component.html index 6a208e5c7..fe7c08ba2 100644 --- a/frontend/src/app/components/mining-dashboard/mining-dashboard.component.html +++ b/frontend/src/app/components/mining-dashboard/mining-dashboard.component.html @@ -51,7 +51,7 @@
Latest blocks
- +
diff --git a/frontend/src/app/dashboard/dashboard.component.html b/frontend/src/app/dashboard/dashboard.component.html index 2ab42a6a0..d26b6660a 100644 --- a/frontend/src/app/dashboard/dashboard.component.html +++ b/frontend/src/app/dashboard/dashboard.component.html @@ -136,7 +136,7 @@
- +
From 2da7ec2519a72cdc55979fb551a6515a2bf88b30 Mon Sep 17 00:00:00 2001 From: nymkappa Date: Sat, 21 May 2022 08:51:36 +0200 Subject: [PATCH 09/15] Use ngClass to avoid multiple [class] conflict --- .../blocks-list/blocks-list.component.html | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/frontend/src/app/components/blocks-list/blocks-list.component.html b/frontend/src/app/components/blocks-list/blocks-list.component.html index d5c8c2ef9..c7c16d1f2 100644 --- a/frontend/src/app/components/blocks-list/blocks-list.component.html +++ b/frontend/src/app/components/blocks-list/blocks-list.component.html @@ -8,14 +8,14 @@
- - + + - + - - + + @@ -24,7 +24,7 @@ {{ block.height }} - - - - - - - - - - - From 88fba3f5064e0488da5bdbdbbc1d5e9e0ec67378 Mon Sep 17 00:00:00 2001 From: nymkappa Date: Mon, 23 May 2022 13:02:18 +0200 Subject: [PATCH 13/15] For non Bitcoin network, run legacy API code, but keep the same endpoint --- backend/src/index.ts | 4 +- backend/src/mempool.interfaces.ts | 2 +- backend/src/routes.ts | 44 +++++++++++++++++-- backend/src/utils/blocks-utils.ts | 2 +- .../blocks-list/blocks-list.component.ts | 8 ++-- 5 files changed, 50 insertions(+), 10 deletions(-) diff --git a/backend/src/index.ts b/backend/src/index.ts index d195ff157..09d8f7669 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -336,8 +336,8 @@ class Server { } this.app - .get(config.MEMPOOL.API_URL_PREFIX + 'blocks', routes.getBlocks) - .get(config.MEMPOOL.API_URL_PREFIX + 'blocks/:height', routes.getBlocks) + .get(config.MEMPOOL.API_URL_PREFIX + 'blocks', routes.getBlocks.bind(routes)) + .get(config.MEMPOOL.API_URL_PREFIX + 'blocks/:height', routes.getBlocks.bind(routes)) .get(config.MEMPOOL.API_URL_PREFIX + 'block/:hash', routes.getBlock); if (config.MEMPOOL.BACKEND !== 'esplora') { diff --git a/backend/src/mempool.interfaces.ts b/backend/src/mempool.interfaces.ts index 0081bd34f..60b07da1b 100644 --- a/backend/src/mempool.interfaces.ts +++ b/backend/src/mempool.interfaces.ts @@ -81,7 +81,7 @@ export interface TransactionStripped { export interface BlockExtension { totalFees?: number; - medianFee?: number; // Actually the median fee rate that we compute ourself + medianFee?: number; feeRange?: number[]; reward?: number; coinbaseTx?: TransactionMinerInfo; diff --git a/backend/src/routes.ts b/backend/src/routes.ts index 5b762ff27..fa298cc22 100644 --- a/backend/src/routes.ts +++ b/backend/src/routes.ts @@ -722,14 +722,52 @@ class Routes { public async getBlocks(req: Request, res: Response) { try { - const height = req.params.height === undefined ? undefined : parseInt(req.params.height, 10); - res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString()); - res.json(await blocks.$getBlocks(height, 15)); + if (['mainnet', 'testnet', 'signet', 'regtest'].includes(config.MEMPOOL.NETWORK)) { // Bitcoin + const height = req.params.height === undefined ? undefined : parseInt(req.params.height, 10); + res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString()); + res.json(await blocks.$getBlocks(height, 15)); + } else { // Liquid, Bisq + return await this.getLegacyBlocks(req, res); + } } catch (e) { res.status(500).send(e instanceof Error ? e.message : e); } } + public async getLegacyBlocks(req: Request, res: Response) { + try { + const returnBlocks: IEsploraApi.Block[] = []; + const fromHeight = parseInt(req.params.height, 10) || blocks.getCurrentBlockHeight(); + + // Check if block height exist in local cache to skip the hash lookup + const blockByHeight = blocks.getBlocks().find((b) => b.height === fromHeight); + let startFromHash: string | null = null; + if (blockByHeight) { + startFromHash = blockByHeight.id; + } else { + startFromHash = await bitcoinApi.$getBlockHash(fromHeight); + } + + let nextHash = startFromHash; + for (let i = 0; i < 10 && nextHash; i++) { + const localBlock = blocks.getBlocks().find((b) => b.id === nextHash); + if (localBlock) { + returnBlocks.push(localBlock); + nextHash = localBlock.previousblockhash; + } else { + const block = await bitcoinApi.$getBlock(nextHash); + returnBlocks.push(block); + nextHash = block.previousblockhash; + } + } + + res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString()); + res.json(returnBlocks); + } catch (e) { + res.status(500).send(e instanceof Error ? e.message : e); + } + } + public async getBlockTransactions(req: Request, res: Response) { try { loadingIndicators.setProgress('blocktxs-' + req.params.hash, 0); diff --git a/backend/src/utils/blocks-utils.ts b/backend/src/utils/blocks-utils.ts index 8760a08c0..937a37448 100644 --- a/backend/src/utils/blocks-utils.ts +++ b/backend/src/utils/blocks-utils.ts @@ -15,7 +15,7 @@ export function prepareBlock(block: any): BlockExtended { weight: block.weight, previousblockhash: block.previousblockhash, extras: { - coinbaseRaw: block.coinbase_raw ?? block.extras.coinbaseRaw, + coinbaseRaw: block.coinbase_raw ?? block.extras?.coinbaseRaw, medianFee: block.medianFee ?? block.median_fee ?? block.extras?.medianFee, feeRange: block.feeRange ?? block.fee_span, reward: block.reward ?? block?.extras?.reward, diff --git a/frontend/src/app/components/blocks-list/blocks-list.component.ts b/frontend/src/app/components/blocks-list/blocks-list.component.ts index f1f81d321..7cca2af62 100644 --- a/frontend/src/app/components/blocks-list/blocks-list.component.ts +++ b/frontend/src/app/components/blocks-list/blocks-list.component.ts @@ -87,9 +87,11 @@ export class BlocksList implements OnInit { return blocks[0]; } this.blocksCount = Math.max(this.blocksCount, blocks[1][0].height) + 1; - // @ts-ignore: Need to add an extra field for the template - blocks[1][0].extras.pool.logo = `./resources/mining-pools/` + - blocks[1][0].extras.pool.name.toLowerCase().replace(' ', '').replace('.', '') + '.svg'; + if (this.stateService.env.MINING_DASHBOARD) { + // @ts-ignore: Need to add an extra field for the template + blocks[1][0].extras.pool.logo = `./resources/mining-pools/` + + blocks[1][0].extras.pool.name.toLowerCase().replace(' ', '').replace('.', '') + '.svg'; + } acc.unshift(blocks[1][0]); acc = acc.slice(0, this.widget ? 6 : 15); return acc; From f8d6dd7c7b3ad1b34e392baba037903ba5cba717 Mon Sep 17 00:00:00 2001 From: hunicus <93150691+hunicus@users.noreply.github.com> Date: Mon, 23 May 2022 08:34:04 -0400 Subject: [PATCH 14/15] Add schildbach bitcoin wallet to about page --- frontend/src/app/components/about/about.component.html | 4 ++++ frontend/src/resources/profile/schildbach.svg | 1 + 2 files changed, 5 insertions(+) create mode 100644 frontend/src/resources/profile/schildbach.svg diff --git a/frontend/src/app/components/about/about.component.html b/frontend/src/app/components/about/about.component.html index 27892626c..47489457c 100644 --- a/frontend/src/app/components/about/about.component.html +++ b/frontend/src/app/components/about/about.component.html @@ -250,6 +250,10 @@ Marina + + + Schildbach + diff --git a/frontend/src/resources/profile/schildbach.svg b/frontend/src/resources/profile/schildbach.svg new file mode 100644 index 000000000..976deec88 --- /dev/null +++ b/frontend/src/resources/profile/schildbach.svg @@ -0,0 +1 @@ + \ No newline at end of file From 584fb47de7c51030e4f9dff856ed5e0feafbade0 Mon Sep 17 00:00:00 2001 From: nymkappa Date: Tue, 24 May 2022 11:19:09 +0200 Subject: [PATCH 15/15] Fix widget size mining dashboard --- .../src/app/components/blocks-list/blocks-list.component.html | 2 +- .../difficulty-adjustments-table.component.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/components/blocks-list/blocks-list.component.html b/frontend/src/app/components/blocks-list/blocks-list.component.html index 1b10fe1d2..0e0f5d1e1 100644 --- a/frontend/src/app/components/blocks-list/blocks-list.component.html +++ b/frontend/src/app/components/blocks-list/blocks-list.component.html @@ -1,6 +1,6 @@ -
+

Blocks

diff --git a/frontend/src/app/components/difficulty-adjustments-table/difficulty-adjustments-table.component.html b/frontend/src/app/components/difficulty-adjustments-table/difficulty-adjustments-table.component.html index 6e80d828a..5f11c2608 100644 --- a/frontend/src/app/components/difficulty-adjustments-table/difficulty-adjustments-table.component.html +++ b/frontend/src/app/components/difficulty-adjustments-table/difficulty-adjustments-table.component.html @@ -1,4 +1,4 @@ -
+
HeightPoolHeightPool Timestamp MinedRewardReward FeesTXsTransactionsTXsTransactions Size
+ + + {{ block.tx_count | number }} From 3723380a369675205955da0828c945318ac29072 Mon Sep 17 00:00:00 2001 From: nymkappa Date: Mon, 23 May 2022 09:44:09 +0200 Subject: [PATCH 10/15] Hide graph selection button on non bitcoin networks --- frontend/src/app/components/graphs/graphs.component.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/components/graphs/graphs.component.html b/frontend/src/app/components/graphs/graphs.component.html index 6c59645ea..b02adae5e 100644 --- a/frontend/src/app/components/graphs/graphs.component.html +++ b/frontend/src/app/components/graphs/graphs.component.html @@ -1,7 +1,7 @@ -
- {{ block.height - }} + {{ block.height }}
@@ -61,29 +60,29 @@
+ + + + + + + +