From 45efb604c1a9171c9edf4826bca52f5e3c39a1d9 Mon Sep 17 00:00:00 2001 From: softsimon Date: Mon, 19 Oct 2020 11:57:02 +0700 Subject: [PATCH] Revamping configuration file. fixes #141 --- backend/mempool-config.sample.json | 67 +++++++++------- backend/src/api/bisq/bisq.ts | 22 ++--- backend/src/api/bisq/markets.ts | 4 +- backend/src/api/bitcoin/electrs-api.ts | 18 ++--- backend/src/api/blocks.ts | 13 +-- backend/src/api/disk-cache.ts | 4 +- backend/src/api/donations.ts | 13 +-- backend/src/api/fee-api.ts | 4 +- backend/src/api/mempool-blocks.ts | 4 +- backend/src/api/mempool.ts | 13 ++- backend/src/api/websocket-handler.ts | 4 +- backend/src/config.ts | 65 +++++++++++++++ backend/src/database.ts | 12 +-- backend/src/index.ts | 107 ++++++++++++------------- backend/src/logger.ts | 8 +- backend/src/routes.ts | 4 +- frontend/proxy.conf.json | 2 +- 17 files changed, 218 insertions(+), 146 deletions(-) create mode 100644 backend/src/config.ts diff --git a/backend/mempool-config.sample.json b/backend/mempool-config.sample.json index 76965bc08..421e04652 100644 --- a/backend/mempool-config.sample.json +++ b/backend/mempool-config.sample.json @@ -1,30 +1,41 @@ { - "HTTP_PORT": 8999, - "DB_HOST": "localhost", - "DB_PORT": 3306, - "DB_USER": "mempool", - "DB_PASSWORD": "mempool", - "DB_DATABASE": "mempool", - "DB_DISABLED": false, - "API_ENDPOINT": "/api/v1/", - "ELECTRS_POLL_RATE_MS": 2000, - "NETWORK": "mainnet", - "MEMPOOL_REFRESH_RATE_MS": 2000, - "DEFAULT_PROJECTED_BLOCKS_AMOUNT": 8, - "KEEP_BLOCK_AMOUNT": 24, - "INITIAL_BLOCK_AMOUNT": 8, - "TX_PER_SECOND_SPAN_SECONDS": 150, - "ELECTRS_API_URL": "http://localhost:50001", - "CLUSTER_NUM_CORES": 1, - "BISQ_ENABLED": false, - "BISQ_BLOCKS_DATA_PATH": "/bisq/seednode-data/btc_mainnet/db/json", - "BISQ_MARKET_ENABLED": false, - "BISQ_MARKETS_DATA_PATH": "/bisq/seednode-data/btc_mainnet/db", - "SSL": false, - "SSL_CERT_FILE_PATH": "/etc/letsencrypt/live/mysite/fullchain.pem", - "SSL_KEY_FILE_PATH": "/etc/letsencrypt/live/mysite/privkey.pem", - "BTCPAY_URL": "", - "BTCPAY_WEBHOOK_URL": "", - "BTCPAY_AUTH": "", - "TWITTER_BEARER_AUTH": "" + "MEMPOOL": { + "NETWORK": "mainnet", + "HTTP_PORT": 8999, + "MINED_BLOCKS_CACHE": 144, + "SPAWN_CLUSTER_PROCS": 0, + "API_URL_PREFIX": "/api/v1/", + "WEBSOCKET_REFRESH_RATE_MS": 2000 + }, + "ELECTRS": { + "REST_API_URL": "http://localhost:50001", + "POLL_RATE_MS": 2000 + }, + "DATABASE": { + "ENABLED": true, + "HOST": "localhost", + "PORT": 3306, + "DATABASE": "mempool", + "USERNAME": "mempool", + "PASSWORD": "mempool" + }, + "STATISTICS": { + "ENABLED": true, + "TX_PER_SECOND_SAMPLE_PERIOD": 150 + }, + "BISQ_BLOCKS": { + "ENABLED": true, + "DATA_PATH": "/bisq/statsnode-data/btc_mainnet/db/json" + }, + "BISQ_MARKETS": { + "ENABLED": true, + "DATA_PATH": "/bisq/statsnode-data/btc_mainnet/db" + }, + "SPONSORS": { + "ENABLED": true, + "BTCPAY_URL": "", + "BTCPAY_AUTH": "", + "BTCPAY_WEBHOOK_URL": "", + "TWITTER_BEARER_AUTH": "" + } } diff --git a/backend/src/api/bisq/bisq.ts b/backend/src/api/bisq/bisq.ts index 4f657b7db..db1bd7746 100644 --- a/backend/src/api/bisq/bisq.ts +++ b/backend/src/api/bisq/bisq.ts @@ -1,4 +1,4 @@ -const config = require('../../../mempool-config.json'); +import config from '../../config'; import * as fs from 'fs'; import * as request from 'request'; import { BisqBlocks, BisqBlock, BisqTransaction, BisqStats, BisqTrade } from './interfaces'; @@ -8,7 +8,7 @@ import { StaticPool } from 'node-worker-threads-pool'; import logger from '../../logger'; class Bisq { - private static BLOCKS_JSON_FILE_PATH = '/all/blocks.json'; + private static BLOCKS_JSON_FILE_PATH = config.BISQ_BLOCKS.DATA_PATH + '/all/blocks.json'; private latestBlockHeight = 0; private blocks: BisqBlock[] = []; private transactions: BisqTransaction[] = []; @@ -87,8 +87,8 @@ class Bisq { } private checkForBisqDataFolder() { - if (!fs.existsSync(config.BISQ_BLOCKS_DATA_PATH + Bisq.BLOCKS_JSON_FILE_PATH)) { - logger.warn(config.BISQ_BLOCKS_DATA_PATH + Bisq.BLOCKS_JSON_FILE_PATH + ` doesn't exist. Make sure Bisq is running and the config is correct before starting the server.`); + if (!fs.existsSync(Bisq.BLOCKS_JSON_FILE_PATH)) { + logger.warn(Bisq.BLOCKS_JSON_FILE_PATH + ` doesn't exist. Make sure Bisq is running and the config is correct before starting the server.`); return process.exit(1); } } @@ -98,7 +98,7 @@ class Bisq { this.topDirectoryWatcher.close(); } let fsWait: NodeJS.Timeout | null = null; - this.topDirectoryWatcher = fs.watch(config.BISQ_BLOCKS_DATA_PATH, () => { + this.topDirectoryWatcher = fs.watch(config.BISQ_BLOCKS.DATA_PATH, () => { if (fsWait) { clearTimeout(fsWait); } @@ -120,13 +120,13 @@ class Bisq { if (this.subdirectoryWatcher) { this.subdirectoryWatcher.close(); } - if (!fs.existsSync(config.BISQ_BLOCKS_DATA_PATH + Bisq.BLOCKS_JSON_FILE_PATH)) { - logger.warn(config.BISQ_BLOCKS_DATA_PATH + Bisq.BLOCKS_JSON_FILE_PATH + ` doesn't exist. Trying to restart sub directory watcher again in 3 minutes.`); + if (!fs.existsSync(Bisq.BLOCKS_JSON_FILE_PATH)) { + logger.warn(Bisq.BLOCKS_JSON_FILE_PATH + ` doesn't exist. Trying to restart sub directory watcher again in 3 minutes.`); setTimeout(() => this.startSubDirectoryWatcher(), 180000); return; } let fsWait: NodeJS.Timeout | null = null; - this.subdirectoryWatcher = fs.watch(config.BISQ_BLOCKS_DATA_PATH + '/all', () => { + this.subdirectoryWatcher = fs.watch(config.BISQ_BLOCKS.DATA_PATH + '/all', () => { if (fsWait) { clearTimeout(fsWait); } @@ -249,7 +249,7 @@ class Bisq { this.blocks.reverse(); this.latestBlockHeight = data.chainHeight; const time = new Date().getTime() - start; - logger.debug('Bisq dump processed in ' + time + ' ms'); + logger.debug('Bisq dump processed in ' + time + ' ms (worker thread)'); } else { throw new Error(`Bisq dump didn't contain any blocks`); } @@ -258,10 +258,10 @@ class Bisq { private loadData(): Promise { return new Promise((resolve, reject) => { - if (!fs.existsSync(config.BISQ_BLOCKS_DATA_PATH + Bisq.BLOCKS_JSON_FILE_PATH)) { + if (!fs.existsSync(Bisq.BLOCKS_JSON_FILE_PATH)) { return reject(Bisq.BLOCKS_JSON_FILE_PATH + ` doesn't exist`); } - fs.readFile(config.BISQ_BLOCKS_DATA_PATH + Bisq.BLOCKS_JSON_FILE_PATH, 'utf8', (err, data) => { + fs.readFile(Bisq.BLOCKS_JSON_FILE_PATH, 'utf8', (err, data) => { if (err) { reject(err); } diff --git a/backend/src/api/bisq/markets.ts b/backend/src/api/bisq/markets.ts index df1921a10..3c89a74f6 100644 --- a/backend/src/api/bisq/markets.ts +++ b/backend/src/api/bisq/markets.ts @@ -1,4 +1,4 @@ -const config = require('../../../mempool-config.json'); +import config from '../../config'; import * as fs from 'fs'; import { OffersData as OffersData, TradesData, Currency } from './interfaces'; import bisqMarket from './markets-api'; @@ -6,7 +6,7 @@ import logger from '../../logger'; class Bisq { private static FOLDER_WATCH_CHANGE_DETECTION_DEBOUNCE = 4000; - private static MARKET_JSON_PATH = config.BISQ_MARKETS_DATA_PATH + '/btc_mainnet/db'; + private static MARKET_JSON_PATH = config.BISQ_MARKETS.DATA_PATH + '/btc_mainnet/db'; private static MARKET_JSON_FILE_PATHS = { cryptoCurrency: '/crypto_currency_list.json', fiatCurrency: '/fiat_currency_list.json', diff --git a/backend/src/api/bitcoin/electrs-api.ts b/backend/src/api/bitcoin/electrs-api.ts index 87d07ffa2..041d2ac68 100644 --- a/backend/src/api/bitcoin/electrs-api.ts +++ b/backend/src/api/bitcoin/electrs-api.ts @@ -1,4 +1,4 @@ -const config = require('../../../mempool-config.json'); +import config from '../../config'; import { Transaction, Block, MempoolInfo } from '../../interfaces'; import * as request from 'request'; @@ -9,7 +9,7 @@ class ElectrsApi { getMempoolInfo(): Promise { return new Promise((resolve, reject) => { - request(config.ELECTRS_API_URL + '/mempool', { json: true, timeout: 10000 }, (err, res, response) => { + request(config.ELECTRS.REST_API_URL + '/mempool', { json: true, timeout: 10000 }, (err, res, response) => { if (err) { reject('getMempoolInfo error: ' + err.message || err); } else if (res.statusCode !== 200) { @@ -30,7 +30,7 @@ class ElectrsApi { getRawMempool(): Promise { return new Promise((resolve, reject) => { - request(config.ELECTRS_API_URL + '/mempool/txids', { json: true, timeout: 10000, forever: true }, (err, res, response) => { + request(config.ELECTRS.REST_API_URL + '/mempool/txids', { json: true, timeout: 10000, forever: true }, (err, res, response) => { if (err) { reject('getRawMempool error: ' + err.message || err); } else if (res.statusCode !== 200) { @@ -48,7 +48,7 @@ class ElectrsApi { getRawTransaction(txId: string): Promise { return new Promise((resolve, reject) => { - request(config.ELECTRS_API_URL + '/tx/' + txId, { json: true, timeout: 10000, forever: true }, (err, res, response) => { + request(config.ELECTRS.REST_API_URL + '/tx/' + txId, { json: true, timeout: 10000, forever: true }, (err, res, response) => { if (err) { reject('getRawTransaction error: ' + err.message || err); } else if (res.statusCode !== 200) { @@ -66,7 +66,7 @@ class ElectrsApi { getBlockHeightTip(): Promise { return new Promise((resolve, reject) => { - request(config.ELECTRS_API_URL + '/blocks/tip/height', { json: true, timeout: 10000 }, (err, res, response) => { + request(config.ELECTRS.REST_API_URL + '/blocks/tip/height', { json: true, timeout: 10000 }, (err, res, response) => { if (err) { reject('getBlockHeightTip error: ' + err.message || err); } else if (res.statusCode !== 200) { @@ -80,7 +80,7 @@ class ElectrsApi { getTxIdsForBlock(hash: string): Promise { return new Promise((resolve, reject) => { - request(config.ELECTRS_API_URL + '/block/' + hash + '/txids', { json: true, timeout: 10000 }, (err, res, response) => { + request(config.ELECTRS.REST_API_URL + '/block/' + hash + '/txids', { json: true, timeout: 10000 }, (err, res, response) => { if (err) { reject('getTxIdsForBlock error: ' + err.message || err); } else if (res.statusCode !== 200) { @@ -98,7 +98,7 @@ class ElectrsApi { getBlockHash(height: number): Promise { return new Promise((resolve, reject) => { - request(config.ELECTRS_API_URL + '/block-height/' + height, { json: true, timeout: 10000 }, (err, res, response) => { + request(config.ELECTRS.REST_API_URL + '/block-height/' + height, { json: true, timeout: 10000 }, (err, res, response) => { if (err) { reject('getBlockHash error: ' + err.message || err); } else if (res.statusCode !== 200) { @@ -112,7 +112,7 @@ class ElectrsApi { getBlocksFromHeight(height: number): Promise { return new Promise((resolve, reject) => { - request(config.ELECTRS_API_URL + '/blocks/' + height, { json: true, timeout: 10000 }, (err, res, response) => { + request(config.ELECTRS.REST_API_URL + '/blocks/' + height, { json: true, timeout: 10000 }, (err, res, response) => { if (err) { reject('getBlocksFromHeight error: ' + err.message || err); } else if (res.statusCode !== 200) { @@ -126,7 +126,7 @@ class ElectrsApi { getBlock(hash: string): Promise { return new Promise((resolve, reject) => { - request(config.ELECTRS_API_URL + '/block/' + hash, { json: true, timeout: 10000 }, (err, res, response) => { + request(config.ELECTRS.REST_API_URL + '/block/' + hash, { json: true, timeout: 10000 }, (err, res, response) => { if (err) { reject('getBlock error: ' + err.message || err); } else if (res.statusCode !== 200) { diff --git a/backend/src/api/blocks.ts b/backend/src/api/blocks.ts index b12b472d9..5e853ba84 100644 --- a/backend/src/api/blocks.ts +++ b/backend/src/api/blocks.ts @@ -1,4 +1,3 @@ -const config = require('../../mempool-config.json'); import bitcoinApi from './bitcoin/electrs-api'; import logger from '../logger'; import memPool from './mempool'; @@ -6,6 +5,8 @@ import { Block, TransactionExtended, TransactionMinerInfo } from '../interfaces' import { Common } from './common'; class Blocks { + private static INITIAL_BLOCK_AMOUNT = 8; + private static KEEP_BLOCK_AMOUNT = 24; private blocks: Block[] = []; private currentBlockHeight = 0; private lastDifficultyAdjustmentTime = 0; @@ -29,14 +30,14 @@ class Blocks { const blockHeightTip = await bitcoinApi.getBlockHeightTip(); if (this.blocks.length === 0) { - this.currentBlockHeight = blockHeightTip - config.INITIAL_BLOCK_AMOUNT; + this.currentBlockHeight = blockHeightTip - Blocks.INITIAL_BLOCK_AMOUNT; } else { this.currentBlockHeight = this.blocks[this.blocks.length - 1].height; } - if (blockHeightTip - this.currentBlockHeight > config.INITIAL_BLOCK_AMOUNT * 2) { - logger.info(`${blockHeightTip - this.currentBlockHeight} blocks since tip. Fast forwarding to the ${config.INITIAL_BLOCK_AMOUNT} recent blocks`); - this.currentBlockHeight = blockHeightTip - config.INITIAL_BLOCK_AMOUNT; + 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; } if (!this.lastDifficultyAdjustmentTime) { @@ -91,7 +92,7 @@ class Blocks { } this.blocks.push(block); - if (this.blocks.length > config.KEEP_BLOCK_AMOUNT) { + if (this.blocks.length > Blocks.KEEP_BLOCK_AMOUNT) { this.blocks.shift(); } diff --git a/backend/src/api/disk-cache.ts b/backend/src/api/disk-cache.ts index 2ae3bb580..f7b6cbe31 100644 --- a/backend/src/api/disk-cache.ts +++ b/backend/src/api/disk-cache.ts @@ -1,4 +1,4 @@ -const config = require('../../mempool-config.json'); +import config from '../config'; import * as fs from 'fs'; import * as process from 'process'; import memPool from './mempool'; @@ -9,7 +9,7 @@ class DiskCache { static FILE_NAME = './cache.json'; constructor() { - if (process.env.workerId === '0' || !config.CLUSTER_NUM_CORES || config.CLUSTER_NUM_CORES === 1) { + if (process.env.workerId === '0' || !config.MEMPOOL.SPAWN_CLUSTER_PROCS) { process.on('SIGINT', () => { this.saveCacheToDisk(); process.exit(2); diff --git a/backend/src/api/donations.ts b/backend/src/api/donations.ts index 6800f2dd3..979a0f851 100644 --- a/backend/src/api/donations.ts +++ b/backend/src/api/donations.ts @@ -1,4 +1,4 @@ -const config = require('../../mempool-config.json'); +import config from '../config'; import * as request from 'request'; import { DB } from '../database'; import logger from '../logger'; @@ -6,16 +6,19 @@ import logger from '../logger'; class Donations { private notifyDonationStatusCallback: ((invoiceId: string) => void) | undefined; private options = { - baseUrl: config.BTCPAY_URL, + baseUrl: config.SPONSORS.BTCPAY_URL, headers: { 'Content-Type': 'application/json', - 'Authorization': config.BTCPAY_AUTH, + 'Authorization': config.SPONSORS.BTCPAY_AUTH, }, }; sponsorsCache: any[] = []; constructor() { + if (!config.SPONSORS.ENABLED) { + return; + } this.$updateCache(); } @@ -39,7 +42,7 @@ class Donations { 'orderId': orderId, 'currency': 'BTC', 'itemDesc': 'Sponsor mempool.space', - 'notificationUrl': config.BTCPAY_WEBHOOK_URL, + 'notificationUrl': config.SPONSORS.BTCPAY_WEBHOOK_URL, 'redirectURL': 'https://mempool.space/about' }; return new Promise((resolve, reject) => { @@ -185,7 +188,7 @@ class Donations { uri: `https://api.twitter.com/1.1/users/show.json?screen_name=${handle}`, json: true, headers: { - Authorization: 'Bearer ' + config.TWITTER_BEARER_AUTH + Authorization: 'Bearer ' + config.SPONSORS.TWITTER_BEARER_AUTH }, }, (err, res, body) => { if (err) { return reject(err); } diff --git a/backend/src/api/fee-api.ts b/backend/src/api/fee-api.ts index 336252dc5..551501240 100644 --- a/backend/src/api/fee-api.ts +++ b/backend/src/api/fee-api.ts @@ -1,11 +1,11 @@ -const config = require('../../mempool-config.json'); +import config from '../config'; import { MempoolBlock } from '../interfaces'; import projectedBlocks from './mempool-blocks'; class FeeApi { constructor() { } - defaultFee = config.NETWORK === 'liquid' ? 0.1 : 1; + defaultFee = config.MEMPOOL.NETWORK === 'liquid' ? 0.1 : 1; public getRecommendedFee() { const pBlocks = projectedBlocks.getMempoolBlocks(); diff --git a/backend/src/api/mempool-blocks.ts b/backend/src/api/mempool-blocks.ts index 353c11b1d..681c4f26e 100644 --- a/backend/src/api/mempool-blocks.ts +++ b/backend/src/api/mempool-blocks.ts @@ -1,8 +1,8 @@ -const config = require('../../mempool-config.json'); import { MempoolBlock, TransactionExtended, MempoolBlockWithTransactions } from '../interfaces'; import { Common } from './common'; class MempoolBlocks { + private static DEFAULT_PROJECTED_BLOCKS_AMOUNT = 8; private mempoolBlocks: MempoolBlockWithTransactions[] = []; constructor() {} @@ -43,7 +43,7 @@ class MempoolBlocks { let blockSize = 0; let transactions: TransactionExtended[] = []; transactionsSorted.forEach((tx) => { - if (blockVSize + tx.vsize <= 1000000 || mempoolBlocks.length === config.DEFAULT_PROJECTED_BLOCKS_AMOUNT - 1) { + if (blockVSize + tx.vsize <= 1000000 || mempoolBlocks.length === MempoolBlocks.DEFAULT_PROJECTED_BLOCKS_AMOUNT - 1) { blockVSize += tx.vsize; blockSize += tx.size; transactions.push(tx); diff --git a/backend/src/api/mempool.ts b/backend/src/api/mempool.ts index 7a6c77e10..3356df7c9 100644 --- a/backend/src/api/mempool.ts +++ b/backend/src/api/mempool.ts @@ -1,4 +1,4 @@ -const config = require('../../mempool-config.json'); +import config from '../config'; import bitcoinApi from './bitcoin/electrs-api'; import { MempoolInfo, TransactionExtended, Transaction, VbytesPerSecond } from '../interfaces'; import logger from '../logger'; @@ -124,14 +124,13 @@ class Mempool { } } - if ((new Date().getTime()) - start > config.MEMPOOL_REFRESH_RATE_MS * 10) { + if ((new Date().getTime()) - start > config.MEMPOOL.WEBSOCKET_REFRESH_RATE_MS * 10) { break; } } // Prevent mempool from clear on bitcoind restart by delaying the deletion - if ((config.NETWORK === 'mainnet' || !config.NETWORK) - && this.mempoolProtection === 0 && transactions.length / currentMempoolSize <= 0.80) { + if (config.MEMPOOL.NETWORK === 'mainnet' && this.mempoolProtection === 0 && transactions.length / currentMempoolSize <= 0.80) { this.mempoolProtection = 1; this.inSync = false; logger.warn(`Mempool clear protection triggered because transactions.length: ${transactions.length} and currentMempoolSize: ${currentMempoolSize}.`); @@ -182,14 +181,14 @@ class Mempool { } private updateTxPerSecond() { - const nowMinusTimeSpan = new Date().getTime() - (1000 * config.TX_PER_SECOND_SPAN_SECONDS); + const nowMinusTimeSpan = new Date().getTime() - (1000 * config.STATISTICS.TX_PER_SECOND_SAMPLE_PERIOD); this.txPerSecondArray = this.txPerSecondArray.filter((unixTime) => unixTime > nowMinusTimeSpan); - this.txPerSecond = this.txPerSecondArray.length / config.TX_PER_SECOND_SPAN_SECONDS || 0; + this.txPerSecond = this.txPerSecondArray.length / config.STATISTICS.TX_PER_SECOND_SAMPLE_PERIOD || 0; this.vBytesPerSecondArray = this.vBytesPerSecondArray.filter((data) => data.unixTime > nowMinusTimeSpan); if (this.vBytesPerSecondArray.length) { this.vBytesPerSecond = Math.round( - this.vBytesPerSecondArray.map((data) => data.vSize).reduce((a, b) => a + b) / config.TX_PER_SECOND_SPAN_SECONDS + this.vBytesPerSecondArray.map((data) => data.vSize).reduce((a, b) => a + b) / config.STATISTICS.TX_PER_SECOND_SAMPLE_PERIOD ); } } diff --git a/backend/src/api/websocket-handler.ts b/backend/src/api/websocket-handler.ts index f7cdb7e4d..753d3d33a 100644 --- a/backend/src/api/websocket-handler.ts +++ b/backend/src/api/websocket-handler.ts @@ -1,4 +1,3 @@ -const config = require('../../mempool-config.json'); import logger from '../logger'; import * as WebSocket from 'ws'; import { Block, TransactionExtended, WebsocketResponse, MempoolBlock, OptimizedStatistic } from '../interfaces'; @@ -10,6 +9,7 @@ import fiatConversion from './fiat-conversion'; import { Common } from './common'; class WebsocketHandler { + private static INITIAL_BLOCK_AMOUNT = 8; private wss: WebSocket.Server | undefined; private nativeAssetId = '6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d'; private extraInitProperties = {}; @@ -85,7 +85,7 @@ class WebsocketHandler { 'mempoolInfo': memPool.getMempoolInfo(), 'vBytesPerSecond': memPool.getVBytesPerSecond(), 'lastDifficultyAdjustment': blocks.getLastDifficultyAdjustmentTime(), - 'blocks': _blocks.slice(Math.max(_blocks.length - config.INITIAL_BLOCK_AMOUNT, 0)), + 'blocks': _blocks.slice(Math.max(_blocks.length - WebsocketHandler.INITIAL_BLOCK_AMOUNT, 0)), 'conversions': fiatConversion.getTickers()['BTCUSD'], 'mempool-blocks': mempoolBlocks.getMempoolBlocks(), 'transactions': memPool.getLatestTransactions(), diff --git a/backend/src/config.ts b/backend/src/config.ts new file mode 100644 index 000000000..4451d9094 --- /dev/null +++ b/backend/src/config.ts @@ -0,0 +1,65 @@ +const configFile = require('../mempool-config.json'); + +export interface IConfig { + MEMPOOL: { + NETWORK: 'mainnet' | 'testnet' | 'liquid'; + HTTP_PORT: number; + MINED_BLOCKS_CACHE: number; + SPAWN_CLUSTER_PROCS: number; + API_URL_PREFIX: string; + WEBSOCKET_REFRESH_RATE_MS: number; + }; + ELECTRS: { + REST_API_URL: string; + POLL_RATE_MS: number; + }; + DATABASE: { + ENABLED: boolean; + HOST: string, + PORT: number; + DATABASE: string; + USERNAME: string; + PASSWORD: string; + }; + STATISTICS: { + ENABLED: boolean; + TX_PER_SECOND_SAMPLE_PERIOD: number; + }; + BISQ_BLOCKS: { + ENABLED: boolean; + DATA_PATH: string; + }; + BISQ_MARKETS: { + ENABLED: boolean; + DATA_PATH: string; + }; + SPONSORS: { + ENABLED: boolean; + BTCPAY_URL: string; + BTCPAY_AUTH: string; + BTCPAY_WEBHOOK_URL: string; + TWITTER_BEARER_AUTH: string; + }; +} + +class Config implements IConfig { + MEMPOOL: IConfig['MEMPOOL']; + ELECTRS: IConfig['ELECTRS']; + DATABASE: IConfig['DATABASE']; + STATISTICS: IConfig['STATISTICS']; + BISQ_BLOCKS: IConfig['BISQ_BLOCKS']; + BISQ_MARKETS: IConfig['BISQ_MARKETS']; + SPONSORS: IConfig['SPONSORS']; + + constructor() { + this.MEMPOOL = configFile.MEMPOOL; + this.ELECTRS = configFile.ELECTRS; + this.DATABASE = configFile.DATABASE; + this.STATISTICS = configFile.STATISTICS; + this.BISQ_BLOCKS = configFile.BISQ_BLOCKS; + this.BISQ_MARKETS = configFile.BISQ_MARKETS; + this.SPONSORS = configFile.SPONSORS; + } +} + +export default new Config(); diff --git a/backend/src/database.ts b/backend/src/database.ts index dd3632ef4..213bf6fb5 100644 --- a/backend/src/database.ts +++ b/backend/src/database.ts @@ -1,14 +1,14 @@ -const config = require('../mempool-config.json'); +import config from './config'; import { createPool } from 'mysql2/promise'; import logger from './logger'; export class DB { static pool = createPool({ - host: config.DB_HOST, - port: config.DB_PORT, - database: config.DB_DATABASE, - user: config.DB_USER, - password: config.DB_PASSWORD, + host: config.DATABASE.HOST, + port: config.DATABASE.PORT, + database: config.DATABASE.DATABASE, + user: config.DATABASE.USERNAME, + password: config.DATABASE.PASSWORD, connectionLimit: 10, supportBigNumbers: true, }); diff --git a/backend/src/index.ts b/backend/src/index.ts index 775c84936..33e4088f6 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -1,6 +1,4 @@ -const config = require('../mempool-config.json'); import { Express, Request, Response, NextFunction } from 'express'; -import * as fs from 'fs'; import * as express from 'express'; import * as compression from 'compression'; import * as http from 'http'; @@ -10,6 +8,7 @@ import * as cluster from 'cluster'; import * as request from 'request'; import { checkDbConnection } from './database'; +import config from './config'; import routes from './routes'; import blocks from './api/blocks'; import memPool from './api/mempool'; @@ -32,15 +31,15 @@ class Server { constructor() { this.app = express(); - if (!config.CLUSTER_NUM_CORES || config.CLUSTER_NUM_CORES === 1) { + if (!config.MEMPOOL.SPAWN_CLUSTER_PROCS) { this.startServer(); return; } if (cluster.isMaster) { - logger.notice(`Mempool Server is running on port ${config.HTTP_PORT} (${backendInfo.getShortCommitHash()})`); + logger.notice(`Mempool Server (Master) is running on port ${config.MEMPOOL.HTTP_PORT} (${backendInfo.getShortCommitHash()})`); - const numCPUs = config.CLUSTER_NUM_CORES; + const numCPUs = config.MEMPOOL.SPAWN_CLUSTER_PROCS; for (let i = 0; i < numCPUs; i++) { const env = { workerId: i }; const worker = cluster.fork(env); @@ -71,20 +70,14 @@ class Server { .use(express.urlencoded({ extended: true })) .use(express.json()); - if (config.SSL === true) { - const credentials = { - cert: fs.readFileSync(config.SSL_CERT_FILE_PATH), - key: fs.readFileSync(config.SSL_KEY_FILE_PATH), - }; - this.server = https.createServer(credentials, this.app); - this.wss = new WebSocket.Server({ server: this.server }); - } else { - this.server = http.createServer(this.app); - this.wss = new WebSocket.Server({ server: this.server }); + this.server = http.createServer(this.app); + this.wss = new WebSocket.Server({ server: this.server }); + + if (config.DATABASE.ENABLED) { + checkDbConnection(); } - if (!config.DB_DISABLED) { - checkDbConnection(); + if (config.STATISTICS.ENABLED && config.DATABASE.ENABLED) { statistics.startStatistics(); } @@ -95,21 +88,21 @@ class Server { fiatConversion.startService(); diskCache.loadMempoolCache(); - if (config.BISQ_ENABLED) { + if (config.BISQ_BLOCKS.ENABLED) { bisq.startBisqService(); bisq.setPriceCallbackFunction((price) => websocketHandler.setExtraInitProperties('bsq-price', price)); blocks.setNewBlockCallback(bisq.handleNewBitcoinBlock.bind(bisq)); } - if (config.BISQ_MARKET_ENABLED) { + if (config.BISQ_MARKETS.ENABLED) { bisqMarkets.startBisqService(); } - this.server.listen(config.HTTP_PORT, () => { + this.server.listen(config.MEMPOOL.HTTP_PORT, () => { if (worker) { logger.info(`Mempool Server worker #${process.pid} started`); } else { - logger.notice(`Mempool Server is running on port ${config.HTTP_PORT} (${backendInfo.getShortCommitHash()})`); + logger.notice(`Mempool Server is running on port ${config.MEMPOOL.HTTP_PORT} (${backendInfo.getShortCommitHash()})`); } }); } @@ -119,7 +112,7 @@ class Server { await memPool.$updateMemPoolInfo(); await blocks.$updateBlocks(); await memPool.$updateMempool(); - setTimeout(this.runMempoolIntervalFunctions.bind(this), config.ELECTRS_POLL_RATE_MS); + setTimeout(this.runMempoolIntervalFunctions.bind(this), config.ELECTRS.POLL_RATE_MS); this.retryOnElectrsErrorAfterSeconds = 5; } catch (e) { logger.warn(`runMempoolIntervalFunctions error: ${(e.message || e)}. Retrying in ${this.retryOnElectrsErrorAfterSeconds} sec.`); @@ -142,57 +135,57 @@ class Server { setUpHttpApiRoutes() { this.app - .get(config.API_ENDPOINT + 'transaction-times', routes.getTransactionTimes) - .get(config.API_ENDPOINT + 'fees/recommended', routes.getRecommendedFees) - .get(config.API_ENDPOINT + 'fees/mempool-blocks', routes.getMempoolBlocks) - .get(config.API_ENDPOINT + 'statistics/2h', routes.get2HStatistics) - .get(config.API_ENDPOINT + 'statistics/24h', routes.get24HStatistics.bind(routes)) - .get(config.API_ENDPOINT + 'statistics/1w', routes.get1WHStatistics.bind(routes)) - .get(config.API_ENDPOINT + 'statistics/1m', routes.get1MStatistics.bind(routes)) - .get(config.API_ENDPOINT + 'statistics/3m', routes.get3MStatistics.bind(routes)) - .get(config.API_ENDPOINT + 'statistics/6m', routes.get6MStatistics.bind(routes)) - .get(config.API_ENDPOINT + 'statistics/1y', routes.get1YStatistics.bind(routes)) - .get(config.API_ENDPOINT + 'backend-info', routes.getBackendInfo) + .get(config.MEMPOOL.API_URL_PREFIX + 'transaction-times', routes.getTransactionTimes) + .get(config.MEMPOOL.API_URL_PREFIX + 'fees/recommended', routes.getRecommendedFees) + .get(config.MEMPOOL.API_URL_PREFIX + 'fees/mempool-blocks', routes.getMempoolBlocks) + .get(config.MEMPOOL.API_URL_PREFIX + 'statistics/2h', routes.get2HStatistics) + .get(config.MEMPOOL.API_URL_PREFIX + 'statistics/24h', routes.get24HStatistics.bind(routes)) + .get(config.MEMPOOL.API_URL_PREFIX + 'statistics/1w', routes.get1WHStatistics.bind(routes)) + .get(config.MEMPOOL.API_URL_PREFIX + 'statistics/1m', routes.get1MStatistics.bind(routes)) + .get(config.MEMPOOL.API_URL_PREFIX + 'statistics/3m', routes.get3MStatistics.bind(routes)) + .get(config.MEMPOOL.API_URL_PREFIX + 'statistics/6m', routes.get6MStatistics.bind(routes)) + .get(config.MEMPOOL.API_URL_PREFIX + 'statistics/1y', routes.get1YStatistics.bind(routes)) + .get(config.MEMPOOL.API_URL_PREFIX + 'backend-info', routes.getBackendInfo) ; - if (config.BISQ_ENABLED) { + if (config.BISQ_BLOCKS.ENABLED) { this.app - .get(config.API_ENDPOINT + 'bisq/stats', routes.getBisqStats) - .get(config.API_ENDPOINT + 'bisq/tx/:txId', routes.getBisqTransaction) - .get(config.API_ENDPOINT + 'bisq/block/:hash', routes.getBisqBlock) - .get(config.API_ENDPOINT + 'bisq/blocks/tip/height', routes.getBisqTip) - .get(config.API_ENDPOINT + 'bisq/blocks/:index/:length', routes.getBisqBlocks) - .get(config.API_ENDPOINT + 'bisq/address/:address', routes.getBisqAddress) - .get(config.API_ENDPOINT + 'bisq/txs/:index/:length', routes.getBisqTransactions) + .get(config.MEMPOOL.API_URL_PREFIX + 'bisq/stats', routes.getBisqStats) + .get(config.MEMPOOL.API_URL_PREFIX + 'bisq/tx/:txId', routes.getBisqTransaction) + .get(config.MEMPOOL.API_URL_PREFIX + 'bisq/block/:hash', routes.getBisqBlock) + .get(config.MEMPOOL.API_URL_PREFIX + 'bisq/blocks/tip/height', routes.getBisqTip) + .get(config.MEMPOOL.API_URL_PREFIX + 'bisq/blocks/:index/:length', routes.getBisqBlocks) + .get(config.MEMPOOL.API_URL_PREFIX + 'bisq/address/:address', routes.getBisqAddress) + .get(config.MEMPOOL.API_URL_PREFIX + 'bisq/txs/:index/:length', routes.getBisqTransactions) ; } - if (config.BISQ_MARKET_ENABLED) { + if (config.BISQ_MARKETS.ENABLED) { this.app - .get(config.API_ENDPOINT + 'bisq/markets/currencies', routes.getBisqMarketCurrencies.bind(routes)) - .get(config.API_ENDPOINT + 'bisq/markets/depth', routes.getBisqMarketDepth.bind(routes)) - .get(config.API_ENDPOINT + 'bisq/markets/hloc', routes.getBisqMarketHloc.bind(routes)) - .get(config.API_ENDPOINT + 'bisq/markets/markets', routes.getBisqMarketMarkets.bind(routes)) - .get(config.API_ENDPOINT + 'bisq/markets/offers', routes.getBisqMarketOffers.bind(routes)) - .get(config.API_ENDPOINT + 'bisq/markets/ticker', routes.getBisqMarketTicker.bind(routes)) - .get(config.API_ENDPOINT + 'bisq/markets/trades', routes.getBisqMarketTrades.bind(routes)) - .get(config.API_ENDPOINT + 'bisq/markets/volumes', routes.getBisqMarketVolumes.bind(routes)) + .get(config.MEMPOOL.API_URL_PREFIX + 'bisq/markets/currencies', routes.getBisqMarketCurrencies.bind(routes)) + .get(config.MEMPOOL.API_URL_PREFIX + 'bisq/markets/depth', routes.getBisqMarketDepth.bind(routes)) + .get(config.MEMPOOL.API_URL_PREFIX + 'bisq/markets/hloc', routes.getBisqMarketHloc.bind(routes)) + .get(config.MEMPOOL.API_URL_PREFIX + 'bisq/markets/markets', routes.getBisqMarketMarkets.bind(routes)) + .get(config.MEMPOOL.API_URL_PREFIX + 'bisq/markets/offers', routes.getBisqMarketOffers.bind(routes)) + .get(config.MEMPOOL.API_URL_PREFIX + 'bisq/markets/ticker', routes.getBisqMarketTicker.bind(routes)) + .get(config.MEMPOOL.API_URL_PREFIX + 'bisq/markets/trades', routes.getBisqMarketTrades.bind(routes)) + .get(config.MEMPOOL.API_URL_PREFIX + 'bisq/markets/volumes', routes.getBisqMarketVolumes.bind(routes)) ; } - if (config.BTCPAY_URL) { + if (config.SPONSORS.ENABLED) { this.app - .get(config.API_ENDPOINT + 'donations', routes.getDonations.bind(routes)) - .get(config.API_ENDPOINT + 'donations/images/:id', routes.getSponsorImage.bind(routes)) - .post(config.API_ENDPOINT + 'donations', routes.createDonationRequest.bind(routes)) - .post(config.API_ENDPOINT + 'donations-webhook', routes.donationWebhook.bind(routes)) + .get(config.MEMPOOL.API_URL_PREFIX + 'donations', routes.getDonations.bind(routes)) + .get(config.MEMPOOL.API_URL_PREFIX + 'donations/images/:id', routes.getSponsorImage.bind(routes)) + .post(config.MEMPOOL.API_URL_PREFIX + 'donations', routes.createDonationRequest.bind(routes)) + .post(config.MEMPOOL.API_URL_PREFIX + 'donations-webhook', routes.donationWebhook.bind(routes)) ; } else { this.app - .get(config.API_ENDPOINT + 'donations', (req, res) => { + .get(config.MEMPOOL.API_URL_PREFIX + 'donations', (req, res) => { req.pipe(request('https://mempool.space/api/v1/donations')).pipe(res); }) - .get(config.API_ENDPOINT + 'donations/images/:id', (req, res) => { + .get(config.MEMPOOL.API_URL_PREFIX + 'donations/images/:id', (req, res) => { req.pipe(request('https://mempool.space/api/v1/donations/images/' + req.params.id)).pipe(res); }); } diff --git a/backend/src/logger.ts b/backend/src/logger.ts index 1c0edfa9e..41aaf5c8f 100644 --- a/backend/src/logger.ts +++ b/backend/src/logger.ts @@ -1,4 +1,4 @@ -const config = require('../mempool-config.json'); +import config from './config'; import * as dgram from 'dgram'; class Logger { @@ -79,11 +79,11 @@ class Logger { } private getNetwork(): string { - if (config.BISQ_ENABLED) { + if (config.BISQ_BLOCKS.ENABLED) { return 'bisq'; } - if (config.NETWORK && config.NETWORK !== 'mainnet') { - return config.NETWORK; + if (config.MEMPOOL.NETWORK && config.MEMPOOL.NETWORK !== 'mainnet') { + return config.MEMPOOL.NETWORK; } return ''; } diff --git a/backend/src/routes.ts b/backend/src/routes.ts index 552c9daec..9ee755ffb 100644 --- a/backend/src/routes.ts +++ b/backend/src/routes.ts @@ -1,4 +1,4 @@ -const config = require('../mempool-config.json'); +import config from './config'; import { Request, Response } from 'express'; import statistics from './api/statistics'; import feeApi from './api/fee-api'; @@ -16,7 +16,7 @@ class Routes { private cache = {}; constructor() { - if (!config.DB_DISABLED) { + if (!config.DATABASE.ENABLED) { this.createCache(); setInterval(this.createCache.bind(this), 600000); } diff --git a/frontend/proxy.conf.json b/frontend/proxy.conf.json index 9e99f4b27..c02eb5e1e 100644 --- a/frontend/proxy.conf.json +++ b/frontend/proxy.conf.json @@ -56,7 +56,7 @@ "target": "http://localhost:8999/", "secure": false, "pathRewrite": { - "^/bisq/api/": "/api/v1/bisq" + "^/bisq/api/": "/api/v1/bisq/" } }, "/bisq/api/v1/ws": {