mempool/backend/src/index.ts

252 lines
8.1 KiB
TypeScript
Raw Normal View History

2019-07-21 16:59:47 +02:00
const config = require('../mempool-config.json');
import * as fs from 'fs';
import * as express from 'express';
import * as compression from 'compression';
import * as http from 'http';
import * as https from 'https';
import * as WebSocket from 'ws';
import routes from './routes';
2019-07-21 16:59:47 +02:00
import blocks from './api/blocks';
import memPool from './api/mempool';
import mempoolBlocks from './api/mempool-blocks';
import diskCache from './api/disk-cache';
2019-07-21 16:59:47 +02:00
import statistics from './api/statistics';
2020-02-23 13:16:50 +01:00
import { Block, TransactionExtended, Statistic } from './interfaces';
2019-07-21 16:59:47 +02:00
import fiatConversion from './api/fiat-conversion';
class Server {
2019-07-21 16:59:47 +02:00
private wss: WebSocket.Server;
private server: https.Server | http.Server;
private app: any;
constructor() {
this.app = express();
2019-07-21 16:59:47 +02:00
this.app
.use((req, res, next) => {
2019-07-21 16:59:47 +02:00
res.setHeader('Access-Control-Allow-Origin', '*');
next();
})
.use(compression());
if (config.SSL === true) {
2019-07-21 16:59:47 +02:00
const credentials = {
cert: fs.readFileSync(config.SSL_CERT_FILE_PATH),
key: fs.readFileSync(config.SSL_KEY_FILE_PATH),
2019-07-21 16:59:47 +02:00
};
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 });
2019-07-21 16:59:47 +02:00
}
this.setUpRoutes();
this.setUpWebsocketHandling();
this.setUpMempoolCache();
this.runMempoolIntervalFunctions();
statistics.startStatistics();
fiatConversion.startService();
this.server.listen(config.HTTP_PORT, () => {
console.log(`Server started on port ${config.HTTP_PORT}`);
2019-07-21 16:59:47 +02:00
});
}
private async runMempoolIntervalFunctions() {
2020-02-17 14:39:20 +01:00
await memPool.updateMemPoolInfo();
2019-07-21 16:59:47 +02:00
await blocks.updateBlocks();
await memPool.updateMempool();
setTimeout(this.runMempoolIntervalFunctions.bind(this), config.ELECTRS_POLL_RATE_MS);
2019-07-21 16:59:47 +02:00
}
private setUpMempoolCache() {
const cacheData = diskCache.loadData();
if (cacheData) {
memPool.setMempool(JSON.parse(cacheData));
}
process.on('SIGINT', (options) => {
console.log('SIGINT');
diskCache.saveData(JSON.stringify(memPool.getMempool()));
process.exit(2);
});
}
private setUpWebsocketHandling() {
this.wss.on('connection', (client: WebSocket) => {
client.on('message', (message: any) => {
2019-07-21 16:59:47 +02:00
try {
const parsedMessage = JSON.parse(message);
if (parsedMessage.action === 'want') {
2020-02-17 14:39:20 +01:00
client['want-blocks'] = parsedMessage.data.indexOf('blocks') > -1;
client['want-mempool-blocks'] = parsedMessage.data.indexOf('mempool-blocks') > -1;
client['want-live-2h-chart'] = parsedMessage.data.indexOf('live-2h-chart') > -1;
2020-02-17 14:39:20 +01:00
client['want-stats'] = parsedMessage.data.indexOf('stats') > -1;
}
if (parsedMessage && parsedMessage['track-tx']) {
if (/^[a-fA-F0-9]{64}$/.test(parsedMessage['track-tx'])) {
client['track-tx'] = parsedMessage['track-tx'];
} else {
client['track-tx'] = null;
}
2019-07-21 16:59:47 +02:00
}
2020-02-17 14:39:20 +01:00
2020-02-23 13:16:50 +01:00
if (parsedMessage && parsedMessage['track-address']) {
if (/^([a-km-zA-HJ-NP-Z1-9]{26,35}|[a-km-zA-HJ-NP-Z1-9]{80}|[a-z]{2,5}1[ac-hj-np-z02-9]{8,87})$/
.test(parsedMessage['track-address'])) {
client['track-address'] = parsedMessage['track-address'];
} else {
client['track-address'] = null;
}
}
2020-02-17 14:39:20 +01:00
if (parsedMessage.action === 'init') {
const _blocks = blocks.getBlocks();
if (!_blocks) {
return;
}
client.send(JSON.stringify({
'mempoolInfo': memPool.getMempoolInfo(),
'vBytesPerSecond': memPool.getVBytesPerSecond(),
'blocks': _blocks,
'conversions': fiatConversion.getTickers()['BTCUSD'],
'mempool-blocks': mempoolBlocks.getMempoolBlocks(),
}));
}
2019-07-21 16:59:47 +02:00
} catch (e) {
console.log(e);
}
});
});
2019-07-21 16:59:47 +02:00
statistics.setNewStatisticsEntryCallback((stats: Statistic) => {
this.wss.clients.forEach((client: WebSocket) => {
2019-07-21 16:59:47 +02:00
if (client.readyState !== WebSocket.OPEN) {
return;
}
2020-02-17 14:39:20 +01:00
if (!client['want-live-2h-chart']) {
return;
2019-07-21 16:59:47 +02:00
}
2020-02-17 14:39:20 +01:00
client.send(JSON.stringify({
'live-2h-chart': stats
}));
2019-07-21 16:59:47 +02:00
});
});
2020-02-23 13:16:50 +01:00
blocks.setNewBlockCallback((block: Block, txIds: string[], transactions: TransactionExtended[]) => {
this.wss.clients.forEach((client) => {
2019-07-21 16:59:47 +02:00
if (client.readyState !== WebSocket.OPEN) {
return;
}
2020-02-17 14:39:20 +01:00
if (!client['want-blocks']) {
return;
}
2020-02-23 13:16:50 +01:00
const response = {
'block': block
};
if (client['track-tx'] && txIds.indexOf(client['track-tx']) > -1) {
client['track-tx'] = null;
2020-02-23 13:16:50 +01:00
response['txConfirmed'] = true;
}
if (client['track-address']) {
const foundTransactions: TransactionExtended[] = [];
transactions.forEach((tx) => {
if (tx.vin.some((vin) => vin.prevout.scriptpubkey_address === client['track-address'])) {
2020-02-23 13:16:50 +01:00
foundTransactions.push(tx);
return;
}
if (tx.vout.some((vout) => vout.scriptpubkey_address === client['track-address'])) {
2020-02-23 13:16:50 +01:00
foundTransactions.push(tx);
}
});
if (foundTransactions.length) {
response['address-block-transactions'] = foundTransactions;
}
}
2020-02-23 13:16:50 +01:00
client.send(JSON.stringify(response));
});
});
2020-02-23 13:16:50 +01:00
memPool.setMempoolChangedCallback((newMempool: { [txid: string]: TransactionExtended }, newTransactions: TransactionExtended[]) => {
mempoolBlocks.updateMempoolBlocks(newMempool);
2020-02-17 14:39:20 +01:00
const mBlocks = mempoolBlocks.getMempoolBlocks();
const mempoolInfo = memPool.getMempoolInfo();
const vBytesPerSecond = memPool.getVBytesPerSecond();
this.wss.clients.forEach((client: WebSocket) => {
if (client.readyState !== WebSocket.OPEN) {
return;
}
2020-02-17 14:39:20 +01:00
const response = {};
if (client['want-stats']) {
response['mempoolInfo'] = mempoolInfo;
response['vBytesPerSecond'] = vBytesPerSecond;
}
if (client['want-mempool-blocks']) {
response['mempool-blocks'] = mBlocks;
}
2020-02-23 13:16:50 +01:00
// Send all new incoming transactions related to tracked address
if (client['track-address']) {
const foundTransactions: TransactionExtended[] = [];
newTransactions.forEach((tx) => {
const someVin = tx.vin.some((vin) => vin.prevout.scriptpubkey_address === client['track-address']);
if (someVin) {
foundTransactions.push(tx);
return;
}
const someVout = tx.vout.some((vout) => vout.scriptpubkey_address === client['track-address']);
if (someVout) {
foundTransactions.push(tx);
}
});
if (foundTransactions.length) {
response['address-transactions'] = foundTransactions;
}
}
2020-02-17 14:39:20 +01:00
if (Object.keys(response).length) {
client.send(JSON.stringify(response));
}
2019-07-21 16:59:47 +02:00
});
});
}
private setUpRoutes() {
this.app
.get(config.API_ENDPOINT + 'fees/recommended', routes.getRecommendedFees)
.get(config.API_ENDPOINT + 'fees/mempool-blocks', routes.getMempoolBlocks)
2019-07-21 16:59:47 +02:00
.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))
2020-02-16 18:26:57 +01:00
.get(config.API_ENDPOINT + 'statistics/1y', routes.get1YStatistics.bind(routes))
2019-07-21 16:59:47 +02:00
;
2019-11-06 08:35:02 +01:00
}
}
2019-11-06 08:35:02 +01:00
const server = new Server();