Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: 1.4 backports (not squash) #2176

Merged
merged 7 commits into from
Sep 27, 2024
36 changes: 25 additions & 11 deletions packages/dapi/lib/externalApis/tenderdash/BlockchainListener.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,12 @@ class BlockchainListener extends EventEmitter {
*/
constructor(tenderdashWsClient) {
super();

this.wsClient = tenderdashWsClient;

this.processLogger = logger.child({
process: 'BlockchainListener',
});
}

/**
Expand All @@ -30,14 +35,7 @@ class BlockchainListener extends EventEmitter {
* Subscribe to blocks and transaction results
*/
start() {
const processLogger = logger.child({
process: 'BlockchainListener',
});

processLogger.info('Subscribed to state transition results');

// Emit transaction results
this.wsClient.subscribe(TX_QUERY);
this.wsClient.on(TX_QUERY, (message) => {
const [hashString] = (message.events || []).map((event) => {
const hashAttribute = event.attributes.find((attribute) => attribute.key === 'hash');
Expand All @@ -53,15 +51,31 @@ class BlockchainListener extends EventEmitter {
return;
}

processLogger.trace(`received transaction result for ${hashString}`);
this.processLogger.trace(`Received transaction result for ${hashString}`);

this.emit(BlockchainListener.getTransactionEventName(hashString), message);
});

// TODO: It's not using
// Emit blocks and contained transactions
// this.wsClient.subscribe(NEW_BLOCK_QUERY);
// this.wsClient.on(NEW_BLOCK_QUERY, (message) => this.emit(EVENTS.NEW_BLOCK, message));
this.wsClient.on(NEW_BLOCK_QUERY, (message) => {
this.processLogger.trace('Received new platform block');

this.emit(EVENTS.NEW_BLOCK, message);
});

this.wsClient.on('connect', () => {
this.#subscribe();
});

if (this.wsClient.isConnected) {
this.#subscribe();
}
}

#subscribe() {
this.wsClient.subscribe(TX_QUERY);
this.wsClient.subscribe(NEW_BLOCK_QUERY);
this.processLogger.debug('Subscribed to platform blockchain events');
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const {
} = require('@dashevo/dapi-grpc');

const BlockchainListener = require('../../../externalApis/tenderdash/BlockchainListener');
const logger = require('../../../logger');

/**
* @param {BlockchainListener} blockchainListener
Expand All @@ -17,12 +18,23 @@ const BlockchainListener = require('../../../externalApis/tenderdash/BlockchainL
* @return {getStatusHandler}
*/
function getStatusHandlerFactory(blockchainListener, driveClient, tenderdashRpcClient) {
// Clean cache when new platform block committed
let cachedResponse = null;
let cleanCacheTimeout = null;

blockchainListener.on(BlockchainListener.EVENTS.NEW_BLOCK, () => {
function cleanCache() {
cachedResponse = null;
});

// cancel scheduled cache cleanup
if (cleanCacheTimeout !== null) {
clearTimeout(cleanCacheTimeout);
cleanCacheTimeout = null;
}

logger.trace({ endpoint: 'getStatus' }, 'cleanup cache');
}

// Clean cache when new platform block committed
blockchainListener.on(BlockchainListener.EVENTS.NEW_BLOCK, cleanCache);

// DAPI Software version
const packageJsonPath = path.resolve(__dirname, '..', '..', '..', '..', 'package.json');
Expand Down Expand Up @@ -210,6 +222,15 @@ function getStatusHandlerFactory(blockchainListener, driveClient, tenderdashRpcC
cachedResponse = new GetStatusResponse();
cachedResponse.setV0(v0);

// Cancel any existing scheduled cache cleanup
if (cleanCacheTimeout !== null) {
clearTimeout(cleanCacheTimeout);
cleanCacheTimeout = null;
}

// Clean cache in 3 minutes
cleanCacheTimeout = setTimeout(cleanCache, 3 * 60 * 1000);

return cachedResponse;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ describe('BlockchainListener', () => {
({ sinon } = this);
wsClientMock = new EventEmitter();
wsClientMock.subscribe = sinon.stub();

blockchainListener = new BlockchainListener(wsClientMock);
blockchainListener.start();

sinon.spy(blockchainListener, 'on');
sinon.spy(blockchainListener, 'off');
Expand Down Expand Up @@ -84,19 +84,23 @@ describe('BlockchainListener', () => {
});

describe('#start', () => {
it('should subscribe to transaction events from WS client', () => {
// TODO: We don't use it for now
// expect(wsClientMock.subscribe).to.be.calledTwice();
expect(wsClientMock.subscribe).to.be.calledOnce();
it('should subscribe to transaction events from WS client if it is connected', () => {
wsClientMock.isConnected = true;

blockchainListener.start();

expect(wsClientMock.subscribe).to.be.calledTwice();
expect(wsClientMock.subscribe.firstCall).to.be.calledWithExactly(
BlockchainListener.TX_QUERY,
);
// expect(wsClientMock.subscribe.secondCall).to.be.calledWithExactly(
// BlockchainListener.NEW_BLOCK_QUERY,
// );
expect(wsClientMock.subscribe.secondCall).to.be.calledWithExactly(
BlockchainListener.NEW_BLOCK_QUERY,
);
});

it.skip('should emit block when new block is arrived', (done) => {
it('should emit block when new block is arrived', (done) => {
blockchainListener.start();

blockchainListener.on(BlockchainListener.EVENTS.NEW_BLOCK, (message) => {
expect(message).to.be.deep.equal(blockMessageMock);

Expand All @@ -107,6 +111,8 @@ describe('BlockchainListener', () => {
});

it('should emit transaction when transaction is arrived', (done) => {
blockchainListener.start();

const topic = BlockchainListener.getTransactionEventName(transactionHash);

blockchainListener.on(topic, (message) => {
Expand Down
16 changes: 13 additions & 3 deletions packages/dashmate/src/docker/DockerCompose.js
Original file line number Diff line number Diff line change
Expand Up @@ -487,13 +487,23 @@ export default class DockerCompose {
* Logs
*
* @param {Config} config
* @return {Promise<void>}
* @param {string[]} services
* @param {Object} options
* @param {number} options.tail
* @return {Promise<{exitCode: number | null, out: string, err: string}>}
*/
async logs(config, services = []) {
async logs(config, services = [], options = {}) {
await this.throwErrorIfNotInstalled();

const args = [...services];
if (options.tail) {
args.unshift('--tail', options.tail.toString());
}

const commandOptions = this.#createOptions(config);

try {
return dockerCompose.logs(services, this.#createOptions(config));
return await dockerCompose.logs(args, commandOptions);
} catch (e) {
throw new DockerComposeError(e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,10 @@ and revoke the previous certificate in the ZeroSSL dashboard`,
description: chalk`ZeroSSL certificate is not valid.`,
solution: chalk`Please run {bold.cyanBright dashmate ssl zerossl obtain} to get a new one.`,
},
[ERRORS.ZERO_SSL_API_ERROR]: {
description: ssl?.data?.error?.message,
solution: chalk`Please contact ZeroSSL support if needed.`,
},
}[ssl.error] ?? {};

if (description) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ export default function collectSamplesTaskFactory(
services.map(async (service) => {
const [inspect, logs] = (await Promise.allSettled([
dockerCompose.inspectService(config, service.name),
dockerCompose.logs(config, [service.name]),
dockerCompose.logs(config, [service.name], { tail: 300000 }),
])).map((e) => e.value || e.reason);

if (logs?.out) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ export default function obtainZeroSSLCertificateTaskFactory(
case ERRORS.CERTIFICATE_ID_IS_NOT_SET:
// eslint-disable-next-line no-param-reassign
task.output = 'Certificate is not configured yet, creating a new one';

// We need to create a new certificate
ctx.certificate = null;
break;
case ERRORS.PRIVATE_KEY_IS_NOT_PRESENT:
// If certificate exists but private key does not, then we can't set up TLS connection
Expand All @@ -85,6 +88,9 @@ export default function obtainZeroSSLCertificateTaskFactory(
case ERRORS.CERTIFICATE_EXPIRES_SOON:
// eslint-disable-next-line no-param-reassign
task.output = `Certificate exists but expires in less than ${ctx.expirationDays} days at ${ctx.certificate.expires}. Obtain a new one`;

// We need to create a new certificate
ctx.certificate = null;
break;
case ERRORS.CERTIFICATE_IS_NOT_VALIDATED:
// eslint-disable-next-line no-param-reassign
Expand All @@ -93,7 +99,12 @@ export default function obtainZeroSSLCertificateTaskFactory(
case ERRORS.CERTIFICATE_IS_NOT_VALID:
// eslint-disable-next-line no-param-reassign
task.output = 'Certificate is not valid. Create a new one';

// We need to create a new certificate
ctx.certificate = null;
break;
case ERRORS.ZERO_SSL_API_ERROR:
throw ctx.error;
default:
throw new Error(`Unknown error: ${error}`);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export const ERRORS = {
CERTIFICATE_EXPIRES_SOON: 'CERTIFICATE_EXPIRES_SOON',
CERTIFICATE_IS_NOT_VALIDATED: 'CERTIFICATE_IS_NOT_VALIDATED',
CERTIFICATE_IS_NOT_VALID: 'CERTIFICATE_IS_NOT_VALID',
ZERO_SSL_API_ERROR: 'ZERO_SSL_API_ERROR',
};

/**
Expand Down Expand Up @@ -68,9 +69,22 @@ export default function validateZeroSslCertificateFactory(homeDir, getCertificat
data.isBundleFilePresent = fs.existsSync(data.bundleFilePath);

// This function will throw an error if certificate with specified ID is not present
const certificate = await getCertificate(data.apiKey, certificateId);
try {
data.certificate = await getCertificate(data.apiKey, certificateId);
} catch (e) {
if (e.code) {
data.error = e;

data.isExpiresSoon = certificate.isExpiredInDays(expirationDays);
return {
error: ERRORS.ZERO_SSL_API_ERROR,
data,
};
}

throw e;
}

data.isExpiresSoon = data.certificate.isExpiredInDays(expirationDays);

// If certificate exists but private key does not, then we can't setup TLS connection
// In this case we need to regenerate a certificate or put back this private key
Expand All @@ -82,17 +96,16 @@ export default function validateZeroSslCertificateFactory(homeDir, getCertificat
}

// We need to make sure that external IP and certificate IP match
if (certificate.common_name !== data.externalIp) {
if (data.certificate.common_name !== data.externalIp) {
return {
error: ERRORS.EXTERNAL_IP_MISMATCH,
data,
};
}

if (['pending_validation', 'draft'].includes(certificate.status)) {
if (['pending_validation', 'draft'].includes(data.certificate.status)) {
// Certificate is already created, so we just need to pass validation
// and download certificate file
data.certificate = certificate;

// We need to download new certificate bundle
data.isBundleFilePresent = false;
Expand All @@ -103,7 +116,7 @@ export default function validateZeroSslCertificateFactory(homeDir, getCertificat
};
}

if (certificate.status !== 'issued' || data.isExpiresSoon) {
if (data.certificate.status !== 'issued' || data.isExpiresSoon) {
// Certificate is going to expire soon, or current certificate is not valid
// we need to obtain a new one

Expand All @@ -128,8 +141,6 @@ export default function validateZeroSslCertificateFactory(homeDir, getCertificat
}

// Certificate is valid, so we might need only to download certificate bundle
data.certificate = certificate;

return {
data,
};
Expand Down
Loading
Loading