Update block API to use indexing if available
This commit is contained in:
parent
b50f9b4e2d
commit
057b5bd2e1
@ -134,26 +134,25 @@ class Blocks {
|
|||||||
blockExtended.extras.avgFeeRate = stats.avgfeerate;
|
blockExtended.extras.avgFeeRate = stats.avgfeerate;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Common.indexingEnabled()) {
|
let pool: PoolTag;
|
||||||
let pool: PoolTag;
|
if (blockExtended.extras?.coinbaseTx !== undefined) {
|
||||||
if (blockExtended.extras?.coinbaseTx !== undefined) {
|
pool = await this.$findBlockMiner(blockExtended.extras?.coinbaseTx);
|
||||||
pool = await this.$findBlockMiner(blockExtended.extras?.coinbaseTx);
|
} else {
|
||||||
} else {
|
pool = await poolsRepository.$getUnknownPool();
|
||||||
pool = await poolsRepository.$getUnknownPool();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!pool) { // We should never have this situation in practise
|
|
||||||
logger.warn(`Cannot assign pool to block ${blockExtended.height} and 'unknown' pool does not exist. Check your "pools" table entries`);
|
|
||||||
return blockExtended;
|
|
||||||
}
|
|
||||||
|
|
||||||
blockExtended.extras.pool = {
|
|
||||||
id: pool.id,
|
|
||||||
name: pool.name,
|
|
||||||
slug: pool.slug,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!pool) { // We should never have this situation in practise
|
||||||
|
logger.warn(`Cannot assign pool to block ${blockExtended.height} and 'unknown' pool does not exist. ` +
|
||||||
|
`Check your "pools" table entries`);
|
||||||
|
return blockExtended;
|
||||||
|
}
|
||||||
|
|
||||||
|
blockExtended.extras.pool = {
|
||||||
|
id: pool.id,
|
||||||
|
name: pool.name,
|
||||||
|
slug: pool.slug,
|
||||||
|
};
|
||||||
|
|
||||||
return blockExtended;
|
return blockExtended;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -389,6 +388,39 @@ class Blocks {
|
|||||||
return prepareBlock(blockExtended);
|
return prepareBlock(blockExtended);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Index a block by hash if it's missing from the database. Returns the block after indexing
|
||||||
|
*/
|
||||||
|
public async $getBlock(hash: string): Promise<BlockExtended | IEsploraApi.Block> {
|
||||||
|
// Block has already been indexed
|
||||||
|
if (Common.indexingEnabled()) {
|
||||||
|
const dbBlock = await blocksRepository.$getBlockByHash(hash);
|
||||||
|
if (dbBlock != null) {
|
||||||
|
logger.info('GET BLOCK: already indexed');
|
||||||
|
return prepareBlock(dbBlock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const block = await bitcoinApi.$getBlock(hash);
|
||||||
|
|
||||||
|
// Not Bitcoin network, return the block as it
|
||||||
|
if (['mainnet', 'testnet', 'signet'].includes(config.MEMPOOL.NETWORK) === false) {
|
||||||
|
logger.info('GET BLOCK: using bitcoin backend');
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bitcoin network, add our custom data on top
|
||||||
|
logger.info('GET BLOCK: index block on the fly');
|
||||||
|
const transactions = await this.$getTransactionsExtended(hash, block.height, true);
|
||||||
|
const blockExtended = await this.$getBlockExtended(block, transactions);
|
||||||
|
if (Common.indexingEnabled()) {
|
||||||
|
delete(blockExtended['coinbaseTx']);
|
||||||
|
await blocksRepository.$saveBlockInDatabase(blockExtended);
|
||||||
|
}
|
||||||
|
|
||||||
|
return blockExtended;
|
||||||
|
}
|
||||||
|
|
||||||
public async $getBlocksExtras(fromHeight?: number, limit: number = 15): Promise<BlockExtended[]> {
|
public async $getBlocksExtras(fromHeight?: number, limit: number = 15): Promise<BlockExtended[]> {
|
||||||
// Note - This API is breaking if indexing is not available. For now it is okay because we only
|
// 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.
|
// use it for the mining pages, and mining pages should not be available if indexing is turned off.
|
||||||
|
@ -174,7 +174,7 @@ export class Common {
|
|||||||
return (
|
return (
|
||||||
['mainnet', 'testnet', 'signet'].includes(config.MEMPOOL.NETWORK) &&
|
['mainnet', 'testnet', 'signet'].includes(config.MEMPOOL.NETWORK) &&
|
||||||
config.DATABASE.ENABLED === true &&
|
config.DATABASE.ENABLED === true &&
|
||||||
config.MEMPOOL.INDEXING_BLOCKS_AMOUNT != 0
|
config.MEMPOOL.INDEXING_BLOCKS_AMOUNT !== 0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -287,6 +287,7 @@ class BlocksRepository {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rows[0].fee_span = JSON.parse(rows[0].fee_span);
|
||||||
return rows[0];
|
return rows[0];
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.err(`Cannot get indexed block ${height}. Reason: ` + (e instanceof Error ? e.message : e));
|
logger.err(`Cannot get indexed block ${height}. Reason: ` + (e instanceof Error ? e.message : e));
|
||||||
@ -294,6 +295,34 @@ class BlocksRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get one block by hash
|
||||||
|
*/
|
||||||
|
public async $getBlockByHash(hash: string): Promise<object | null> {
|
||||||
|
try {
|
||||||
|
const query = `
|
||||||
|
SELECT *, UNIX_TIMESTAMP(blocks.blockTimestamp) as blockTimestamp, hash as id,
|
||||||
|
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
|
||||||
|
FROM blocks
|
||||||
|
JOIN pools ON blocks.pool_id = pools.id
|
||||||
|
WHERE hash = '${hash}';
|
||||||
|
`;
|
||||||
|
const [rows]: any[] = await DB.query(query);
|
||||||
|
|
||||||
|
if (rows.length <= 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
rows[0].fee_span = JSON.parse(rows[0].fee_span);
|
||||||
|
return rows[0];
|
||||||
|
} catch (e) {
|
||||||
|
logger.err(`Cannot get indexed block ${hash}. Reason: ` + (e instanceof Error ? e.message : e));
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return blocks difficulty
|
* Return blocks difficulty
|
||||||
*/
|
*/
|
||||||
@ -457,7 +486,7 @@ class BlocksRepository {
|
|||||||
/**
|
/**
|
||||||
* Get the historical averaged block rewards
|
* Get the historical averaged block rewards
|
||||||
*/
|
*/
|
||||||
public async $getHistoricalBlockRewards(div: number, interval: string | null): Promise<any> {
|
public async $getHistoricalBlockRewards(div: number, interval: string | null): Promise<any> {
|
||||||
try {
|
try {
|
||||||
let query = `SELECT
|
let query = `SELECT
|
||||||
CAST(AVG(height) as INT) as avg_height,
|
CAST(AVG(height) as INT) as avg_height,
|
||||||
|
@ -702,8 +702,8 @@ class Routes {
|
|||||||
|
|
||||||
public async getBlock(req: Request, res: Response) {
|
public async getBlock(req: Request, res: Response) {
|
||||||
try {
|
try {
|
||||||
const result = await bitcoinApi.$getBlock(req.params.hash);
|
const block = await blocks.$getBlock(req.params.hash);
|
||||||
res.json(result);
|
res.json(block);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
res.status(500).send(e instanceof Error ? e.message : e);
|
res.status(500).send(e instanceof Error ? e.message : e);
|
||||||
}
|
}
|
||||||
@ -727,7 +727,7 @@ class Routes {
|
|||||||
res.status(500).send(e instanceof Error ? e.message : 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);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { BlockExtended } from "../mempool.interfaces";
|
import { BlockExtended } from '../mempool.interfaces';
|
||||||
|
|
||||||
export function prepareBlock(block: any): BlockExtended {
|
export function prepareBlock(block: any): BlockExtended {
|
||||||
return <BlockExtended>{
|
return <BlockExtended>{
|
||||||
@ -17,9 +17,11 @@ export function prepareBlock(block: any): BlockExtended {
|
|||||||
extras: {
|
extras: {
|
||||||
coinbaseRaw: block.coinbase_raw ?? block.extras.coinbaseRaw,
|
coinbaseRaw: block.coinbase_raw ?? block.extras.coinbaseRaw,
|
||||||
medianFee: block.medianFee ?? block.median_fee ?? block.extras?.medianFee,
|
medianFee: block.medianFee ?? block.median_fee ?? block.extras?.medianFee,
|
||||||
feeRange: block.feeRange ?? block.fee_range ?? block?.extras?.feeSpan,
|
feeRange: block.feeRange ?? block.fee_span,
|
||||||
reward: block.reward ?? block?.extras?.reward,
|
reward: block.reward ?? block?.extras?.reward,
|
||||||
totalFees: block.totalFees ?? block?.fees ?? block?.extras.totalFees,
|
totalFees: block.totalFees ?? block?.fees ?? block?.extras?.totalFees,
|
||||||
|
avgFee: block?.extras?.avgFee ?? block.avg_fee,
|
||||||
|
avgFeeRate: block?.avgFeeRate ?? block.avg_fee_rate,
|
||||||
pool: block?.extras?.pool ?? (block?.pool_id ? {
|
pool: block?.extras?.pool ?? (block?.pool_id ? {
|
||||||
id: block.pool_id,
|
id: block.pool_id,
|
||||||
name: block.pool_name,
|
name: block.pool_name,
|
||||||
|
@ -105,7 +105,18 @@
|
|||||||
</ng-template>
|
</ng-template>
|
||||||
<tr>
|
<tr>
|
||||||
<td i18n="block.miner">Miner</td>
|
<td i18n="block.miner">Miner</td>
|
||||||
<td><app-miner [coinbaseTransaction]="coinbaseTx"></app-miner></td>
|
<td *ngIf="stateService.env.MINING_DASHBOARD">
|
||||||
|
<a placement="bottom" [routerLink]="['/mining/pool' | relativeUrl, block.extras.pool.slug]" class="badge"
|
||||||
|
[class]="block.extras.pool.name === 'Unknown' ? 'badge-secondary' : 'badge-primary'">
|
||||||
|
{{ block.extras.pool.name }}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td *ngIf="!stateService.env.MINING_DASHBOARD">
|
||||||
|
<span placement="bottom" class="badge"
|
||||||
|
[class]="block.extras.pool.name === 'Unknown' ? 'badge-secondary' : 'badge-primary'">
|
||||||
|
{{ block.extras.pool.name }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
@ -31,7 +31,6 @@ export class BlockComponent implements OnInit, OnDestroy {
|
|||||||
blockSubsidy: number;
|
blockSubsidy: number;
|
||||||
fees: number;
|
fees: number;
|
||||||
paginationMaxSize: number;
|
paginationMaxSize: number;
|
||||||
coinbaseTx: Transaction;
|
|
||||||
page = 1;
|
page = 1;
|
||||||
itemsPerPage: number;
|
itemsPerPage: number;
|
||||||
txsLoadingStatus$: Observable<number>;
|
txsLoadingStatus$: Observable<number>;
|
||||||
@ -50,7 +49,7 @@ export class BlockComponent implements OnInit, OnDestroy {
|
|||||||
private location: Location,
|
private location: Location,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private electrsApiService: ElectrsApiService,
|
private electrsApiService: ElectrsApiService,
|
||||||
private stateService: StateService,
|
public stateService: StateService,
|
||||||
private seoService: SeoService,
|
private seoService: SeoService,
|
||||||
private websocketService: WebsocketService,
|
private websocketService: WebsocketService,
|
||||||
private relativeUrlPipe: RelativeUrlPipe,
|
private relativeUrlPipe: RelativeUrlPipe,
|
||||||
@ -88,7 +87,6 @@ export class BlockComponent implements OnInit, OnDestroy {
|
|||||||
const blockHash: string = params.get('id') || '';
|
const blockHash: string = params.get('id') || '';
|
||||||
this.block = undefined;
|
this.block = undefined;
|
||||||
this.page = 1;
|
this.page = 1;
|
||||||
this.coinbaseTx = undefined;
|
|
||||||
this.error = undefined;
|
this.error = undefined;
|
||||||
this.fees = undefined;
|
this.fees = undefined;
|
||||||
this.stateService.markBlock$.next({});
|
this.stateService.markBlock$.next({});
|
||||||
@ -145,7 +143,6 @@ export class BlockComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
this.seoService.setTitle($localize`:@@block.component.browser-title:Block ${block.height}:BLOCK_HEIGHT:: ${block.id}:BLOCK_ID:`);
|
this.seoService.setTitle($localize`:@@block.component.browser-title:Block ${block.height}:BLOCK_HEIGHT:: ${block.id}:BLOCK_ID:`);
|
||||||
this.isLoadingBlock = false;
|
this.isLoadingBlock = false;
|
||||||
this.coinbaseTx = block?.extras?.coinbaseTx;
|
|
||||||
this.setBlockSubsidy();
|
this.setBlockSubsidy();
|
||||||
if (block?.extras?.reward !== undefined) {
|
if (block?.extras?.reward !== undefined) {
|
||||||
this.fees = block.extras.reward / 100000000 - this.blockSubsidy;
|
this.fees = block.extras.reward / 100000000 - this.blockSubsidy;
|
||||||
@ -167,9 +164,6 @@ export class BlockComponent implements OnInit, OnDestroy {
|
|||||||
if (this.fees === undefined && transactions[0]) {
|
if (this.fees === undefined && transactions[0]) {
|
||||||
this.fees = transactions[0].vout.reduce((acc: number, curr: Vout) => acc + curr.value, 0) / 100000000 - this.blockSubsidy;
|
this.fees = transactions[0].vout.reduce((acc: number, curr: Vout) => acc + curr.value, 0) / 100000000 - this.blockSubsidy;
|
||||||
}
|
}
|
||||||
if (!this.coinbaseTx && transactions[0]) {
|
|
||||||
this.coinbaseTx = transactions[0];
|
|
||||||
}
|
|
||||||
this.transactions = transactions;
|
this.transactions = transactions;
|
||||||
this.isLoadingTransactions = false;
|
this.isLoadingTransactions = false;
|
||||||
},
|
},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user