UNPKG

@iexec/dataprotector

Version:

This product enables users to confidentially store data–such as mail address, documents, personal information ...

197 lines (181 loc) 5.6 kB
import { ZeroAddress } from 'ethers'; import { DATASET_INFINITE_VOLUME, NULL_ADDRESS } from 'iexec/utils'; import { ValidationError, WorkflowError, grantAccessErrorMessage, handleIfProtocolError, } from '../../utils/errors.js'; import { formatGrantedAccess } from '../../utils/formatGrantedAccess.js'; import { addressOrEnsSchema, booleanSchema, isEnsTest, positiveIntegerStringSchema, positiveStrictIntegerStringSchema, throwIfMissing, validateOnStatusUpdateCallback, } from '../../utils/validators.js'; import { isERC734 } from '../../utils/whitelist.js'; import { GrantAccessParams, GrantAccessStatuses, GrantedAccess, OnStatusUpdateFn, } from '../types/index.js'; import { IExecConsumer } from '../types/internalTypes.js'; import { getGrantedAccess } from './getGrantedAccess.js'; const inferTagFromAppMREnclave = (mrenclave: string) => { const tag = ['tee']; try { const { framework } = JSON.parse(mrenclave); if (framework.toLowerCase() === 'scone') { tag.push('scone'); return tag; } } catch (e) { // noop } throw new WorkflowError({ message: grantAccessErrorMessage, errorCause: Error('App does not use a supported TEE framework'), }); }; export const grantAccess = async ({ iexec = throwIfMissing(), protectedData, authorizedApp, authorizedUser, pricePerAccess = 0, numberOfAccess, allowBulk = false, onStatusUpdate = () => {}, }: IExecConsumer & GrantAccessParams): Promise<GrantedAccess> => { const vProtectedData = addressOrEnsSchema() .required() .label('protectedData') .validateSync(protectedData); let vAuthorizedApp = addressOrEnsSchema() .required() .label('authorizedApp') .validateSync(authorizedApp); const vAuthorizedUser = addressOrEnsSchema() .label('authorizedUser') .validateSync(authorizedUser); let vPricePerAccess = positiveIntegerStringSchema() .label('pricePerAccess') .validateSync(pricePerAccess); let vNumberOfAccess = positiveStrictIntegerStringSchema() .label('numberOfAccess') .validateSync(numberOfAccess); const vAllowBulk = booleanSchema().label('allowBulk').validateSync(allowBulk); const vOnStatusUpdate = validateOnStatusUpdateCallback<OnStatusUpdateFn<GrantAccessStatuses>>( onStatusUpdate ); // Validate consistency between allowBulk, pricePerAccess and numberOfAccess if (vAllowBulk) { if (vPricePerAccess && vPricePerAccess !== '0') { throw new ValidationError( 'allowBulk requires pricePerAccess to be 0 or undefined' ); } vPricePerAccess = '0'; if ( vNumberOfAccess && vNumberOfAccess !== DATASET_INFINITE_VOLUME.toString() ) { throw new ValidationError( `allowBulk requires numberOfAccess to be ${DATASET_INFINITE_VOLUME.toString()} or undefined` ); } vNumberOfAccess = DATASET_INFINITE_VOLUME.toString(); } if (vAuthorizedApp && isEnsTest(vAuthorizedApp)) { const resolved = await iexec.ens.resolveName(vAuthorizedApp); if (!resolved) { throw new ValidationError('authorizedApp ENS name is not valid'); } vAuthorizedApp = resolved.toLowerCase(); } if (vAuthorizedApp === ZeroAddress) { throw Error( `Forbidden to use ${ZeroAddress} as authorizedApp, this would give access to any app` ); } const { grantedAccess: publishedDatasetOrders } = await getGrantedAccess({ iexec, protectedData: vProtectedData, authorizedApp: vAuthorizedApp, authorizedUser: vAuthorizedUser, isUserStrict: true, }); if (publishedDatasetOrders.length > 0) { throw new WorkflowError({ message: grantAccessErrorMessage, errorCause: Error( `An access has been already granted to the user: ${ vAuthorizedUser || NULL_ADDRESS } with the app: ${vAuthorizedApp}` ), }); } let tag; const isDeployedApp = await iexec.app.checkDeployedApp(authorizedApp); if (isDeployedApp) { tag = await iexec.app.showApp(authorizedApp).then(({ app }) => { return inferTagFromAppMREnclave(app.appMREnclave); }); } else if (await isERC734(iexec, authorizedApp)) { tag = ['tee', 'scone']; } else { throw new WorkflowError({ message: grantAccessErrorMessage, errorCause: Error( `Invalid app set for address ${authorizedApp}. The app either has an invalid tag (possibly non-TEE) or an invalid whitelist smart contract address.` ), }); } vOnStatusUpdate({ title: 'CREATE_DATASET_ORDER', isDone: false, }); const datasetorder = await iexec.order .createDatasetorder({ dataset: vProtectedData, apprestrict: vAuthorizedApp, requesterrestrict: vAuthorizedUser, datasetprice: vPricePerAccess, volume: vNumberOfAccess, tag, }) .then((datasetorderTemplate) => iexec.order.signDatasetorder(datasetorderTemplate) ) .catch((e) => { throw new WorkflowError({ message: 'Failed to sign data access', errorCause: e, }); }); vOnStatusUpdate({ title: 'CREATE_DATASET_ORDER', isDone: true, }); vOnStatusUpdate({ title: 'PUBLISH_DATASET_ORDER', isDone: false, }); await iexec.order.publishDatasetorder(datasetorder).catch((e) => { handleIfProtocolError(e); throw new WorkflowError({ message: 'Failed to publish data access', errorCause: e, }); }); vOnStatusUpdate({ title: 'PUBLISH_DATASET_ORDER', isDone: true, }); return formatGrantedAccess(datasetorder, parseInt(datasetorder.volume)); };