2020-12-20 22:36:36 +07:00
|
|
|
import config from '../config';
|
|
|
|
import bitcoinApi from './bitcoin/bitcoin-api-factory';
|
2020-10-13 15:27:52 +07:00
|
|
|
import logger from '../logger';
|
2020-02-23 19:16:50 +07:00
|
|
|
import memPool from './mempool';
|
2020-12-30 01:47:07 +07:00
|
|
|
import { BlockExtended, TransactionExtended } from '../mempool.interfaces';
|
2020-05-24 16:29:30 +07:00
|
|
|
import { Common } from './common';
|
2020-10-27 00:05:06 +07:00
|
|
|
import diskCache from './disk-cache';
|
2020-12-21 23:08:34 +07:00
|
|
|
import transactionUtils from './transaction-utils';
|
2019-07-21 17:59:47 +03:00
|
|
|
|
|
|
|
class Blocks {
|
2020-12-30 01:47:07 +07:00
|
|
|
private static INITIAL_BLOCK_AMOUNT = 8;
|
2020-12-28 04:47:22 +07:00
|
|
|
private blocks: BlockExtended[] = [];
|
2019-08-29 01:17:31 +02:00
|
|
|
private currentBlockHeight = 0;
|
2021-07-23 14:35:04 +03:00
|
|
|
private currentDifficulty = 0;
|
2020-09-21 19:41:12 +07:00
|
|
|
private lastDifficultyAdjustmentTime = 0;
|
2021-07-23 14:35:04 +03:00
|
|
|
private previousDifficultyRetarget = 0;
|
2020-12-28 04:47:22 +07:00
|
|
|
private newBlockCallbacks: ((block: BlockExtended, txIds: string[], transactions: TransactionExtended[]) => void)[] = [];
|
2019-08-29 01:17:31 +02:00
|
|
|
|
2020-02-16 22:15:07 +07:00
|
|
|
constructor() { }
|
2019-07-21 17:59:47 +03:00
|
|
|
|
2020-12-28 04:47:22 +07:00
|
|
|
public getBlocks(): BlockExtended[] {
|
2019-07-21 17:59:47 +03:00
|
|
|
return this.blocks;
|
|
|
|
}
|
|
|
|
|
2020-12-28 04:47:22 +07:00
|
|
|
public setBlocks(blocks: BlockExtended[]) {
|
2020-02-29 21:52:04 +07:00
|
|
|
this.blocks = blocks;
|
|
|
|
}
|
|
|
|
|
2020-12-28 04:47:22 +07:00
|
|
|
public setNewBlockCallback(fn: (block: BlockExtended, txIds: string[], transactions: TransactionExtended[]) => void) {
|
2020-09-27 17:21:18 +07:00
|
|
|
this.newBlockCallbacks.push(fn);
|
2019-07-21 17:59:47 +03:00
|
|
|
}
|
|
|
|
|
2020-10-18 21:47:47 +07:00
|
|
|
public async $updateBlocks() {
|
2020-12-21 23:08:34 +07:00
|
|
|
const blockHeightTip = await bitcoinApi.$getBlockHeightTip();
|
2019-07-21 17:59:47 +03:00
|
|
|
|
2020-10-18 21:47:47 +07:00
|
|
|
if (this.blocks.length === 0) {
|
2020-12-30 01:47:07 +07:00
|
|
|
this.currentBlockHeight = blockHeightTip - Blocks.INITIAL_BLOCK_AMOUNT;
|
2020-10-18 21:47:47 +07:00
|
|
|
} else {
|
|
|
|
this.currentBlockHeight = this.blocks[this.blocks.length - 1].height;
|
|
|
|
}
|
|
|
|
|
2020-12-30 01:47:07 +07:00
|
|
|
if (blockHeightTip - this.currentBlockHeight > Blocks.INITIAL_BLOCK_AMOUNT * 2) {
|
|
|
|
logger.info(`${blockHeightTip - this.currentBlockHeight} blocks since tip. Fast forwarding to the ${Blocks.INITIAL_BLOCK_AMOUNT} recent blocks`);
|
|
|
|
this.currentBlockHeight = blockHeightTip - Blocks.INITIAL_BLOCK_AMOUNT;
|
2020-10-18 21:47:47 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!this.lastDifficultyAdjustmentTime) {
|
|
|
|
const heightDiff = blockHeightTip % 2016;
|
2020-12-21 23:08:34 +07:00
|
|
|
const blockHash = await bitcoinApi.$getBlockHash(blockHeightTip - heightDiff);
|
|
|
|
const block = await bitcoinApi.$getBlock(blockHash);
|
2020-10-18 21:47:47 +07:00
|
|
|
this.lastDifficultyAdjustmentTime = block.timestamp;
|
2021-07-23 14:35:04 +03:00
|
|
|
this.currentDifficulty = block.difficulty;
|
|
|
|
|
|
|
|
const previousPeriodBlockHash = await bitcoinApi.$getBlockHash(blockHeightTip - heightDiff - 2016);
|
|
|
|
const previousPeriodBlock = await bitcoinApi.$getBlock(previousPeriodBlockHash);
|
|
|
|
this.previousDifficultyRetarget = (block.difficulty - previousPeriodBlock.difficulty) / previousPeriodBlock.difficulty * 100;
|
2020-10-18 21:47:47 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
while (this.currentBlockHeight < blockHeightTip) {
|
|
|
|
if (this.currentBlockHeight === 0) {
|
|
|
|
this.currentBlockHeight = blockHeightTip;
|
2019-07-21 17:59:47 +03:00
|
|
|
} else {
|
2020-10-18 21:47:47 +07:00
|
|
|
this.currentBlockHeight++;
|
|
|
|
logger.debug(`New block found (#${this.currentBlockHeight})!`);
|
2019-07-21 17:59:47 +03:00
|
|
|
}
|
|
|
|
|
2020-12-21 23:08:34 +07:00
|
|
|
const transactions: TransactionExtended[] = [];
|
2020-12-20 22:36:36 +07:00
|
|
|
|
2020-12-21 23:08:34 +07:00
|
|
|
const blockHash = await bitcoinApi.$getBlockHash(this.currentBlockHeight);
|
|
|
|
const block = await bitcoinApi.$getBlock(blockHash);
|
|
|
|
const txIds: string[] = await bitcoinApi.$getTxIdsForBlock(blockHash);
|
2020-03-03 15:11:14 +07:00
|
|
|
|
2020-10-18 21:47:47 +07:00
|
|
|
const mempool = memPool.getMempool();
|
2020-12-28 20:17:32 +07:00
|
|
|
let transactionsFound = 0;
|
2019-07-21 17:59:47 +03:00
|
|
|
|
2020-10-18 21:47:47 +07:00
|
|
|
for (let i = 0; i < txIds.length; i++) {
|
|
|
|
if (mempool[txIds[i]]) {
|
|
|
|
transactions.push(mempool[txIds[i]]);
|
2020-12-28 20:17:32 +07:00
|
|
|
transactionsFound++;
|
2021-01-24 23:56:51 +07:00
|
|
|
} else if (config.MEMPOOL.BACKEND === 'esplora' || memPool.isInSync() || i === 0) {
|
2020-12-22 06:04:31 +07:00
|
|
|
logger.debug(`Fetching block tx ${i} of ${txIds.length}`);
|
2021-01-24 02:51:22 +07:00
|
|
|
try {
|
|
|
|
const tx = await transactionUtils.$getTransactionExtended(txIds[i]);
|
2020-12-22 06:04:31 +07:00
|
|
|
transactions.push(tx);
|
2021-01-24 02:51:22 +07:00
|
|
|
} catch (e) {
|
|
|
|
logger.debug('Error fetching block tx: ' + e.message || e);
|
2021-01-24 23:56:51 +07:00
|
|
|
if (i === 0) {
|
|
|
|
throw new Error('Failed to fetch Coinbase transaction: ' + txIds[i]);
|
|
|
|
}
|
2020-02-23 19:16:50 +07:00
|
|
|
}
|
|
|
|
}
|
2020-10-18 21:47:47 +07:00
|
|
|
}
|
2020-02-23 19:16:50 +07:00
|
|
|
|
2021-03-18 23:47:40 +07:00
|
|
|
transactions.forEach((tx) => {
|
|
|
|
if (!tx.cpfpChecked) {
|
|
|
|
Common.setRelativesAndGetCpfpInfo(tx, mempool);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2020-12-28 20:17:32 +07:00
|
|
|
logger.debug(`${transactionsFound} of ${txIds.length} found in mempool. ${txIds.length - transactionsFound} not found.`);
|
2020-02-16 22:15:07 +07:00
|
|
|
|
2020-12-28 04:47:22 +07:00
|
|
|
const blockExtended: BlockExtended = Object.assign({}, block);
|
|
|
|
blockExtended.reward = transactions[0].vout.reduce((acc, curr) => acc + curr.value, 0);
|
2020-12-28 20:17:32 +07:00
|
|
|
blockExtended.coinbaseTx = transactionUtils.stripCoinbaseTransaction(transactions[0]);
|
2021-03-18 23:47:40 +07:00
|
|
|
transactions.shift();
|
|
|
|
transactions.sort((a, b) => b.effectiveFeePerVsize - a.effectiveFeePerVsize);
|
|
|
|
blockExtended.medianFee = transactions.length > 1 ? Common.median(transactions.map((tx) => tx.effectiveFeePerVsize)) : 0;
|
|
|
|
blockExtended.feeRange = transactions.length > 1 ? Common.getFeesInRange(transactions, 8) : [0, 0];
|
2020-09-21 19:41:12 +07:00
|
|
|
|
2020-10-18 21:47:47 +07:00
|
|
|
if (block.height % 2016 === 0) {
|
2021-07-23 14:35:04 +03:00
|
|
|
this.previousDifficultyRetarget = (block.difficulty - this.currentDifficulty) / this.currentDifficulty * 100;
|
2020-10-18 21:47:47 +07:00
|
|
|
this.lastDifficultyAdjustmentTime = block.timestamp;
|
2021-07-23 14:35:04 +03:00
|
|
|
this.currentDifficulty = block.difficulty;
|
2020-10-18 21:47:47 +07:00
|
|
|
}
|
2019-07-21 17:59:47 +03:00
|
|
|
|
2020-12-28 04:47:22 +07:00
|
|
|
this.blocks.push(blockExtended);
|
2020-12-30 01:47:07 +07:00
|
|
|
if (this.blocks.length > Blocks.INITIAL_BLOCK_AMOUNT * 4) {
|
|
|
|
this.blocks = this.blocks.slice(-Blocks.INITIAL_BLOCK_AMOUNT * 4);
|
2019-07-21 17:59:47 +03:00
|
|
|
}
|
|
|
|
|
2020-10-18 21:47:47 +07:00
|
|
|
if (this.newBlockCallbacks.length) {
|
2020-12-28 04:47:22 +07:00
|
|
|
this.newBlockCallbacks.forEach((cb) => cb(blockExtended, txIds, transactions));
|
2020-10-18 21:47:47 +07:00
|
|
|
}
|
2021-01-22 23:20:39 +07:00
|
|
|
if (memPool.isInSync()) {
|
|
|
|
diskCache.$saveCacheToDisk();
|
|
|
|
}
|
2019-08-29 01:17:31 +02:00
|
|
|
}
|
|
|
|
}
|
2020-05-24 22:45:45 +07:00
|
|
|
|
2020-09-21 19:41:12 +07:00
|
|
|
public getLastDifficultyAdjustmentTime(): number {
|
|
|
|
return this.lastDifficultyAdjustmentTime;
|
|
|
|
}
|
|
|
|
|
2021-07-23 14:35:04 +03:00
|
|
|
public getPreviousDifficultyRetarget(): number {
|
|
|
|
return this.previousDifficultyRetarget;
|
|
|
|
}
|
|
|
|
|
2020-12-21 23:08:34 +07:00
|
|
|
public getCurrentBlockHeight(): number {
|
|
|
|
return this.currentBlockHeight;
|
|
|
|
}
|
2019-07-21 17:59:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
export default new Blocks();
|