diff --git a/backend/src/api/blocks.ts b/backend/src/api/blocks.ts index b9b7b3aff..526d4889e 100644 --- a/backend/src/api/blocks.ts +++ b/backend/src/api/blocks.ts @@ -426,25 +426,32 @@ class Blocks { return returnBlocks; } + if (currentHeight === 0 && Common.indexingEnabled()) { + currentHeight = await blocksRepository.$mostRecentBlockHeight(); + } + // Check if block height exist in local cache to skip the hash lookup const blockByHeight = this.getBlocks().find((b) => b.height === currentHeight); let startFromHash: string | null = null; if (blockByHeight) { startFromHash = blockByHeight.id; - } else { + } else if (!Common.indexingEnabled()) { startFromHash = await bitcoinApi.$getBlockHash(currentHeight); } let nextHash = startFromHash; for (let i = 0; i < limit && currentHeight >= 0; i++) { let block = this.getBlocks().find((b) => b.height === currentHeight); - if (!block && Common.indexingEnabled()) { + if (block) { + returnBlocks.push(block); + } else if (Common.indexingEnabled()) { block = await this.$indexBlock(currentHeight); - } else if (!block) { + returnBlocks.push(block); + } else if (nextHash != null) { block = prepareBlock(await bitcoinApi.$getBlock(nextHash)); + nextHash = block.previousblockhash; + returnBlocks.push(block); } - returnBlocks.push(block); - nextHash = block.previousblockhash; currentHeight--; } diff --git a/backend/src/api/fee-api.ts b/backend/src/api/fee-api.ts index 7bc7d06f3..82778825e 100644 --- a/backend/src/api/fee-api.ts +++ b/backend/src/api/fee-api.ts @@ -1,4 +1,3 @@ -import config from '../config'; import { MempoolBlock } from '../mempool.interfaces'; import { Common } from './common'; import mempool from './mempool'; @@ -19,6 +18,7 @@ class FeeApi { 'fastestFee': this.defaultFee, 'halfHourFee': this.defaultFee, 'hourFee': this.defaultFee, + 'economyFee': minimumFee, 'minimumFee': minimumFee, }; } @@ -31,6 +31,7 @@ class FeeApi { 'fastestFee': firstMedianFee, 'halfHourFee': secondMedianFee, 'hourFee': thirdMedianFee, + 'economyFee': Math.min(2 * minimumFee, thirdMedianFee), 'minimumFee': minimumFee, }; } diff --git a/backend/src/api/mining.ts b/backend/src/api/mining.ts index 7e7008351..be786db42 100644 --- a/backend/src/api/mining.ts +++ b/backend/src/api/mining.ts @@ -94,8 +94,13 @@ class Mining { poolsStatistics['blockCount'] = blockCount; const totalBlock24h: number = await BlocksRepository.$blockCount(null, '24h'); - const lastBlockHashrate = await bitcoinClient.getNetworkHashPs(totalBlock24h); - poolsStatistics['lastEstimatedHashrate'] = lastBlockHashrate; + + try { + poolsStatistics['lastEstimatedHashrate'] = await bitcoinClient.getNetworkHashPs(totalBlock24h); + } catch (e) { + poolsStatistics['lastEstimatedHashrate'] = 0; + logger.debug('Bitcoin Core is not available, using zeroed value for current hashrate'); + } return poolsStatistics; } @@ -118,7 +123,12 @@ class Mining { const blockCount1w: number = await BlocksRepository.$blockCount(pool.id, '1w'); const totalBlock1w: number = await BlocksRepository.$blockCount(null, '1w'); - const currentEstimatedkHashrate = await bitcoinClient.getNetworkHashPs(totalBlock24h); + let currentEstimatedHashrate = 0; + try { + currentEstimatedHashrate = await bitcoinClient.getNetworkHashPs(totalBlock24h); + } catch (e) { + logger.debug('Bitcoin Core is not available, using zeroed value for current hashrate'); + } return { pool: pool, @@ -132,7 +142,7 @@ class Mining { '24h': blockCount24h / totalBlock24h, '1w': blockCount1w / totalBlock1w, }, - estimatedHashrate: currentEstimatedkHashrate * (blockCount24h / totalBlock24h), + estimatedHashrate: currentEstimatedHashrate * (blockCount24h / totalBlock24h), reportedHashrate: null, }; } diff --git a/backend/src/index.ts b/backend/src/index.ts index 30c5ecf37..9e90edbc4 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -25,8 +25,6 @@ import databaseMigration from './api/database-migration'; import syncAssets from './sync-assets'; import icons from './api/liquid/icons'; import { Common } from './api/common'; -import mining from './api/mining'; -import HashratesRepository from './repositories/HashratesRepository'; import poolsUpdater from './tasks/pools-updater'; import indexer from './indexer'; diff --git a/backend/src/repositories/BlocksRepository.ts b/backend/src/repositories/BlocksRepository.ts index dcd5b48d1..1d5843d6e 100644 --- a/backend/src/repositories/BlocksRepository.ts +++ b/backend/src/repositories/BlocksRepository.ts @@ -121,6 +121,19 @@ class BlocksRepository { } } + /** + * Return most recent block height + */ + public async $mostRecentBlockHeight(): Promise { + try { + const [row] = await DB.query('SELECT MAX(height) as maxHeight from blocks'); + return row[0]['maxHeight']; + } catch (e) { + logger.err(`Cannot count blocks for this pool (using offset). Reason: ` + (e instanceof Error ? e.message : e)); + throw e; + } + } + /** * Get blocks count for a period */ @@ -240,8 +253,26 @@ class BlocksRepository { } const params: any[] = []; - let query = ` SELECT *, UNIX_TIMESTAMP(blocks.blockTimestamp) as blockTimestamp, - previous_block_hash as previousblockhash + let query = ` SELECT + height, + hash as id, + UNIX_TIMESTAMP(blocks.blockTimestamp) as blockTimestamp, + size, + weight, + tx_count, + coinbase_raw, + difficulty, + fees, + fee_span, + median_fee, + reward, + version, + bits, + nonce, + merkle_root, + previous_block_hash as previousblockhash, + avg_fee, + avg_fee_rate FROM blocks WHERE pool_id = ?`; params.push(pool.id); @@ -274,11 +305,32 @@ class BlocksRepository { */ public async $getBlockByHeight(height: number): Promise { try { - const [rows]: any[] = await DB.query(` - SELECT *, UNIX_TIMESTAMP(blocks.blockTimestamp) as blockTimestamp, - pools.id as pool_id, pools.name as pool_name, pools.link as pool_link, pools.slug as pool_slug, - pools.addresses as pool_addresses, pools.regexes as pool_regexes, - previous_block_hash as previousblockhash + const [rows]: any[] = await DB.query(`SELECT + height, + hash as id, + UNIX_TIMESTAMP(blocks.blockTimestamp) as blockTimestamp, + size, + weight, + tx_count, + coinbase_raw, + difficulty, + pools.id as pool_id, + pools.name as pool_name, + pools.link as pool_link, + pools.slug as pool_slug, + pools.addresses as pool_addresses, + pools.regexes as pool_regexes, + fees, + fee_span, + median_fee, + reward, + version, + bits, + nonce, + merkle_root, + previous_block_hash as previousblockhash, + avg_fee, + avg_fee_rate FROM blocks JOIN pools ON blocks.pool_id = pools.id WHERE height = ${height}; diff --git a/backend/src/routes.ts b/backend/src/routes.ts index ef8dd47e5..91c41faa6 100644 --- a/backend/src/routes.ts +++ b/backend/src/routes.ts @@ -619,6 +619,14 @@ class Routes { } public async $getHistoricalHashrate(req: Request, res: Response) { + let currentHashrate = 0, currentDifficulty = 0; + try { + currentHashrate = await bitcoinClient.getNetworkHashPs(); + currentDifficulty = await bitcoinClient.getDifficulty(); + } catch (e) { + logger.debug('Bitcoin Core is not available, using zeroed value for current hashrate and difficulty'); + } + try { const hashrates = await HashratesRepository.$getNetworkDailyHashrate(req.params.interval); const difficulty = await BlocksRepository.$getBlocksDifficulty(req.params.interval); @@ -630,8 +638,8 @@ class Routes { res.json({ hashrates: hashrates, difficulty: difficulty, - currentHashrate: await bitcoinClient.getNetworkHashPs(), - currentDifficulty: await bitcoinClient.getDifficulty(), + currentHashrate: currentHashrate, + currentDifficulty: currentDifficulty, }); } catch (e) { res.status(500).send(e instanceof Error ? e.message : e); diff --git a/backend/src/tasks/pools-updater.ts b/backend/src/tasks/pools-updater.ts index aac301256..05a1da5dc 100644 --- a/backend/src/tasks/pools-updater.ts +++ b/backend/src/tasks/pools-updater.ts @@ -16,7 +16,7 @@ class PoolsUpdater { } public async updatePoolsJson() { - if (['mainnet', 'testnet', 'signet'].includes(config.MEMPOOL.NETWORK) === false) { + if (['mainnet', 'testnet', 'signet'].includes(config.MEMPOOL.NETWORK) === false || config.DATABASE.ENABLED === false) { return; } 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 4a5694153..4a6bcf14a 100644 --- a/frontend/src/app/components/blocks-list/blocks-list.component.html +++ b/frontend/src/app/components/blocks-list/blocks-list.component.html @@ -22,7 +22,7 @@ - {{ block.height }} + {{ block.height }}
diff --git a/frontend/src/app/components/pool/pool.component.html b/frontend/src/app/components/pool/pool.component.html index 849744253..baddba88f 100644 --- a/frontend/src/app/components/pool/pool.component.html +++ b/frontend/src/app/components/pool/pool.component.html @@ -221,8 +221,7 @@ - {{ block.height - }} + {{ block.height }} ‎{{ block.timestamp * 1000 | date:'yyyy-MM-dd HH:mm' }} 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 2e8a61230..c876a67ae 100644 --- a/frontend/src/app/docs/api-docs/api-docs-data.ts +++ b/frontend/src/app/docs/api-docs/api-docs-data.ts @@ -4470,6 +4470,7 @@ export const restApiDocsData = [ fastestFee: 1, halfHourFee: 1, hourFee: 1, + economyFee: 1, minimumFee: 1 }` }, @@ -4481,6 +4482,7 @@ export const restApiDocsData = [ fastestFee: 1, halfHourFee: 1, hourFee: 1, + economyFee: 1, minimumFee: 1 }` }, @@ -4492,6 +4494,7 @@ export const restApiDocsData = [ fastestFee: 1, halfHourFee: 1, hourFee: 1, + economyFee: 1, minimumFee: 1 }` }, @@ -4503,7 +4506,8 @@ export const restApiDocsData = [ fastestFee: 0.1, halfHourFee: 0.1, hourFee: 0.1, - minimumFee: 1 + economyFee: 0.1, + minimumFee: 0.1 }` }, codeSampleLiquidTestnet: { @@ -4514,7 +4518,8 @@ export const restApiDocsData = [ fastestFee: 0.1, halfHourFee: 0.1, hourFee: 0.1, - minimumFee: 1 + economyFee: 0.1, + minimumFee: 0.1 }` }, codeSampleBisq: emptyCodeSample, diff --git a/frontend/src/app/docs/api-docs/api-docs.component.ts b/frontend/src/app/docs/api-docs/api-docs.component.ts index 9ede3c09f..0041fa8cd 100644 --- a/frontend/src/app/docs/api-docs/api-docs.component.ts +++ b/frontend/src/app/docs/api-docs/api-docs.component.ts @@ -1,7 +1,6 @@ import { Component, OnInit, Input, ViewChild, ElementRef } from '@angular/core'; import { Env, StateService } from '../../services/state.service'; import { Observable, merge, of } from 'rxjs'; -import { SeoService } from '../../services/seo.service'; import { tap } from 'rxjs/operators'; import { ActivatedRoute } from "@angular/router"; import { faqData, restApiDocsData, wsApiDocsData } from './api-docs-data'; @@ -27,7 +26,6 @@ export class ApiDocsComponent implements OnInit { constructor( private stateService: StateService, - private seoService: SeoService, private route: ActivatedRoute, ) { } @@ -45,7 +43,6 @@ export class ApiDocsComponent implements OnInit { ngOnInit(): void { this.env = this.stateService.env; - this.seoService.setTitle($localize`:@@e351b40b3869a5c7d19c3d4918cb1ac7aaab95c4:API`); this.network$ = merge(of(''), this.stateService.networkChanged$).pipe( tap((network: string) => { if (this.env.BASE_MODULE === 'mempool' && network !== '') { diff --git a/frontend/src/app/docs/docs/docs.component.ts b/frontend/src/app/docs/docs/docs.component.ts index 9a06f1127..74cebc88f 100644 --- a/frontend/src/app/docs/docs/docs.component.ts +++ b/frontend/src/app/docs/docs/docs.component.ts @@ -2,6 +2,7 @@ import { Component, OnInit, HostBinding } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Env, StateService } from '../../services/state.service'; import { WebsocketService } from '../../services/websocket.service'; +import { SeoService } from '../../services/seo.service'; @Component({ selector: 'app-docs', @@ -21,6 +22,7 @@ export class DocsComponent implements OnInit { private route: ActivatedRoute, private stateService: StateService, private websocket: WebsocketService, + private seoService: SeoService, ) { } ngOnInit(): void { @@ -28,10 +30,13 @@ export class DocsComponent implements OnInit { const url = this.route.snapshot.url; if (url[0].path === "faq" ) { this.activeTab = 0; + this.seoService.setTitle($localize`:@@docs.faq.button-title:FAQ`); } else if( url[1].path === "rest" ) { this.activeTab = 1; + this.seoService.setTitle($localize`:@@e351b40b3869a5c7d19c3d4918cb1ac7aaab95c4:API`); } else { this.activeTab = 2; + this.seoService.setTitle($localize`:@@e351b40b3869a5c7d19c3d4918cb1ac7aaab95c4:API`); } this.env = this.stateService.env; diff --git a/frontend/src/locale/messages.xlf b/frontend/src/locale/messages.xlf index 578865b5c..8ea9d70cf 100644 --- a/frontend/src/locale/messages.xlf +++ b/frontend/src/locale/messages.xlf @@ -3934,13 +3934,6 @@ api-docs.websocket.websocket - - API - - src/app/docs/api-docs/api-docs.component.ts - 48 - - Code Example @@ -3973,6 +3966,24 @@ API Docs API response + + FAQ + + src/app/docs/docs/docs.component.ts + 33 + + + + API + + src/app/docs/docs/docs.component.ts + 36 + + + src/app/docs/docs/docs.component.ts + 39 + + year