mempool/backend/src/api/blocks.ts
2020-05-26 11:02:12 +07:00

105 lines
3.4 KiB
TypeScript

const config = require('../../mempool-config.json');
import bitcoinApi from './bitcoin/electrs-api';
import memPool from './mempool';
import { Block, TransactionExtended } from '../interfaces';
import { Common } from './common';
class Blocks {
private blocks: Block[] = [];
private currentBlockHeight = 0;
private newBlockCallback: Function = () => {};
constructor() { }
public getBlocks(): Block[] {
return this.blocks;
}
public setBlocks(blocks: Block[]) {
this.blocks = blocks;
}
public setNewBlockCallback(fn: Function) {
this.newBlockCallback = fn;
}
public async updateBlocks() {
try {
const blockHeightTip = await bitcoinApi.getBlockHeightTip();
if (this.blocks.length === 0) {
this.currentBlockHeight = blockHeightTip - config.INITIAL_BLOCK_AMOUNT;
} else {
this.currentBlockHeight = this.blocks[this.blocks.length - 1].height;
}
if (blockHeightTip - this.currentBlockHeight > config.INITIAL_BLOCK_AMOUNT * 2) {
console.log(`${blockHeightTip - this.currentBlockHeight} blocks since tip. Fast forwarding to the ${config.INITIAL_BLOCK_AMOUNT} recent blocks`);
this.currentBlockHeight = blockHeightTip - config.INITIAL_BLOCK_AMOUNT;
}
while (this.currentBlockHeight < blockHeightTip) {
if (this.currentBlockHeight === 0) {
this.currentBlockHeight = blockHeightTip;
} else {
this.currentBlockHeight++;
console.log(`New block found (#${this.currentBlockHeight})!`);
}
const blockHash = await bitcoinApi.getBlockHash(this.currentBlockHeight);
const block = await bitcoinApi.getBlock(blockHash);
const txIds = await bitcoinApi.getTxIdsForBlock(blockHash);
const mempool = memPool.getMempool();
let found = 0;
let notFound = 0;
const transactions: TransactionExtended[] = [];
for (let i = 0; i < txIds.length; i++) {
if (mempool[txIds[i]]) {
transactions.push(mempool[txIds[i]]);
found++;
} else {
console.log(`Fetching block tx ${i} of ${txIds.length}`);
const tx = await memPool.getTransactionExtended(txIds[i]);
if (tx) {
transactions.push(tx);
}
notFound++;
}
}
console.log(`${found} of ${txIds.length} found in mempool. ${notFound} not found.`);
block.reward = transactions[0].vout.reduce((acc, curr) => acc + curr.value, 0);
transactions.sort((a, b) => b.feePerVsize - a.feePerVsize);
block.medianFee = transactions.length > 1 ? Common.median(transactions.map((tx) => tx.feePerVsize)) : 0;
block.feeRange = transactions.length > 1 ? Common.getFeesInRange(transactions, 8, 1) : [0, 0];
block.coinbaseTx = this.stripCoinbaseTransaction(transactions[0]);
this.blocks.push(block);
if (this.blocks.length > config.KEEP_BLOCK_AMOUNT) {
this.blocks.shift();
}
this.newBlockCallback(block, txIds, transactions);
}
} catch (err) {
console.log('updateBlocks error', err);
}
}
private stripCoinbaseTransaction(tx: TransactionExtended): any {
return {
vin: [{
scriptsig: tx.vin[0].scriptsig
}],
vout: tx.vout.map((vout) => ({ scriptpubkey_address: vout.scriptpubkey_address }))
};
}
}
export default new Blocks();