From d3205788fb33dbba24a99d8e054b18573983dffc Mon Sep 17 00:00:00 2001 From: Ivan Shumkov Date: Fri, 12 Jan 2024 19:54:02 +0300 Subject: [PATCH] fix(dashmate): dapi kills host machine on container stop (#1670) --- .dockerignore | 1 + .github/package-filters/js-packages.yml | 7 ++++- .pnp.cjs | 1 + Dockerfile | 5 ---- .../lib/externalApis/tenderdash/WsClient.js | 22 ++++---------- packages/dapi/pm2.yml | 24 --------------- packages/dapi/scripts/api.js | 3 -- packages/dapi/scripts/core-streams.js | 3 -- .../configs/defaults/getBaseConfigFactory.js | 3 ++ .../defaults/getTestnetConfigFactory.js | 1 + .../configs/getConfigFileMigrationsFactory.js | 3 +- packages/dashmate/docker-compose.yml | 15 ++++++++-- packages/dashmate/package.json | 1 + .../dashmate/src/config/configJsonSchema.js | 24 ++++++++++++++- .../src/core/waitForMasternodesSync.js | 17 ++++++----- .../waitForNodeToBeReadyTaskFactory.js | 30 ++++++++++++------- .../migrateConfigFileFactory.spec.js | 8 ++--- packages/js-dapi-client/lib/DAPIClient.js | 7 ++++- packages/js-dapi-client/lib/logger/index.js | 6 +++- .../transport/GrpcTransport/GrpcTransport.js | 2 ++ .../test/unit/DAPIClient.spec.js | 4 +++ yarn.lock | 1 + 22 files changed, 107 insertions(+), 81 deletions(-) delete mode 100644 packages/dapi/pm2.yml rename packages/dashmate/test/unit/{commands => }/config/configFile/migrateConfigFileFactory.spec.js (82%) diff --git a/.dockerignore b/.dockerignore index 2d179b88e9..d93249fec2 100644 --- a/.dockerignore +++ b/.dockerignore @@ -23,6 +23,7 @@ packages/*/dist packages/*/wasm packages/*/lib/wasm packages/*/node_modules +packages/*/.env !packages/platform-test-suite/test diff --git a/.github/package-filters/js-packages.yml b/.github/package-filters/js-packages.yml index b9e3618155..d158bb39f6 100644 --- a/.github/package-filters/js-packages.yml +++ b/.github/package-filters/js-packages.yml @@ -71,7 +71,12 @@ dash: &dash dashmate: - .github/workflows/tests* - packages/dashmate/** - - *dash + - *dashpay-contract + - *masternode-reward-shares-contract + - *dpns-contract + - *withdrawals-contract + - *wallet-lib + - *dapi-client '@dashevo/platform-test-suite': - .github/workflows/tests* diff --git a/.pnp.cjs b/.pnp.cjs index 508c94cd6e..7bd36798d5 100755 --- a/.pnp.cjs +++ b/.pnp.cjs @@ -8702,6 +8702,7 @@ const RAW_RUNTIME_STATE = ["@babel/core", "npm:7.23.3"],\ ["@babel/eslint-parser", "virtual:880cda903c2a2be387819a3f857d21494004437a03c92969b9853f7bdeebdfed08d417e68364ee9e158338603a6d78d690c457a55ab11e56398bc10f0ad232fc#npm:7.23.3"],\ ["@dashevo/bls", "npm:1.2.9"],\ + ["@dashevo/dapi-client", "workspace:packages/js-dapi-client"],\ ["@dashevo/dashcore-lib", "npm:0.21.0"],\ ["@dashevo/dashd-rpc", "npm:18.2.0"],\ ["@dashevo/dashpay-contract", "workspace:packages/dashpay-contract"],\ diff --git a/Dockerfile b/Dockerfile index 25ab93f437..756e0ae73b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -392,9 +392,6 @@ LABEL description="DAPI Node.JS" # Install ZMQ shared library RUN apk add --no-cache zeromq-dev -# Install pm2 -RUN npm install -g pm2 - WORKDIR /platform/packages/dapi COPY --from=build-dapi /platform/.yarn /platform/.yarn @@ -411,5 +408,3 @@ RUN cp /platform/packages/dapi/.env.example /platform/packages/dapi/.env EXPOSE 2500 2501 2510 USER node - -ENTRYPOINT ["yarn", "node", "/usr/local/bin/pm2-runtime", "pm2.yml"] diff --git a/packages/dapi/lib/externalApis/tenderdash/WsClient.js b/packages/dapi/lib/externalApis/tenderdash/WsClient.js index 1bf9e260bc..f886483bf2 100644 --- a/packages/dapi/lib/externalApis/tenderdash/WsClient.js +++ b/packages/dapi/lib/externalApis/tenderdash/WsClient.js @@ -43,6 +43,11 @@ class WsClient extends EventEmitter { this.emit(event.type, event); + this.ws.removeAllListeners(); + this.ws.terminate(); + this.ws = null; + this.isConnected = false; + setTimeout(this.open.bind(this), this.autoReconnectInterval); } else { const event = { @@ -82,12 +87,6 @@ class WsClient extends EventEmitter { this.emit(event.type, event); - if (e.code === 1000) { // close normal - this.disconnect(); - - return; - } - reconnect(); }; @@ -100,16 +99,7 @@ class WsClient extends EventEmitter { this.emit(event.type, event); - switch (e.code) { - case 'ENOTFOUND': - case 'EAI_AGAIN': - case 'ECONNREFUSED': - reconnect(); - break; - default: - this.disconnect(); - break; - } + reconnect(); }; const onMessageListener = (rawData) => { diff --git a/packages/dapi/pm2.yml b/packages/dapi/pm2.yml deleted file mode 100644 index df8f450e3a..0000000000 --- a/packages/dapi/pm2.yml +++ /dev/null @@ -1,24 +0,0 @@ -- script: scripts/api.js - name: api - exec_mode: cluster - watch: false - instances: 2 - source_map_support: false - merge_logs: true - autorestart: true - out_file: "/dev/null" - error_file: "/dev/null" - vizion: false - wait_ready: true -- script: scripts/core-streams.js - name: core-streams - exec_mode: cluster - watch: false - instances: 2 - source_map_support: false - merge_logs: true - autorestart: true - out_file: "/dev/null" - error_file: "/dev/null" - vizion: false - wait_ready: true diff --git a/packages/dapi/scripts/api.js b/packages/dapi/scripts/api.js index 5339ed977a..72d3b1f033 100644 --- a/packages/dapi/scripts/api.js +++ b/packages/dapi/scripts/api.js @@ -170,6 +170,3 @@ process.on('SIGINT', () => { process.exit(); }); - -// Tell PM2 that process ready to receive connections -process.send('ready'); diff --git a/packages/dapi/scripts/core-streams.js b/packages/dapi/scripts/core-streams.js index 7c3620781f..0bc1a11522 100644 --- a/packages/dapi/scripts/core-streams.js +++ b/packages/dapi/scripts/core-streams.js @@ -219,6 +219,3 @@ process.on('SIGINT', () => { process.exit(); }); - -// Tell PM2 that process ready to receive connections -process.send('ready'); diff --git a/packages/dashmate/configs/defaults/getBaseConfigFactory.js b/packages/dashmate/configs/defaults/getBaseConfigFactory.js index 76b639b516..f8645fa8eb 100644 --- a/packages/dashmate/configs/defaults/getBaseConfigFactory.js +++ b/packages/dashmate/configs/defaults/getBaseConfigFactory.js @@ -137,6 +137,9 @@ export default function getBaseConfigFactory(homeDir) { api: { docker: { image: `dashpay/dapi:${dockerImageVersion}`, + deploy: { + replicas: 1, + }, build: { enabled: false, context: path.join(PACKAGE_ROOT_DIR, '..', '..'), diff --git a/packages/dashmate/configs/defaults/getTestnetConfigFactory.js b/packages/dashmate/configs/defaults/getTestnetConfigFactory.js index 26e75f5b74..7ea6d6f3f3 100644 --- a/packages/dashmate/configs/defaults/getTestnetConfigFactory.js +++ b/packages/dashmate/configs/defaults/getTestnetConfigFactory.js @@ -83,6 +83,7 @@ export default function getTestnetConfigFactory(homeDir, getBaseConfig) { genesis_time: '2023-11-02T10:18:00.000Z', chain_id: 'dash-testnet-37', validator_quorum_type: 6, + initial_core_chain_locked_height: 918609, }, }, }, diff --git a/packages/dashmate/configs/getConfigFileMigrationsFactory.js b/packages/dashmate/configs/getConfigFileMigrationsFactory.js index 9c3bf957d3..e13d4acc19 100644 --- a/packages/dashmate/configs/getConfigFileMigrationsFactory.js +++ b/packages/dashmate/configs/getConfigFileMigrationsFactory.js @@ -410,8 +410,9 @@ export default function getConfigFileMigrationsFactory(homeDir, defaultConfigs) Object.entries(configFile.configs) .forEach(([name, options]) => { if (defaultConfigs.has(name)) { - options.platform.drive.tenderdash.genesis = defaultConfigs.get(name).get('options.platform.drive.tenderdash.genesis'); + options.platform.drive.tenderdash.genesis = defaultConfigs.get(name).get('platform.drive.tenderdash.genesis'); } + options.platform.dapi.api.docker.deploy = base.get('platform.dapi.api.docker.deploy'); }); return configFile; diff --git a/packages/dashmate/docker-compose.yml b/packages/dashmate/docker-compose.yml index 8ecff50b5b..5dcdba9f8f 100644 --- a/packages/dashmate/docker-compose.yml +++ b/packages/dashmate/docker-compose.yml @@ -102,6 +102,9 @@ services: labels: org.dashmate.service.title: "DAPI API" restart: unless-stopped + deploy: + mode: replicated + replicas: ${PLATFORM_DAPI_API_DOCKER_DEPLOY_REPLICAS:-1} depends_on: - drive_tenderdash environment: @@ -120,16 +123,22 @@ services: - TENDERMINT_RPC_HOST=drive_tenderdash - TENDERMINT_RPC_PORT=${PLATFORM_DRIVE_TENDERDASH_RPC_PORT:?err} - NODE_ENV=${ENVIRONMENT:?err} - command: --only api + command: yarn run api stop_grace_period: 10s profiles: - platform + expose: + - 3004 + - 3005 dapi_tx_filter_stream: image: ${PLATFORM_DAPI_API_DOCKER_IMAGE:?err} labels: org.dashmate.service.title: "DAPI Transactions Filter Stream" restart: unless-stopped + deploy: + mode: replicated + replicas: ${PLATFORM_DAPI_API_DOCKER_DEPLOY_REPLICAS:-1} environment: - TX_FILTER_STREAM_GRPC_PORT=3006 - DASHCORE_RPC_HOST=core @@ -144,10 +153,12 @@ services: - NETWORK=devnet - TENDERMINT_RPC_HOST=drive_tenderdash - TENDERMINT_RPC_PORT=26657 - command: --only core-streams + command: yarn run core-streams stop_grace_period: 10s profiles: - platform + expose: + - 3006 dapi_envoy: image: ${PLATFORM_DAPI_ENVOY_DOCKER_IMAGE:?err} diff --git a/packages/dashmate/package.json b/packages/dashmate/package.json index fe46f6854a..d91d65b18e 100644 --- a/packages/dashmate/package.json +++ b/packages/dashmate/package.json @@ -57,6 +57,7 @@ "@babel/core": "^7.23.3", "@babel/eslint-parser": "^7.23.3", "@dashevo/bls": "~1.2.9", + "@dashevo/dapi-client": "workspace:*", "@dashevo/dashcore-lib": "~0.21.0", "@dashevo/dashd-rpc": "^18.2.0", "@dashevo/dashpay-contract": "workspace:*", diff --git a/packages/dashmate/src/config/configJsonSchema.js b/packages/dashmate/src/config/configJsonSchema.js index d60a43e2d9..90d2b9fe0c 100644 --- a/packages/dashmate/src/config/configJsonSchema.js +++ b/packages/dashmate/src/config/configJsonSchema.js @@ -440,7 +440,29 @@ export default { type: 'object', properties: { docker: { - $ref: '#/definitions/dockerWithBuild', + type: 'object', + properties: { + image: { + type: 'string', + minLength: 1, + }, + deploy: { + type: 'object', + properties: { + replicas: { + type: 'integer', + minimum: 0, + }, + }, + additionalProperties: false, + required: ['replicas'], + }, + build: { + $ref: '#/definitions/dockerBuild', + }, + }, + required: ['image', 'build', 'deploy'], + additionalProperties: false, }, }, required: ['docker'], diff --git a/packages/dashmate/src/core/waitForMasternodesSync.js b/packages/dashmate/src/core/waitForMasternodesSync.js index fdb686cd17..8f115410ea 100644 --- a/packages/dashmate/src/core/waitForMasternodesSync.js +++ b/packages/dashmate/src/core/waitForMasternodesSync.js @@ -15,9 +15,17 @@ export default async function waitForMasternodesSync(rpcClient, progressCallback do { try { await rpcClient.mnsync('next'); + + ({ + result: { IsSynced: isSynced }, + } = await rpcClient.mnsync('status')); + + ({ + result: { verificationprogress: verificationProgress }, + } = await rpcClient.getBlockchainInfo()); } catch (e) { // Core RPC is not started yet - if (!e.message.includes('Dash JSON-RPC: Request Error: ') && e.code !== -28) { + if (!e.message.includes('Dash JSON-RPC: Request Error: ') && !e.message.includes('Timeout') && e.code !== -28) { throw e; } @@ -29,13 +37,6 @@ export default async function waitForMasternodesSync(rpcClient, progressCallback continue; } - ({ - result: { IsSynced: isSynced }, - } = await rpcClient.mnsync('status')); - ({ - result: { verificationprogress: verificationProgress }, - } = await rpcClient.getBlockchainInfo()); - if (!isSynced) { progressCallback(verificationProgress); diff --git a/packages/dashmate/src/listr/tasks/platform/waitForNodeToBeReadyTaskFactory.js b/packages/dashmate/src/listr/tasks/platform/waitForNodeToBeReadyTaskFactory.js index 371097e061..e1dd60ee28 100644 --- a/packages/dashmate/src/listr/tasks/platform/waitForNodeToBeReadyTaskFactory.js +++ b/packages/dashmate/src/listr/tasks/platform/waitForNodeToBeReadyTaskFactory.js @@ -1,14 +1,12 @@ +import DAPIClient from '@dashevo/dapi-client'; import { Listr } from 'listr2'; import wait from '../../../util/wait.js'; /** * - * @param {createTenderdashRpcClient} createTenderdashRpcClient * @return {waitForNodeToBeReadyTask} */ -export default function waitForNodeToBeReadyTaskFactory( - createTenderdashRpcClient, -) { +export default function waitForNodeToBeReadyTaskFactory() { /** * @typedef waitForNodeToBeReadyTask * @param {Config} config @@ -19,18 +17,28 @@ export default function waitForNodeToBeReadyTaskFactory( { title: `Wait for node ${config.getName()} to be ready`, task: async () => { - const host = config.get('platform.drive.tenderdash.rpc.host'); - const port = config.get('platform.drive.tenderdash.rpc.port'); + let host = config.get('platform.dapi.envoy.http.host'); + const port = config.get('platform.dapi.envoy.http.port'); - const tenderdashRpcClient = createTenderdashRpcClient({ host, port }); + if (host === '0.0.0.0') { + host = '127.0.0.1'; + } + + const dapiClient = new DAPIClient({ + dapiAddresses: [`${host}:${port}:no-ssl`], + loggerOptions: { + level: 'silent', + }, + }); let success = false; do { - const response = await tenderdashRpcClient.request('status', {}).catch(() => {}); + const response = await dapiClient.platform.getEpochsInfo(0, 1, { + retries: 0, + }) + .catch(() => {}); - if (response) { - success = !response.result.sync_info.catching_up; - } + success = Boolean(response); if (!success) { await wait(500); diff --git a/packages/dashmate/test/unit/commands/config/configFile/migrateConfigFileFactory.spec.js b/packages/dashmate/test/unit/config/configFile/migrateConfigFileFactory.spec.js similarity index 82% rename from packages/dashmate/test/unit/commands/config/configFile/migrateConfigFileFactory.spec.js rename to packages/dashmate/test/unit/config/configFile/migrateConfigFileFactory.spec.js index dae0bbe871..eb7efe51ae 100644 --- a/packages/dashmate/test/unit/commands/config/configFile/migrateConfigFileFactory.spec.js +++ b/packages/dashmate/test/unit/config/configFile/migrateConfigFileFactory.spec.js @@ -1,9 +1,9 @@ import fs from 'fs'; import path from 'path'; -import HomeDir from '../../../../../src/config/HomeDir.js'; -import { PACKAGE_ROOT_DIR } from '../../../../../src/constants.js'; -import createDIContainer from '../../../../../src/createDIContainer.js'; -import getConfigFileDataV0250 from '../../../../../src/test/fixtures/getConfigFileDataV0250.js'; +import HomeDir from '../../../../src/config/HomeDir.js'; +import { PACKAGE_ROOT_DIR } from '../../../../src/constants.js'; +import createDIContainer from '../../../../src/createDIContainer.js'; +import getConfigFileDataV0250 from '../../../../src/test/fixtures/getConfigFileDataV0250.js'; describe('migrateConfigFileFactory', () => { let mockConfigFileData; diff --git a/packages/js-dapi-client/lib/DAPIClient.js b/packages/js-dapi-client/lib/DAPIClient.js index d6561c2468..026cb51d18 100644 --- a/packages/js-dapi-client/lib/DAPIClient.js +++ b/packages/js-dapi-client/lib/DAPIClient.js @@ -34,6 +34,7 @@ class DAPIClient extends EventEmitter { blockHeadersProviderOptions: BlockHeadersProvider.defaultOptions, loggerOptions: { identifier: '', + level: undefined, }, ...options, }; @@ -57,7 +58,10 @@ class DAPIClient extends EventEmitter { this.core = new CoreMethodsFacade(jsonRpcTransport, grpcTransport); this.platform = new PlatformMethodsFacade(grpcTransport); - this.logger = logger.getForId(this.options.loggerOptions.identifier); + this.logger = logger.getForId( + this.options.loggerOptions.identifier, + this.options.loggerOptions.level, + ); this.initBlockHeadersProvider(); } @@ -89,6 +93,7 @@ DAPIClient.EVENTS = EVENTS; * @property {boolean} [throwDeadlineExceeded] * @property {object} [loggerOptions] * @property {string} [loggerOptions.identifier] + * @property {string} [loggerOptions.level] * @property {BlockHeadersProvider} [blockHeadersProvider] * @property {BlockHeadersProviderOptions} [blockHeadersProviderOptions] */ diff --git a/packages/js-dapi-client/lib/logger/index.js b/packages/js-dapi-client/lib/logger/index.js index c59df018a9..a06b34c145 100644 --- a/packages/js-dapi-client/lib/logger/index.js +++ b/packages/js-dapi-client/lib/logger/index.js @@ -1,7 +1,9 @@ const util = require('util'); const winston = require('winston'); -const LOG_LEVEL = process.env.LOG_LEVEL || 'info'; +// TODO: Refactor to use params instead on envs + +const LOG_LEVEL = process.env.LOG_LEVEL || 'silent'; const LOG_TO_FILE = process.env.LOG_WALLET_TO_FILE || 'false'; // Log levels: @@ -36,6 +38,7 @@ const createLogger = (formats = [], id = '') => { const transports = [ new winston.transports.Console({ format, + silent: LOG_LEVEL === 'silent', }), ]; @@ -44,6 +47,7 @@ const createLogger = (formats = [], id = '') => { new winston.transports.File({ filename: `wallet${id !== '' ? `_${id}` : ''}`, format, + silent: LOG_LEVEL === 'silent', }), ); } diff --git a/packages/js-dapi-client/lib/transport/GrpcTransport/GrpcTransport.js b/packages/js-dapi-client/lib/transport/GrpcTransport/GrpcTransport.js index f792c0738a..0618ca42ad 100644 --- a/packages/js-dapi-client/lib/transport/GrpcTransport/GrpcTransport.js +++ b/packages/js-dapi-client/lib/transport/GrpcTransport/GrpcTransport.js @@ -98,6 +98,8 @@ class GrpcTransport { throw responseError; } + // TODO: Shouldn't we call address.markAsBanned() here? + if (options.retries === 0) { throw new MaxRetriesReachedError(responseError); } diff --git a/packages/js-dapi-client/test/unit/DAPIClient.spec.js b/packages/js-dapi-client/test/unit/DAPIClient.spec.js index 73283a9d8a..dd57d1ff7e 100644 --- a/packages/js-dapi-client/test/unit/DAPIClient.spec.js +++ b/packages/js-dapi-client/test/unit/DAPIClient.spec.js @@ -26,6 +26,7 @@ describe('DAPIClient', () => { blockHeadersProviderOptions: BlockHeadersProvider.defaultOptions, loggerOptions: { identifier: '', + level: undefined, }, }); @@ -51,6 +52,7 @@ describe('DAPIClient', () => { blockHeadersProviderOptions: BlockHeadersProvider.defaultOptions, loggerOptions: { identifier: '', + level: undefined, }, }); @@ -82,6 +84,7 @@ describe('DAPIClient', () => { blockHeadersProviderOptions: BlockHeadersProvider.defaultOptions, loggerOptions: { identifier: '', + level: undefined, }, }); @@ -109,6 +112,7 @@ describe('DAPIClient', () => { blockHeadersProviderOptions: BlockHeadersProvider.defaultOptions, loggerOptions: { identifier: '', + level: undefined, }, }); diff --git a/yarn.lock b/yarn.lock index 3c98e0fa3d..3cc59e34fb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6557,6 +6557,7 @@ __metadata: "@babel/core": "npm:^7.23.3" "@babel/eslint-parser": "npm:^7.23.3" "@dashevo/bls": "npm:~1.2.9" + "@dashevo/dapi-client": "workspace:*" "@dashevo/dashcore-lib": "npm:~0.21.0" "@dashevo/dashd-rpc": "npm:^18.2.0" "@dashevo/dashpay-contract": "workspace:*"