mirror of
https://github.com/mempool/mempool.git
synced 2025-02-24 14:50:52 +01:00
Merge branch 'master' into nymkappa/bugfix/price
This commit is contained in:
commit
fca813147d
17 changed files with 134 additions and 98 deletions
|
@ -6,7 +6,7 @@ import websocketHandler from '../websocket-handler';
|
||||||
import mempool from '../mempool';
|
import mempool from '../mempool';
|
||||||
import feeApi from '../fee-api';
|
import feeApi from '../fee-api';
|
||||||
import mempoolBlocks from '../mempool-blocks';
|
import mempoolBlocks from '../mempool-blocks';
|
||||||
import bitcoinApi from './bitcoin-api-factory';
|
import bitcoinApi, { bitcoinCoreApi } from './bitcoin-api-factory';
|
||||||
import { Common } from '../common';
|
import { Common } from '../common';
|
||||||
import backendInfo from '../backend-info';
|
import backendInfo from '../backend-info';
|
||||||
import transactionUtils from '../transaction-utils';
|
import transactionUtils from '../transaction-utils';
|
||||||
|
@ -469,7 +469,7 @@ class BitcoinRoutes {
|
||||||
returnBlocks.push(localBlock);
|
returnBlocks.push(localBlock);
|
||||||
nextHash = localBlock.previousblockhash;
|
nextHash = localBlock.previousblockhash;
|
||||||
} else {
|
} else {
|
||||||
const block = await bitcoinApi.$getBlock(nextHash);
|
const block = await bitcoinCoreApi.$getBlock(nextHash);
|
||||||
returnBlocks.push(block);
|
returnBlocks.push(block);
|
||||||
nextHash = block.previousblockhash;
|
nextHash = block.previousblockhash;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import config from '../config';
|
import config from '../config';
|
||||||
import bitcoinApi from './bitcoin/bitcoin-api-factory';
|
import bitcoinApi, { bitcoinCoreApi } from './bitcoin/bitcoin-api-factory';
|
||||||
import logger from '../logger';
|
import logger from '../logger';
|
||||||
import memPool from './mempool';
|
import memPool from './mempool';
|
||||||
import { BlockExtended, BlockExtension, BlockSummary, PoolTag, TransactionExtended, TransactionStripped, TransactionMinerInfo } from '../mempool.interfaces';
|
import { BlockExtended, BlockExtension, BlockSummary, PoolTag, TransactionExtended, TransactionStripped, TransactionMinerInfo } from '../mempool.interfaces';
|
||||||
|
@ -484,7 +484,7 @@ class Blocks {
|
||||||
loadingIndicators.setProgress('block-indexing', progress, false);
|
loadingIndicators.setProgress('block-indexing', progress, false);
|
||||||
}
|
}
|
||||||
const blockHash = await bitcoinApi.$getBlockHash(blockHeight);
|
const blockHash = await bitcoinApi.$getBlockHash(blockHeight);
|
||||||
const block: IEsploraApi.Block = await bitcoinApi.$getBlock(blockHash);
|
const block: IEsploraApi.Block = await bitcoinCoreApi.$getBlock(blockHash);
|
||||||
const transactions = await this.$getTransactionsExtended(blockHash, block.height, true, true);
|
const transactions = await this.$getTransactionsExtended(blockHash, block.height, true, true);
|
||||||
const blockExtended = await this.$getBlockExtended(block, transactions);
|
const blockExtended = await this.$getBlockExtended(block, transactions);
|
||||||
|
|
||||||
|
@ -532,13 +532,13 @@ class Blocks {
|
||||||
if (blockchainInfo.blocks === blockchainInfo.headers) {
|
if (blockchainInfo.blocks === blockchainInfo.headers) {
|
||||||
const heightDiff = blockHeightTip % 2016;
|
const heightDiff = blockHeightTip % 2016;
|
||||||
const blockHash = await bitcoinApi.$getBlockHash(blockHeightTip - heightDiff);
|
const blockHash = await bitcoinApi.$getBlockHash(blockHeightTip - heightDiff);
|
||||||
const block: IEsploraApi.Block = await bitcoinApi.$getBlock(blockHash);
|
const block: IEsploraApi.Block = await bitcoinCoreApi.$getBlock(blockHash);
|
||||||
this.lastDifficultyAdjustmentTime = block.timestamp;
|
this.lastDifficultyAdjustmentTime = block.timestamp;
|
||||||
this.currentDifficulty = block.difficulty;
|
this.currentDifficulty = block.difficulty;
|
||||||
|
|
||||||
if (blockHeightTip >= 2016) {
|
if (blockHeightTip >= 2016) {
|
||||||
const previousPeriodBlockHash = await bitcoinApi.$getBlockHash(blockHeightTip - heightDiff - 2016);
|
const previousPeriodBlockHash = await bitcoinApi.$getBlockHash(blockHeightTip - heightDiff - 2016);
|
||||||
const previousPeriodBlock: IEsploraApi.Block = await bitcoinApi.$getBlock(previousPeriodBlockHash);
|
const previousPeriodBlock: IEsploraApi.Block = await bitcoinCoreApi.$getBlock(previousPeriodBlockHash);
|
||||||
this.previousDifficultyRetarget = (block.difficulty - previousPeriodBlock.difficulty) / previousPeriodBlock.difficulty * 100;
|
this.previousDifficultyRetarget = (block.difficulty - previousPeriodBlock.difficulty) / previousPeriodBlock.difficulty * 100;
|
||||||
logger.debug(`Initial difficulty adjustment data set.`);
|
logger.debug(`Initial difficulty adjustment data set.`);
|
||||||
}
|
}
|
||||||
|
@ -662,7 +662,7 @@ class Blocks {
|
||||||
}
|
}
|
||||||
|
|
||||||
const blockHash = await bitcoinApi.$getBlockHash(height);
|
const blockHash = await bitcoinApi.$getBlockHash(height);
|
||||||
const block: IEsploraApi.Block = await bitcoinApi.$getBlock(blockHash);
|
const block: IEsploraApi.Block = await bitcoinCoreApi.$getBlock(blockHash);
|
||||||
const transactions = await this.$getTransactionsExtended(blockHash, block.height, true);
|
const transactions = await this.$getTransactionsExtended(blockHash, block.height, true);
|
||||||
const blockExtended = await this.$getBlockExtended(block, transactions);
|
const blockExtended = await this.$getBlockExtended(block, transactions);
|
||||||
|
|
||||||
|
@ -685,11 +685,11 @@ class Blocks {
|
||||||
|
|
||||||
// Not Bitcoin network, return the block as it from the bitcoin backend
|
// Not Bitcoin network, return the block as it from the bitcoin backend
|
||||||
if (['mainnet', 'testnet', 'signet'].includes(config.MEMPOOL.NETWORK) === false) {
|
if (['mainnet', 'testnet', 'signet'].includes(config.MEMPOOL.NETWORK) === false) {
|
||||||
return await bitcoinApi.$getBlock(hash);
|
return await bitcoinCoreApi.$getBlock(hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bitcoin network, add our custom data on top
|
// Bitcoin network, add our custom data on top
|
||||||
const block: IEsploraApi.Block = await bitcoinApi.$getBlock(hash);
|
const block: IEsploraApi.Block = await bitcoinCoreApi.$getBlock(hash);
|
||||||
return await this.$indexBlock(block.height);
|
return await this.$indexBlock(block.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ import cpfpRepository from '../repositories/CpfpRepository';
|
||||||
import { RowDataPacket } from 'mysql2';
|
import { RowDataPacket } from 'mysql2';
|
||||||
|
|
||||||
class DatabaseMigration {
|
class DatabaseMigration {
|
||||||
private static currentVersion = 57;
|
private static currentVersion = 58;
|
||||||
private queryTimeout = 3600_000;
|
private queryTimeout = 3600_000;
|
||||||
private statisticsAddedIndexed = false;
|
private statisticsAddedIndexed = false;
|
||||||
private uniqueLogs: string[] = [];
|
private uniqueLogs: string[] = [];
|
||||||
|
@ -505,6 +505,11 @@ class DatabaseMigration {
|
||||||
await this.$executeQuery(`ALTER TABLE nodes MODIFY updated_at datetime NULL`);
|
await this.$executeQuery(`ALTER TABLE nodes MODIFY updated_at datetime NULL`);
|
||||||
await this.updateToSchemaVersion(57);
|
await this.updateToSchemaVersion(57);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (databaseSchemaVersion < 58) {
|
||||||
|
// We only run some migration queries for this version
|
||||||
|
await this.updateToSchemaVersion(58);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -632,6 +637,11 @@ class DatabaseMigration {
|
||||||
queries.push(`INSERT INTO state(name, number, string) VALUES ('last_weekly_hashrates_indexing', 0, NULL)`);
|
queries.push(`INSERT INTO state(name, number, string) VALUES ('last_weekly_hashrates_indexing', 0, NULL)`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (version < 58) {
|
||||||
|
queries.push(`DELETE FROM state WHERE name = 'last_hashrates_indexing'`);
|
||||||
|
queries.push(`DELETE FROM state WHERE name = 'last_weekly_hashrates_indexing'`);
|
||||||
|
}
|
||||||
|
|
||||||
return queries;
|
return queries;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1023,6 +1033,7 @@ class DatabaseMigration {
|
||||||
|
|
||||||
await this.$executeQuery(`TRUNCATE blocks`);
|
await this.$executeQuery(`TRUNCATE blocks`);
|
||||||
await this.$executeQuery(`TRUNCATE hashrates`);
|
await this.$executeQuery(`TRUNCATE hashrates`);
|
||||||
|
await this.$executeQuery(`TRUNCATE difficulty_adjustments`);
|
||||||
await this.$executeQuery('DELETE FROM `pools`');
|
await this.$executeQuery('DELETE FROM `pools`');
|
||||||
await this.$executeQuery('ALTER TABLE pools AUTO_INCREMENT = 1');
|
await this.$executeQuery('ALTER TABLE pools AUTO_INCREMENT = 1');
|
||||||
await this.$executeQuery(`UPDATE state SET string = NULL WHERE name = 'pools_json_sha'`);
|
await this.$executeQuery(`UPDATE state SET string = NULL WHERE name = 'pools_json_sha'`);
|
||||||
|
|
|
@ -97,14 +97,14 @@ class MempoolBlocks {
|
||||||
blockSize += tx.size;
|
blockSize += tx.size;
|
||||||
transactions.push(tx);
|
transactions.push(tx);
|
||||||
} else {
|
} else {
|
||||||
mempoolBlocks.push(this.dataToMempoolBlocks(transactions, blockSize, blockWeight, mempoolBlocks.length));
|
mempoolBlocks.push(this.dataToMempoolBlocks(transactions, mempoolBlocks.length));
|
||||||
blockWeight = tx.weight;
|
blockWeight = tx.weight;
|
||||||
blockSize = tx.size;
|
blockSize = tx.size;
|
||||||
transactions = [tx];
|
transactions = [tx];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (transactions.length) {
|
if (transactions.length) {
|
||||||
mempoolBlocks.push(this.dataToMempoolBlocks(transactions, blockSize, blockWeight, mempoolBlocks.length));
|
mempoolBlocks.push(this.dataToMempoolBlocks(transactions, mempoolBlocks.length));
|
||||||
}
|
}
|
||||||
|
|
||||||
return mempoolBlocks;
|
return mempoolBlocks;
|
||||||
|
@ -281,7 +281,7 @@ class MempoolBlocks {
|
||||||
const mempoolBlocks = blocks.map((transactions, blockIndex) => {
|
const mempoolBlocks = blocks.map((transactions, blockIndex) => {
|
||||||
return this.dataToMempoolBlocks(transactions.map(tx => {
|
return this.dataToMempoolBlocks(transactions.map(tx => {
|
||||||
return mempool[tx.txid] || null;
|
return mempool[tx.txid] || null;
|
||||||
}).filter(tx => !!tx), undefined, undefined, blockIndex);
|
}).filter(tx => !!tx), blockIndex);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (saveResults) {
|
if (saveResults) {
|
||||||
|
@ -293,18 +293,17 @@ class MempoolBlocks {
|
||||||
return mempoolBlocks;
|
return mempoolBlocks;
|
||||||
}
|
}
|
||||||
|
|
||||||
private dataToMempoolBlocks(transactions: TransactionExtended[],
|
private dataToMempoolBlocks(transactions: TransactionExtended[], blocksIndex: number): MempoolBlockWithTransactions {
|
||||||
blockSize: number | undefined, blockWeight: number | undefined, blocksIndex: number): MempoolBlockWithTransactions {
|
let totalSize = 0;
|
||||||
let totalSize = blockSize || 0;
|
let totalWeight = 0;
|
||||||
let totalWeight = blockWeight || 0;
|
const fitTransactions: TransactionExtended[] = [];
|
||||||
if (blockSize === undefined && blockWeight === undefined) {
|
transactions.forEach(tx => {
|
||||||
totalSize = 0;
|
totalSize += tx.size;
|
||||||
totalWeight = 0;
|
totalWeight += tx.weight;
|
||||||
transactions.forEach(tx => {
|
if ((totalWeight + tx.weight) <= config.MEMPOOL.BLOCK_WEIGHT_UNITS * 1.2) {
|
||||||
totalSize += tx.size;
|
fitTransactions.push(tx);
|
||||||
totalWeight += tx.weight;
|
}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
let rangeLength = 4;
|
let rangeLength = 4;
|
||||||
if (blocksIndex === 0) {
|
if (blocksIndex === 0) {
|
||||||
rangeLength = 8;
|
rangeLength = 8;
|
||||||
|
@ -322,7 +321,7 @@ class MempoolBlocks {
|
||||||
medianFee: Common.percentile(transactions.map((tx) => tx.effectiveFeePerVsize), config.MEMPOOL.RECOMMENDED_FEE_PERCENTILE),
|
medianFee: Common.percentile(transactions.map((tx) => tx.effectiveFeePerVsize), config.MEMPOOL.RECOMMENDED_FEE_PERCENTILE),
|
||||||
feeRange: Common.getFeesInRange(transactions, rangeLength),
|
feeRange: Common.getFeesInRange(transactions, rangeLength),
|
||||||
transactionIds: transactions.map((tx) => tx.txid),
|
transactionIds: transactions.map((tx) => tx.txid),
|
||||||
transactions: transactions.map((tx) => Common.stripTransaction(tx)),
|
transactions: fitTransactions.map((tx) => Common.stripTransaction(tx)),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,14 +11,13 @@ import DifficultyAdjustmentsRepository from '../../repositories/DifficultyAdjust
|
||||||
import config from '../../config';
|
import config from '../../config';
|
||||||
import BlocksAuditsRepository from '../../repositories/BlocksAuditsRepository';
|
import BlocksAuditsRepository from '../../repositories/BlocksAuditsRepository';
|
||||||
import PricesRepository from '../../repositories/PricesRepository';
|
import PricesRepository from '../../repositories/PricesRepository';
|
||||||
import bitcoinApiFactory from '../bitcoin/bitcoin-api-factory';
|
import { bitcoinCoreApi } from '../bitcoin/bitcoin-api-factory';
|
||||||
import { IEsploraApi } from '../bitcoin/esplora-api.interface';
|
import { IEsploraApi } from '../bitcoin/esplora-api.interface';
|
||||||
|
|
||||||
class Mining {
|
class Mining {
|
||||||
blocksPriceIndexingRunning = false;
|
private blocksPriceIndexingRunning = false;
|
||||||
|
public lastHashrateIndexingDate: number | null = null;
|
||||||
constructor() {
|
public lastWeeklyHashrateIndexingDate: number | null = null;
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get historical block predictions match rate
|
* Get historical block predictions match rate
|
||||||
|
@ -178,20 +177,21 @@ class Mining {
|
||||||
*/
|
*/
|
||||||
public async $generatePoolHashrateHistory(): Promise<void> {
|
public async $generatePoolHashrateHistory(): Promise<void> {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const lastestRunDate = await HashratesRepository.$getLatestRun('last_weekly_hashrates_indexing');
|
|
||||||
|
|
||||||
// Run only if:
|
// Run only if:
|
||||||
// * lastestRunDate is set to 0 (node backend restart, reorg)
|
// * this.lastWeeklyHashrateIndexingDate is set to null (node backend restart, reorg)
|
||||||
// * we started a new week (around Monday midnight)
|
// * we started a new week (around Monday midnight)
|
||||||
const runIndexing = lastestRunDate === 0 || now.getUTCDay() === 1 && lastestRunDate !== now.getUTCDate();
|
const runIndexing = this.lastWeeklyHashrateIndexingDate === null ||
|
||||||
|
now.getUTCDay() === 1 && this.lastWeeklyHashrateIndexingDate !== now.getUTCDate();
|
||||||
if (!runIndexing) {
|
if (!runIndexing) {
|
||||||
|
logger.debug(`Pool hashrate history indexing is up to date, nothing to do`, logger.tags.mining);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const oldestConsecutiveBlockTimestamp = 1000 * (await BlocksRepository.$getOldestConsecutiveBlock()).timestamp;
|
const oldestConsecutiveBlockTimestamp = 1000 * (await BlocksRepository.$getOldestConsecutiveBlock()).timestamp;
|
||||||
|
|
||||||
const genesisBlock: IEsploraApi.Block = await bitcoinApiFactory.$getBlock(await bitcoinClient.getBlockHash(0));
|
const genesisBlock: IEsploraApi.Block = await bitcoinCoreApi.$getBlock(await bitcoinClient.getBlockHash(0));
|
||||||
const genesisTimestamp = genesisBlock.timestamp * 1000;
|
const genesisTimestamp = genesisBlock.timestamp * 1000;
|
||||||
|
|
||||||
const indexedTimestamp = await HashratesRepository.$getWeeklyHashrateTimestamps();
|
const indexedTimestamp = await HashratesRepository.$getWeeklyHashrateTimestamps();
|
||||||
|
@ -266,7 +266,7 @@ class Mining {
|
||||||
++indexedThisRun;
|
++indexedThisRun;
|
||||||
++totalIndexed;
|
++totalIndexed;
|
||||||
}
|
}
|
||||||
await HashratesRepository.$setLatestRun('last_weekly_hashrates_indexing', new Date().getUTCDate());
|
this.lastWeeklyHashrateIndexingDate = new Date().getUTCDate();
|
||||||
if (newlyIndexed > 0) {
|
if (newlyIndexed > 0) {
|
||||||
logger.notice(`Weekly mining pools hashrates indexing completed: indexed ${newlyIndexed}`, logger.tags.mining);
|
logger.notice(`Weekly mining pools hashrates indexing completed: indexed ${newlyIndexed}`, logger.tags.mining);
|
||||||
} else {
|
} else {
|
||||||
|
@ -285,16 +285,16 @@ class Mining {
|
||||||
*/
|
*/
|
||||||
public async $generateNetworkHashrateHistory(): Promise<void> {
|
public async $generateNetworkHashrateHistory(): Promise<void> {
|
||||||
// We only run this once a day around midnight
|
// We only run this once a day around midnight
|
||||||
const latestRunDate = await HashratesRepository.$getLatestRun('last_hashrates_indexing');
|
const today = new Date().getUTCDate();
|
||||||
const now = new Date().getUTCDate();
|
if (today === this.lastHashrateIndexingDate) {
|
||||||
if (now === latestRunDate) {
|
logger.debug(`Network hashrate history indexing is up to date, nothing to do`, logger.tags.mining);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const oldestConsecutiveBlockTimestamp = 1000 * (await BlocksRepository.$getOldestConsecutiveBlock()).timestamp;
|
const oldestConsecutiveBlockTimestamp = 1000 * (await BlocksRepository.$getOldestConsecutiveBlock()).timestamp;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const genesisBlock: IEsploraApi.Block = await bitcoinApiFactory.$getBlock(await bitcoinClient.getBlockHash(0));
|
const genesisBlock: IEsploraApi.Block = await bitcoinCoreApi.$getBlock(await bitcoinClient.getBlockHash(0));
|
||||||
const genesisTimestamp = genesisBlock.timestamp * 1000;
|
const genesisTimestamp = genesisBlock.timestamp * 1000;
|
||||||
const indexedTimestamp = (await HashratesRepository.$getRawNetworkDailyHashrate(null)).map(hashrate => hashrate.timestamp);
|
const indexedTimestamp = (await HashratesRepository.$getRawNetworkDailyHashrate(null)).map(hashrate => hashrate.timestamp);
|
||||||
const lastMidnight = this.getDateMidnight(new Date());
|
const lastMidnight = this.getDateMidnight(new Date());
|
||||||
|
@ -371,7 +371,7 @@ class Mining {
|
||||||
newlyIndexed += hashrates.length;
|
newlyIndexed += hashrates.length;
|
||||||
await HashratesRepository.$saveHashrates(hashrates);
|
await HashratesRepository.$saveHashrates(hashrates);
|
||||||
|
|
||||||
await HashratesRepository.$setLatestRun('last_hashrates_indexing', new Date().getUTCDate());
|
this.lastHashrateIndexingDate = new Date().getUTCDate();
|
||||||
if (newlyIndexed > 0) {
|
if (newlyIndexed > 0) {
|
||||||
logger.notice(`Daily network hashrate indexing completed: indexed ${newlyIndexed} days`, logger.tags.mining);
|
logger.notice(`Daily network hashrate indexing completed: indexed ${newlyIndexed} days`, logger.tags.mining);
|
||||||
} else {
|
} else {
|
||||||
|
@ -396,7 +396,7 @@ class Mining {
|
||||||
}
|
}
|
||||||
|
|
||||||
const blocks: any = await BlocksRepository.$getBlocksDifficulty();
|
const blocks: any = await BlocksRepository.$getBlocksDifficulty();
|
||||||
const genesisBlock: IEsploraApi.Block = await bitcoinApiFactory.$getBlock(await bitcoinClient.getBlockHash(0));
|
const genesisBlock: IEsploraApi.Block = await bitcoinCoreApi.$getBlock(await bitcoinClient.getBlockHash(0));
|
||||||
let currentDifficulty = genesisBlock.difficulty;
|
let currentDifficulty = genesisBlock.difficulty;
|
||||||
let totalIndexed = 0;
|
let totalIndexed = 0;
|
||||||
|
|
||||||
|
|
|
@ -87,9 +87,6 @@ class Server {
|
||||||
await databaseMigration.$blocksReindexingTruncate();
|
await databaseMigration.$blocksReindexingTruncate();
|
||||||
}
|
}
|
||||||
await databaseMigration.$initializeOrMigrateDatabase();
|
await databaseMigration.$initializeOrMigrateDatabase();
|
||||||
if (Common.indexingEnabled()) {
|
|
||||||
await indexer.$resetHashratesIndexingState();
|
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new Error(e instanceof Error ? e.message : 'Error');
|
throw new Error(e instanceof Error ? e.message : 'Error');
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@ import blocks from './api/blocks';
|
||||||
import mempool from './api/mempool';
|
import mempool from './api/mempool';
|
||||||
import mining from './api/mining/mining';
|
import mining from './api/mining/mining';
|
||||||
import logger from './logger';
|
import logger from './logger';
|
||||||
import HashratesRepository from './repositories/HashratesRepository';
|
|
||||||
import bitcoinClient from './api/bitcoin/bitcoin-client';
|
import bitcoinClient from './api/bitcoin/bitcoin-client';
|
||||||
import priceUpdater from './tasks/price-updater';
|
import priceUpdater from './tasks/price-updater';
|
||||||
import PricesRepository from './repositories/PricesRepository';
|
import PricesRepository from './repositories/PricesRepository';
|
||||||
|
@ -131,7 +130,6 @@ class Indexer {
|
||||||
|
|
||||||
this.runSingleTask('blocksPrices');
|
this.runSingleTask('blocksPrices');
|
||||||
await mining.$indexDifficultyAdjustments();
|
await mining.$indexDifficultyAdjustments();
|
||||||
await this.$resetHashratesIndexingState(); // TODO - Remove this as it's not efficient
|
|
||||||
await mining.$generateNetworkHashrateHistory();
|
await mining.$generateNetworkHashrateHistory();
|
||||||
await mining.$generatePoolHashrateHistory();
|
await mining.$generatePoolHashrateHistory();
|
||||||
await blocks.$generateBlocksSummariesDatabase();
|
await blocks.$generateBlocksSummariesDatabase();
|
||||||
|
@ -150,16 +148,6 @@ class Indexer {
|
||||||
logger.debug(`Indexing completed. Next run planned at ${new Date(new Date().getTime() + runEvery).toUTCString()}`);
|
logger.debug(`Indexing completed. Next run planned at ${new Date(new Date().getTime() + runEvery).toUTCString()}`);
|
||||||
setTimeout(() => this.reindex(), runEvery);
|
setTimeout(() => this.reindex(), runEvery);
|
||||||
}
|
}
|
||||||
|
|
||||||
async $resetHashratesIndexingState(): Promise<void> {
|
|
||||||
try {
|
|
||||||
await HashratesRepository.$setLatestRun('last_hashrates_indexing', 0);
|
|
||||||
await HashratesRepository.$setLatestRun('last_weekly_hashrates_indexing', 0);
|
|
||||||
} catch (e) {
|
|
||||||
logger.err(`Cannot reset hashrate indexing timestamps. Reason: ` + (e instanceof Error ? e.message : e));
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default new Indexer();
|
export default new Indexer();
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { Common } from '../api/common';
|
import { Common } from '../api/common';
|
||||||
import config from '../config';
|
|
||||||
import DB from '../database';
|
import DB from '../database';
|
||||||
import logger from '../logger';
|
import logger from '../logger';
|
||||||
import { IndexedDifficultyAdjustment } from '../mempool.interfaces';
|
import { IndexedDifficultyAdjustment } from '../mempool.interfaces';
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { escape } from 'mysql2';
|
import { escape } from 'mysql2';
|
||||||
import { Common } from '../api/common';
|
import { Common } from '../api/common';
|
||||||
|
import mining from '../api/mining/mining';
|
||||||
import DB from '../database';
|
import DB from '../database';
|
||||||
import logger from '../logger';
|
import logger from '../logger';
|
||||||
import PoolsRepository from './PoolsRepository';
|
import PoolsRepository from './PoolsRepository';
|
||||||
|
@ -177,20 +178,6 @@ class HashratesRepository {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Set latest run timestamp
|
|
||||||
*/
|
|
||||||
public async $setLatestRun(key: string, val: number) {
|
|
||||||
const query = `UPDATE state SET number = ? WHERE name = ?`;
|
|
||||||
|
|
||||||
try {
|
|
||||||
await DB.query(query, [val, key]);
|
|
||||||
} catch (e) {
|
|
||||||
logger.err(`Cannot set last indexing run for ${key}. Reason: ` + (e instanceof Error ? e.message : e));
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get latest run timestamp
|
* Get latest run timestamp
|
||||||
*/
|
*/
|
||||||
|
@ -222,8 +209,8 @@ class HashratesRepository {
|
||||||
await DB.query(`DELETE FROM hashrates WHERE hashrate_timestamp = ?`, [row.timestamp]);
|
await DB.query(`DELETE FROM hashrates WHERE hashrate_timestamp = ?`, [row.timestamp]);
|
||||||
}
|
}
|
||||||
// Re-run the hashrate indexing to fill up missing data
|
// Re-run the hashrate indexing to fill up missing data
|
||||||
await this.$setLatestRun('last_hashrates_indexing', 0);
|
mining.lastHashrateIndexingDate = null;
|
||||||
await this.$setLatestRun('last_weekly_hashrates_indexing', 0);
|
mining.lastWeeklyHashrateIndexingDate = null;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.err('Cannot delete latest hashrates data points. Reason: ' + (e instanceof Error ? e.message : e));
|
logger.err('Cannot delete latest hashrates data points. Reason: ' + (e instanceof Error ? e.message : e));
|
||||||
}
|
}
|
||||||
|
@ -238,8 +225,8 @@ class HashratesRepository {
|
||||||
try {
|
try {
|
||||||
await DB.query(`DELETE FROM hashrates WHERE hashrate_timestamp >= FROM_UNIXTIME(?)`, [timestamp]);
|
await DB.query(`DELETE FROM hashrates WHERE hashrate_timestamp >= FROM_UNIXTIME(?)`, [timestamp]);
|
||||||
// Re-run the hashrate indexing to fill up missing data
|
// Re-run the hashrate indexing to fill up missing data
|
||||||
await this.$setLatestRun('last_hashrates_indexing', 0);
|
mining.lastHashrateIndexingDate = null;
|
||||||
await this.$setLatestRun('last_weekly_hashrates_indexing', 0);
|
mining.lastWeeklyHashrateIndexingDate = null;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.err('Cannot delete latest hashrates data points. Reason: ' + (e instanceof Error ? e.message : e));
|
logger.err('Cannot delete latest hashrates data points. Reason: ' + (e instanceof Error ? e.message : e));
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="td-width" i18n="dashboard.latest-transactions.amount">Amount</td>
|
<td class="td-width" i18n="dashboard.latest-transactions.amount">Amount</td>
|
||||||
<td><app-amount [blockConversion]="blockConversion" [satoshis]="value"></app-amount></td>
|
<td><app-amount [blockConversion]="blockConversion" [satoshis]="value" [noFiat]="true"></app-amount></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="td-width" i18n="transaction.fee|Transaction fee">Fee</td>
|
<td class="td-width" i18n="transaction.fee|Transaction fee">Fee</td>
|
||||||
|
|
|
@ -107,7 +107,13 @@ export class SearchFormComponent implements OnInit {
|
||||||
}))),
|
}))),
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
tap((result: any[]) => {
|
map((result: any[]) => {
|
||||||
|
if (this.network === 'bisq') {
|
||||||
|
result[0] = result[0].map((address: string) => 'B' + address);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}),
|
||||||
|
tap(() => {
|
||||||
this.isTypeaheading$.next(false);
|
this.isTypeaheading$.next(false);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
@ -126,7 +132,7 @@ export class SearchFormComponent implements OnInit {
|
||||||
]
|
]
|
||||||
).pipe(
|
).pipe(
|
||||||
map((latestData) => {
|
map((latestData) => {
|
||||||
const searchText = latestData[0];
|
let searchText = latestData[0];
|
||||||
if (!searchText.length) {
|
if (!searchText.length) {
|
||||||
return {
|
return {
|
||||||
searchText: '',
|
searchText: '',
|
||||||
|
@ -144,15 +150,15 @@ export class SearchFormComponent implements OnInit {
|
||||||
const addressPrefixSearchResults = result[0];
|
const addressPrefixSearchResults = result[0];
|
||||||
const lightningResults = result[1];
|
const lightningResults = result[1];
|
||||||
|
|
||||||
if (this.network === 'bisq') {
|
|
||||||
return searchText.map((address: string) => 'B' + address);
|
|
||||||
}
|
|
||||||
|
|
||||||
const matchesBlockHeight = this.regexBlockheight.test(searchText);
|
const matchesBlockHeight = this.regexBlockheight.test(searchText);
|
||||||
const matchesTxId = this.regexTransaction.test(searchText) && !this.regexBlockhash.test(searchText);
|
const matchesTxId = this.regexTransaction.test(searchText) && !this.regexBlockhash.test(searchText);
|
||||||
const matchesBlockHash = this.regexBlockhash.test(searchText);
|
const matchesBlockHash = this.regexBlockhash.test(searchText);
|
||||||
const matchesAddress = this.regexAddress.test(searchText);
|
const matchesAddress = this.regexAddress.test(searchText);
|
||||||
|
|
||||||
|
if (matchesAddress && this.network === 'bisq') {
|
||||||
|
searchText = 'B' + searchText;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
searchText: searchText,
|
searchText: searchText,
|
||||||
hashQuickMatch: +(matchesBlockHeight || matchesBlockHash || matchesTxId || matchesAddress),
|
hashQuickMatch: +(matchesBlockHeight || matchesBlockHash || matchesTxId || matchesAddress),
|
||||||
|
|
|
@ -38,6 +38,9 @@ export class StartComponent implements OnInit, OnDestroy {
|
||||||
pageIndex: number = 0;
|
pageIndex: number = 0;
|
||||||
pages: any[] = [];
|
pages: any[] = [];
|
||||||
pendingMark: number | void = null;
|
pendingMark: number | void = null;
|
||||||
|
lastUpdate: number = 0;
|
||||||
|
lastMouseX: number;
|
||||||
|
velocity: number = 0;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private stateService: StateService,
|
private stateService: StateService,
|
||||||
|
@ -136,6 +139,7 @@ export class StartComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
onMouseDown(event: MouseEvent) {
|
onMouseDown(event: MouseEvent) {
|
||||||
this.mouseDragStartX = event.clientX;
|
this.mouseDragStartX = event.clientX;
|
||||||
|
this.resetMomentum(event.clientX);
|
||||||
this.blockchainScrollLeftInit = this.blockchainContainer.nativeElement.scrollLeft;
|
this.blockchainScrollLeftInit = this.blockchainContainer.nativeElement.scrollLeft;
|
||||||
}
|
}
|
||||||
onPointerDown(event: PointerEvent) {
|
onPointerDown(event: PointerEvent) {
|
||||||
|
@ -159,6 +163,7 @@ export class StartComponent implements OnInit, OnDestroy {
|
||||||
@HostListener('document:mousemove', ['$event'])
|
@HostListener('document:mousemove', ['$event'])
|
||||||
onMouseMove(event: MouseEvent): void {
|
onMouseMove(event: MouseEvent): void {
|
||||||
if (this.mouseDragStartX != null) {
|
if (this.mouseDragStartX != null) {
|
||||||
|
this.updateVelocity(event.clientX);
|
||||||
this.stateService.setBlockScrollingInProgress(true);
|
this.stateService.setBlockScrollingInProgress(true);
|
||||||
this.blockchainContainer.nativeElement.scrollLeft =
|
this.blockchainContainer.nativeElement.scrollLeft =
|
||||||
this.blockchainScrollLeftInit + this.mouseDragStartX - event.clientX;
|
this.blockchainScrollLeftInit + this.mouseDragStartX - event.clientX;
|
||||||
|
@ -167,7 +172,7 @@ export class StartComponent implements OnInit, OnDestroy {
|
||||||
@HostListener('document:mouseup', [])
|
@HostListener('document:mouseup', [])
|
||||||
onMouseUp() {
|
onMouseUp() {
|
||||||
this.mouseDragStartX = null;
|
this.mouseDragStartX = null;
|
||||||
this.stateService.setBlockScrollingInProgress(false);
|
this.animateMomentum();
|
||||||
}
|
}
|
||||||
@HostListener('document:pointermove', ['$event'])
|
@HostListener('document:pointermove', ['$event'])
|
||||||
onPointerMove(event: PointerEvent): void {
|
onPointerMove(event: PointerEvent): void {
|
||||||
|
@ -183,6 +188,45 @@ export class StartComponent implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resetMomentum(x: number) {
|
||||||
|
this.lastUpdate = performance.now();
|
||||||
|
this.lastMouseX = x;
|
||||||
|
this.velocity = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateVelocity(x: number) {
|
||||||
|
const now = performance.now();
|
||||||
|
let dt = now - this.lastUpdate;
|
||||||
|
if (dt > 0) {
|
||||||
|
this.lastUpdate = now;
|
||||||
|
const velocity = (x - this.lastMouseX) / dt;
|
||||||
|
this.velocity = (0.8 * this.velocity) + (0.2 * velocity);
|
||||||
|
this.lastMouseX = x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
animateMomentum() {
|
||||||
|
this.lastUpdate = performance.now();
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
const now = performance.now();
|
||||||
|
const dt = now - this.lastUpdate;
|
||||||
|
this.lastUpdate = now;
|
||||||
|
if (Math.abs(this.velocity) < 0.005) {
|
||||||
|
this.stateService.setBlockScrollingInProgress(false);
|
||||||
|
} else {
|
||||||
|
const deceleration = Math.max(0.0025, 0.001 * this.velocity * this.velocity) * (this.velocity > 0 ? -1 : 1);
|
||||||
|
const displacement = (this.velocity * dt) - (0.5 * (deceleration * dt * dt));
|
||||||
|
const dv = (deceleration * dt);
|
||||||
|
if ((this.velocity < 0 && dv + this.velocity > 0) || (this.velocity > 0 && dv + this.velocity < 0)) {
|
||||||
|
this.velocity = 0;
|
||||||
|
} else {
|
||||||
|
this.velocity += dv;
|
||||||
|
}
|
||||||
|
this.blockchainContainer.nativeElement.scrollLeft -= displacement;
|
||||||
|
this.animateMomentum();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
onScroll(e) {
|
onScroll(e) {
|
||||||
const middlePage = this.pageIndex === 0 ? this.pages[0] : this.pages[1];
|
const middlePage = this.pageIndex === 0 ? this.pages[0] : this.pages[1];
|
||||||
|
|
|
@ -204,6 +204,12 @@
|
||||||
.txids {
|
.txids {
|
||||||
width: 60%;
|
width: 60%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: 500px) {
|
||||||
|
.txids {
|
||||||
|
width: 40%;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.tx-list {
|
.tx-list {
|
||||||
|
|
|
@ -250,7 +250,7 @@
|
||||||
</span>
|
</span>
|
||||||
<ng-template #inSync>
|
<ng-template #inSync>
|
||||||
<div class="progress inc-tx-progress-bar">
|
<div class="progress inc-tx-progress-bar">
|
||||||
<div class="progress-bar" role="progressbar" [ngStyle]="{'width': mempoolInfoData.value.progressWidth, 'background-color': mempoolInfoData.value.progressColor}"> </div>
|
<div class="progress-bar {{ mempoolInfoData.value.progressColor }}" role="progressbar" [ngStyle]="{'width': mempoolInfoData.value.progressWidth}"> </div>
|
||||||
<div class="progress-text">‎{{ mempoolInfoData.value.vBytesPerSecond | ceil | number }} <ng-container i18n="shared.vbytes-per-second|vB/s">vB/s</ng-container></div>
|
<div class="progress-text">‎{{ mempoolInfoData.value.vBytesPerSecond | ceil | number }} <ng-container i18n="shared.vbytes-per-second|vB/s">vB/s</ng-container></div>
|
||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
|
@ -78,21 +78,12 @@ export class DashboardComponent implements OnInit, OnDestroy {
|
||||||
map(([mempoolInfo, vbytesPerSecond]) => {
|
map(([mempoolInfo, vbytesPerSecond]) => {
|
||||||
const percent = Math.round((Math.min(vbytesPerSecond, this.vBytesPerSecondLimit) / this.vBytesPerSecondLimit) * 100);
|
const percent = Math.round((Math.min(vbytesPerSecond, this.vBytesPerSecondLimit) / this.vBytesPerSecondLimit) * 100);
|
||||||
|
|
||||||
let progressColor = '#7CB342';
|
let progressColor = 'bg-success';
|
||||||
if (vbytesPerSecond > 1667) {
|
if (vbytesPerSecond > 1667) {
|
||||||
progressColor = '#FDD835';
|
progressColor = 'bg-warning';
|
||||||
}
|
|
||||||
if (vbytesPerSecond > 2000) {
|
|
||||||
progressColor = '#FFB300';
|
|
||||||
}
|
|
||||||
if (vbytesPerSecond > 2500) {
|
|
||||||
progressColor = '#FB8C00';
|
|
||||||
}
|
}
|
||||||
if (vbytesPerSecond > 3000) {
|
if (vbytesPerSecond > 3000) {
|
||||||
progressColor = '#F4511E';
|
progressColor = 'bg-danger';
|
||||||
}
|
|
||||||
if (vbytesPerSecond > 3500) {
|
|
||||||
progressColor = '#D81B60';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const mempoolSizePercentage = (mempoolInfo.usage / mempoolInfo.maxmempool * 100);
|
const mempoolSizePercentage = (mempoolInfo.usage / mempoolInfo.maxmempool * 100);
|
||||||
|
|
|
@ -23,6 +23,10 @@ export class FiatCurrencyPipe implements PipeTransform {
|
||||||
const digits = args[0] || 1;
|
const digits = args[0] || 1;
|
||||||
const currency = args[1] || this.currency || 'USD';
|
const currency = args[1] || this.currency || 'USD';
|
||||||
|
|
||||||
return new Intl.NumberFormat(this.locale, { style: 'currency', currency }).format(num);
|
if (num >= 1000) {
|
||||||
|
return new Intl.NumberFormat(this.locale, { style: 'currency', currency, maximumFractionDigits: 0 }).format(num);
|
||||||
|
} else {
|
||||||
|
return new Intl.NumberFormat(this.locale, { style: 'currency', currency }).format(num);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -9,6 +9,10 @@ location /api/tx/ {
|
||||||
rewrite ^/api/(.*) /$1 break;
|
rewrite ^/api/(.*) /$1 break;
|
||||||
try_files /dev/null @esplora-api-cache-disabled;
|
try_files /dev/null @esplora-api-cache-disabled;
|
||||||
}
|
}
|
||||||
|
location /api/address-prefix/ {
|
||||||
|
rewrite ^/api/(.*) /$1 break;
|
||||||
|
try_files /dev/null @esplora-api-cache-disabled;
|
||||||
|
}
|
||||||
|
|
||||||
# rewrite APIs to match what backend expects
|
# rewrite APIs to match what backend expects
|
||||||
location /api/currencies {
|
location /api/currencies {
|
||||||
|
|
Loading…
Add table
Reference in a new issue