diff --git a/backend/mempool-config.sample.json b/backend/mempool-config.sample.json index 1b55f38f4..8a9295b3a 100644 --- a/backend/mempool-config.sample.json +++ b/backend/mempool-config.sample.json @@ -12,6 +12,7 @@ "BLOCK_WEIGHT_UNITS": 4000000, "INITIAL_BLOCKS_AMOUNT": 8, "MEMPOOL_BLOCKS_AMOUNT": 8, + "INDEXING_BLOCKS_AMOUNT": 1100, "PRICE_FEED_UPDATE_INTERVAL": 3600, "USE_SECOND_NODE_FOR_MINFEE": false, "EXTERNAL_ASSETS": [ diff --git a/backend/src/api/blocks.ts b/backend/src/api/blocks.ts index 5613abfa1..22bea2480 100644 --- a/backend/src/api/blocks.ts +++ b/backend/src/api/blocks.ts @@ -114,7 +114,7 @@ class Blocks { * @returns */ private async $findBlockMiner(txMinerInfo: TransactionMinerInfo | undefined): Promise { - if (txMinerInfo === undefined) { + if (txMinerInfo === undefined || txMinerInfo.vout.length < 1) { return await poolsRepository.$getUnknownPool(); } @@ -147,9 +147,9 @@ class Blocks { */ public async $generateBlockDatabase() { if (['mainnet', 'testnet', 'signet'].includes(config.MEMPOOL.NETWORK) === false || // Bitcoin only - config.MEMPOOL.INDEXING_BLOCKS_AMOUNT <= 0 || // Indexing must be enabled - this.blockIndexingStarted === true || // Indexing must not already be in progress - !memPool.isInSync() // We sync the mempool first + config.MEMPOOL.INDEXING_BLOCKS_AMOUNT === 0 || // Indexing must be enabled + !memPool.isInSync() || // We sync the mempool first + this.blockIndexingStarted === true // Indexing must not already be in progress ) { return; } @@ -163,7 +163,13 @@ class Blocks { try { let currentBlockHeight = blockchainInfo.blocks; - const lastBlockToIndex = Math.max(0, currentBlockHeight - config.MEMPOOL.INDEXING_BLOCKS_AMOUNT + 1); + + let indexingBlockAmount = config.MEMPOOL.INDEXING_BLOCKS_AMOUNT; + if (indexingBlockAmount <= -1) { + indexingBlockAmount = currentBlockHeight + 1; + } + + const lastBlockToIndex = Math.max(0, currentBlockHeight - indexingBlockAmount + 1); logger.info(`Indexing blocks from #${currentBlockHeight} to #${lastBlockToIndex}`); diff --git a/backend/src/api/mempool.ts b/backend/src/api/mempool.ts index 859912f6c..4d6c35860 100644 --- a/backend/src/api/mempool.ts +++ b/backend/src/api/mempool.ts @@ -118,11 +118,11 @@ class Mempool { }); } hasChange = true; - // if (diff > 0) { - // logger.debug('Fetched transaction ' + txCount + ' / ' + diff); - // } else { - // logger.debug('Fetched transaction ' + txCount); - // } + if (diff > 0) { + logger.debug('Fetched transaction ' + txCount + ' / ' + diff); + } else { + logger.debug('Fetched transaction ' + txCount); + } newTransactions.push(transaction); } catch (e) { logger.debug('Error finding transaction in mempool: ' + (e instanceof Error ? e.message : e)); diff --git a/backend/src/api/mining.ts b/backend/src/api/mining.ts index b6d3d8750..c89ea9324 100644 --- a/backend/src/api/mining.ts +++ b/backend/src/api/mining.ts @@ -2,7 +2,6 @@ import { PoolInfo, PoolStats } from '../mempool.interfaces'; import BlocksRepository, { EmptyBlocks } from '../repositories/BlocksRepository'; import PoolsRepository from '../repositories/PoolsRepository'; import bitcoinClient from './bitcoin/bitcoin-client'; -import BitcoinApi from './bitcoin/bitcoin-api'; class Mining { constructor() { @@ -11,14 +10,25 @@ class Mining { /** * Generate high level overview of the pool ranks and general stats */ - public async $getPoolsStats(interval: string = '100 YEAR') : Promise { + public async $getPoolsStats(interval: string | null) : Promise { + let sqlInterval: string | null = null; + switch (interval) { + case '24h': sqlInterval = '1 DAY'; break; + case '3d': sqlInterval = '3 DAY'; break; + case '1w': sqlInterval = '1 WEEK'; break; + case '1m': sqlInterval = '1 MONTH'; break; + case '3m': sqlInterval = '3 MONTH'; break; + case '6m': sqlInterval = '6 MONTH'; break; + case '1y': sqlInterval = '1 YEAR'; break; + case '2y': sqlInterval = '2 YEAR'; break; + case '3y': sqlInterval = '3 YEAR'; break; + default: sqlInterval = null; break; + } + const poolsStatistics = {}; - const blockHeightTip = await bitcoinClient.getBlockCount(); - const lastBlockHashrate = await bitcoinClient.getNetworkHashPs(120, blockHeightTip); - const poolsInfo: PoolInfo[] = await PoolsRepository.$getPoolsInfo(interval); - const blockCount: number = await BlocksRepository.$blockCount(interval); - const emptyBlocks: EmptyBlocks[] = await BlocksRepository.$countEmptyBlocks(interval); + const poolsInfo: PoolInfo[] = await PoolsRepository.$getPoolsInfo(sqlInterval); + const emptyBlocks: EmptyBlocks[] = await BlocksRepository.$countEmptyBlocks(sqlInterval); const poolsStats: PoolStats[] = []; let rank = 1; @@ -40,12 +50,20 @@ class Mining { poolsStats.push(poolStat); }); - poolsStatistics['blockCount'] = blockCount; - poolsStatistics['lastEstimatedHashrate'] = lastBlockHashrate; poolsStatistics['pools'] = poolsStats; + const oldestBlock = new Date(await BlocksRepository.$oldestBlockTimestamp()); + poolsStatistics['oldestIndexedBlockTimestamp'] = oldestBlock.getTime(); + + const blockCount: number = await BlocksRepository.$blockCount(sqlInterval); + poolsStatistics['blockCount'] = blockCount; + + const blockHeightTip = await bitcoinClient.getBlockCount(); + const lastBlockHashrate = await bitcoinClient.getNetworkHashPs(120, blockHeightTip); + poolsStatistics['lastEstimatedHashrate'] = lastBlockHashrate; + return poolsStatistics; } } -export default new Mining(); \ No newline at end of file +export default new Mining(); diff --git a/backend/src/config.ts b/backend/src/config.ts index 7cd789934..085d538c4 100644 --- a/backend/src/config.ts +++ b/backend/src/config.ts @@ -78,7 +78,7 @@ const defaults: IConfig = { 'BLOCK_WEIGHT_UNITS': 4000000, 'INITIAL_BLOCKS_AMOUNT': 8, 'MEMPOOL_BLOCKS_AMOUNT': 8, - 'INDEXING_BLOCKS_AMOUNT': 432, // ~3 days at 10 minutes / block. Set to 0 to disable indexing + 'INDEXING_BLOCKS_AMOUNT': 1100, // 0 = disable indexing, -1 = index all blocks 'PRICE_FEED_UPDATE_INTERVAL': 3600, 'USE_SECOND_NODE_FOR_MINFEE': false, 'EXTERNAL_ASSETS': [ diff --git a/backend/src/index.ts b/backend/src/index.ts index 7b4c8ae08..5b3cdc9c2 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -255,7 +255,7 @@ class Server { .get(config.MEMPOOL.API_URL_PREFIX + 'statistics/1y', routes.$getStatisticsByTime.bind(routes, '1y')) .get(config.MEMPOOL.API_URL_PREFIX + 'statistics/2y', routes.$getStatisticsByTime.bind(routes, '2y')) .get(config.MEMPOOL.API_URL_PREFIX + 'statistics/3y', routes.$getStatisticsByTime.bind(routes, '3y')) - .get(config.MEMPOOL.API_URL_PREFIX + 'pools', routes.$getPools) + .get(config.MEMPOOL.API_URL_PREFIX + 'mining/pools', routes.$getPools) ; } diff --git a/backend/src/repositories/BlocksRepository.ts b/backend/src/repositories/BlocksRepository.ts index 413d4a226..947403e88 100644 --- a/backend/src/repositories/BlocksRepository.ts +++ b/backend/src/repositories/BlocksRepository.ts @@ -72,14 +72,16 @@ class BlocksRepository { /** * Count empty blocks for all pools */ - public async $countEmptyBlocks(interval: string = '100 YEAR'): Promise { - const connection = await DB.pool.getConnection(); - const [rows] = await connection.query(` + public async $countEmptyBlocks(interval: string | null): Promise { + const query = ` SELECT pool_id as poolId FROM blocks - WHERE blockTimestamp BETWEEN DATE_SUB(NOW(), INTERVAL ${interval}) AND NOW() - AND tx_count = 1; - `); + WHERE tx_count = 1` + + (interval != null ? ` AND blockTimestamp BETWEEN DATE_SUB(NOW(), INTERVAL ${interval}) AND NOW()` : ``) + ; + + const connection = await DB.pool.getConnection(); + const [rows] = await connection.query(query); connection.release(); return rows; @@ -88,17 +90,39 @@ class BlocksRepository { /** * Get blocks count for a period */ - public async $blockCount(interval: string = '100 YEAR'): Promise { - const connection = await DB.pool.getConnection(); - const [rows] = await connection.query(` + public async $blockCount(interval: string | null): Promise { + const query = ` SELECT count(height) as blockCount - FROM blocks - WHERE blockTimestamp BETWEEN DATE_SUB(NOW(), INTERVAL ${interval}) AND NOW(); - `); + FROM blocks` + + (interval != null ? ` WHERE blockTimestamp BETWEEN DATE_SUB(NOW(), INTERVAL ${interval}) AND NOW()` : ``) + ; + + const connection = await DB.pool.getConnection(); + const [rows] = await connection.query(query); connection.release(); return rows[0].blockCount; } + + /** + * Get the oldest indexed block + */ + public async $oldestBlockTimestamp(): Promise { + const connection = await DB.pool.getConnection(); + const [rows]: any[] = await connection.query(` + SELECT blockTimestamp + FROM blocks + ORDER BY height + LIMIT 1; + `); + connection.release(); + + if (rows.length <= 0) { + return -1; + } + + return rows[0].blockTimestamp; + } } export default new BlocksRepository(); \ No newline at end of file diff --git a/backend/src/repositories/PoolsRepository.ts b/backend/src/repositories/PoolsRepository.ts index 060418bf8..d1fb0da9a 100644 --- a/backend/src/repositories/PoolsRepository.ts +++ b/backend/src/repositories/PoolsRepository.ts @@ -25,17 +25,20 @@ class PoolsRepository { /** * Get basic pool info and block count */ - public async $getPoolsInfo(interval: string = '100 YEARS'): Promise { - const connection = await DB.pool.getConnection(); - const [rows] = await connection.query(` + public async $getPoolsInfo(interval: string | null): Promise { + const query = ` SELECT COUNT(height) as blockCount, pool_id as poolId, pools.name as name, pools.link as link FROM blocks - JOIN pools on pools.id = pool_id - WHERE blocks.blockTimestamp BETWEEN DATE_SUB(NOW(), INTERVAL ${interval}) AND NOW() - GROUP BY pool_id - ORDER BY COUNT(height) DESC; - `); + JOIN pools on pools.id = pool_id` + + (interval != null ? ` WHERE blocks.blockTimestamp BETWEEN DATE_SUB(NOW(), INTERVAL ${interval}) AND NOW()` : ``) + + ` GROUP BY pool_id + ORDER BY COUNT(height) DESC + `; + + const connection = await DB.pool.getConnection(); + const [rows] = await connection.query(query); connection.release(); + return rows; } } diff --git a/backend/src/routes.ts b/backend/src/routes.ts index 00e2ceee0..a273fd95d 100644 --- a/backend/src/routes.ts +++ b/backend/src/routes.ts @@ -535,9 +535,9 @@ class Routes { public async $getPools(req: Request, res: Response) { try { let stats = await miningStats.$getPoolsStats(req.query.interval as string); - // res.header('Pragma', 'public'); - // res.header('Cache-control', 'public'); - // res.setHeader('Expires', new Date(Date.now() + 1000 * 300).toUTCString()); + res.header('Pragma', 'public'); + res.header('Cache-control', 'public'); + res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString()); res.json(stats); } catch (e) { res.status(500).send(e instanceof Error ? e.message : e); diff --git a/docker/backend/start.sh b/docker/backend/start.sh index f9c784641..372be346e 100644 --- a/docker/backend/start.sh +++ b/docker/backend/start.sh @@ -13,7 +13,7 @@ __MEMPOOL_RECOMMENDED_FEE_PERCENTILE__=${MEMPOOL_RECOMMENDED_FEE_PERCENTILE:=50} __MEMPOOL_BLOCK_WEIGHT_UNITS__=${MEMPOOL_BLOCK_WEIGHT_UNITS:=4000000} __MEMPOOL_INITIAL_BLOCKS_AMOUNT__=${MEMPOOL_INITIAL_BLOCKS_AMOUNT:=8} __MEMPOOL_MEMPOOL_BLOCKS_AMOUNT__=${MEMPOOL_MEMPOOL_BLOCKS_AMOUNT:=8} -__MEMPOOL_INDEXING_BLOCKS_AMOUNT__=${MEMPOOL_INDEXING_BLOCKS_AMOUNT:=432} +__MEMPOOL_INDEXING_BLOCKS_AMOUNT__=${MEMPOOL_INDEXING_BLOCKS_AMOUNT:=1100} __MEMPOOL_PRICE_FEED_UPDATE_INTERVAL__=${MEMPOOL_PRICE_FEED_UPDATE_INTERVAL:=3600} __MEMPOOL_USE_SECOND_NODE_FOR_MINFEE__=${MEMPOOL_USE_SECOND_NODE_FOR_MINFEE:=false} __MEMPOOL_EXTERNAL_ASSETS__=${MEMPOOL_EXTERNAL_ASSETS:=[]} diff --git a/frontend/mempool-frontend-config.sample.json b/frontend/mempool-frontend-config.sample.json index 5cb296537..139c55962 100644 --- a/frontend/mempool-frontend-config.sample.json +++ b/frontend/mempool-frontend-config.sample.json @@ -11,7 +11,6 @@ "NGINX_HOSTNAME": "127.0.0.1", "NGINX_PORT": "80", "MEMPOOL_BLOCKS_AMOUNT": 8, - "INDEXING_BLOCKS_AMOUNT": 432, "BASE_MODULE": "mempool", "MEMPOOL_WEBSITE_URL": "https://mempool.space", "LIQUID_WEBSITE_URL": "https://liquid.network", diff --git a/frontend/server.ts b/frontend/server.ts index 2be6458b5..df4ab1294 100644 --- a/frontend/server.ts +++ b/frontend/server.ts @@ -65,7 +65,7 @@ export function app(locale: string): express.Express { server.get('/mempool-block/*', getLocalizedSSR(indexHtml)); server.get('/address/*', getLocalizedSSR(indexHtml)); server.get('/blocks', getLocalizedSSR(indexHtml)); - server.get('/pools', getLocalizedSSR(indexHtml)); + server.get('/mining/pools', getLocalizedSSR(indexHtml)); server.get('/graphs', getLocalizedSSR(indexHtml)); server.get('/liquid', getLocalizedSSR(indexHtml)); server.get('/liquid/tx/*', getLocalizedSSR(indexHtml)); @@ -86,7 +86,7 @@ export function app(locale: string): express.Express { server.get('/testnet/mempool-block/*', getLocalizedSSR(indexHtml)); server.get('/testnet/address/*', getLocalizedSSR(indexHtml)); server.get('/testnet/blocks', getLocalizedSSR(indexHtml)); - server.get('/testnet/pools', getLocalizedSSR(indexHtml)); + server.get('/testnet/mining/pools', getLocalizedSSR(indexHtml)); server.get('/testnet/graphs', getLocalizedSSR(indexHtml)); server.get('/testnet/api', getLocalizedSSR(indexHtml)); server.get('/testnet/tv', getLocalizedSSR(indexHtml)); @@ -98,7 +98,7 @@ export function app(locale: string): express.Express { server.get('/signet/mempool-block/*', getLocalizedSSR(indexHtml)); server.get('/signet/address/*', getLocalizedSSR(indexHtml)); server.get('/signet/blocks', getLocalizedSSR(indexHtml)); - server.get('/signet/pools', getLocalizedSSR(indexHtml)); + server.get('/signet/mining/pools', getLocalizedSSR(indexHtml)); server.get('/signet/graphs', getLocalizedSSR(indexHtml)); server.get('/signet/api', getLocalizedSSR(indexHtml)); server.get('/signet/tv', getLocalizedSSR(indexHtml)); diff --git a/frontend/src/app/app-routing.module.ts b/frontend/src/app/app-routing.module.ts index 19e4ee7ab..36a53781f 100644 --- a/frontend/src/app/app-routing.module.ts +++ b/frontend/src/app/app-routing.module.ts @@ -60,7 +60,7 @@ let routes: Routes = [ component: LatestBlocksComponent, }, { - path: 'pools', + path: 'mining/pools', component: PoolRankingComponent, }, { @@ -147,6 +147,10 @@ let routes: Routes = [ path: 'blocks', component: LatestBlocksComponent, }, + { + path: 'mining/pools', + component: PoolRankingComponent, + }, { path: 'graphs', component: StatisticsComponent, @@ -225,6 +229,10 @@ let routes: Routes = [ path: 'blocks', component: LatestBlocksComponent, }, + { + path: 'mining/pools', + component: PoolRankingComponent, + }, { path: 'graphs', component: StatisticsComponent, diff --git a/frontend/src/app/components/master-page/master-page.component.html b/frontend/src/app/components/master-page/master-page.component.html index 479f324c2..4624340d3 100644 --- a/frontend/src/app/components/master-page/master-page.component.html +++ b/frontend/src/app/components/master-page/master-page.component.html @@ -32,7 +32,7 @@