2022-06-27 21:28:21 -07:00
|
|
|
import express from "express";
|
2022-07-06 21:43:47 +02:00
|
|
|
import { Application, Request, Response, NextFunction } from 'express';
|
2019-07-21 17:59:47 +03:00
|
|
|
import * as http from 'http';
|
|
|
|
import * as WebSocket from 'ws';
|
2022-06-27 21:28:21 -07:00
|
|
|
import cluster from 'cluster';
|
2022-04-13 17:38:42 +04:00
|
|
|
import DB from './database';
|
2020-10-19 11:57:02 +07:00
|
|
|
import config from './config';
|
2019-07-21 17:59:47 +03:00
|
|
|
import blocks from './api/blocks';
|
2020-02-16 22:15:07 +07:00
|
|
|
import memPool from './api/mempool';
|
|
|
|
import diskCache from './api/disk-cache';
|
2022-07-11 19:15:28 +02:00
|
|
|
import statistics from './api/statistics/statistics';
|
2020-02-26 17:49:53 +07:00
|
|
|
import websocketHandler from './api/websocket-handler';
|
2019-07-21 17:59:47 +03:00
|
|
|
import fiatConversion from './api/fiat-conversion';
|
2020-09-10 14:46:23 +07:00
|
|
|
import bisq from './api/bisq/bisq';
|
|
|
|
import bisqMarkets from './api/bisq/markets';
|
2020-10-13 15:27:52 +07:00
|
|
|
import logger from './logger';
|
2020-10-13 17:48:43 +07:00
|
|
|
import backendInfo from './api/backend-info';
|
2021-01-05 18:57:06 +07:00
|
|
|
import loadingIndicators from './api/loading-indicators';
|
2021-01-20 17:16:43 +07:00
|
|
|
import mempool from './api/mempool';
|
2021-09-18 13:37:25 +04:00
|
|
|
import elementsParser from './api/liquid/elements-parser';
|
2021-12-11 04:27:58 +04:00
|
|
|
import databaseMigration from './api/database-migration';
|
2021-12-21 02:00:50 +04:00
|
|
|
import syncAssets from './sync-assets';
|
|
|
|
import icons from './api/liquid/icons';
|
2021-12-31 02:28:40 +04:00
|
|
|
import { Common } from './api/common';
|
2022-04-07 14:37:16 +09:00
|
|
|
import poolsUpdater from './tasks/pools-updater';
|
2022-05-19 16:40:38 +02:00
|
|
|
import indexer from './indexer';
|
2022-07-06 11:58:06 +02:00
|
|
|
import nodesRoutes from './api/explorer/nodes.routes';
|
|
|
|
import channelsRoutes from './api/explorer/channels.routes';
|
|
|
|
import generalLightningRoutes from './api/explorer/general.routes';
|
2022-07-06 21:43:47 +02:00
|
|
|
import lightningStatsUpdater from './tasks/lightning/stats-updater.service';
|
2022-08-02 16:18:19 +02:00
|
|
|
import networkSyncService from './tasks/lightning/network-sync.service';
|
2022-08-01 17:48:04 +02:00
|
|
|
import statisticsRoutes from './api/statistics/statistics.routes';
|
|
|
|
import miningRoutes from './api/mining/mining-routes';
|
|
|
|
import bisqRoutes from './api/bisq/bisq.routes';
|
|
|
|
import liquidRoutes from './api/liquid/liquid.routes';
|
|
|
|
import bitcoinRoutes from './api/bitcoin/bitcoin.routes';
|
2022-08-02 16:39:34 +02:00
|
|
|
import fundingTxFetcher from "./tasks/lightning/sync-tasks/funding-tx-fetcher";
|
2019-07-21 17:59:47 +03:00
|
|
|
|
2020-02-16 22:15:07 +07:00
|
|
|
class Server {
|
2020-09-22 03:52:54 +07:00
|
|
|
private wss: WebSocket.Server | undefined;
|
2021-02-07 02:20:07 +07:00
|
|
|
private server: http.Server | undefined;
|
2022-06-27 21:28:21 -07:00
|
|
|
private app: Application;
|
2021-01-06 22:49:28 +07:00
|
|
|
private currentBackendRetryInterval = 5;
|
2019-07-21 17:59:47 +03:00
|
|
|
|
|
|
|
constructor() {
|
|
|
|
this.app = express();
|
2020-02-16 22:15:07 +07:00
|
|
|
|
2020-10-19 11:57:02 +07:00
|
|
|
if (!config.MEMPOOL.SPAWN_CLUSTER_PROCS) {
|
2020-09-22 03:52:54 +07:00
|
|
|
this.startServer();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-06-27 21:28:21 -07:00
|
|
|
if (cluster.isPrimary) {
|
2020-10-19 11:57:02 +07:00
|
|
|
logger.notice(`Mempool Server (Master) is running on port ${config.MEMPOOL.HTTP_PORT} (${backendInfo.getShortCommitHash()})`);
|
2020-09-22 03:52:54 +07:00
|
|
|
|
2020-10-19 11:57:02 +07:00
|
|
|
const numCPUs = config.MEMPOOL.SPAWN_CLUSTER_PROCS;
|
2020-09-22 03:52:54 +07:00
|
|
|
for (let i = 0; i < numCPUs; i++) {
|
2020-09-30 00:25:43 +07:00
|
|
|
const env = { workerId: i };
|
|
|
|
const worker = cluster.fork(env);
|
|
|
|
worker.process['env'] = env;
|
2020-09-22 03:52:54 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
cluster.on('exit', (worker, code, signal) => {
|
2020-09-30 00:25:43 +07:00
|
|
|
const workerId = worker.process['env'].workerId;
|
2020-10-13 16:43:09 +07:00
|
|
|
logger.warn(`Mempool Worker PID #${worker.process.pid} workerId: ${workerId} died. Restarting in 10 seconds... ${signal || code}`);
|
2020-09-30 00:25:43 +07:00
|
|
|
setTimeout(() => {
|
|
|
|
const env = { workerId: workerId };
|
|
|
|
const newWorker = cluster.fork(env);
|
|
|
|
newWorker.process['env'] = env;
|
|
|
|
}, 10000);
|
2020-09-22 03:52:54 +07:00
|
|
|
});
|
|
|
|
} else {
|
|
|
|
this.startServer(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-30 21:09:30 +07:00
|
|
|
async startServer(worker = false) {
|
2022-06-01 01:09:08 +09:00
|
|
|
logger.notice(`Starting Mempool Server${worker ? ' (worker)' : ''}... (${backendInfo.getShortCommitHash()})`);
|
2021-01-20 01:34:21 +07:00
|
|
|
|
2019-07-21 17:59:47 +03:00
|
|
|
this.app
|
2020-06-07 17:30:32 +07:00
|
|
|
.use((req: Request, res: Response, next: NextFunction) => {
|
2019-07-21 17:59:47 +03:00
|
|
|
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
|
|
next();
|
|
|
|
})
|
2020-10-07 20:15:42 +07:00
|
|
|
.use(express.urlencoded({ extended: true }))
|
2021-10-19 15:37:45 +04:00
|
|
|
.use(express.text())
|
2022-06-27 21:28:21 -07:00
|
|
|
;
|
2020-02-16 22:15:07 +07:00
|
|
|
|
2020-10-19 11:57:02 +07:00
|
|
|
this.server = http.createServer(this.app);
|
|
|
|
this.wss = new WebSocket.Server({ server: this.server });
|
2019-07-21 17:59:47 +03:00
|
|
|
|
2021-03-18 23:47:40 +07:00
|
|
|
this.setUpWebsocketHandling();
|
|
|
|
|
2022-04-22 04:03:08 -04:00
|
|
|
await syncAssets.syncAssets$();
|
2021-01-20 01:34:21 +07:00
|
|
|
diskCache.loadMempoolCache();
|
|
|
|
|
2020-10-19 11:57:02 +07:00
|
|
|
if (config.DATABASE.ENABLED) {
|
2022-04-13 17:38:42 +04:00
|
|
|
await DB.checkDbConnection();
|
2021-12-11 04:27:58 +04:00
|
|
|
try {
|
2022-04-22 04:03:08 -04:00
|
|
|
if (process.env.npm_config_reindex !== undefined) { // Re-index requests
|
2022-02-21 16:38:18 +09:00
|
|
|
const tables = process.env.npm_config_reindex.split(',');
|
2022-04-13 16:29:52 +09:00
|
|
|
logger.warn(`Indexed data for "${process.env.npm_config_reindex}" tables will be erased in 5 seconds (using '--reindex')`);
|
2022-04-22 04:03:08 -04:00
|
|
|
await Common.sleep$(5000);
|
2022-02-21 16:38:18 +09:00
|
|
|
await databaseMigration.$truncateIndexedData(tables);
|
|
|
|
}
|
2021-12-11 04:27:58 +04:00
|
|
|
await databaseMigration.$initializeOrMigrateDatabase();
|
2022-03-11 21:09:56 +01:00
|
|
|
if (Common.indexingEnabled()) {
|
2022-05-19 16:40:38 +02:00
|
|
|
await indexer.$resetHashratesIndexingState();
|
2022-03-11 21:09:56 +01:00
|
|
|
}
|
2021-12-11 04:27:58 +04:00
|
|
|
} catch (e) {
|
|
|
|
throw new Error(e instanceof Error ? e.message : 'Error');
|
|
|
|
}
|
2020-10-19 11:57:02 +07:00
|
|
|
}
|
|
|
|
|
2022-06-27 21:28:21 -07:00
|
|
|
if (config.STATISTICS.ENABLED && config.DATABASE.ENABLED && cluster.isPrimary) {
|
2020-07-23 00:04:29 +07:00
|
|
|
statistics.startStatistics();
|
|
|
|
}
|
|
|
|
|
2021-12-31 02:28:40 +04:00
|
|
|
if (Common.isLiquid()) {
|
2022-04-15 13:20:54 +09:00
|
|
|
try {
|
|
|
|
icons.loadIcons();
|
|
|
|
} catch (e) {
|
|
|
|
logger.err('Cannot load liquid icons. Ignoring. Reason: ' + (e instanceof Error ? e.message : e));
|
|
|
|
}
|
2021-12-21 02:00:50 +04:00
|
|
|
}
|
|
|
|
|
2021-01-30 22:12:22 +07:00
|
|
|
fiatConversion.startService();
|
|
|
|
|
2020-02-26 17:49:53 +07:00
|
|
|
this.setUpHttpApiRoutes();
|
2020-10-23 16:27:02 +07:00
|
|
|
this.runMainUpdateLoop();
|
2019-07-21 17:59:47 +03:00
|
|
|
|
2021-04-25 02:38:46 +04:00
|
|
|
if (config.BISQ.ENABLED) {
|
2020-07-03 23:45:19 +07:00
|
|
|
bisq.startBisqService();
|
2020-07-14 21:26:02 +07:00
|
|
|
bisq.setPriceCallbackFunction((price) => websocketHandler.setExtraInitProperties('bsq-price', price));
|
2020-09-27 17:21:18 +07:00
|
|
|
blocks.setNewBlockCallback(bisq.handleNewBitcoinBlock.bind(bisq));
|
2020-09-10 14:46:23 +07:00
|
|
|
bisqMarkets.startBisqService();
|
|
|
|
}
|
|
|
|
|
2022-07-06 11:58:06 +02:00
|
|
|
if (config.LIGHTNING.ENABLED) {
|
2022-08-02 16:39:34 +02:00
|
|
|
fundingTxFetcher.$init()
|
|
|
|
.then(() => networkSyncService.$startService())
|
2022-08-02 16:18:19 +02:00
|
|
|
.then(() => lightningStatsUpdater.$startService());
|
2022-07-06 11:58:06 +02:00
|
|
|
}
|
|
|
|
|
2020-10-19 11:57:02 +07:00
|
|
|
this.server.listen(config.MEMPOOL.HTTP_PORT, () => {
|
2020-09-22 03:52:54 +07:00
|
|
|
if (worker) {
|
2020-10-13 15:27:52 +07:00
|
|
|
logger.info(`Mempool Server worker #${process.pid} started`);
|
2020-09-22 03:52:54 +07:00
|
|
|
} else {
|
2021-01-20 01:34:21 +07:00
|
|
|
logger.notice(`Mempool Server is running on port ${config.MEMPOOL.HTTP_PORT}`);
|
2020-09-22 03:52:54 +07:00
|
|
|
}
|
2019-07-21 17:59:47 +03:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-10-23 16:27:02 +07:00
|
|
|
async runMainUpdateLoop() {
|
2020-10-18 21:47:47 +07:00
|
|
|
try {
|
2021-04-02 11:47:13 +04:00
|
|
|
try {
|
|
|
|
await memPool.$updateMemPoolInfo();
|
|
|
|
} catch (e) {
|
2021-08-31 15:09:33 +03:00
|
|
|
const msg = `updateMempoolInfo: ${(e instanceof Error ? e.message : e)}`;
|
2021-09-19 02:40:16 +04:00
|
|
|
if (config.MEMPOOL.USE_SECOND_NODE_FOR_MINFEE) {
|
2021-04-02 11:47:13 +04:00
|
|
|
logger.warn(msg);
|
|
|
|
} else {
|
|
|
|
logger.debug(msg);
|
|
|
|
}
|
|
|
|
}
|
2022-04-27 16:57:07 +09:00
|
|
|
await poolsUpdater.updatePoolsJson();
|
2020-10-18 21:47:47 +07:00
|
|
|
await blocks.$updateBlocks();
|
|
|
|
await memPool.$updateMempool();
|
2022-05-19 16:40:38 +02:00
|
|
|
indexer.$run();
|
2022-01-05 15:41:14 +09:00
|
|
|
|
2021-01-06 22:49:28 +07:00
|
|
|
setTimeout(this.runMainUpdateLoop.bind(this), config.MEMPOOL.POLL_RATE_MS);
|
|
|
|
this.currentBackendRetryInterval = 5;
|
2020-10-18 21:47:47 +07:00
|
|
|
} catch (e) {
|
2021-08-31 15:09:33 +03:00
|
|
|
const loggerMsg = `runMainLoop error: ${(e instanceof Error ? e.message : e)}. Retrying in ${this.currentBackendRetryInterval} sec.`;
|
2021-01-06 22:49:28 +07:00
|
|
|
if (this.currentBackendRetryInterval > 5) {
|
2020-10-23 16:27:02 +07:00
|
|
|
logger.warn(loggerMsg);
|
2021-01-20 17:16:43 +07:00
|
|
|
mempool.setOutOfSync();
|
2020-10-23 16:27:02 +07:00
|
|
|
} else {
|
|
|
|
logger.debug(loggerMsg);
|
|
|
|
}
|
2021-01-02 04:40:10 +07:00
|
|
|
logger.debug(JSON.stringify(e));
|
2021-01-06 22:49:28 +07:00
|
|
|
setTimeout(this.runMainUpdateLoop.bind(this), 1000 * this.currentBackendRetryInterval);
|
|
|
|
this.currentBackendRetryInterval *= 2;
|
|
|
|
this.currentBackendRetryInterval = Math.min(this.currentBackendRetryInterval, 60);
|
2020-10-18 21:47:47 +07:00
|
|
|
}
|
2019-07-21 17:59:47 +03:00
|
|
|
}
|
|
|
|
|
2020-02-26 17:49:53 +07:00
|
|
|
setUpWebsocketHandling() {
|
2020-09-22 03:52:54 +07:00
|
|
|
if (this.wss) {
|
|
|
|
websocketHandler.setWebsocketServer(this.wss);
|
|
|
|
}
|
2021-12-31 02:28:40 +04:00
|
|
|
if (Common.isLiquid() && config.DATABASE.ENABLED) {
|
2021-09-18 13:37:25 +04:00
|
|
|
blocks.setNewBlockCallback(async () => {
|
|
|
|
try {
|
|
|
|
await elementsParser.$parse();
|
|
|
|
} catch (e) {
|
|
|
|
logger.warn('Elements parsing error: ' + (e instanceof Error ? e.message : e));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2020-02-26 17:49:53 +07:00
|
|
|
websocketHandler.setupConnectionHandling();
|
|
|
|
statistics.setNewStatisticsEntryCallback(websocketHandler.handleNewStatistic.bind(websocketHandler));
|
|
|
|
blocks.setNewBlockCallback(websocketHandler.handleNewBlock.bind(websocketHandler));
|
|
|
|
memPool.setMempoolChangedCallback(websocketHandler.handleMempoolChange.bind(websocketHandler));
|
2021-01-06 23:31:33 +07:00
|
|
|
fiatConversion.setProgressChangedCallback(websocketHandler.handleNewConversionRates.bind(websocketHandler));
|
2021-01-05 18:57:06 +07:00
|
|
|
loadingIndicators.setProgressChangedCallback(websocketHandler.handleLoadingChanged.bind(websocketHandler));
|
2019-07-21 17:59:47 +03:00
|
|
|
}
|
|
|
|
|
2020-02-26 17:49:53 +07:00
|
|
|
setUpHttpApiRoutes() {
|
2022-07-11 19:15:28 +02:00
|
|
|
bitcoinRoutes.initRoutes(this.app);
|
2020-10-19 18:47:10 +07:00
|
|
|
if (config.STATISTICS.ENABLED && config.DATABASE.ENABLED) {
|
2022-07-11 19:15:28 +02:00
|
|
|
statisticsRoutes.initRoutes(this.app);
|
2022-02-08 18:28:53 +09:00
|
|
|
}
|
2022-02-16 21:20:28 +09:00
|
|
|
if (Common.indexingEnabled()) {
|
2022-07-11 19:15:28 +02:00
|
|
|
miningRoutes.initRoutes(this.app);
|
2020-10-19 18:47:10 +07:00
|
|
|
}
|
2021-04-25 02:38:46 +04:00
|
|
|
if (config.BISQ.ENABLED) {
|
2022-07-11 19:15:28 +02:00
|
|
|
bisqRoutes.initRoutes(this.app);
|
2020-09-10 14:46:23 +07:00
|
|
|
}
|
2021-12-31 02:28:40 +04:00
|
|
|
if (Common.isLiquid()) {
|
2022-07-11 19:15:28 +02:00
|
|
|
liquidRoutes.initRoutes(this.app);
|
2021-12-14 16:06:03 +04:00
|
|
|
}
|
2022-07-06 11:58:06 +02:00
|
|
|
if (config.LIGHTNING.ENABLED) {
|
|
|
|
generalLightningRoutes.initRoutes(this.app);
|
|
|
|
nodesRoutes.initRoutes(this.app);
|
|
|
|
channelsRoutes.initRoutes(this.app);
|
|
|
|
}
|
2020-02-26 17:49:53 +07:00
|
|
|
}
|
2020-02-16 22:15:07 +07:00
|
|
|
}
|
2019-11-06 15:35:02 +08:00
|
|
|
|
2020-02-16 22:15:07 +07:00
|
|
|
const server = new Server();
|