Merge pull request #1235 from nymkappa/feature/blocks-extras
Added /api/v1/blocks-extras endpoint
This commit is contained in:
commit
039a627d1c
@ -10,6 +10,7 @@ import bitcoinClient from './bitcoin/bitcoin-client';
|
|||||||
import { IEsploraApi } from './bitcoin/esplora-api.interface';
|
import { IEsploraApi } from './bitcoin/esplora-api.interface';
|
||||||
import poolsRepository from '../repositories/PoolsRepository';
|
import poolsRepository from '../repositories/PoolsRepository';
|
||||||
import blocksRepository from '../repositories/BlocksRepository';
|
import blocksRepository from '../repositories/BlocksRepository';
|
||||||
|
import loadingIndicators from './loading-indicators';
|
||||||
|
|
||||||
class Blocks {
|
class Blocks {
|
||||||
private blocks: BlockExtended[] = [];
|
private blocks: BlockExtended[] = [];
|
||||||
@ -41,7 +42,12 @@ class Blocks {
|
|||||||
* @param onlyCoinbase - Set to true if you only need the coinbase transaction
|
* @param onlyCoinbase - Set to true if you only need the coinbase transaction
|
||||||
* @returns Promise<TransactionExtended[]>
|
* @returns Promise<TransactionExtended[]>
|
||||||
*/
|
*/
|
||||||
private async $getTransactionsExtended(blockHash: string, blockHeight: number, onlyCoinbase: boolean): Promise<TransactionExtended[]> {
|
private async $getTransactionsExtended(
|
||||||
|
blockHash: string,
|
||||||
|
blockHeight: number,
|
||||||
|
onlyCoinbase: boolean,
|
||||||
|
quiet: boolean = false,
|
||||||
|
): Promise<TransactionExtended[]> {
|
||||||
const transactions: TransactionExtended[] = [];
|
const transactions: TransactionExtended[] = [];
|
||||||
const txIds: string[] = await bitcoinApi.$getTxIdsForBlock(blockHash);
|
const txIds: string[] = await bitcoinApi.$getTxIdsForBlock(blockHash);
|
||||||
|
|
||||||
@ -57,7 +63,7 @@ class Blocks {
|
|||||||
transactionsFound++;
|
transactionsFound++;
|
||||||
} else if (config.MEMPOOL.BACKEND === 'esplora' || memPool.isInSync() || i === 0) {
|
} else if (config.MEMPOOL.BACKEND === 'esplora' || memPool.isInSync() || i === 0) {
|
||||||
// Otherwise we fetch the tx data through backend services (esplora, electrum, core rpc...)
|
// Otherwise we fetch the tx data through backend services (esplora, electrum, core rpc...)
|
||||||
if (i % (Math.round((txIds.length) / 10)) === 0 || i + 1 === txIds.length) { // Avoid log spam
|
if (!quiet && (i % (Math.round((txIds.length) / 10)) === 0 || i + 1 === txIds.length)) { // Avoid log spam
|
||||||
logger.debug(`Indexing tx ${i + 1} of ${txIds.length} in block #${blockHeight}`);
|
logger.debug(`Indexing tx ${i + 1} of ${txIds.length} in block #${blockHeight}`);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
@ -83,7 +89,9 @@ class Blocks {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
logger.debug(`${transactionsFound} of ${txIds.length} found in mempool. ${transactionsFetched} fetched through backend service.`);
|
if (!quiet) {
|
||||||
|
logger.debug(`${transactionsFound} of ${txIds.length} found in mempool. ${transactionsFetched} fetched through backend service.`);
|
||||||
|
}
|
||||||
|
|
||||||
return transactions;
|
return transactions;
|
||||||
}
|
}
|
||||||
@ -94,13 +102,10 @@ class Blocks {
|
|||||||
* @param transactions
|
* @param transactions
|
||||||
* @returns BlockExtended
|
* @returns BlockExtended
|
||||||
*/
|
*/
|
||||||
private getBlockExtended(block: IEsploraApi.Block, transactions: TransactionExtended[]): BlockExtended {
|
private async $getBlockExtended(block: IEsploraApi.Block, transactions: TransactionExtended[]): Promise<BlockExtended> {
|
||||||
const blockExtended: BlockExtended = Object.assign({}, block);
|
const blockExtended: BlockExtended = Object.assign({extras: {}}, block);
|
||||||
|
blockExtended.extras.reward = transactions[0].vout.reduce((acc, curr) => acc + curr.value, 0);
|
||||||
blockExtended.extras = {
|
blockExtended.extras.coinbaseTx = transactionUtils.stripCoinbaseTransaction(transactions[0]);
|
||||||
reward: transactions[0].vout.reduce((acc, curr) => acc + curr.value, 0),
|
|
||||||
coinbaseTx: transactionUtils.stripCoinbaseTransaction(transactions[0]),
|
|
||||||
};
|
|
||||||
|
|
||||||
const transactionsTmp = [...transactions];
|
const transactionsTmp = [...transactions];
|
||||||
transactionsTmp.shift();
|
transactionsTmp.shift();
|
||||||
@ -111,6 +116,22 @@ class Blocks {
|
|||||||
blockExtended.extras.feeRange = transactionsTmp.length > 0 ?
|
blockExtended.extras.feeRange = transactionsTmp.length > 0 ?
|
||||||
Common.getFeesInRange(transactionsTmp, 8) : [0, 0];
|
Common.getFeesInRange(transactionsTmp, 8) : [0, 0];
|
||||||
|
|
||||||
|
const indexingAvailable =
|
||||||
|
['mainnet', 'testnet', 'signet'].includes(config.MEMPOOL.NETWORK) &&
|
||||||
|
config.DATABASE.ENABLED === true;
|
||||||
|
if (indexingAvailable) {
|
||||||
|
let pool: PoolTag;
|
||||||
|
if (blockExtended.extras?.coinbaseTx !== undefined) {
|
||||||
|
pool = await this.$findBlockMiner(blockExtended.extras?.coinbaseTx);
|
||||||
|
} else {
|
||||||
|
pool = await poolsRepository.$getUnknownPool();
|
||||||
|
}
|
||||||
|
blockExtended.extras.pool = {
|
||||||
|
id: pool.id,
|
||||||
|
name: pool.name
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return blockExtended;
|
return blockExtended;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,19 +174,21 @@ class Blocks {
|
|||||||
*/
|
*/
|
||||||
public async $generateBlockDatabase() {
|
public async $generateBlockDatabase() {
|
||||||
if (['mainnet', 'testnet', 'signet'].includes(config.MEMPOOL.NETWORK) === false || // Bitcoin only
|
if (['mainnet', 'testnet', 'signet'].includes(config.MEMPOOL.NETWORK) === false || // Bitcoin only
|
||||||
config.MEMPOOL.INDEXING_BLOCKS_AMOUNT === 0 || // Indexing must be enabled
|
config.MEMPOOL.INDEXING_BLOCKS_AMOUNT === 0 || // Indexing of older blocks must be enabled
|
||||||
!memPool.isInSync() || // We sync the mempool first
|
!memPool.isInSync() || // We sync the mempool first
|
||||||
this.blockIndexingStarted === true // Indexing must not already be in progress
|
this.blockIndexingStarted === true || // Indexing must not already be in progress
|
||||||
|
config.DATABASE.ENABLED === false
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const blockchainInfo = await bitcoinClient.getBlockchainInfo();
|
const blockchainInfo = await bitcoinClient.getBlockchainInfo();
|
||||||
if (blockchainInfo.blocks !== blockchainInfo.headers) {
|
if (blockchainInfo.blocks !== blockchainInfo.headers) { // Wait for node to sync
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.blockIndexingStarted = true;
|
this.blockIndexingStarted = true;
|
||||||
|
const startedAt = new Date().getTime() / 1000;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let currentBlockHeight = blockchainInfo.blocks;
|
let currentBlockHeight = blockchainInfo.blocks;
|
||||||
@ -180,6 +203,7 @@ class Blocks {
|
|||||||
logger.info(`Indexing blocks from #${currentBlockHeight} to #${lastBlockToIndex}`);
|
logger.info(`Indexing blocks from #${currentBlockHeight} to #${lastBlockToIndex}`);
|
||||||
|
|
||||||
const chunkSize = 10000;
|
const chunkSize = 10000;
|
||||||
|
let totaIndexed = 0;
|
||||||
while (currentBlockHeight >= lastBlockToIndex) {
|
while (currentBlockHeight >= lastBlockToIndex) {
|
||||||
const endBlock = Math.max(0, lastBlockToIndex, currentBlockHeight - chunkSize + 1);
|
const endBlock = Math.max(0, lastBlockToIndex, currentBlockHeight - chunkSize + 1);
|
||||||
|
|
||||||
@ -198,21 +222,17 @@ class Blocks {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
logger.debug(`Indexing block #${blockHeight}`);
|
if (totaIndexed % 100 === 0 || blockHeight === lastBlockToIndex) {
|
||||||
|
const elapsedSeconds = Math.max(1, Math.round((new Date().getTime() / 1000) - startedAt));
|
||||||
|
const blockPerSeconds = Math.round(totaIndexed / elapsedSeconds);
|
||||||
|
logger.debug(`Indexing block #${blockHeight} | ~${blockPerSeconds} blocks/sec | total: ${totaIndexed} | elapsed: ${elapsedSeconds} seconds`);
|
||||||
|
}
|
||||||
const blockHash = await bitcoinApi.$getBlockHash(blockHeight);
|
const blockHash = await bitcoinApi.$getBlockHash(blockHeight);
|
||||||
const block = await bitcoinApi.$getBlock(blockHash);
|
const block = await bitcoinApi.$getBlock(blockHash);
|
||||||
const transactions = await this.$getTransactionsExtended(blockHash, block.height, true);
|
const transactions = await this.$getTransactionsExtended(blockHash, block.height, true, true);
|
||||||
const blockExtended = this.getBlockExtended(block, transactions);
|
const blockExtended = await this.$getBlockExtended(block, transactions);
|
||||||
|
await blocksRepository.$saveBlockInDatabase(blockExtended);
|
||||||
let miner: PoolTag;
|
++totaIndexed;
|
||||||
if (blockExtended?.extras?.coinbaseTx) {
|
|
||||||
miner = await this.$findBlockMiner(blockExtended.extras.coinbaseTx);
|
|
||||||
} else {
|
|
||||||
miner = await poolsRepository.$getUnknownPool();
|
|
||||||
}
|
|
||||||
|
|
||||||
const coinbase: IEsploraApi.Transaction = await bitcoinApi.$getRawTransaction(transactions[0].txid, true);
|
|
||||||
await blocksRepository.$saveBlockInDatabase(blockExtended, blockHash, coinbase.hex, miner);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.err(`Something went wrong while indexing blocks.` + e);
|
logger.err(`Something went wrong while indexing blocks.` + e);
|
||||||
}
|
}
|
||||||
@ -271,17 +291,13 @@ class Blocks {
|
|||||||
const block = await bitcoinApi.$getBlock(blockHash);
|
const block = await bitcoinApi.$getBlock(blockHash);
|
||||||
const txIds: string[] = await bitcoinApi.$getTxIdsForBlock(blockHash);
|
const txIds: string[] = await bitcoinApi.$getTxIdsForBlock(blockHash);
|
||||||
const transactions = await this.$getTransactionsExtended(blockHash, block.height, false);
|
const transactions = await this.$getTransactionsExtended(blockHash, block.height, false);
|
||||||
const blockExtended: BlockExtended = this.getBlockExtended(block, transactions);
|
const blockExtended: BlockExtended = await this.$getBlockExtended(block, transactions);
|
||||||
const coinbase: IEsploraApi.Transaction = await bitcoinApi.$getRawTransaction(transactions[0].txid, true);
|
|
||||||
|
|
||||||
if (['mainnet', 'testnet', 'signet'].includes(config.MEMPOOL.NETWORK) === true) {
|
const indexingAvailable =
|
||||||
let miner: PoolTag;
|
['mainnet', 'testnet', 'signet'].includes(config.MEMPOOL.NETWORK) &&
|
||||||
if (blockExtended?.extras?.coinbaseTx) {
|
config.DATABASE.ENABLED === true;
|
||||||
miner = await this.$findBlockMiner(blockExtended.extras.coinbaseTx);
|
if (indexingAvailable) {
|
||||||
} else {
|
await blocksRepository.$saveBlockInDatabase(blockExtended);
|
||||||
miner = await poolsRepository.$getUnknownPool();
|
|
||||||
}
|
|
||||||
await blocksRepository.$saveBlockInDatabase(blockExtended, blockHash, coinbase.hex, miner);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (block.height % 2016 === 0) {
|
if (block.height % 2016 === 0) {
|
||||||
@ -304,6 +320,96 @@ class Blocks {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Index a block if it's missing from the database. Returns the block after indexing
|
||||||
|
*/
|
||||||
|
public async $indexBlock(height: number): Promise<BlockExtended> {
|
||||||
|
const dbBlock = await blocksRepository.$getBlockByHeight(height);
|
||||||
|
if (dbBlock != null) {
|
||||||
|
return this.prepareBlock(dbBlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
const blockHash = await bitcoinApi.$getBlockHash(height);
|
||||||
|
const block = await bitcoinApi.$getBlock(blockHash);
|
||||||
|
const transactions = await this.$getTransactionsExtended(blockHash, block.height, true);
|
||||||
|
const blockExtended = await this.$getBlockExtended(block, transactions);
|
||||||
|
|
||||||
|
await blocksRepository.$saveBlockInDatabase(blockExtended);
|
||||||
|
|
||||||
|
return blockExtended;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async $getBlocksExtras(fromHeight: number): Promise<BlockExtended[]> {
|
||||||
|
const indexingAvailable =
|
||||||
|
['mainnet', 'testnet', 'signet'].includes(config.MEMPOOL.NETWORK) &&
|
||||||
|
config.DATABASE.ENABLED === true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
loadingIndicators.setProgress('blocks', 0);
|
||||||
|
|
||||||
|
let currentHeight = fromHeight ? fromHeight : this.getCurrentBlockHeight();
|
||||||
|
const returnBlocks: BlockExtended[] = [];
|
||||||
|
|
||||||
|
if (currentHeight < 0) {
|
||||||
|
return returnBlocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
startFromHash = await bitcoinApi.$getBlockHash(currentHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
let nextHash = startFromHash;
|
||||||
|
for (let i = 0; i < 10 && currentHeight >= 0; i++) {
|
||||||
|
let block = this.getBlocks().find((b) => b.height === currentHeight);
|
||||||
|
if (!block && indexingAvailable) {
|
||||||
|
block = this.prepareBlock(await this.$indexBlock(currentHeight));
|
||||||
|
} else if (!block) {
|
||||||
|
block = this.prepareBlock(await bitcoinApi.$getBlock(nextHash));
|
||||||
|
}
|
||||||
|
returnBlocks.push(block);
|
||||||
|
nextHash = block.previousblockhash;
|
||||||
|
loadingIndicators.setProgress('blocks', i / 10 * 100);
|
||||||
|
currentHeight--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnBlocks;
|
||||||
|
} catch (e) {
|
||||||
|
loadingIndicators.setProgress('blocks', 100);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private prepareBlock(block: any): BlockExtended {
|
||||||
|
return <BlockExtended>{
|
||||||
|
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: {
|
||||||
|
medianFee: block?.medianFee,
|
||||||
|
feeRange: block?.feeRange ?? [], // TODO
|
||||||
|
reward: block?.reward,
|
||||||
|
pool: block?.extras?.pool ?? (block?.pool_id ? {
|
||||||
|
id: block?.pool_id,
|
||||||
|
name: block?.pool_name,
|
||||||
|
} : undefined),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public getLastDifficultyAdjustmentTime(): number {
|
public getLastDifficultyAdjustmentTime(): number {
|
||||||
return this.lastDifficultyAdjustmentTime;
|
return this.lastDifficultyAdjustmentTime;
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ import logger from '../logger';
|
|||||||
const sleep = (ms: number) => new Promise(res => setTimeout(res, ms));
|
const sleep = (ms: number) => new Promise(res => setTimeout(res, ms));
|
||||||
|
|
||||||
class DatabaseMigration {
|
class DatabaseMigration {
|
||||||
private static currentVersion = 4;
|
private static currentVersion = 5;
|
||||||
private queryTimeout = 120000;
|
private queryTimeout = 120000;
|
||||||
private statisticsAddedIndexed = false;
|
private statisticsAddedIndexed = false;
|
||||||
|
|
||||||
@ -229,6 +229,10 @@ class DatabaseMigration {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (version < 5 && (['mainnet', 'testnet', 'signet'].includes(config.MEMPOOL.NETWORK) === true)) {
|
||||||
|
queries.push('ALTER TABLE blocks ADD `reward` double unsigned NOT NULL DEFAULT "0"');
|
||||||
|
}
|
||||||
|
|
||||||
return queries;
|
return queries;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -290,6 +290,10 @@ 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);
|
||||||
|
|
||||||
if (config.MEMPOOL.BACKEND !== 'esplora') {
|
if (config.MEMPOOL.BACKEND !== 'esplora') {
|
||||||
this.app
|
this.app
|
||||||
.get(config.MEMPOOL.API_URL_PREFIX + 'mempool', routes.getMempool)
|
.get(config.MEMPOOL.API_URL_PREFIX + 'mempool', routes.getMempool)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { IEsploraApi } from './api/bitcoin/esplora-api.interface';
|
import { IEsploraApi } from './api/bitcoin/esplora-api.interface';
|
||||||
|
|
||||||
export interface PoolTag {
|
export interface PoolTag {
|
||||||
id: number | null, // mysql row id
|
id: number, // mysql row id
|
||||||
name: string,
|
name: string,
|
||||||
link: string,
|
link: string,
|
||||||
regexes: string, // JSON array
|
regexes: string, // JSON array
|
||||||
@ -83,10 +83,14 @@ export interface BlockExtension {
|
|||||||
reward?: number;
|
reward?: number;
|
||||||
coinbaseTx?: TransactionMinerInfo;
|
coinbaseTx?: TransactionMinerInfo;
|
||||||
matchRate?: number;
|
matchRate?: number;
|
||||||
|
pool?: {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BlockExtended extends IEsploraApi.Block {
|
export interface BlockExtended extends IEsploraApi.Block {
|
||||||
extras?: BlockExtension;
|
extras: BlockExtension;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TransactionMinerInfo {
|
export interface TransactionMinerInfo {
|
||||||
|
@ -11,38 +11,36 @@ class BlocksRepository {
|
|||||||
/**
|
/**
|
||||||
* Save indexed block data in the database
|
* Save indexed block data in the database
|
||||||
*/
|
*/
|
||||||
public async $saveBlockInDatabase(
|
public async $saveBlockInDatabase(block: BlockExtended) {
|
||||||
block: BlockExtended,
|
|
||||||
blockHash: string,
|
|
||||||
coinbaseHex: string | undefined,
|
|
||||||
poolTag: PoolTag
|
|
||||||
) {
|
|
||||||
const connection = await DB.pool.getConnection();
|
const connection = await DB.pool.getConnection();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const query = `INSERT INTO blocks(
|
const query = `INSERT INTO blocks(
|
||||||
height, hash, blockTimestamp, size,
|
height, hash, blockTimestamp, size,
|
||||||
weight, tx_count, coinbase_raw, difficulty,
|
weight, tx_count, coinbase_raw, difficulty,
|
||||||
pool_id, fees, fee_span, median_fee
|
pool_id, fees, fee_span, median_fee,
|
||||||
|
reward
|
||||||
) VALUE (
|
) VALUE (
|
||||||
?, ?, FROM_UNIXTIME(?), ?,
|
?, ?, FROM_UNIXTIME(?), ?,
|
||||||
?, ?, ?, ?,
|
?, ?, ?, ?,
|
||||||
?, ?, ?, ?
|
?, ?, ?, ?,
|
||||||
|
?
|
||||||
)`;
|
)`;
|
||||||
|
|
||||||
const params: any[] = [
|
const params: any[] = [
|
||||||
block.height,
|
block.height,
|
||||||
blockHash,
|
block.id,
|
||||||
block.timestamp,
|
block.timestamp,
|
||||||
block.size,
|
block.size,
|
||||||
block.weight,
|
block.weight,
|
||||||
block.tx_count,
|
block.tx_count,
|
||||||
coinbaseHex ? coinbaseHex : '',
|
'',
|
||||||
block.difficulty,
|
block.difficulty,
|
||||||
poolTag.id,
|
block.extras?.pool?.id, // Should always be set to something
|
||||||
0,
|
0,
|
||||||
'[]',
|
'[]',
|
||||||
block.extras ? block.extras.medianFee : 0,
|
block.extras.medianFee ?? 0,
|
||||||
|
block.extras?.reward ?? 0,
|
||||||
];
|
];
|
||||||
|
|
||||||
await connection.query(query, params);
|
await connection.query(query, params);
|
||||||
@ -136,6 +134,26 @@ class BlocksRepository {
|
|||||||
|
|
||||||
return <number>rows[0].blockTimestamp;
|
return <number>rows[0].blockTimestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get one block by height
|
||||||
|
*/
|
||||||
|
public async $getBlockByHeight(height: number): Promise<object | null> {
|
||||||
|
const connection = await DB.pool.getConnection();
|
||||||
|
const [rows]: any[] = await connection.query(`
|
||||||
|
SELECT *, UNIX_TIMESTAMP(blocks.blockTimestamp) as blockTimestamp, pools.id as pool_id, pools.name as pool_name, pools.link as pool_link, pools.addresses as pool_addresses, pools.regexes as pool_regexes
|
||||||
|
FROM blocks
|
||||||
|
JOIN pools ON blocks.pool_id = pools.id
|
||||||
|
WHERE height = ${height};
|
||||||
|
`);
|
||||||
|
connection.release();
|
||||||
|
|
||||||
|
if (rows.length <= 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rows[0];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default new BlocksRepository();
|
export default new BlocksRepository();
|
@ -7,7 +7,7 @@ class PoolsRepository {
|
|||||||
*/
|
*/
|
||||||
public async $getPools(): Promise<PoolTag[]> {
|
public async $getPools(): Promise<PoolTag[]> {
|
||||||
const connection = await DB.pool.getConnection();
|
const connection = await DB.pool.getConnection();
|
||||||
const [rows] = await connection.query('SELECT * FROM pools;');
|
const [rows] = await connection.query('SELECT id, name, addresses, regexes FROM pools;');
|
||||||
connection.release();
|
connection.release();
|
||||||
return <PoolTag[]>rows;
|
return <PoolTag[]>rows;
|
||||||
}
|
}
|
||||||
@ -17,7 +17,7 @@ class PoolsRepository {
|
|||||||
*/
|
*/
|
||||||
public async $getUnknownPool(): Promise<PoolTag> {
|
public async $getUnknownPool(): Promise<PoolTag> {
|
||||||
const connection = await DB.pool.getConnection();
|
const connection = await DB.pool.getConnection();
|
||||||
const [rows] = await connection.query('SELECT * FROM pools where name = "Unknown"');
|
const [rows] = await connection.query('SELECT id, name FROM pools where name = "Unknown"');
|
||||||
connection.release();
|
connection.release();
|
||||||
return <PoolTag>rows[0];
|
return <PoolTag>rows[0];
|
||||||
}
|
}
|
||||||
|
@ -564,6 +564,14 @@ class Routes {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async getBlocksExtras(req: Request, res: Response) {
|
||||||
|
try {
|
||||||
|
res.json(await blocks.$getBlocksExtras(parseInt(req.params.height, 10)))
|
||||||
|
} catch (e) {
|
||||||
|
res.status(500).send(e instanceof Error ? e.message : e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async getBlocks(req: Request, res: Response) {
|
public async getBlocks(req: Request, res: Response) {
|
||||||
try {
|
try {
|
||||||
loadingIndicators.setProgress('blocks', 0);
|
loadingIndicators.setProgress('blocks', 0);
|
||||||
|
@ -15,7 +15,8 @@
|
|||||||
"PRICE_FEED_UPDATE_INTERVAL": __MEMPOOL_PRICE_FEED_UPDATE_INTERVAL__,
|
"PRICE_FEED_UPDATE_INTERVAL": __MEMPOOL_PRICE_FEED_UPDATE_INTERVAL__,
|
||||||
"USE_SECOND_NODE_FOR_MINFEE": __MEMPOOL_USE_SECOND_NODE_FOR_MINFEE__,
|
"USE_SECOND_NODE_FOR_MINFEE": __MEMPOOL_USE_SECOND_NODE_FOR_MINFEE__,
|
||||||
"EXTERNAL_ASSETS": __MEMPOOL_EXTERNAL_ASSETS__,
|
"EXTERNAL_ASSETS": __MEMPOOL_EXTERNAL_ASSETS__,
|
||||||
"STDOUT_LOG_MIN_PRIORITY": "__MEMPOOL_STDOUT_LOG_MIN_PRIORITY__"
|
"STDOUT_LOG_MIN_PRIORITY": "__MEMPOOL_STDOUT_LOG_MIN_PRIORITY__",
|
||||||
|
"INDEXING_BLOCKS_AMOUNT": __MEMPOOL_INDEXING_BLOCKS_AMOUNT__
|
||||||
},
|
},
|
||||||
"CORE_RPC": {
|
"CORE_RPC": {
|
||||||
"HOST": "__CORE_RPC_HOST__",
|
"HOST": "__CORE_RPC_HOST__",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user