diff --git a/config/config.json b/config/config.json index 5cbff178d5..8f3679f4c2 100644 --- a/config/config.json +++ b/config/config.json @@ -168,6 +168,7 @@ } } }, + "maximumAssertionSizeInKb": 2500, "commandExecutorVerboseLoggingEnabled": false, "appDataPath": "data", "logLevel": "info", @@ -302,6 +303,7 @@ } } }, + "maximumAssertionSizeInKb": 2500, "commandExecutorVerboseLoggingEnabled": false, "appDataPath": "data", "logLevel": "trace", @@ -449,6 +451,7 @@ } } }, + "maximumAssertionSizeInKb": 2500, "commandExecutorVerboseLoggingEnabled": false, "appDataPath": "data", "logLevel": "trace", @@ -597,6 +600,7 @@ } } }, + "maximumAssertionSizeInKb": 2500, "commandExecutorVerboseLoggingEnabled": false, "appDataPath": "data", "logLevel": "trace", diff --git a/src/commands/protocols/common/handle-protocol-message-command.js b/src/commands/protocols/common/handle-protocol-message-command.js index 43b74e4e8a..f6a559d2d8 100644 --- a/src/commands/protocols/common/handle-protocol-message-command.js +++ b/src/commands/protocols/common/handle-protocol-message-command.js @@ -1,5 +1,5 @@ import Command from '../../command.js'; -import { NETWORK_MESSAGE_TYPES } from '../../../constants/constants.js'; +import { BYTES_IN_KILOBYTE, NETWORK_MESSAGE_TYPES } from '../../../constants/constants.js'; class HandleProtocolMessageCommand extends Command { constructor(ctx) { @@ -127,6 +127,18 @@ class HandleProtocolMessageCommand extends Command { this.blockchainModuleManager.getR0(blockchain), getAsk(), ]); + const blockchainAssertionSizeInKb = blockchainAssertionSize / BYTES_IN_KILOBYTE; + if (blockchainAssertionSizeInKb > this.config.maximumAssertionSizeInKb) { + this.logger.warn( + `The size of the received assertion exceeds the maximum limit allowed.. Maximum allowed assertion size in kb: ${this.config.maximumAssertionSizeInKb}, assertion size read from blockchain in kb: ${blockchainAssertionSizeInKb}`, + ); + return { + errorMessage: + 'The size of the received assertion exceeds the maximum limit allowed.', + agreementId, + agreementData, + }; + } const now = await this.blockchainModuleManager.getBlockchainTimestamp(blockchain); diff --git a/src/commands/protocols/publish/sender/publish-schedule-messages-command.js b/src/commands/protocols/publish/sender/publish-schedule-messages-command.js index 8e048be7a5..f5314aca85 100644 --- a/src/commands/protocols/publish/sender/publish-schedule-messages-command.js +++ b/src/commands/protocols/publish/sender/publish-schedule-messages-command.js @@ -1,4 +1,5 @@ import ProtocolScheduleMessagesCommand from '../../common/protocol-schedule-messages-command.js'; +import Command from '../../../command.js'; import { OPERATION_ID_STATUS, ERROR_TYPE } from '../../../../constants/constants.js'; class PublishScheduleMessagesCommand extends ProtocolScheduleMessagesCommand { @@ -10,6 +11,105 @@ class PublishScheduleMessagesCommand extends ProtocolScheduleMessagesCommand { this.errorType = ERROR_TYPE.PUBLISH.PUBLISH_START_ERROR; } + async execute(command) { + const { + operationId, + keyword, + leftoverNodes, + numberOfFoundNodes, + blockchain, + minAckResponses, + hashFunctionId, + assertionId, + tokenId, + contract, + } = command.data; + let isValid = true; + // perform check only first time not for every batch + if (leftoverNodes === numberOfFoundNodes) { + isValid = await this.validateBidsForNeighbourhood( + blockchain, + contract, + tokenId, + keyword, + hashFunctionId, + assertionId, + leftoverNodes, + minAckResponses, + operationId, + ); + } + if (isValid) { + return super.execute(command); + } + return Command.empty(); + } + + async validateBidsForNeighbourhood( + blockchain, + contract, + tokenId, + keyword, + hashFunctionId, + assertionId, + nodes, + minAckResponses, + operationId, + ) { + const agreementId = await this.serviceAgreementService.generateId( + blockchain, + contract, + tokenId, + keyword, + hashFunctionId, + ); + + const agreementData = await this.blockchainModuleManager.getAgreementData( + blockchain, + agreementId, + ); + + const r0 = await this.blockchainModuleManager.getR0(blockchain); + + const blockchainAssertionSize = await this.blockchainModuleManager.getAssertionSize( + blockchain, + assertionId, + ); + + const divisor = this.blockchainModuleManager + .toBigNumber(blockchain, r0) + .mul(Number(agreementData.epochsNumber)) + .mul(blockchainAssertionSize); + + const serviceAgreementBid = this.blockchainModuleManager + .toBigNumber(blockchain, agreementData.tokenAmount) + .add(agreementData.updateTokenAmount) + .mul(1024) + .div(divisor) + .add(1); // add 1 wei because of the precision loss + + let validBids = 0; + + nodes.forEach((node) => { + const askNumber = this.blockchainModuleManager.convertToWei(blockchain, node.ask); + + const ask = this.blockchainModuleManager.toBigNumber(blockchain, askNumber); + + if (ask.lte(serviceAgreementBid)) { + validBids += 1; + } + }); + if (validBids < minAckResponses) { + await this.operationService.markOperationAsFailed( + operationId, + 'Unable to start publish, not enough nodes in neighbourhood satisfy the bid.', + ERROR_TYPE.PUBLISH.PUBLISH_START_ERROR, + ); + return false; + } + return true; + } + /** * Builds default publishScheduleMessagesCommand * @param map diff --git a/src/modules/blockchain/implementation/web3-service.js b/src/modules/blockchain/implementation/web3-service.js index 6a30def3b1..e003f881d8 100644 --- a/src/modules/blockchain/implementation/web3-service.js +++ b/src/modules/blockchain/implementation/web3-service.js @@ -371,6 +371,9 @@ class Web3Service { TRANSACTION_CONFIRMATIONS, TRANSACTION_POLLING_TIMEOUT_MILLIS, ); + if (result?.status === 0) { + throw Error(); + } } catch (error) { this.logger.warn( `Failed executing smart contract function ${functionName}. Error: ${error.message}`, diff --git a/src/service/validation-service.js b/src/service/validation-service.js index d74700b897..a6cd284152 100644 --- a/src/service/validation-service.js +++ b/src/service/validation-service.js @@ -1,8 +1,10 @@ import { assertionMetadata } from 'assertion-tools'; +import { BYTES_IN_KILOBYTE } from '../constants/constants.js'; class ValidationService { constructor(ctx) { this.logger = ctx.logger; + this.config = ctx.config; this.validationModuleManager = ctx.validationModuleManager; this.blockchainModuleManager = ctx.blockchainModuleManager; } @@ -45,6 +47,13 @@ class ValidationService { blockchain, assertionId, ); + + const blockchainAssertionSizeInKb = blockchainAssertionSize / BYTES_IN_KILOBYTE; + if (blockchainAssertionSizeInKb > this.config.maximumAssertionSizeInKb) { + throw Error( + `The size of the received assertion exceeds the maximum limit allowed.. Maximum allowed assertion size in kb: ${this.config.maximumAssertionSizeInKb}, assertion size read from blockchain in kb: ${blockchainAssertionSizeInKb}`, + ); + } const assertionSize = assertionMetadata.getAssertionSizeInBytes(assertion); if (blockchainAssertionSize !== assertionSize) { throw Error( diff --git a/test/bdd/steps/api/publish.mjs b/test/bdd/steps/api/publish.mjs index 87a3d5ab52..1b18aea9b7 100644 --- a/test/bdd/steps/api/publish.mjs +++ b/test/bdd/steps/api/publish.mjs @@ -91,7 +91,7 @@ When('I wait for latest Publish to finalize', { timeout: 80000 }, async function assert.fail('Unable to fetch publish result'); } // eslint-disable-next-line no-await-in-loop - await setTimeout(4000); + await setTimeout(6000); } }); diff --git a/test/unit/service/validation-service.test.js b/test/unit/service/validation-service.test.js index 4a5d6eb263..7cb77e625e 100644 --- a/test/unit/service/validation-service.test.js +++ b/test/unit/service/validation-service.test.js @@ -13,6 +13,9 @@ describe('Validation service test', async () => { validationModuleManager: new ValidationModuleManagerMock(), blockchainModuleManager: new BlockchainModuleManagerMock(), logger: new Logger(), + config: { + maximumAssertionSizeInKb: 2500, + }, }); });