diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 64356fb2f4..f7dee6cf44 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -2,8 +2,7 @@ name: checks on: pull_request: - branches: - - v6/develop + types: [opened, reopened, synchronize] env: REPOSITORY_PASSWORD: password diff --git a/package-lock.json b/package-lock.json index 175adb0e38..85d120a487 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "origintrail_node", - "version": "6.0.12", + "version": "6.0.13", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "origintrail_node", - "version": "6.0.12", + "version": "6.0.13", "license": "ISC", "dependencies": { "@comunica/query-sparql": "^2.4.3", diff --git a/src/commands/local-store/local-store-command.js b/src/commands/local-store/local-store-command.js index 8719f8d322..5bf84abc00 100644 --- a/src/commands/local-store/local-store-command.js +++ b/src/commands/local-store/local-store-command.js @@ -93,8 +93,11 @@ class LocalStoreCommand extends Command { await this.commandExecutor.add({ name: 'deletePendingStateCommand', sequence: [], - delay: updateCommitWindowDuration * 1000, - data: { ...command.data, repository: PENDING_STORAGE_REPOSITORIES.PRIVATE }, + delay: (updateCommitWindowDuration + 60) * 1000, + data: { + ...command.data, + assertionId: cachedData.public.assertionId, + }, transactional: false, }); } diff --git a/src/commands/protocols/get/receiver/v1.0.0/v1-0-0-handle-get-init-command.js b/src/commands/protocols/get/receiver/v1.0.0/v1-0-0-handle-get-init-command.js index e8bcd7aee4..8dbef0a104 100644 --- a/src/commands/protocols/get/receiver/v1.0.0/v1-0-0-handle-get-init-command.js +++ b/src/commands/protocols/get/receiver/v1.0.0/v1-0-0-handle-get-init-command.js @@ -42,22 +42,24 @@ class HandleGetInitCommand extends HandleProtocolMessageCommand { blockchain, contract, tokenId, + assertionId, ); } - for (const repository of [ - TRIPLE_STORE_REPOSITORIES.PUBLIC_CURRENT, - TRIPLE_STORE_REPOSITORIES.PUBLIC_HISTORY, - ]) { - if (assertionExists) { - break; + if (!assertionExists) { + for (const repository of [ + TRIPLE_STORE_REPOSITORIES.PUBLIC_CURRENT, + TRIPLE_STORE_REPOSITORIES.PUBLIC_HISTORY, + ]) { + // eslint-disable-next-line no-await-in-loop + assertionExists = await this.tripleStoreService.assertionExists( + repository, + assertionId, + ); + if (assertionExists) { + break; + } } - - // eslint-disable-next-line no-await-in-loop - assertionExists = await this.tripleStoreService.assertionExists( - repository, - assertionId, - ); } await this.operationIdService.updateOperationIdStatus( diff --git a/src/commands/protocols/get/receiver/v1.0.0/v1-0-0-handle-get-request-command.js b/src/commands/protocols/get/receiver/v1.0.0/v1-0-0-handle-get-request-command.js index 55adc3989e..576dbe8a5a 100644 --- a/src/commands/protocols/get/receiver/v1.0.0/v1-0-0-handle-get-request-command.js +++ b/src/commands/protocols/get/receiver/v1.0.0/v1-0-0-handle-get-request-command.js @@ -26,6 +26,7 @@ class HandleGetRequestCommand extends HandleProtocolMessageCommand { OPERATION_ID_STATUS.GET.GET_REMOTE_START, ); + let nquads; if ( state !== GET_STATES.FINALIZED && blockchain != null && @@ -37,26 +38,24 @@ class HandleGetRequestCommand extends HandleProtocolMessageCommand { blockchain, contract, tokenId, + assertionId, operationId, ); if (cachedAssertion?.public?.assertion?.length) { - return { - messageType: NETWORK_MESSAGE_TYPES.RESPONSES.ACK, - messageData: { nquads: cachedAssertion.public.assertion }, - }; + nquads = cachedAssertion.public.assertion; } } - let nquads; - for (const repository of [ - TRIPLE_STORE_REPOSITORIES.PUBLIC_CURRENT, - TRIPLE_STORE_REPOSITORIES.PUBLIC_HISTORY, - ]) { - // eslint-disable-next-line no-await-in-loop - nquads = await this.tripleStoreService.getAssertion(repository, assertionId); - - if (nquads.length) { - break; + if (!nquads?.length) { + for (const repository of [ + TRIPLE_STORE_REPOSITORIES.PUBLIC_CURRENT, + TRIPLE_STORE_REPOSITORIES.PUBLIC_HISTORY, + ]) { + // eslint-disable-next-line no-await-in-loop + nquads = await this.tripleStoreService.getAssertion(repository, assertionId); + if (nquads.length) { + break; + } } } diff --git a/src/commands/protocols/get/sender/get-assertion-id-command.js b/src/commands/protocols/get/sender/get-assertion-id-command.js index b4af9d93d3..cf40c8d681 100644 --- a/src/commands/protocols/get/sender/get-assertion-id-command.js +++ b/src/commands/protocols/get/sender/get-assertion-id-command.js @@ -75,10 +75,7 @@ class GetAssertionIdCommand extends Command { } } - return this.continueSequence( - { ...command.data, state: assertionId, assertionId }, - command.sequence, - ); + return this.continueSequence({ ...command.data, state, assertionId }, command.sequence); } async handleError(operationId, errorMessage, errorType) { diff --git a/src/commands/protocols/get/sender/local-get-command.js b/src/commands/protocols/get/sender/local-get-command.js index f9d09cf0f8..8d64f8186e 100644 --- a/src/commands/protocols/get/sender/local-get-command.js +++ b/src/commands/protocols/get/sender/local-get-command.js @@ -2,6 +2,7 @@ import Command from '../../../command.js'; import { OPERATION_ID_STATUS, ERROR_TYPE, + GET_STATES, TRIPLE_STORE_REPOSITORIES, PENDING_STORAGE_REPOSITORIES, } from '../../../../constants/constants.js'; @@ -24,42 +25,50 @@ class LocalGetCommand extends Command { * @param command */ async execute(command) { - const { operationId, blockchain, contract, tokenId, state } = command.data; + const { operationId, blockchain, contract, tokenId, assertionId, state } = command.data; await this.operationIdService.updateOperationIdStatus( operationId, OPERATION_ID_STATUS.GET.GET_LOCAL_START, ); const response = {}; - for (const repository of [ - PENDING_STORAGE_REPOSITORIES.PRIVATE, - PENDING_STORAGE_REPOSITORIES.PUBLIC, - ]) { - // eslint-disable-next-line no-await-in-loop - const stateIsPending = await this.pendingStorageService.assetHasPendingState( - repository, - blockchain, - contract, - tokenId, - state, - ); - - if (stateIsPending) { + if ( + state !== GET_STATES.FINALIZED && + blockchain != null && + contract != null && + tokenId != null + ) { + for (const repository of [ + PENDING_STORAGE_REPOSITORIES.PRIVATE, + PENDING_STORAGE_REPOSITORIES.PUBLIC, + ]) { // eslint-disable-next-line no-await-in-loop - const cachedAssertion = await this.pendingStorageService.getCachedAssertion( + const stateIsPending = await this.pendingStorageService.assetHasPendingState( repository, blockchain, contract, tokenId, - operationId, + assertionId, ); - if (cachedAssertion?.public?.assertion?.length) { - response.assertion = cachedAssertion.public.assertion; - if (cachedAssertion?.private?.assertion?.length) { - response.privateAssertion = cachedAssertion.private.assertion; + if (stateIsPending) { + // eslint-disable-next-line no-await-in-loop + const cachedAssertion = await this.pendingStorageService.getCachedAssertion( + repository, + blockchain, + contract, + tokenId, + assertionId, + operationId, + ); + + if (cachedAssertion?.public?.assertion?.length) { + response.assertion = cachedAssertion.public.assertion; + if (cachedAssertion?.private?.assertion?.length) { + response.privateAssertion = cachedAssertion.private.assertion; + } + break; } - break; } } } @@ -72,7 +81,10 @@ class LocalGetCommand extends Command { TRIPLE_STORE_REPOSITORIES.PUBLIC_HISTORY, ]) { // eslint-disable-next-line no-await-in-loop - response.assertion = await this.tripleStoreService.getAssertion(repository, state); + response.assertion = await this.tripleStoreService.getAssertion( + repository, + assertionId, + ); if (response?.assertion?.length) break; } } diff --git a/src/commands/protocols/update/receiver/delete-pending-state-command.js b/src/commands/protocols/update/receiver/delete-pending-state-command.js index a8b824e674..defa18d8d2 100644 --- a/src/commands/protocols/update/receiver/delete-pending-state-command.js +++ b/src/commands/protocols/update/receiver/delete-pending-state-command.js @@ -1,25 +1,64 @@ import Command from '../../../command.js'; -import { ERROR_TYPE } from '../../../../constants/constants.js'; +import { ERROR_TYPE, PENDING_STORAGE_REPOSITORIES } from '../../../../constants/constants.js'; class DeletePendingStateCommand extends Command { constructor(ctx) { super(ctx); + this.blockchainModuleManager = ctx.blockchainModuleManager; this.pendingStorageService = ctx.pendingStorageService; this.errorType = ERROR_TYPE.UPDATE.UPDATE_DELETE_PENDING_STATE_ERROR; } async execute(command) { - const { blockchain, contract, tokenId, operationId, repository } = command.data; + const { blockchain, contract, tokenId, assertionId, operationId } = command.data; - await this.pendingStorageService.removeCachedAssertion( - repository, + this.logger.trace( + `Started ${command.name} for blockchain: ${blockchain} contract: ${contract}, ` + + `token id: ${tokenId}, assertion id: ${assertionId}`, + ); + + const assetStates = await this.blockchainModuleManager.getAssertionIds( blockchain, contract, tokenId, - operationId, ); + if (assetStates.includes(assertionId)) { + this.logger.trace( + `Not clearing the pending storage as state was finalized and clearing is triggered by StateFinalized event.`, + ); + return Command.empty(); + } + + for (const repository of [ + PENDING_STORAGE_REPOSITORIES.PUBLIC, + PENDING_STORAGE_REPOSITORIES.PRIVATE, + ]) { + // eslint-disable-next-line no-await-in-loop + const pendingStateExists = await this.pendingStorageService.assetHasPendingState( + repository, + blockchain, + contract, + tokenId, + assertionId, + ); + + if (!pendingStateExists) { + continue; + } + + // eslint-disable-next-line no-await-in-loop + await this.pendingStorageService.removeCachedAssertion( + repository, + blockchain, + contract, + tokenId, + assertionId, + operationId, + ); + } + return Command.empty(); } diff --git a/src/commands/protocols/update/receiver/v1.0.0/v1-0-0-handle-update-request-command.js b/src/commands/protocols/update/receiver/v1.0.0/v1-0-0-handle-update-request-command.js index 83eff51b71..0429b3c1f3 100644 --- a/src/commands/protocols/update/receiver/v1.0.0/v1-0-0-handle-update-request-command.js +++ b/src/commands/protocols/update/receiver/v1.0.0/v1-0-0-handle-update-request-command.js @@ -100,8 +100,11 @@ class HandleUpdateRequestCommand extends HandleProtocolMessageCommand { this.commandExecutor.add({ name: 'deletePendingStateCommand', sequence: [], - delay: updateCommitWindowDuration * 1000, - data: { ...commandData, repository: PENDING_STORAGE_REPOSITORIES.PUBLIC }, + delay: (updateCommitWindowDuration + 60) * 1000, + data: { + ...commandData, + assertionId: cachedData.assertionId, + }, transactional: false, }), ); diff --git a/src/service/blockchain-event-listener-service.js b/src/service/blockchain-event-listener-service.js index c5a252be41..29d63ff71a 100644 --- a/src/service/blockchain-event-listener-service.js +++ b/src/service/blockchain-event-listener-service.js @@ -488,6 +488,7 @@ class BlockchainEventListenerService { blockchain, contract, tokenId, + assertionId, ); const storePromises = []; @@ -559,12 +560,15 @@ class BlockchainEventListenerService { await Promise.all(storePromises); // remove asset from pending storage - await this.pendingStorageService.removeCachedAssertion( - pendingRepository, - blockchain, - contract, - tokenId, - ); + if (cachedData) { + await this.pendingStorageService.removeCachedAssertion( + pendingRepository, + blockchain, + contract, + tokenId, + assertionId, + ); + } } } diff --git a/src/service/file-service.js b/src/service/file-service.js index aa1c5be91b..68096b91f6 100644 --- a/src/service/file-service.js +++ b/src/service/file-service.js @@ -90,7 +90,7 @@ class FileService { } async removeFolder(folderPath) { - // this.logger.trace(`Removing folder at path: ${folderPath}`); + this.logger.trace(`Removing folder at path: ${folderPath}`); try { await rm(folderPath, { recursive: true }); @@ -146,19 +146,16 @@ class FileService { tokenId, ); - let pendingStorageFileName; - if (assertionId === undefined) { - [pendingStorageFileName] = await this.readDirectory(pendingStorageFolder); - } else { - pendingStorageFileName = assertionId; - } - - return path.join(pendingStorageFolder, pendingStorageFileName); + return path.join(pendingStorageFolder, assertionId); } getArchiveFolderPath(subFolder) { return path.join(this.getDataFolderPath(), ARCHIVE_FOLDER_NAME, subFolder); } + + getParentDirectory(filePath) { + return path.dirname(filePath); + } } export default FileService; diff --git a/src/service/pending-storage-service.js b/src/service/pending-storage-service.js index a882ce40af..0817453d27 100644 --- a/src/service/pending-storage-service.js +++ b/src/service/pending-storage-service.js @@ -34,11 +34,11 @@ class PendingStorageService { ); } - async getCachedAssertion(repository, blockchain, contract, tokenId, operationId) { + async getCachedAssertion(repository, blockchain, contract, tokenId, assertionId, operationId) { const ual = this.ualService.deriveUAL(blockchain, contract, tokenId); this.logger.debug( - `Reading cached assertion for ual: ${ual}, operation id: ${operationId} from file in ${repository} pending storage`, + `Reading cached assertion for ual: ${ual}, assertion id: ${assertionId}, operation id: ${operationId} from file in ${repository} pending storage`, ); try { const documentPath = await this.fileService.getPendingStorageDocumentPath( @@ -46,30 +46,57 @@ class PendingStorageService { blockchain, contract, tokenId, + assertionId, ); const data = await this.fileService.readFile(documentPath, true); return data; } catch (error) { - this.logger.debug('Assertion not found in pending storage'); + this.logger.debug( + `Assertion not found in ${repository} pending storage. Error message: ${error.message}, ${error.stackTrace}`, + ); return null; } } - async removeCachedAssertion(repository, blockchain, contract, tokenId, operationId) { + async removeCachedAssertion( + repository, + blockchain, + contract, + tokenId, + assertionId, + operationId, + ) { const ual = this.ualService.deriveUAL(blockchain, contract, tokenId); this.logger.debug( `Removing cached assertion for ual: ${ual} operation id: ${operationId} from file in ${repository} pending storage`, ); - const pendingStorageFolderPath = this.fileService.getPendingStorageFolderPath( + const pendingAssertionPath = await this.fileService.getPendingStorageDocumentPath( repository, blockchain, contract, tokenId, + assertionId, ); - await this.fileService.removeFolder(pendingStorageFolderPath); + await this.fileService.removeFile(pendingAssertionPath); + + const pendingStorageFolderPath = this.fileService.getParentDirectory(pendingAssertionPath); + + try { + const otherPendingAssertions = await this.fileService.readDirectory( + pendingStorageFolderPath, + ); + if (otherPendingAssertions.length === 0) { + await this.fileService.removeFolder(pendingStorageFolderPath); + } + } catch (error) { + this.logger.debug( + `Assertions folder not found in ${repository} pending storage. ` + + `Error message: ${error.message}, ${error.stackTrace}`, + ); + } } async assetHasPendingState(repository, blockchain, contract, tokenId, assertionId) { diff --git a/test/bdd/steps/lib/local-blockchain.mjs b/test/bdd/steps/lib/local-blockchain.mjs index f9abf86ec8..777238c95d 100644 --- a/test/bdd/steps/lib/local-blockchain.mjs +++ b/test/bdd/steps/lib/local-blockchain.mjs @@ -20,6 +20,7 @@ const testParametersStorageParams = { minProofWindowOffsetPerc: 66, // 4 minutes maxProofWindowOffsetPerc: 66, // 4 minutes proofWindowDurationPerc: 33, // 2 minutes + updateCommitWindowDuration: 60, // 1 minute finalizationCommitsNumber: 3, }; /**