UNPKG

@iexec/dataprotector

Version:

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

188 lines 7.92 kB
import { string } from 'yup'; import { SCONE_TAG, DEFAULT_MAX_PRICE } from '../../config/config.js'; import { WorkflowError, consumeProtectedDataErrorMessage, handleIfProtocolError, } from '../../utils/errors.js'; import { getEventFromLogs } from '../../utils/getEventFromLogs.js'; import { resolveENS } from '../../utils/resolveENS.js'; import { getPemFormattedKeyPair, formatPemPublicKeyForSMS, } from '../../utils/rsa.js'; import { addressOrEnsSchema, throwIfMissing, validateOnStatusUpdateCallback, positiveNumberSchema, stringSchema, } from '../../utils/validators.js'; import { getResultFromCompletedTask } from '../dataProtectorCore/getResultFromCompletedTask.js'; import { getAppWhitelistContract } from './smartContract/getAddOnlyAppWhitelistContract.js'; import { getSharingContract } from './smartContract/getSharingContract.js'; import { onlyAppInAddOnlyAppWhitelist, onlyProtectedDataAuthorizedToBeConsumed, } from './smartContract/preflightChecks.js'; import { getProtectedDataDetails } from './smartContract/sharingContract.reads.js'; export const consumeProtectedData = async ({ iexec = throwIfMissing(), sharingContractAddress = throwIfMissing(), defaultWorkerpool, protectedData, app, path, workerpool, maxPrice = DEFAULT_MAX_PRICE, pemPrivateKey, onStatusUpdate = () => { }, }) => { let vProtectedData = addressOrEnsSchema() .required() .label('protectedData') .validateSync(protectedData); const vMaxPrice = positiveNumberSchema() .label('maxPrice') .validateSync(maxPrice); const vPath = stringSchema().label('path').validateSync(path); let vApp = addressOrEnsSchema().required().label('app').validateSync(app); let vWorkerpool = addressOrEnsSchema() .label('workerpool') .default(defaultWorkerpool) .validateSync(workerpool); const vPemPrivateKey = string() .label('pemPrivateKey') .validateSync(pemPrivateKey); const vOnStatusUpdate = validateOnStatusUpdateCallback(onStatusUpdate); // ENS resolution if needed vProtectedData = await resolveENS(iexec, vProtectedData); vApp = await resolveENS(iexec, vApp); vWorkerpool = await resolveENS(iexec, vWorkerpool); let userAddress = await iexec.wallet.getAddress(); userAddress = userAddress.toLowerCase(); const sharingContract = await getSharingContract(iexec, sharingContractAddress); //---------- Smart Contract Call ---------- const protectedDataDetails = await getProtectedDataDetails({ sharingContract, protectedData: vProtectedData, userAddress, }); const addOnlyAppWhitelistContract = await getAppWhitelistContract(iexec, protectedDataDetails.addOnlyAppWhitelist); //---------- Pre flight check---------- onlyProtectedDataAuthorizedToBeConsumed(protectedDataDetails); await onlyAppInAddOnlyAppWhitelist({ addOnlyAppWhitelistContract, app: vApp, }); vOnStatusUpdate({ title: 'FETCH_WORKERPOOL_ORDERBOOK', isDone: false, }); try { const workerpoolOrderbook = await iexec.orderbook.fetchWorkerpoolOrderbook({ workerpool: vWorkerpool, app: vApp, dataset: vProtectedData, minTag: SCONE_TAG, maxTag: SCONE_TAG, }); const workerpoolOrder = workerpoolOrderbook.orders[0]?.order; if (!workerpoolOrder) { throw new WorkflowError({ message: consumeProtectedDataErrorMessage, errorCause: Error('Could not find a workerpool order, maybe too many requests? You might want to try again later.'), }); } if (workerpoolOrder.workerpoolprice > vMaxPrice) { throw new WorkflowError({ message: consumeProtectedDataErrorMessage, errorCause: Error(`No orders found within the specified price limit of ${vMaxPrice} nRLC.`), }); } vOnStatusUpdate({ title: 'FETCH_WORKERPOOL_ORDERBOOK', isDone: true, }); const pemKeyPair = await getPemFormattedKeyPair({ pemPrivateKey: vPemPrivateKey, }); vOnStatusUpdate({ title: 'PUSH_ENCRYPTION_KEY', isDone: false, payload: { pemPublicKey: pemKeyPair.pemPublicKey, }, }); await iexec.result.pushResultEncryptionKey(formatPemPublicKeyForSMS(pemKeyPair.pemPublicKey), { forceUpdate: true, }); vOnStatusUpdate({ title: 'PUSH_ENCRYPTION_KEY', isDone: true, payload: { pemPublicKey: pemKeyPair.pemPublicKey, }, }); // Make a deal vOnStatusUpdate({ title: 'CONSUME_ORDER_REQUESTED', isDone: false, }); const { txOptions } = await iexec.config.resolveContractsClient(); let tx; let transactionReceipt; try { // TODO: when non free workerpoolorders is supported add approveAndCall (see implementation of buyProtectedData/rentProtectedData/subscribeToCollection) tx = await sharingContract.consumeProtectedData(vProtectedData, workerpoolOrder, vApp, txOptions); transactionReceipt = await tx.wait(); } catch (err) { console.error('Smart-contract consumeProtectedData() ERROR', err); throw err; } vOnStatusUpdate({ title: 'CONSUME_ORDER_REQUESTED', isDone: true, payload: { txHash: tx.hash, }, }); const specificEventForPreviousTx = getEventFromLogs({ contract: sharingContract, eventName: 'ProtectedDataConsumed', logs: transactionReceipt.logs, }); const dealId = specificEventForPreviousTx.args?.dealId; const taskId = await iexec.deal.computeTaskId(dealId, 0); vOnStatusUpdate({ title: 'TASK_EXECUTION', isDone: false, payload: { dealId, taskId }, }); const taskObservable = await iexec.task.obsTask(taskId, { dealid: dealId }); await new Promise((resolve, reject) => { taskObservable.subscribe({ next: () => { }, error: (err) => reject(err), complete: () => resolve(undefined), }); }); vOnStatusUpdate({ title: 'TASK_EXECUTION', isDone: true, payload: { dealId, taskId }, }); const { result } = await getResultFromCompletedTask({ iexec, taskId, path: vPath, pemPrivateKey: pemKeyPair.pemPrivateKey, onStatusUpdate: vOnStatusUpdate, }); return { txHash: tx.hash, dealId, taskId, result, pemPrivateKey: pemKeyPair.pemPrivateKey, }; } catch (e) { handleIfProtocolError(e); // Try to extract some meaningful error like: // "insufficient funds for transfer" if (e?.info?.error?.data?.message) { throw new WorkflowError({ message: `${consumeProtectedDataErrorMessage}: ${e.info.error.data.message}`, errorCause: e, }); } // Try to extract some meaningful error like: // "User denied transaction signature" if (e?.info?.error?.message) { throw new WorkflowError({ message: `${consumeProtectedDataErrorMessage}: ${e.info.error.message}`, errorCause: e, }); } throw new WorkflowError({ message: 'Sharing smart contract: Failed to consume protected data', errorCause: e, }); } }; //# sourceMappingURL=consumeProtectedData.js.map