2019-07-21 16:59:47 +02:00
|
|
|
const config = require('../../mempool-config.json');
|
2020-02-16 16:15:07 +01:00
|
|
|
import bitcoinApi from './bitcoin/electrs-api';
|
2020-02-23 13:16:50 +01:00
|
|
|
import memPool from './mempool';
|
|
|
|
import { Block, TransactionExtended } from '../interfaces';
|
2019-07-21 16:59:47 +02:00
|
|
|
|
|
|
|
class Blocks {
|
2020-02-16 16:15:07 +01:00
|
|
|
private blocks: Block[] = [];
|
2019-08-29 01:17:31 +02:00
|
|
|
private currentBlockHeight = 0;
|
2020-02-16 16:15:07 +01:00
|
|
|
private newBlockCallback: Function = () => {};
|
2019-08-29 01:17:31 +02:00
|
|
|
|
2020-02-16 16:15:07 +01:00
|
|
|
constructor() { }
|
2019-07-21 16:59:47 +02:00
|
|
|
|
2020-02-16 16:15:07 +01:00
|
|
|
public getBlocks(): Block[] {
|
2019-07-21 16:59:47 +02:00
|
|
|
return this.blocks;
|
|
|
|
}
|
|
|
|
|
2020-02-29 15:52:04 +01:00
|
|
|
public setBlocks(blocks: Block[]) {
|
|
|
|
this.blocks = blocks;
|
|
|
|
}
|
|
|
|
|
2020-02-16 16:15:07 +01:00
|
|
|
public setNewBlockCallback(fn: Function) {
|
|
|
|
this.newBlockCallback = fn;
|
2019-07-21 16:59:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public async updateBlocks() {
|
|
|
|
try {
|
2020-02-16 16:15:07 +01:00
|
|
|
const blockHeightTip = await bitcoinApi.getBlockHeightTip();
|
2019-07-21 16:59:47 +02:00
|
|
|
|
|
|
|
if (this.blocks.length === 0) {
|
2020-02-16 16:15:07 +01:00
|
|
|
this.currentBlockHeight = blockHeightTip - config.INITIAL_BLOCK_AMOUNT;
|
2019-07-21 16:59:47 +02:00
|
|
|
} else {
|
2019-08-29 01:17:31 +02:00
|
|
|
this.currentBlockHeight = this.blocks[this.blocks.length - 1].height;
|
2019-07-21 16:59:47 +02:00
|
|
|
}
|
|
|
|
|
2020-02-16 16:15:07 +01:00
|
|
|
while (this.currentBlockHeight < blockHeightTip) {
|
|
|
|
if (this.currentBlockHeight === 0) {
|
|
|
|
this.currentBlockHeight = blockHeightTip;
|
2019-07-21 16:59:47 +02:00
|
|
|
} else {
|
2020-02-16 16:15:07 +01:00
|
|
|
this.currentBlockHeight++;
|
|
|
|
console.log(`New block found (#${this.currentBlockHeight})!`);
|
2019-07-21 16:59:47 +02:00
|
|
|
}
|
|
|
|
|
2020-02-16 16:15:07 +01:00
|
|
|
const blockHash = await bitcoinApi.getBlockHash(this.currentBlockHeight);
|
|
|
|
const block = await bitcoinApi.getBlock(blockHash);
|
|
|
|
const txIds = await bitcoinApi.getTxIdsForBlock(blockHash);
|
|
|
|
|
2020-02-23 13:16:50 +01:00
|
|
|
const mempool = memPool.getMempool();
|
|
|
|
let found = 0;
|
|
|
|
let notFound = 0;
|
|
|
|
|
|
|
|
const transactions: TransactionExtended[] = [];
|
|
|
|
|
|
|
|
for (let i = 1; 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++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-23 21:42:29 +01:00
|
|
|
console.log(`${found} of ${txIds.length} found in mempool. ${notFound} not found.`);
|
|
|
|
|
2020-02-23 13:16:50 +01:00
|
|
|
transactions.sort((a, b) => b.feePerVsize - a.feePerVsize);
|
2020-02-24 04:58:52 +01:00
|
|
|
block.medianFee = transactions.length ? this.median(transactions.map((tx) => tx.feePerVsize)) : 0;
|
2020-02-24 16:51:27 +01:00
|
|
|
block.feeRange = transactions.length ? this.getFeesInRange(transactions, 8) : [0, 0];
|
2020-02-16 16:15:07 +01:00
|
|
|
|
2019-07-21 16:59:47 +02:00
|
|
|
this.blocks.push(block);
|
|
|
|
if (this.blocks.length > config.KEEP_BLOCK_AMOUNT) {
|
|
|
|
this.blocks.shift();
|
|
|
|
}
|
|
|
|
|
2020-02-23 13:16:50 +01:00
|
|
|
this.newBlockCallback(block, txIds, transactions);
|
2019-07-21 16:59:47 +02:00
|
|
|
}
|
|
|
|
|
2020-02-16 16:15:07 +01:00
|
|
|
} catch (err) {
|
|
|
|
console.log('updateBlocks error', err);
|
2019-08-29 01:17:31 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-21 16:59:47 +02:00
|
|
|
private median(numbers: number[]) {
|
|
|
|
let medianNr = 0;
|
|
|
|
const numsLen = numbers.length;
|
|
|
|
numbers.sort();
|
|
|
|
if (numsLen % 2 === 0) {
|
|
|
|
medianNr = (numbers[numsLen / 2 - 1] + numbers[numsLen / 2]) / 2;
|
|
|
|
} else {
|
|
|
|
medianNr = numbers[(numsLen - 1) / 2];
|
|
|
|
}
|
|
|
|
return medianNr;
|
|
|
|
}
|
2020-02-16 16:15:07 +01:00
|
|
|
|
|
|
|
private getFeesInRange(transactions: any[], rangeLength: number) {
|
|
|
|
const arr = [transactions[transactions.length - 1].feePerVsize];
|
|
|
|
const chunk = 1 / (rangeLength - 1);
|
|
|
|
let itemsToAdd = rangeLength - 2;
|
|
|
|
|
|
|
|
while (itemsToAdd > 0) {
|
|
|
|
arr.push(transactions[Math.floor(transactions.length * chunk * itemsToAdd)].feePerVsize);
|
|
|
|
itemsToAdd--;
|
|
|
|
}
|
|
|
|
|
|
|
|
arr.push(transactions[0].feePerVsize);
|
|
|
|
return arr;
|
|
|
|
}
|
2019-07-21 16:59:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
export default new Blocks();
|