diff --git a/packages/bitcore-node/scripts/reloadConfig.sh b/packages/bitcore-node/scripts/reloadConfig.sh new file mode 100755 index 00000000000..92dbd4fdcee --- /dev/null +++ b/packages/bitcore-node/scripts/reloadConfig.sh @@ -0,0 +1,83 @@ +#!/bin/sh + +dir=$(pwd) + +if [ $# = 0 ]; then + pid_paths=$dir/pids/* + + for path in $pid_paths; do + pid=$(cat "$path") + printf "$(basename "$path" .pid)::$pid " + pids="$pids $pid" + done + echo '' + + kill -USR1 $pids && + echo "Refreshed all workers" + exit 0 +fi + +if [ $1 = --help ]; then + cat << EOF +Usage: $(basename "$0") [OPTIONS] [WORKER...] + +Reload configuration for bitcore workers + +Options: + --help Show this help message and exit + list List all running workers + +Arguments: + WORKER Name(s) of worker(s) to reload configs (e.g., all api p2p) + If no worker is specified, reload all running workers configs. + +Examples: + $(basename "$0") Reload config for all workers + $(basename "$0") api p2p Reload config for 'api' and 'p2p' workers + $(basename "$0") list List all running workers +EOF + exit 0 +fi + +if [ $1 = list ]; then + pid_paths=$(ls $dir/pids/*.pid 2>/dev/null) + for path in $pid_paths; do + worker=$(basename "$path" .pid) + pid=$(cat "$path") + printf "%-3s %s\n" "$worker" "$pid" + done + exit 0 +fi + +for worker in $@; do + if [ ! -f "$dir/pids/$worker.pid" ]; then + echo "$worker is not running\n$worker.pid not found in $dir/pids" + case $worker in + all|api|p2p) ;; + *) + echo "$worker is not a standard worker\nstandard workers: all, api, p2p" + ;; + esac + exit 1 + fi +done + +pid_paths=$( + for worker in $@; do + printf "$dir/pids/$worker.pid " + done +) + +pids=$( + for path in $pid_paths; do + cat $path + printf ' ' + done +) + +kill -USR1 $pids && + +cat << EOF +Sent reload signal(s) SIGUSR1 to '$@' +pids: $pids +EOF \ No newline at end of file diff --git a/packages/bitcore-node/src/config.ts b/packages/bitcore-node/src/config.ts index bcedb8dd5c7..c422a107232 100644 --- a/packages/bitcore-node/src/config.ts +++ b/packages/bitcore-node/src/config.ts @@ -1,3 +1,4 @@ +import cluster from 'cluster'; import fs from 'fs'; import { cpus, homedir } from 'os'; import path from 'path'; @@ -26,7 +27,8 @@ function findConfig(): ConfigType | undefined { } bitcoreConfigPath = path.join(bitcoreConfigPath, 'bitcore.config.json'); } - logger.info('Using config at: ' + bitcoreConfigPath); + + logger.info(`${cluster.isPrimary ? 'Main' : 'Child'} ${process.pid} using config at: ${path.resolve(bitcoreConfigPath)}`); let rawBitcoreConfig; try { @@ -62,7 +64,7 @@ function setTrustedPeers(config: ConfigType): ConfigType { } return config; } -const Config = function(): ConfigType { +const loadConfig = function(): ConfigType { let config: ConfigType = { maxPoolSize: 50, port: 3000, @@ -129,4 +131,4 @@ const Config = function(): ConfigType { return config; }; -export default Config(); \ No newline at end of file +export default loadConfig; diff --git a/packages/bitcore-node/src/modules/bitcoin/p2p.ts b/packages/bitcore-node/src/modules/bitcoin/p2p.ts index 29abcb99c3d..a2ac01b8be2 100644 --- a/packages/bitcore-node/src/modules/bitcoin/p2p.ts +++ b/packages/bitcore-node/src/modules/bitcoin/p2p.ts @@ -5,6 +5,7 @@ import { StateStorage } from '../../models/state'; import { TransactionStorage } from '../../models/transaction'; import { ChainStateProvider } from '../../providers/chain-state'; import { Libs } from '../../providers/libs'; +import { Config } from '../../services/config'; import { BaseP2PWorker } from '../../services/p2p'; import { SpentHeightIndicators } from '../../types/Coin'; import { IUtxoNetworkConfig } from '../../types/Config'; @@ -57,6 +58,10 @@ export class BitcoinP2PWorker extends BaseP2PWorker { network: this.network, messages: this.messages }); + + process.on('SIGUSR1', async () => { + await this.reload(); + }); } cacheInv(type: number, hash: string): void { @@ -186,6 +191,36 @@ export class BitcoinP2PWorker extends BaseP2PWorker { } } + async reload() { + this.chainConfig = Config.chainConfig({ chain: this.chain, network: this.network }) as IUtxoNetworkConfig; + const configPeerUris: string[] = []; + + for (const peer of Object.values(this.chainConfig.trustedPeers) as any[]) { + const uri = peer.host + ':' + peer.port; + configPeerUris.push(uri); + const hashes = Object.values(this.pool._addrs).map((a: any) => a.hash); + const addr = this.pool._addAddr({ ip: { v4: peer.host }, port: peer.port }); + if (!hashes.includes(addr.hash)) { + logger.info(`Adding peer ${uri}`); + } + } + + for (const addr of Object.values(this.pool._addrs) as any[]) { + const uri = addr.ip.v4 + ':' + addr.port; + if (!configPeerUris.includes(uri)) { + this.pool._addrs = (this.pool._addrs as any[]).filter(({ hash }) => hash !== addr.hash); + if (this.pool._connectedPeers[addr.hash]) { + logger.info(`Removing peer ${uri}`); + } else { + logger.info(`Removing unconnected peer ${uri}`); + continue; + } + this.pool._connectedPeers[addr.hash].disconnect(); + delete this.pool._connectedPeers[addr.hash]; + } + }; + } + public async getHeaders(candidateHashes: string[]): Promise { let received = false; return new Promise(async resolve => { diff --git a/packages/bitcore-node/src/modules/moralis/api/csp.ts b/packages/bitcore-node/src/modules/moralis/api/csp.ts index dd7bdbf2de2..0c561e279db 100644 --- a/packages/bitcore-node/src/modules/moralis/api/csp.ts +++ b/packages/bitcore-node/src/modules/moralis/api/csp.ts @@ -1,7 +1,7 @@ import os from 'os'; import request from 'request'; import Web3 from 'web3'; -import config from '../../../config'; +import { Config } from '../../../../src/services/config'; import logger from '../../../logger'; import { MongoBound } from '../../../models/base'; import { CacheStorage } from '../../../models/cache'; @@ -19,8 +19,6 @@ import { GetBlockBeforeTimeParams, GetBlockParams, StreamAddressUtxosParams, Str import { isDateValid } from '../../../utils'; import { ReadableWithEventPipe } from '../../../utils/streamWithEventPipe'; - - export interface MoralisAddressSubscription { id?: string; message?: string; @@ -30,17 +28,26 @@ export interface MoralisAddressSubscription { export class MoralisStateProvider extends BaseEVMStateProvider { baseUrl = 'https://deep-index.moralis.io/api/v2.2'; baseStreamUrl = 'https://api.moralis-streams.com/streams/evm'; - apiKey = config.externalProviders?.moralis?.apiKey; - baseWebhookurl = config.externalProviders?.moralis?.webhookBaseUrl; - headers = { - 'Content-Type': 'application/json', - 'X-API-Key': this.apiKey, - }; + apiKey; + baseWebhookurl; + headers; constructor(chain: string) { super(chain); + this.loadConfig(); + } + + loadConfig() { + const config = Config.get(); + this.apiKey = config.externalProviders?.moralis?.apiKey; + this.baseWebhookurl = config.externalProviders?.moralis?.webhookBaseUrl; + this.headers = { + 'Content-Type': 'application/json', + 'X-API-Key': this.apiKey, + }; } + // @override async getBlockBeforeTime(params: GetBlockBeforeTimeParams): Promise { const { chain, network, time } = params; diff --git a/packages/bitcore-node/src/modules/ripple/api/csp.ts b/packages/bitcore-node/src/modules/ripple/api/csp.ts index 80b8a683b0e..5bb7a6a49b9 100644 --- a/packages/bitcore-node/src/modules/ripple/api/csp.ts +++ b/packages/bitcore-node/src/modules/ripple/api/csp.ts @@ -11,12 +11,12 @@ import { TransactionMetadata } from 'xrpl/dist/npm/models/transactions'; import { Node } from 'xrpl/dist/npm/models/transactions/metadata'; -import Config from '../../../config'; import logger from '../../../logger'; import { CacheStorage } from '../../../models/cache'; import { ICoin } from '../../../models/coin'; import { WalletAddressStorage } from '../../../models/walletAddress'; import { InternalStateProvider } from '../../../providers/chain-state/internal/internal'; +import { Config } from '../../../services/config'; import { Storage } from '../../../services/storage'; import { IBlock } from '../../../types/Block'; import { ChainNetwork } from '../../../types/ChainNetwork'; @@ -44,7 +44,7 @@ export class RippleStateProvider extends InternalStateProvider implements IChain constructor(public chain: string = 'XRP') { super(chain, RippleDbWalletTransactions); - this.config = Config.chains[this.chain]; + this.config = Config.get().chains[this.chain]; } async getClient(network: string) { diff --git a/packages/bitcore-node/src/providers/chain-state/evm/api/csp.ts b/packages/bitcore-node/src/providers/chain-state/evm/api/csp.ts index d585d1f4655..b3c448df4fe 100644 --- a/packages/bitcore-node/src/providers/chain-state/evm/api/csp.ts +++ b/packages/bitcore-node/src/providers/chain-state/evm/api/csp.ts @@ -3,7 +3,6 @@ import { ObjectID } from 'mongodb'; import Web3 from 'web3'; import { Transaction } from 'web3-eth'; import { AbiItem } from 'web3-utils'; -import Config from '../../../../config'; import { historical, internal, @@ -15,6 +14,7 @@ import { ITransaction } from '../../../../models/baseTransaction'; import { CacheStorage } from '../../../../models/cache'; import { WalletAddressStorage } from '../../../../models/walletAddress'; import { InternalStateProvider } from '../../../../providers/chain-state/internal/internal'; +import { Config } from '../../../../services/config'; import { Storage } from '../../../../services/storage'; import { IBlock } from '../../../../types/Block'; import { ChainId } from '../../../../types/ChainNetwork'; @@ -69,7 +69,7 @@ export class BaseEVMStateProvider extends InternalStateProvider implements IChai constructor(public chain: string = 'ETH') { super(chain); - this.config = Config.chains[this.chain] as IChainConfig; + this.config = Config.get().chains[this.chain] as IChainConfig; } async getWeb3(network: string, params?: { type: IProvider['dataType'] }): Promise { diff --git a/packages/bitcore-node/src/providers/chain-state/evm/api/routes.ts b/packages/bitcore-node/src/providers/chain-state/evm/api/routes.ts index 869ad1ec5c9..25ee879e8c6 100644 --- a/packages/bitcore-node/src/providers/chain-state/evm/api/routes.ts +++ b/packages/bitcore-node/src/providers/chain-state/evm/api/routes.ts @@ -1,7 +1,6 @@ import cors from 'cors'; import { Router } from 'express'; import Web3 from 'web3'; -import config from '../../../../config'; import logger from '../../../../logger'; import { WebhookStorage } from '../../../../models/webhook'; import { Config } from '../../../../services/config'; @@ -231,7 +230,7 @@ export class EVMRouter { private _validateMoralisWebhook(req, res, next) { - const secret = config.externalProviders?.moralis?.streamSecret; + const secret = Config.get().externalProviders?.moralis?.streamSecret; if (!secret) { return res.status(404).send('Moralis not configured'); } @@ -247,7 +246,7 @@ export class EVMRouter { } private postMoralisWebhook(router: Router) { - const webhookCors = config.externalProviders?.moralis?.webhookCors; + const webhookCors = Config.get().externalProviders?.moralis?.webhookCors; router.post(`/webhook/${this.chain}/:network/moralis`, cors(webhookCors), this._validateMoralisWebhook, async (req, res) => { try { const { network } = req.params; diff --git a/packages/bitcore-node/src/providers/chain-state/evm/p2p/sync.ts b/packages/bitcore-node/src/providers/chain-state/evm/p2p/sync.ts index 4c23eb1c625..c0d6863536d 100644 --- a/packages/bitcore-node/src/providers/chain-state/evm/p2p/sync.ts +++ b/packages/bitcore-node/src/providers/chain-state/evm/p2p/sync.ts @@ -3,9 +3,9 @@ import * as os from 'os'; import { Worker as Thread, threadId } from 'worker_threads'; import { CryptoRpc } from 'crypto-rpc'; import { ChainStateProvider } from '../../'; -import Config from '../../../../config'; import logger, { timestamp } from '../../../../logger'; import { StateStorage } from '../../../../models/state'; +import { Config } from '../../../../services/config'; import { IEVMNetworkConfig } from '../../../../types/Config'; import { wait } from '../../../../utils'; import { EVMBlockStorage } from '../models/block'; diff --git a/packages/bitcore-node/src/providers/chain-state/svm/api/csp.ts b/packages/bitcore-node/src/providers/chain-state/svm/api/csp.ts index a62b6f3f317..2df5ccc1c6e 100644 --- a/packages/bitcore-node/src/providers/chain-state/svm/api/csp.ts +++ b/packages/bitcore-node/src/providers/chain-state/svm/api/csp.ts @@ -5,9 +5,9 @@ import { TokenListProvider } from '@solana/spl-token-registry'; import { CryptoRpc } from 'crypto-rpc'; import { SolRpc } from 'crypto-rpc/lib/sol/SolRpc'; import { instructionKeys } from 'crypto-rpc/lib/sol/transaction-parser'; -import Config from '../../../../config'; import logger from '../../../../logger'; import { CacheStorage } from '../../../../models/cache'; +import { Config } from '../../../../services/config'; import { IBlock } from '../../../../types/Block'; import { CoinListingJSON } from '../../../../types/Coin'; import { IChainConfig, IProvider, ISVMNetworkConfig } from '../../../../types/Config'; @@ -29,7 +29,7 @@ export class BaseSVMStateProvider extends InternalStateProvider implements IChai constructor(public chain: string = 'SOL') { super(chain); - this.config = Config.chains[this.chain] as IChainConfig; + this.config = Config.get().chains[this.chain] as IChainConfig; } async getRpc(network: string, params?: { type: IProvider['dataType'] }): Promise { diff --git a/packages/bitcore-node/src/routes/api/fee.ts b/packages/bitcore-node/src/routes/api/fee.ts index 93d92e2b8c1..07fd5985d12 100644 --- a/packages/bitcore-node/src/routes/api/fee.ts +++ b/packages/bitcore-node/src/routes/api/fee.ts @@ -1,5 +1,5 @@ import express, { Request, Response } from 'express'; -import config from '../../config'; +import { Config } from '../../../src/services/config'; import logger from '../../logger'; import { ChainStateProvider } from '../../providers/chain-state'; import { QueryType } from '../../types/Api'; @@ -35,7 +35,7 @@ router.get('/:target', CacheMiddleware(CacheTimes.Second), async (req: Request, return res.status(400).send('invalid target specified'); } if (!mode) { - mode = (config.chains[chain]?.[network] as IUtxoNetworkConfig)?.defaultFeeMode; + mode = (Config.get().chains[chain]?.[network] as IUtxoNetworkConfig)?.defaultFeeMode; } else if (!feeModes[chain]) { mode = undefined; } else if (!feeModes[chain]?.includes(mode)) { diff --git a/packages/bitcore-node/src/routes/api/wallet.ts b/packages/bitcore-node/src/routes/api/wallet.ts index 616af9c3b85..b9c1e6875fa 100644 --- a/packages/bitcore-node/src/routes/api/wallet.ts +++ b/packages/bitcore-node/src/routes/api/wallet.ts @@ -1,6 +1,6 @@ import { Validation } from 'crypto-wallet-core'; import { Request, Response, Router } from 'express'; -import config from '../../config'; +import { Config } from '../../../src/services/config'; import logger from '../../logger'; import { ChainStateProvider } from '../../providers/chain-state'; import { StreamWalletAddressesParams } from '../../types/namespaces/ChainStateProvider'; @@ -112,7 +112,7 @@ router.post('/:pubKey', Auth.authenticateMiddleware, async (req: AuthenticatedRe if (req.headers['x-reprocess']) { const reprocessOk = Auth.verifyRequestSignature({ message: ['reprocess', '/addAddresses' + pubKey, JSON.stringify(req.body)].join('|'), - pubKey: config.services.socket.bwsKeys[0], + pubKey: Config.get().services.socket.bwsKeys[0], signature: req.headers['x-reprocess'] }); if (!reprocessOk) { diff --git a/packages/bitcore-node/src/routes/index.ts b/packages/bitcore-node/src/routes/index.ts index 10efc4ebc1c..d7b5e04db9a 100644 --- a/packages/bitcore-node/src/routes/index.ts +++ b/packages/bitcore-node/src/routes/index.ts @@ -1,6 +1,5 @@ import cors from 'cors'; import express from 'express'; -import config from '../config'; import { Config } from '../services/config'; import * as apiRoutes from './api'; import { CacheMiddleware, CacheTimes, LogMiddleware, RateLimiter } from './middleware'; @@ -24,7 +23,7 @@ app.use( const chains = Config.chains(); const networks: any = {}; for (const chain of chains) { - for (const network of Object.keys(config.chains[chain])) { + for (const network of Object.keys(Config.get().chains[chain])) { networks[chain] = networks[chain] || {}; Object.assign(networks[chain], { [network]: true diff --git a/packages/bitcore-node/src/routes/status.ts b/packages/bitcore-node/src/routes/status.ts index b79154f93df..1c9c8734dfb 100644 --- a/packages/bitcore-node/src/routes/status.ts +++ b/packages/bitcore-node/src/routes/status.ts @@ -1,5 +1,5 @@ import express from 'express'; -import config from '../config'; +import { Config } from '../../src/services/config'; import { PerformanceTracker } from '../decorators/Loggify'; import { StateStorage } from '../models/state'; import { ChainNetwork } from '../types/ChainNetwork'; @@ -8,8 +8,8 @@ const router = express.Router({ mergeParams: true }); router.get('/enabled-chains', function(_, res) { const chainNetworks = new Array(); - for (const chain of Object.keys(config.chains)) { - for (const network of Object.keys(config.chains[chain])) { + for (const chain of Object.keys(Config.get().chains)) { + for (const network of Object.keys(Config.get().chains[chain])) { chainNetworks.push({ chain, network }); } } diff --git a/packages/bitcore-node/src/services/api.ts b/packages/bitcore-node/src/services/api.ts index bc6d01a10c9..f07c7affba8 100644 --- a/packages/bitcore-node/src/services/api.ts +++ b/packages/bitcore-node/src/services/api.ts @@ -1,5 +1,4 @@ import * as http from 'http'; -import config from '../config'; import { LoggifyClass } from '../decorators/Loggify'; import logger from '../logger'; import app from '../routes'; @@ -46,6 +45,17 @@ export class ApiService { this.stopped = false; this.httpServer = new http.Server(app); this.httpServer.timeout = this.timeout; + + process.on('SIGUSR1', async () => { + this.reload(); + }); + + process.on('message', async (msg: any) => { + if (msg === 'reloadconfig') { + await this.reload(); + } + }); + this.httpServer.listen(this.port, () => { logger.info(`Starting API Service on port ${this.port}`); this.socketService.start({ server: this.httpServer }); @@ -54,6 +64,16 @@ export class ApiService { return this.httpServer; } + async reload() { + if (this.port !== Config.get().port) { + this.port = Config.get().port; + if (!this.stopped) { + await this.stop(); + await this.start(); + } + } + } + async stop() { this.stopped = true; await this.socketService.stop(); @@ -69,5 +89,5 @@ export class ApiService { // TOOO: choose a place in the config for the API timeout and include it here export const Api = new ApiService({ - port: config.port + port: Config.get().port }); diff --git a/packages/bitcore-node/src/services/config.ts b/packages/bitcore-node/src/services/config.ts index 8360767bb72..03c62b5a00d 100644 --- a/packages/bitcore-node/src/services/config.ts +++ b/packages/bitcore-node/src/services/config.ts @@ -1,4 +1,6 @@ -import config from '../config'; +import cluster from 'cluster'; +import loadConfig from '../config'; +import logger from '../logger'; import { ChainNetwork } from '../types/ChainNetwork'; import { ConfigType } from '../types/Config'; import { valueOrDefault } from '../utils'; @@ -6,19 +8,61 @@ import { valueOrDefault } from '../utils'; type ServiceName = keyof ConfigType['services']; export class ConfigService { - _config: ConfigType; + config: ConfigType; - constructor({ _config = config } = {}) { - this._config = _config; + constructor({ config = loadConfig() } = {}) { + this.config = config; + + // Listen for SIGUSR1 on both main and child processes + process.on('SIGUSR1', () => { + this.reload(); + if (cluster.workers) { + for (const worker of Object.values(cluster.workers)) { + worker?.send('reloadconfig'); + } + } + }); + process.on('message', msg => { + if (msg === 'reloadconfig') { + this.reload(); + } + }); + } + + public reload() { + const oldConfig = this.config; + this.config = loadConfig(); + + // Only show config change for one process + if (!cluster.isPrimary) + return; + const diff = (obj1: any, obj2: any, path: string[] = []) => { + const changes: string[] = []; + const keys = new Set([...Object.keys(obj1 || {}), ...Object.keys(obj2 || {})]); + for (const key of keys) { + const val1 = obj1?.[key]; + const val2 = obj2?.[key]; + const currentPath = [...path, key]; + if (typeof val1 === 'object' && val1 !== null && typeof val2 === 'object' && val2 !== null) { + changes.push(...diff(val1, val2, currentPath)); + } else if (val1 !== val2) { + changes.push(currentPath.join('.')); + logger.info(`${currentPath.join('.')} ${JSON.stringify(val1)} -> ${JSON.stringify(val2)}`); + } + } + return changes; + }; + + diff(oldConfig, this.config); } public get() { - return this._config; + return this.config; } public updateConfig(partialConfig: Partial) { const newConfig = Object.assign({}, this.get(), partialConfig); - this._config = newConfig; + this.config = newConfig; } public chains() { diff --git a/packages/bitcore-node/src/services/worker.ts b/packages/bitcore-node/src/services/worker.ts index dded5a4284b..218088976a8 100644 --- a/packages/bitcore-node/src/services/worker.ts +++ b/packages/bitcore-node/src/services/worker.ts @@ -1,6 +1,6 @@ import cluster, { Worker as ClusterWorker } from 'cluster'; import { EventEmitter } from 'events'; -import config from '../config'; +import { Config } from '../../src/services/config'; import { LoggifyClass } from '../decorators/Loggify'; import logger from '../logger'; import { CallbackType } from '../types/Callback'; @@ -20,7 +20,7 @@ export class WorkerService extends EventEmitter { if (cluster.isPrimary) { logger.verbose(`Master ${process.pid} is running`); if (!args.DEBUG) { - for (let worker = 0; worker < config.numWorkers; worker++) { + for (let worker = 0; worker < Config.get().numWorkers; worker++) { const newWorker = cluster.fork(); logger.verbose(`Starting worker number ${worker}`); newWorker.on('message', (msg: any) => { diff --git a/packages/bitcore-node/src/workers/all.ts b/packages/bitcore-node/src/workers/all.ts index c8ef66e7058..203e4ecbb3e 100644 --- a/packages/bitcore-node/src/workers/all.ts +++ b/packages/bitcore-node/src/workers/all.ts @@ -1,5 +1,6 @@ import cluster from 'cluster'; import 'source-map-support/register'; +import fs from 'fs'; import logger from '../logger'; import { Modules } from '../modules'; import { Api } from '../services/api'; @@ -23,6 +24,8 @@ export const FullClusteredWorker = async () => { services.push(Storage, Event); if (cluster.isPrimary) { + fs.mkdirSync('pids', { recursive: true }); + fs.writeFileSync('pids/all.pid', String(process.pid)); services.push(P2P); if (args.DEBUG) { services.push(Api); @@ -48,6 +51,10 @@ const stop = async () => { } stopping = true; + if (cluster.isPrimary) { + fs.unlinkSync('pids/all.pid'); + } + setTimeout(() => { logger.error('All workers did not shut down gracefully after 30 seconds, exiting'); process.exit(1); diff --git a/packages/bitcore-node/src/workers/api.ts b/packages/bitcore-node/src/workers/api.ts index 049e29660b7..ab921053a33 100644 --- a/packages/bitcore-node/src/workers/api.ts +++ b/packages/bitcore-node/src/workers/api.ts @@ -1,5 +1,6 @@ import cluster from 'cluster'; import 'source-map-support/register'; +import fs from 'fs'; import logger from '../logger'; import { Modules } from '../modules'; import { Api } from '../services/api'; @@ -22,6 +23,8 @@ export const ClusteredApiWorker = async () => { services.push(Storage, Event); if (cluster.isPrimary) { + fs.mkdirSync('pids', { recursive: true }); + fs.writeFileSync('pids/api.pid', String(process.pid)); if (args.DEBUG || !args.CLUSTER) { services.push(Api); } else { @@ -46,6 +49,10 @@ const stop = async () => { } stopping = true; + if (cluster.isPrimary) { + fs.unlinkSync('pids/api.pid'); + } + setTimeout(() => { logger.warn('API Worker did not shut down gracefully after 30 seconds, exiting'); process.exit(1); diff --git a/packages/bitcore-node/src/workers/p2p.ts b/packages/bitcore-node/src/workers/p2p.ts index 64354308624..630a374ecab 100644 --- a/packages/bitcore-node/src/workers/p2p.ts +++ b/packages/bitcore-node/src/workers/p2p.ts @@ -1,5 +1,6 @@ import cluster from 'cluster'; import 'source-map-support/register'; +import fs from 'fs'; import logger from '../logger'; import { Modules } from '../modules'; import { Config } from '../services/config'; @@ -18,6 +19,9 @@ export const P2pWorker = async () => { process.on('SIGTERM', stop); process.on('SIGINT', stop); + fs.mkdirSync('pids', { recursive: true }); + fs.writeFileSync('pids/p2p.pid', String(process.pid)); + services.push(Storage, Event); const { CHAIN: chain, NETWORK: network } = process.env; @@ -54,6 +58,8 @@ const stop = async () => { } stopping = true; + fs.unlinkSync('pids/p2p.pid'); + setTimeout(() => { logger.warn('P2P Worker did not shut down gracefully after 30 seconds, exiting'); process.exit(1); diff --git a/packages/bitcore-node/test/benchmark/benchmark.ts b/packages/bitcore-node/test/benchmark/benchmark.ts index 6c65873a2f8..c66f355cb48 100644 --- a/packages/bitcore-node/test/benchmark/benchmark.ts +++ b/packages/bitcore-node/test/benchmark/benchmark.ts @@ -3,13 +3,13 @@ import { BitcoreLib as bitcoreLib } from 'crypto-wallet-core'; const { Transaction, PrivateKey } = bitcoreLib; const UnspentOutput = Transaction.UnspentOutput; -import config from '../../src/config'; import { Storage } from '../../src/services/storage'; import { BitcoinBlockStorage } from '../../src/models/block'; import { BitcoinBlockType } from '../../src/types/namespaces/Bitcoin/Block'; import { resetDatabase } from '../helpers/index.js'; import * as crypto from 'crypto'; import { BitcoinTransactionType } from '../../src/types/namespaces/Bitcoin/Transaction'; +import { Config } from '../../src/services/config'; function randomHash() { return crypto.randomBytes(32).toString('hex'); @@ -110,7 +110,7 @@ function newAddress() { function startBenchmarkDatabase() { const storageArgs = { - dbHost: config.dbHost, + dbHost: Config.get().dbHost, dbName: 'bitcore-benchmark' }; diff --git a/packages/bitcore-node/test/helpers/integration.ts b/packages/bitcore-node/test/helpers/integration.ts index c6b33c05f53..a1a1d81acf0 100644 --- a/packages/bitcore-node/test/helpers/integration.ts +++ b/packages/bitcore-node/test/helpers/integration.ts @@ -1,10 +1,10 @@ -import config from '../../src/config'; +import { Config } from '../../src/services/config'; import { Modules } from '../../src/modules'; import { Storage } from '../../src/services/storage'; import { wait } from '../../src/utils'; const storageArgs = { - dbHost: config.dbHost, + dbHost: Config.get().dbHost, dbName: 'bitcore-integration' }; diff --git a/packages/bitcore-node/test/integration/ethereum/p2p.spec.ts b/packages/bitcore-node/test/integration/ethereum/p2p.spec.ts index 3a0ec6150ba..3986e069c4b 100644 --- a/packages/bitcore-node/test/integration/ethereum/p2p.spec.ts +++ b/packages/bitcore-node/test/integration/ethereum/p2p.spec.ts @@ -2,7 +2,6 @@ import * as BitcoreClient from 'bitcore-client'; import { expect } from 'chai'; import { Web3 } from 'crypto-wallet-core'; import sinon from 'sinon'; -import config from '../../../src/config'; import { CacheStorage } from '../../../src/models/cache'; import { EthP2pWorker } from '../../../src/modules/ethereum/p2p/p2p'; import { EVMBlockStorage } from '../../../src/providers/chain-state/evm/models/block'; @@ -11,11 +10,12 @@ import { IEVMNetworkConfig } from '../../../src/types/Config'; import { wait } from '../../../src/utils'; import { resetDatabase } from '../../helpers'; import { intAfterHelper, intBeforeHelper } from '../../helpers/integration'; +import { Config } from '../../../src/services/config'; const { StreamUtil } = BitcoreClient; const chain = 'ETH'; const network = 'regtest'; -const chainConfig = config.chains[chain][network] as IEVMNetworkConfig; +const chainConfig = Config.get().chains[chain][network] as IEVMNetworkConfig; const name = 'EthereumWallet-Ci'; const storageType = 'Level'; const baseUrl = 'http://localhost:3000/api'; diff --git a/packages/bitcore-node/test/integration/matic/p2p.spec.ts b/packages/bitcore-node/test/integration/matic/p2p.spec.ts index d4a5acf5199..4fa8343a47d 100644 --- a/packages/bitcore-node/test/integration/matic/p2p.spec.ts +++ b/packages/bitcore-node/test/integration/matic/p2p.spec.ts @@ -2,7 +2,6 @@ import * as BitcoreClient from 'bitcore-client'; import { expect } from 'chai'; import { Web3, Transactions } from 'crypto-wallet-core'; import sinon from 'sinon'; -import config from '../../../src/config'; import { CacheStorage } from '../../../src/models/cache'; import { EVMBlockStorage } from '../../../src/providers/chain-state/evm/models/block'; import { EVMP2pWorker } from '../../../src/providers/chain-state/evm/p2p/p2p'; @@ -11,11 +10,12 @@ import { IEVMNetworkConfig } from '../../../src/types/Config'; import { wait } from '../../../src/utils'; import { resetDatabase } from '../../helpers'; import { intAfterHelper, intBeforeHelper } from '../../helpers/integration'; +import { Config } from '../../../src/services/config'; const { StreamUtil } = BitcoreClient; const chain = 'MATIC'; const network = 'regtest'; -const chainConfig = config.chains[chain][network] as IEVMNetworkConfig; +const chainConfig = Config.get().chains[chain][network] as IEVMNetworkConfig; const name = 'PolygonWallet-Ci'; const storageType = 'Level'; const baseUrl = 'http://localhost:3000/api'; diff --git a/packages/bitcore-node/test/integration/models/wallet.spec.ts b/packages/bitcore-node/test/integration/models/wallet.spec.ts index 01cab772372..c7b78da33a4 100644 --- a/packages/bitcore-node/test/integration/models/wallet.spec.ts +++ b/packages/bitcore-node/test/integration/models/wallet.spec.ts @@ -1,6 +1,5 @@ import { Wallet, IWalletExt } from 'bitcore-client'; import { expect } from 'chai'; -import config from '../../../src/config'; import { WalletStorage } from '../../../src/models/wallet'; import { WalletAddressStorage } from '../../../src/models/walletAddress'; import { AsyncRPC } from '../../../src/rpc'; @@ -8,13 +7,14 @@ import { Api } from '../../../src/services/api'; import { Event } from '../../../src/services/event'; import { IUtxoNetworkConfig } from '../../../src/types/Config'; import { intAfterHelper, intBeforeHelper } from '../../helpers/integration'; +import { Config } from '../../../src/services/config'; let lockedWallet: Wallet; const walletName = 'Test Wallet'; const password = 'iamsatoshi'; const chain = 'BTC'; const network = 'regtest'; -const chainConfig = config.chains[chain][network] as IUtxoNetworkConfig; +const chainConfig = Config.get().chains[chain][network] as IUtxoNetworkConfig; const creds = chainConfig.rpc; const rpc = new AsyncRPC(creds.username, creds.password, creds.host, creds.port); diff --git a/packages/bitcore-node/test/integration/verification.spec.ts b/packages/bitcore-node/test/integration/verification.spec.ts index 0581d8bb81a..1fe7c951081 100644 --- a/packages/bitcore-node/test/integration/verification.spec.ts +++ b/packages/bitcore-node/test/integration/verification.spec.ts @@ -1,5 +1,4 @@ import { expect } from 'chai'; -import config from '../../src/config'; import { BitcoinBlockStorage } from '../../src/models/block'; import { CoinStorage } from '../../src/models/coin'; import { TransactionStorage } from '../../src/models/transaction'; @@ -16,7 +15,7 @@ const chain = 'BTC'; const network = 'regtest'; const address = '2MuYKLUaKCenkEpwPkWUwYpBoDBNA2dgY3t'; -const chainConfig = config.chains[chain][network] as IUtxoNetworkConfig; +const chainConfig = Config.get().chains[chain][network] as IUtxoNetworkConfig; const creds = chainConfig.rpc; const rpc = new AsyncRPC(creds.username, creds.password, creds.host, creds.port); diff --git a/packages/bitcore-node/test/integration/wallet-benchmark.spec.ts b/packages/bitcore-node/test/integration/wallet-benchmark.spec.ts index 0ebdfa45e88..a1ad174fd4c 100644 --- a/packages/bitcore-node/test/integration/wallet-benchmark.spec.ts +++ b/packages/bitcore-node/test/integration/wallet-benchmark.spec.ts @@ -2,7 +2,6 @@ import { Wallet } from 'bitcore-client'; import { ParseApiStream } from 'bitcore-client'; import { expect } from 'chai'; import * as io from 'socket.io-client'; -import config from '../../src/config'; import { MongoBound } from '../../src/models/base'; import { CoinStorage, ICoin } from '../../src/models/coin'; import { TransactionStorage } from '../../src/models/transaction'; @@ -17,10 +16,11 @@ import { wait } from '../../src/utils'; import { createWallet } from '../benchmark/wallet-benchmark'; import { resetDatabase } from '../helpers'; import { intAfterHelper, intBeforeHelper } from '../helpers/integration'; +import { Config } from '../../src/services/config'; const chain = 'BTC'; const network = 'regtest'; -const chainConfig = config.chains[chain][network] as IUtxoNetworkConfig; +const chainConfig = Config.get().chains[chain][network] as IUtxoNetworkConfig; const creds = chainConfig.rpc; const rpc = new AsyncRPC(creds.username, creds.password, creds.host, creds.port); diff --git a/packages/bitcore-node/test/integration/websocket.spec.ts b/packages/bitcore-node/test/integration/websocket.spec.ts index 6e5fb99e333..5d91fe56b01 100644 --- a/packages/bitcore-node/test/integration/websocket.spec.ts +++ b/packages/bitcore-node/test/integration/websocket.spec.ts @@ -1,7 +1,6 @@ import { expect } from 'chai'; import sinon from 'sinon'; import * as io from 'socket.io-client'; -import config from '../../src/config'; import { BitcoinP2PWorker } from '../../src/modules/bitcoin/p2p'; import { AsyncRPC } from '../../src/rpc'; import { Api } from '../../src/services/api'; @@ -9,10 +8,11 @@ import { Event } from '../../src/services/event'; import { IUtxoNetworkConfig } from '../../src/types/Config'; import { resetDatabase } from '../helpers'; import { BitcoreLib } from 'crypto-wallet-core'; +import { Config } from '../../src/services/config'; const chain = 'BTC'; const network = 'regtest'; -const chainConfig = config.chains[chain][network] as IUtxoNetworkConfig; +const chainConfig = Config.get().chains[chain][network] as IUtxoNetworkConfig; const creds = chainConfig.rpc; const rpc = new AsyncRPC(creds.username, creds.password, creds.host, creds.port); const { PrivateKey } = BitcoreLib; diff --git a/packages/bitcore-node/test/verification/rpc-verify.ts b/packages/bitcore-node/test/verification/rpc-verify.ts index b64fa00d030..c19bf5cf763 100755 --- a/packages/bitcore-node/test/verification/rpc-verify.ts +++ b/packages/bitcore-node/test/verification/rpc-verify.ts @@ -1,6 +1,5 @@ #!/usr/bin/env node import { expect } from 'chai'; -import config from '../../src/config'; import logger from '../../src/logger'; import { BitcoinBlockStorage, IBtcBlock } from '../../src/models/block'; import { CoinStorage } from '../../src/models/coin'; @@ -11,6 +10,7 @@ import { AsyncRPC } from '../../src/rpc'; import { Storage } from '../../src/services/storage'; import { ChainNetwork } from '../../src/types/ChainNetwork'; import { IUtxoNetworkConfig } from '../../src/types/Config'; +import { Config } from '../../src/services/config'; const SATOSHI = 100000000.0; @@ -235,7 +235,7 @@ if (require.main === module) chain: process.env.CHAIN || 'BTC', network: process.env.NETWORK || 'testnet' }; - const creds = (config.chains[info.chain][info.network] as IUtxoNetworkConfig).rpc; + const creds = (Config.get().chains[info.chain][info.network] as IUtxoNetworkConfig).rpc; await Storage.start({}); logger.info('verifying blocks');