From 56bf267664f9fe1d7073dcb810c4cabe408a6c73 Mon Sep 17 00:00:00 2001 From: nymkappa Date: Wed, 16 Mar 2022 17:00:06 +0100 Subject: [PATCH 1/5] Updated blocks list in /pool/{id} page --- backend/src/api/blocks.ts | 38 ++------ backend/src/repositories/BlocksRepository.ts | 23 +++-- backend/src/utils/blocks-utils.ts | 29 ++++++ .../app/components/pool/pool.component.html | 90 ++++++++++++++---- .../app/components/pool/pool.component.scss | 95 ++++++++++++++++--- .../src/app/components/pool/pool.component.ts | 6 +- 6 files changed, 202 insertions(+), 79 deletions(-) create mode 100644 backend/src/utils/blocks-utils.ts diff --git a/backend/src/api/blocks.ts b/backend/src/api/blocks.ts index 922c0e639..b32a1689e 100644 --- a/backend/src/api/blocks.ts +++ b/backend/src/api/blocks.ts @@ -12,6 +12,7 @@ import poolsRepository from '../repositories/PoolsRepository'; import blocksRepository from '../repositories/BlocksRepository'; import loadingIndicators from './loading-indicators'; import BitcoinApi from './bitcoin/bitcoin-api'; +import { prepareBlock } from '../utils/blocks-utils'; class Blocks { private blocks: BlockExtended[] = []; @@ -336,7 +337,7 @@ class Blocks { public async $indexBlock(height: number): Promise { const dbBlock = await blocksRepository.$getBlockByHeight(height); if (dbBlock != null) { - return this.prepareBlock(dbBlock); + return prepareBlock(dbBlock); } const blockHash = await bitcoinApi.$getBlockHash(height); @@ -346,10 +347,11 @@ class Blocks { await blocksRepository.$saveBlockInDatabase(blockExtended); - return this.prepareBlock(blockExtended); + return prepareBlock(blockExtended); } - public async $getBlocksExtras(fromHeight: number, limit: number = 15): Promise { + public async $getBlocksExtras(fromHeight: number, limit: number = 15, + poolId: number | undefined= undefined): 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 @@ -378,7 +380,7 @@ class Blocks { if (!block && Common.indexingEnabled()) { block = await this.$indexBlock(currentHeight); } else if (!block) { - block = this.prepareBlock(await bitcoinApi.$getBlock(nextHash)); + block = prepareBlock(await bitcoinApi.$getBlock(nextHash)); } returnBlocks.push(block); nextHash = block.previousblockhash; @@ -393,34 +395,6 @@ class Blocks { } } - private prepareBlock(block: any): BlockExtended { - return { - id: block.id ?? block.hash, // hash for indexed block - timestamp: block.timestamp ?? block.blockTimestamp, // blockTimestamp for indexed block - height: block.height, - version: block.version, - bits: block.bits, - nonce: block.nonce, - difficulty: block.difficulty, - merkle_root: block.merkle_root, - tx_count: block.tx_count, - size: block.size, - weight: block.weight, - previousblockhash: block.previousblockhash, - extras: { - coinbaseRaw: block.coinbase_raw ?? block.extras.coinbaseRaw, - medianFee: block.medianFee ?? block.median_fee ?? block.extras?.medianFee, - feeRange: block.feeRange ?? block.fee_range ?? block?.extras?.feeSpan, - reward: block.reward ?? block?.extras?.reward, - totalFees: block.totalFees ?? block?.fees ?? block?.extras.totalFees, - pool: block?.extras?.pool ?? (block?.pool_id ? { - id: block.pool_id, - name: block.pool_name, - } : undefined), - } - }; - } - public getLastDifficultyAdjustmentTime(): number { return this.lastDifficultyAdjustmentTime; } diff --git a/backend/src/repositories/BlocksRepository.ts b/backend/src/repositories/BlocksRepository.ts index 4ac0a9d9d..fb9b48eba 100644 --- a/backend/src/repositories/BlocksRepository.ts +++ b/backend/src/repositories/BlocksRepository.ts @@ -2,6 +2,7 @@ import { BlockExtended, PoolTag } from '../mempool.interfaces'; import { DB } from '../database'; import logger from '../logger'; import { Common } from '../api/common'; +import { prepareBlock } from '../utils/blocks-utils'; class BlocksRepository { /** @@ -153,7 +154,6 @@ class BlocksRepository { query += ` blockTimestamp BETWEEN DATE_SUB(NOW(), INTERVAL ${interval}) AND NOW()`; } - // logger.debug(query); const connection = await DB.getConnection(); try { const [rows] = await connection.query(query, params); @@ -169,10 +169,10 @@ class BlocksRepository { /** * Get blocks count between two dates - * @param poolId + * @param poolId * @param from - The oldest timestamp * @param to - The newest timestamp - * @returns + * @returns */ public async $blockCountBetweenTimestamp(poolId: number | null, from: number, to: number): Promise { const params: any[] = []; @@ -193,7 +193,6 @@ class BlocksRepository { } query += ` blockTimestamp BETWEEN FROM_UNIXTIME('${from}') AND FROM_UNIXTIME('${to}')`; - // logger.debug(query); const connection = await DB.getConnection(); try { const [rows] = await connection.query(query, params); @@ -216,7 +215,6 @@ class BlocksRepository { ORDER BY height LIMIT 1;`; - // logger.debug(query); const connection = await DB.getConnection(); try { const [rows]: any[] = await connection.query(query); @@ -239,7 +237,8 @@ class BlocksRepository { */ public async $getBlocksByPool(poolId: number, startHeight: number | null = null): Promise { const params: any[] = []; - let query = `SELECT height, hash as id, tx_count, size, weight, pool_id, UNIX_TIMESTAMP(blockTimestamp) as timestamp, reward + let query = ` SELECT *, UNIX_TIMESTAMP(blocks.blockTimestamp) as blockTimestamp, + previous_block_hash as previousblockhash FROM blocks WHERE pool_id = ?`; params.push(poolId); @@ -252,17 +251,17 @@ class BlocksRepository { query += ` ORDER BY height DESC LIMIT 10`; - // logger.debug(query); const connection = await DB.getConnection(); try { const [rows] = await connection.query(query, params); connection.release(); - for (const block of rows) { - delete block['blockTimestamp']; + const blocks: BlockExtended[] = []; + for (let block of rows) { + blocks.push(prepareBlock(block)); } - return rows; + return blocks; } catch (e) { connection.release(); logger.err('$getBlocksByPool() error' + (e instanceof Error ? e.message : e)); @@ -314,7 +313,7 @@ class BlocksRepository { let query = ` SELECT * - FROM + FROM ( SELECT UNIX_TIMESTAMP(blockTimestamp) as timestamp, difficulty, height, @@ -322,7 +321,7 @@ class BlocksRepository { IF(@prevStatus := YT.difficulty, @rn := 1, @rn := 1) ) AS rn FROM blocks YT - CROSS JOIN + CROSS JOIN ( SELECT @prevStatus := -1, @rn := 1 ) AS var diff --git a/backend/src/utils/blocks-utils.ts b/backend/src/utils/blocks-utils.ts new file mode 100644 index 000000000..107099ba3 --- /dev/null +++ b/backend/src/utils/blocks-utils.ts @@ -0,0 +1,29 @@ +import { BlockExtended } from "../mempool.interfaces"; + +export function prepareBlock(block: any): BlockExtended { + return { + id: block.id ?? block.hash, // hash for indexed block + timestamp: block.timestamp ?? block.blockTimestamp, // blockTimestamp for indexed block + height: block.height, + version: block.version, + bits: block.bits, + nonce: block.nonce, + difficulty: block.difficulty, + merkle_root: block.merkle_root, + tx_count: block.tx_count, + size: block.size, + weight: block.weight, + previousblockhash: block.previousblockhash, + extras: { + coinbaseRaw: block.coinbase_raw ?? block.extras.coinbaseRaw, + medianFee: block.medianFee ?? block.median_fee ?? block.extras?.medianFee, + feeRange: block.feeRange ?? block.fee_range ?? block?.extras?.feeSpan, + reward: block.reward ?? block?.extras?.reward, + totalFees: block.totalFees ?? block?.fees ?? block?.extras.totalFees, + pool: block?.extras?.pool ?? (block?.pool_id ? { + id: block.pool_id, + name: block.pool_name, + } : undefined), + } + }; +} diff --git a/frontend/src/app/components/pool/pool.component.html b/frontend/src/app/components/pool/pool.component.html index af71351ad..ca350437c 100644 --- a/frontend/src/app/components/pool/pool.component.html +++ b/frontend/src/app/components/pool/pool.component.html @@ -1,11 +1,11 @@ -
+
-

+
- {{ poolStats.pool.name }} -

+

{{ poolStats.pool.name }}

+
@@ -63,27 +63,44 @@ [infiniteScrollDistance]="1.5" [infiniteScrollUpDistance]="1.5" [infiniteScrollThrottle]="50" (scrolled)="loadMore()"> - Height - Timestamp - Mined - Reward - - Transactions - Size + Height + Timestamp + Mined + + Coinbase + + Reward + Fees + Txs + Size - - - {{ block.height }} - ‎{{ block.timestamp * 1000 | date:'yyyy-MM-dd HH:mm' }} - + + + + {{ block.height + }} + + + ‎{{ block.timestamp * 1000 | date:'yyyy-MM-dd HH:mm' }} + + - - + + + {{ block.extras.coinbaseRaw | hex2ascii }} + - {{ block.tx_count | number }} - + + + + + + + + {{ block.tx_count | number }} + +
@@ -92,6 +109,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/frontend/src/app/components/pool/pool.component.scss b/frontend/src/app/components/pool/pool.component.scss index 291177f73..03a34acb1 100644 --- a/frontend/src/app/components/pool/pool.component.scss +++ b/frontend/src/app/components/pool/pool.component.scss @@ -1,5 +1,6 @@ -.progress { - background-color: #2d3348; +.container-xl { + max-width: 1400px; + padding-bottom: 100px; } @media (min-width: 768px) { @@ -44,13 +45,8 @@ div.scrollable { max-width: 90px; } -.table { - margin: 0px auto; - max-width: 900px; -} - .box { - padding-bottom: 0px; + padding-bottom: 5px; } .label { @@ -59,8 +55,85 @@ div.scrollable { } .data { - text-align: center; - @media (max-width: 767.98px) { + text-align: left; + padding-left: 25%; + @media (max-width: 991px) { + padding-left: 12px; + } + @media (max-width: 450px) { text-align: right; } -} \ No newline at end of file +} + +.progress { + background-color: #2d3348; +} + +.coinbase { + width: 20%; + @media (max-width: 875px) { + display: none; + } +} + +.height { + width: 10%; +} + +.timestamp { + @media (max-width: 875px) { + padding-left: 50px; + } + @media (max-width: 650px) { + display: none; + } +} + +.mined { + width: 13%; + @media (max-width: 1100px) { + display: none; + } +} + +.txs { + padding-right: 40px; + @media (max-width: 1100px) { + padding-right: 10px; + } + @media (max-width: 875px) { + padding-right: 50px; + } + @media (max-width: 567px) { + padding-right: 10px; + } +} + +.fees { + width: 0%; +} + +.size { + width: 12%; + @media (max-width: 1000px) { + width: 15%; + } + @media (max-width: 875px) { + width: 20%; + } + @media (max-width: 650px) { + width: 20%; + } + @media (max-width: 450px) { + display: none; + } +} + +.scriptmessage { + overflow: hidden; + display: inline-block; + text-overflow: ellipsis; + vertical-align: middle; + width: auto; + text-align: left; +} diff --git a/frontend/src/app/components/pool/pool.component.ts b/frontend/src/app/components/pool/pool.component.ts index da05f0403..95df5709b 100644 --- a/frontend/src/app/components/pool/pool.component.ts +++ b/frontend/src/app/components/pool/pool.component.ts @@ -31,6 +31,7 @@ export class PoolComponent implements OnInit { poolStats$: Observable; blocks$: Observable; isLoading = true; + skeletonLines: number[] = [...Array(5).keys()]; chartOptions: EChartsOption = {}; chartInitOptions = { @@ -133,7 +134,7 @@ export class PoolComponent implements OnInit { align: 'left', }, borderColor: '#000', - formatter: function (data) { + formatter: function(data) { let hashratePowerOfTen: any = selectPowerOfTen(1); let hashrate = data[0].data[1]; @@ -154,11 +155,10 @@ export class PoolComponent implements OnInit { }, yAxis: [ { - min: function (value) { + min: (value) => { return value.min * 0.9; }, type: 'value', - name: 'Hashrate', axisLabel: { color: 'rgb(110, 112, 121)', formatter: (val) => { From 63a47c14d9cd8719bafe2849b7f726b579b9c23c Mon Sep 17 00:00:00 2001 From: nymkappa Date: Wed, 16 Mar 2022 19:17:33 +0100 Subject: [PATCH 2/5] Fix duplicated blocks in pool details page --- .../app/components/pool/pool.component.html | 10 +++--- .../src/app/components/pool/pool.component.ts | 33 ++++++++----------- 2 files changed, 19 insertions(+), 24 deletions(-) diff --git a/frontend/src/app/components/pool/pool.component.html b/frontend/src/app/components/pool/pool.component.html index ca350437c..d25e7d810 100644 --- a/frontend/src/app/components/pool/pool.component.html +++ b/frontend/src/app/components/pool/pool.component.html @@ -3,8 +3,8 @@
-

{{ poolStats.pool.name }}

+ onError="this.src = './resources/mining-pools/default.svg'" class="mr-3 mb-3"> +

{{ poolStats.pool.name }}

@@ -59,15 +59,15 @@
- - + diff --git a/frontend/src/app/components/pool/pool.component.ts b/frontend/src/app/components/pool/pool.component.ts index 95df5709b..529298f65 100644 --- a/frontend/src/app/components/pool/pool.component.ts +++ b/frontend/src/app/components/pool/pool.component.ts @@ -2,7 +2,7 @@ import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, OnInit } import { ActivatedRoute } from '@angular/router'; import { EChartsOption, graphic } from 'echarts'; import { BehaviorSubject, Observable } from 'rxjs'; -import { distinctUntilChanged, map, switchMap, tap, toArray } from 'rxjs/operators'; +import { map, switchMap, tap } from 'rxjs/operators'; import { BlockExtended, PoolStat } from 'src/app/interfaces/node-api.interface'; import { ApiService } from 'src/app/services/api.service'; import { StateService } from 'src/app/services/state.service'; @@ -40,8 +40,7 @@ export class PoolComponent implements OnInit { height: 'auto', }; - fromHeight: number = -1; - fromHeightSubject: BehaviorSubject = new BehaviorSubject(this.fromHeight); + loadMoreSubject: BehaviorSubject = new BehaviorSubject(undefined); blocks: BlockExtended[] = []; poolId: number = undefined; @@ -67,12 +66,9 @@ export class PoolComponent implements OnInit { this.prepareChartOptions(data.hashrates.map(val => [val.timestamp * 1000, val.avgHashrate])); return poolId; }), - ) + ); }), switchMap(() => { - if (this.blocks.length === 0) { - this.fromHeightSubject.next(undefined); - } return this.apiService.getPoolStats$(this.poolId); }), map((poolStats) => { @@ -89,17 +85,16 @@ export class PoolComponent implements OnInit { }) ); - this.blocks$ = this.fromHeightSubject + this.blocks$ = this.loadMoreSubject .pipe( - distinctUntilChanged(), - switchMap((fromHeight) => { - return this.apiService.getPoolBlocks$(this.poolId, fromHeight); + switchMap((flag) => { + return this.apiService.getPoolBlocks$(this.poolId, this.blocks[this.blocks.length - 1]?.height); }), tap((newBlocks) => { this.blocks = this.blocks.concat(newBlocks); }), map(() => this.blocks) - ) + ); } prepareChartOptions(data) { @@ -134,18 +129,18 @@ export class PoolComponent implements OnInit { align: 'left', }, borderColor: '#000', - formatter: function(data) { + formatter: function(ticks: any[]) { let hashratePowerOfTen: any = selectPowerOfTen(1); - let hashrate = data[0].data[1]; + let hashrate = ticks[0].data[1]; if (this.isMobile()) { - hashratePowerOfTen = selectPowerOfTen(data[0].data[1]); - hashrate = Math.round(data[0].data[1] / hashratePowerOfTen.divider); + hashratePowerOfTen = selectPowerOfTen(ticks[0].data[1]); + hashrate = Math.round(ticks[0].data[1] / hashratePowerOfTen.divider); } return ` - ${data[0].axisValueLabel}
- ${data[0].marker} ${data[0].seriesName}: ${formatNumber(hashrate, this.locale, '1.0-0')} ${hashratePowerOfTen.unit}H/s
+ ${ticks[0].axisValueLabel}
+ ${ticks[0].marker} ${ticks[0].seriesName}: ${formatNumber(hashrate, this.locale, '1.0-0')} ${hashratePowerOfTen.unit}H/s
`; }.bind(this) }, @@ -192,7 +187,7 @@ export class PoolComponent implements OnInit { } loadMore() { - this.fromHeightSubject.next(this.blocks[this.blocks.length - 1]?.height); + this.loadMoreSubject.next(undefined); } trackByBlock(index: number, block: BlockExtended) { From 96dc3fb24a6f5121df48cc7ddf75b6a11f24ff60 Mon Sep 17 00:00:00 2001 From: nymkappa Date: Wed, 16 Mar 2022 19:26:56 +0100 Subject: [PATCH 3/5] Rename `ScriptSig` to `Coinbase Tag` --- frontend/src/app/components/pool/pool.component.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/components/pool/pool.component.html b/frontend/src/app/components/pool/pool.component.html index d25e7d810..afcda05e7 100644 --- a/frontend/src/app/components/pool/pool.component.html +++ b/frontend/src/app/components/pool/pool.component.html @@ -66,8 +66,8 @@
- + From dfff57d204094b9b624ab8a20d21b240a734633f Mon Sep 17 00:00:00 2001 From: nymkappa Date: Wed, 16 Mar 2022 20:10:01 +0100 Subject: [PATCH 4/5] Fix pool page skeleton --- .../app/components/pool/pool.component.html | 43 +++++++++---------- .../app/components/pool/pool.component.scss | 6 --- 2 files changed, 21 insertions(+), 28 deletions(-) diff --git a/frontend/src/app/components/pool/pool.component.html b/frontend/src/app/components/pool/pool.component.html index afcda05e7..f22fb0bcb 100644 --- a/frontend/src/app/components/pool/pool.component.html +++ b/frontend/src/app/components/pool/pool.component.html @@ -9,7 +9,7 @@
-
+
Height Timestamp Mined - Coinbase + ScriptSig Reward FeesHeight Timestamp Mined - ScriptSig + Coinbase Tag Reward Fees
@@ -36,7 +36,7 @@
-
+
@@ -115,16 +115,15 @@ - + @@ -134,7 +133,7 @@ - @@ -146,10 +145,10 @@
-

- -
-

+
+ +

+
@@ -157,7 +156,13 @@
- - - + + +
- + + + + + - - - -
AddressesTags +
+
Addresses
@@ -167,12 +172,6 @@
~
Coinbase Tags -
-
@@ -180,14 +179,14 @@ - - + - - + diff --git a/frontend/src/app/components/pool/pool.component.scss b/frontend/src/app/components/pool/pool.component.scss index 03a34acb1..3ecc2f227 100644 --- a/frontend/src/app/components/pool/pool.component.scss +++ b/frontend/src/app/components/pool/pool.component.scss @@ -40,17 +40,11 @@ div.scrollable { max-height: 75px; } -.skeleton-loader { - width: 100%; - max-width: 90px; -} - .box { padding-bottom: 5px; } .label { - max-width: 50px; width: 30%; } From 41fed984cbac076a0d0569825f6808ccfed7560c Mon Sep 17 00:00:00 2001 From: nymkappa Date: Wed, 16 Mar 2022 20:19:39 +0100 Subject: [PATCH 5/5] Fix pool get blocks API undefined start block behavior --- backend/src/repositories/BlocksRepository.ts | 4 ++-- backend/src/routes.ts | 2 +- frontend/src/app/components/pool/pool.component.html | 10 +++++----- frontend/src/app/components/pool/pool.component.scss | 6 ++++++ 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/backend/src/repositories/BlocksRepository.ts b/backend/src/repositories/BlocksRepository.ts index fb9b48eba..40c705bdb 100644 --- a/backend/src/repositories/BlocksRepository.ts +++ b/backend/src/repositories/BlocksRepository.ts @@ -235,7 +235,7 @@ class BlocksRepository { /** * Get blocks mined by a specific mining pool */ - public async $getBlocksByPool(poolId: number, startHeight: number | null = null): Promise { + public async $getBlocksByPool(poolId: number, startHeight: number | undefined = undefined): Promise { const params: any[] = []; let query = ` SELECT *, UNIX_TIMESTAMP(blocks.blockTimestamp) as blockTimestamp, previous_block_hash as previousblockhash @@ -243,7 +243,7 @@ class BlocksRepository { WHERE pool_id = ?`; params.push(poolId); - if (startHeight) { + if (startHeight !== undefined) { query += ` AND height < ?`; params.push(startHeight); } diff --git a/backend/src/routes.ts b/backend/src/routes.ts index 7f3a98296..870c62ab8 100644 --- a/backend/src/routes.ts +++ b/backend/src/routes.ts @@ -553,7 +553,7 @@ class Routes { try { const poolBlocks = await BlocksRepository.$getBlocksByPool( parseInt(req.params.poolId, 10), - parseInt(req.params.height, 10) ?? null, + req.params.height === undefined ? undefined : parseInt(req.params.height, 10), ); res.header('Pragma', 'public'); res.header('Cache-control', 'public'); diff --git a/frontend/src/app/components/pool/pool.component.html b/frontend/src/app/components/pool/pool.component.html index f22fb0bcb..bef441a85 100644 --- a/frontend/src/app/components/pool/pool.component.html +++ b/frontend/src/app/components/pool/pool.component.html @@ -14,7 +14,7 @@ - - - - + + @@ -168,7 +168,7 @@
- + diff --git a/frontend/src/app/components/pool/pool.component.scss b/frontend/src/app/components/pool/pool.component.scss index 3ecc2f227..2e8a4f154 100644 --- a/frontend/src/app/components/pool/pool.component.scss +++ b/frontend/src/app/components/pool/pool.component.scss @@ -131,3 +131,9 @@ div.scrollable { width: auto; text-align: left; } + +.right-mobile { + @media (max-width: 450px) { + text-align: right; + } +} \ No newline at end of file
Mined Blocks + Mined Blocks
Empty Blocks + Empty Blocks
Tags +
{{ poolStats.pool.regexes }}
@@ -22,15 +22,15 @@
Addresses + ~~
~