diff --git a/src/ddo/interfaces/Service.ts b/src/ddo/interfaces/Service.ts index 70e69e80..fb723f98 100644 --- a/src/ddo/interfaces/Service.ts +++ b/src/ddo/interfaces/Service.ts @@ -33,6 +33,7 @@ export interface publisherTrustedAlgorithm { export interface ServiceComputePrivacy { allowRawAlgorithm: boolean allowNetworkAccess: boolean + allowAllPublishedAlgorithms: boolean publisherTrustedAlgorithms?: publisherTrustedAlgorithm[] } diff --git a/src/ocean/Compute.ts b/src/ocean/Compute.ts index d3d7f54c..24d63dc0 100644 --- a/src/ocean/Compute.ts +++ b/src/ocean/Compute.ts @@ -408,58 +408,56 @@ export class Compute extends Instantiable { if (algorithmMeta) { // check if raw algo is allowed if (service.attributes.main.privacy) - if (!service.attributes.main.privacy.allowRawAlgorithm) { - this.logger.error('ERROR: This service does not allow raw algorithm') - return false - } + if (service.attributes.main.privacy.allowRawAlgorithm) return true + this.logger.error('ERROR: This service does not allow raw algorithm') + return false } if (algorithmDid) { // check if did is in trusted list - if (service.attributes.main.privacy) - if (service.attributes.main.privacy.publisherTrustedAlgorithms) - if (service.attributes.main.privacy.publisherTrustedAlgorithms.length > 0) { - // loop through all publisherTrustedAlgorithms and see if we have a match - let algo: publisherTrustedAlgorithm - for (algo of service.attributes.main.privacy.publisherTrustedAlgorithms) { - if (algo.did === algorithmDid) { - // compute checkusms and compare them - const trustedAlgorithm = await this.createPublisherTrustedAlgorithmfromDID( - algorithmDid - ) - if ( - algo.containerSectionChecksum && - algo.containerSectionChecksum !== - trustedAlgorithm.containerSectionChecksum - ) { - this.logger.error( - 'ERROR: Algorithm container section was altered since it was added as trusted by ' + - datasetDid - ) - return false - } - if ( - algo.filesChecksum && - algo.filesChecksum !== trustedAlgorithm.filesChecksum - ) { - this.logger.error( - 'ERROR: Algorithm files section was altered since it was added as trusted by ' + - datasetDid - ) - return false - } - // all conditions are meet - return true - } - } - // algorithmDid was not found - this.logger.error( - 'ERROR: Algorithm ' + algorithmDid + ' is not allowed by ' + datasetDid + if (service.attributes.main.privacy) { + if (service.attributes.main.privacy.allowAllPublishedAlgorithms) return true + if (!service.attributes.main.privacy.publisherTrustedAlgorithms) return false + let algo: publisherTrustedAlgorithm + for (algo of service.attributes.main.privacy.publisherTrustedAlgorithms) { + if (algo.did === algorithmDid) { + // compute checkusms and compare them + const trustedAlgorithm = await this.createPublisherTrustedAlgorithmfromDID( + algorithmDid ) - return false + if ( + algo.containerSectionChecksum && + algo.containerSectionChecksum !== + trustedAlgorithm.containerSectionChecksum + ) { + this.logger.error( + 'ERROR: Algorithm container section was altered since it was added as trusted by ' + + datasetDid + ) + return false + } + if ( + algo.filesChecksum && + algo.filesChecksum !== trustedAlgorithm.filesChecksum + ) { + this.logger.error( + 'ERROR: Algorithm files section was altered since it was added as trusted by ' + + datasetDid + ) + return false + } + // all conditions are meet + return true } + } + // algorithmDid was not found + this.logger.error( + 'ERROR: Algorithm ' + algorithmDid + ' is not allowed by ' + datasetDid + ) + return false + } } } - return true + return true // not a compute asset } /** @@ -563,6 +561,8 @@ export class Compute extends Instantiable { if (ddo.service[serviceIndex].type !== 'compute') return null ddo.service[serviceIndex].attributes.main.privacy.allowRawAlgorithm = computePrivacy.allowRawAlgorithm + ddo.service[serviceIndex].attributes.main.privacy.allowAllPublishedAlgorithms = + computePrivacy.allowAllPublishedAlgorithms ddo.service[serviceIndex].attributes.main.privacy.allowNetworkAccess = computePrivacy.allowNetworkAccess ddo.service[serviceIndex].attributes.main.privacy.publisherTrustedAlgorithms = @@ -570,6 +570,32 @@ export class Compute extends Instantiable { return ddo } + /** + * Toogle allowAllPublishedAlgorithms + * @param {ddo} DDO + * @param {number} serviceIndex Index of the compute service in the DDO. If -1, will try to find it + * @param {boolean} newState + * @return {Promise} Returns the new DDO + */ + public async toggleAllowAllPublishedAlgorithms( + ddo: DDO, + serviceIndex: number, + newState: boolean + ): Promise { + if (!ddo) return null + if (serviceIndex === -1) { + const service = ddo.findServiceByType('compute') + if (!service) return null + serviceIndex = service.index + } + if (typeof ddo.service[serviceIndex] === 'undefined') return null + if (ddo.service[serviceIndex].type !== 'compute') return null + ddo.service[ + serviceIndex + ].attributes.main.privacy.allowAllPublishedAlgorithms = newState + return ddo + } + /** * Generates a publisherTrustedAlgorithm object from a algorithm did * @param {did} string DID. You can leave this empty if you already have the DDO @@ -650,8 +676,8 @@ export class Compute extends Instantiable { } if (typeof ddo.service[serviceIndex] === 'undefined') return false if (ddo.service[serviceIndex].type !== 'compute') return false - if (!ddo.service[serviceIndex].attributes.main.privacy.publisherTrustedAlgorithms) - return false + if (ddo.service[serviceIndex].attributes.main.privacy.allowAllPublishedAlgorithms) + return true let algo: publisherTrustedAlgorithm for (algo of ddo.service[serviceIndex].attributes.main.privacy .publisherTrustedAlgorithms) diff --git a/test/integration/ComputeFlow.test.ts b/test/integration/ComputeFlow.test.ts index 9cae500f..b85924e0 100644 --- a/test/integration/ComputeFlow.test.ts +++ b/test/integration/ComputeFlow.test.ts @@ -252,6 +252,7 @@ describe('Compute flow', () => { ) const origComputePrivacy = { allowRawAlgorithm: true, + allowAllPublishedAlgorithms: false, allowNetworkAccess: false, publisherTrustedAlgorithms: [] } @@ -302,6 +303,7 @@ describe('Compute flow', () => { ) const origComputePrivacy2 = { allowRawAlgorithm: true, + allowAllPublishedAlgorithms: true, allowNetworkAccess: false, publisherTrustedAlgorithms: [] } @@ -363,6 +365,7 @@ describe('Compute flow', () => { const origComputePrivacy = { allowRawAlgorithm: false, allowNetworkAccess: false, + allowAllPublishedAlgorithms: false, publisherTrustedAlgorithms: [] } @@ -396,6 +399,7 @@ describe('Compute flow', () => { const origComputePrivacy = { allowRawAlgorithm: false, allowNetworkAccess: false, + allowAllPublishedAlgorithms: false, publisherTrustedAlgorithms: [ { did: 'did:op:1234', @@ -754,6 +758,88 @@ describe('Compute flow', () => { ) assert(order === null, 'Order should be null') }) + + it('should not allow a compute job with a published algo because asset does not allow allowAllPublishedAlgorithms', async () => { + const output = {} + const computeService = ddo.findServiceByType('compute') + // get the compute address first + computeAddress = await ocean.compute.getComputeAddress(ddo.id, computeService.index) + assert(ddo != null, 'ddo should not be null') + + const allowed = await ocean.compute.isOrderable( + ddo.id, + computeService.index, + algorithmAsset.id, + undefined + ) + assert(allowed === false) + const order = await ocean.compute.orderAsset( + bob.getId(), + ddo.id, + computeService.index, + algorithmAsset.id, + undefined, + null, // no marketplace fee + computeAddress // CtD is the consumer of the dataset + ) + assert(order === null, 'Order should be null') + }) + it('Alice updates Compute Privacy', async () => { + const newComputePrivacy = { + allowRawAlgorithm: false, + allowNetworkAccess: true, + allowAllPublishedAlgorithms: false, // is false, in order to test enableAllowAllPublishedAlgorithms below + publisherTrustedAlgorithms: [ + { + did: 'did:op:1234', + filesChecksum: '1234', + containerSectionChecksum: '1234' + }, + { + did: 'did:op:12345', + filesChecksum: '1234', + containerSectionChecksum: '1234' + } + ] + } + const computeService = ddo.findServiceByType('compute') + assert(computeService, 'ComputeIndex should be >0') + let newDdo = await ocean.compute.editComputePrivacy( + ddo, + computeService.index, + newComputePrivacy + ) + newDdo = await ocean.compute.toggleAllowAllPublishedAlgorithms( + newDdo, + computeService.index, + true + ) + assert(newDdo !== null, 'newDDO should not be null') + const txid = await ocean.onChainMetadata.update(ddo.id, newDdo, alice.getId()) + assert(txid !== null, 'TxId should not be null') + await sleep(aquaSleep) + const metaData = await ocean.assets.getServiceByType(ddo.id, 'compute') + assert.equal( + metaData.attributes.main.privacy.allowRawAlgorithm, + newComputePrivacy.allowRawAlgorithm, + 'allowRawAlgorithm does not match' + ) + assert.equal( + metaData.attributes.main.privacy.allowAllPublishedAlgorithms, + true, + 'allowAllPublishedAlgorithms does not match' + ) + assert.equal( + metaData.attributes.main.privacy.allowNetworkAccess, + newComputePrivacy.allowNetworkAccess, + 'allowNetworkAccess does not match' + ) + assert.deepEqual( + metaData.attributes.main.privacy.publisherTrustedAlgorithms, + newComputePrivacy.publisherTrustedAlgorithms, + 'allowNetworkAccess does not match' + ) + }) it('should start a compute job with a published algo', async () => { const output = {} const computeService = ddo.findServiceByType('compute') @@ -973,61 +1059,23 @@ describe('Compute flow', () => { assert(response.status >= 1, 'Invalid response.status') assert(response.jobId, 'Invalid jobId') }) - it('Alice updates Compute Privacy', async () => { - const newComputePrivacy = { - allowRawAlgorithm: false, - allowNetworkAccess: true, - publisherTrustedAlgorithms: [ - { - did: 'did:op:1234', - filesChecksum: '1234', - containerSectionChecksum: '1234' - }, - { - did: 'did:op:12345', - filesChecksum: '1234', - containerSectionChecksum: '1234' - } - ] - } - const computeService = ddo.findServiceByType('compute') - assert(computeService, 'ComputeIndex should be >0') - const newDdo = await ocean.compute.editComputePrivacy( - ddo, - computeService.index, - newComputePrivacy - ) - assert(newDdo !== null, 'newDDO should not be null') - const txid = await ocean.onChainMetadata.update(ddo.id, newDdo, alice.getId()) - assert(txid !== null, 'TxId should not be null') - await sleep(aquaSleep) - const metaData = await ocean.assets.getServiceByType(ddo.id, 'compute') - assert.equal( - metaData.attributes.main.privacy.allowRawAlgorithm, - newComputePrivacy.allowRawAlgorithm, - 'allowRawAlgorithm does not match' - ) - assert.equal( - metaData.attributes.main.privacy.allowNetworkAccess, - newComputePrivacy.allowNetworkAccess, - 'allowNetworkAccess does not match' - ) - assert.deepEqual( - metaData.attributes.main.privacy.publisherTrustedAlgorithms, - newComputePrivacy.publisherTrustedAlgorithms, - 'allowNetworkAccess does not match' - ) - }) + it('Alice updates Compute Privacy, allowing some published algos', async () => { const computeService = ddo.findServiceByType('compute') assert(computeService, 'ComputeIndex should be >0') - // allow algorithmAsset - let newDdo = await ocean.compute.addTrustedAlgorithmtoAsset( + + let newDdo = await ocean.compute.toggleAllowAllPublishedAlgorithms( ddo, computeService.index, + false + ) + assert(newDdo !== null, 'newDDO should not be null') + // allow algorithmAsset + newDdo = await ocean.compute.addTrustedAlgorithmtoAsset( + newDdo, + computeService.index, algorithmAsset.id ) - assert(newDdo !== null, 'newDDO should not be null') let isAlgoAllowed = await ocean.compute.isAlgorithmTrusted( newDdo, computeService.index,