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