Skip to content

Commit 4d078fb

Browse files
Merge remote-tracking branch 'origin/develop' into feature/allow-debug
2 parents 50841fd + 8cb988d commit 4d078fb

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+3162
-1654
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,7 @@
1+
## Next
2+
3+
### Changed
4+
5+
- fixed cryptic TypeError when `@iexec/dataprotector-deserializer` was used in a context without protected data, `getValue()` now rejects with Error "Missing protected data" in such a case.
6+
17
## [0.1.0] Initial release

packages/dataprotector-deserializer/src/index.ts

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -40,23 +40,31 @@ type Mode = 'optimistic' | 'legacy' | 'borsh';
4040
* - `protectedDataPath`: overrides the dataset path, by default use the standard dataset path provided in the iExec worker runtime
4141
*/
4242
class IExecDataProtectorDeserializer {
43-
private protectedDataPath: string;
43+
private protectedDataPath?: string;
4444

4545
private mode: Mode;
4646

47-
private zipPromise: Promise<JSZip>;
47+
private zipPromise?: Promise<JSZip>;
4848

4949
constructor(options?: { mode?: Mode; protectedDataPath?: string }) {
5050
this.mode = options?.mode || 'optimistic';
51-
this.protectedDataPath =
52-
options?.protectedDataPath ||
53-
join(process.env.IEXEC_IN, process.env.IEXEC_DATASET_FILENAME);
54-
this.zipPromise = readFile(this.protectedDataPath).then((buffer) => {
55-
return new JSZip().loadAsync(buffer);
56-
});
57-
this.zipPromise.catch(() => {
58-
/* prevents unhandled promise rejection */
59-
});
51+
if (options?.protectedDataPath) {
52+
this.protectedDataPath = options?.protectedDataPath;
53+
} else if (process.env.IEXEC_DATASET_FILENAME && process.env.IEXEC_IN) {
54+
this.protectedDataPath = join(
55+
process.env.IEXEC_IN,
56+
process.env.IEXEC_DATASET_FILENAME
57+
);
58+
}
59+
60+
if (this.protectedDataPath) {
61+
this.zipPromise = readFile(this.protectedDataPath).then((buffer) => {
62+
return new JSZip().loadAsync(buffer);
63+
});
64+
this.zipPromise.catch(() => {
65+
/* prevents unhandled promise rejection */
66+
});
67+
}
6068
}
6169

6270
/**
@@ -93,6 +101,9 @@ class IExecDataProtectorDeserializer {
93101
| StringSchemaFilter<T>
94102
| BinarySchemaFilter<T>
95103
> {
104+
if (this.zipPromise === undefined) {
105+
throw Error('Missing protected data');
106+
}
96107
const zip = await this.zipPromise.catch(() => {
97108
throw Error('Failed to load protected data');
98109
});

packages/dataprotector-deserializer/tests/index.test.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,20 @@ import { writeFile } from 'fs/promises';
22
import {
33
createZipFromObject as legacyCreateZipFromObject,
44
extractDataSchema as legacyExtractDataSchema,
5-
} from '@iexec/dataprotector/dist/utils/data.js'; // run `prepare-test-deps` script before running this test file
6-
import { describe, it, beforeAll, expect } from '@jest/globals';
5+
} from '@iexec/dataprotector/dist/utils/data.js'; // run `test:prepare` script before running this test file
6+
import { describe, it, beforeAll, expect, beforeEach } from '@jest/globals';
77
import {
88
createZipFromObject,
99
extractDataSchema,
1010
} from '../../sdk/dist/src/utils/data.js';
1111
import { IExecDataProtectorDeserializer } from '../src/index.js';
1212

1313
describe('IExecDataProtectorDeserializer', () => {
14+
beforeEach(() => {
15+
// reset env
16+
delete process.env.IEXEC_IN;
17+
delete process.env.IEXEC_DATASET_FILENAME;
18+
});
1419
describe('constructor', () => {
1520
it('set default protectedDataPath with iexec envs', () => {
1621
process.env.IEXEC_IN = 'iexec_in';
@@ -27,6 +32,15 @@ describe('IExecDataProtectorDeserializer', () => {
2732
expect(protectedDataDeserializer['mode']).toBe('optimistic');
2833
});
2934
});
35+
describe('when used without protected data', () => {
36+
it('getValue() fails with missing protected data', async () => {
37+
process.env.IEXEC_IN = 'iexec_in';
38+
const protectedDataDeserializer = new IExecDataProtectorDeserializer();
39+
await expect(
40+
protectedDataDeserializer.getValue('foo', 'string')
41+
).rejects.toThrow(Error('Missing protected data'));
42+
});
43+
});
3044
describe('with a file that is not a protected data', () => {
3145
it('getValue() fails to load the data', async () => {
3246
const protectedDataDeserializer = new IExecDataProtectorDeserializer({

packages/sdk/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ All notable changes to this project will be documented in this file.
88

99
- Added `allowDebug` option for `protectData` to allow using the protected data in TEE debug apps (default `false`)
1010

11+
### Changed
12+
13+
- `processProtectedData` and `getCollectionsByOwner` returns a ValidationError and not a WorkflowError anymore in case of a bad input parameter.
14+
1115
## [2.0.0-beta.10] (2024-09-20)
1216

1317
### Changed

packages/sdk/src/lib/dataProtectorCore/getProtectedData.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export const getProtectedData = async ({
3232
}: GetProtectedDataParams & IExecConsumer & SubgraphConsumer): Promise<
3333
ProtectedData[]
3434
> => {
35-
const vCreationTimestampGte = positiveNumberSchema()
35+
const vCreatedAfterTimestamp = positiveNumberSchema()
3636
.label('createdAfterTimestamp')
3737
.validateSync(createdAfterTimestamp);
3838
const vProtectedDataAddress = addressOrEnsSchema()
@@ -66,8 +66,8 @@ export const getProtectedData = async ({
6666
if (vOwner) {
6767
whereFilters.push({ owner: vOwner });
6868
}
69-
if (vCreationTimestampGte) {
70-
whereFilters.push({ creationTimestamp_gte: vCreationTimestampGte });
69+
if (vCreatedAfterTimestamp) {
70+
whereFilters.push({ creationTimestamp_gte: vCreatedAfterTimestamp });
7171
}
7272
if (requiredSchemas.length > 0) {
7373
whereFilters.push({ schema_contains: requiredSchemas });
@@ -145,7 +145,7 @@ function flattenSchema(
145145
acc.anyOfSchemas.push(value.map((entry) => `${newKey}:${entry}`));
146146
} else {
147147
// Array of only one type. Similar to single type.
148-
acc.requiredSchemas.push(...value);
148+
acc.requiredSchemas.push(`${newKey}:${value[0]}`);
149149
}
150150
}
151151
// nested schema

packages/sdk/src/lib/dataProtectorCore/grantAccess.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { ZeroAddress } from 'ethers';
2+
import { NULL_ADDRESS } from 'iexec/utils';
23
import {
34
ValidationError,
45
WorkflowError,
@@ -98,7 +99,9 @@ export const grantAccess = async ({
9899
throw new WorkflowError({
99100
message: grantAccessErrorMessage,
100101
errorCause: Error(
101-
`An access has been already granted to the user: ${vAuthorizedUser} with the app: ${vAuthorizedApp}`
102+
`An access has been already granted to the user: ${
103+
vAuthorizedUser || NULL_ADDRESS
104+
} with the app: ${vAuthorizedApp}`
102105
),
103106
});
104107
}

packages/sdk/src/lib/dataProtectorCore/processProtectedData.ts

Lines changed: 30 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,12 @@ import { IExecConsumer } from '../types/internalTypes.js';
3333
import { getWhitelistContract } from './smartContract/getWhitelistContract.js';
3434
import { isAddressInWhitelist } from './smartContract/whitelistContract.read.js';
3535

36+
export type ProcessProtectedData = typeof processProtectedData;
37+
3638
export const processProtectedData = async ({
3739
iexec = throwIfMissing(),
38-
protectedData = throwIfMissing(),
39-
app = throwIfMissing(),
40+
protectedData,
41+
app,
4042
userWhitelist,
4143
maxPrice = DEFAULT_MAX_PRICE,
4244
args,
@@ -46,30 +48,31 @@ export const processProtectedData = async ({
4648
onStatusUpdate = () => {},
4749
}: IExecConsumer &
4850
ProcessProtectedDataParams): Promise<ProcessProtectedDataResponse> => {
51+
const vProtectedData = addressOrEnsSchema()
52+
.required()
53+
.label('protectedData')
54+
.validateSync(protectedData);
55+
const vApp = addressOrEnsSchema()
56+
.required()
57+
.label('authorizedApp')
58+
.validateSync(app);
59+
const vUserWhitelist = addressSchema()
60+
.label('userWhitelist')
61+
.validateSync(userWhitelist);
62+
const vMaxPrice = positiveNumberSchema()
63+
.label('maxPrice')
64+
.validateSync(maxPrice);
65+
const vInputFiles = urlArraySchema()
66+
.label('inputFiles')
67+
.validateSync(inputFiles);
68+
const vArgs = stringSchema().label('args').validateSync(args);
69+
const vSecrets = secretsSchema().label('secrets').validateSync(secrets);
70+
const vWorkerpool = addressOrEnsSchema()
71+
.default(WORKERPOOL_ADDRESS) // Default workerpool if none is specified
72+
.label('workerpool')
73+
.validateSync(workerpool);
74+
4975
try {
50-
const vApp = addressOrEnsSchema()
51-
.required()
52-
.label('authorizedApp')
53-
.validateSync(app);
54-
const vProtectedData = addressOrEnsSchema()
55-
.required()
56-
.label('protectedData')
57-
.validateSync(protectedData);
58-
const vUserWhitelist = addressSchema()
59-
.label('userWhitelist')
60-
.validateSync(userWhitelist);
61-
const vMaxPrice = positiveNumberSchema()
62-
.label('maxPrice')
63-
.validateSync(maxPrice);
64-
const vInputFiles = urlArraySchema()
65-
.label('inputFiles')
66-
.validateSync(inputFiles);
67-
const vArgs = stringSchema().label('args').validateSync(args);
68-
const vSecrets = secretsSchema().label('secrets').validateSync(secrets);
69-
const vWorkerpool = addressOrEnsSchema()
70-
.default(WORKERPOOL_ADDRESS) // Default workerpool if none is specified
71-
.label('workerpool')
72-
.validateSync(workerpool);
7376
const vOnStatusUpdate =
7477
validateOnStatusUpdateCallback<
7578
OnStatusUpdateFn<ProcessProtectedDataStatuses>
@@ -95,7 +98,7 @@ export const processProtectedData = async ({
9598

9699
if (!isRequesterInWhitelist) {
97100
throw new Error(
98-
`As a user, you are not in the whitelist. So you can't access to the protectedData in order process it`
101+
"As a user, you are not in the whitelist. You can't access the protectedData so you can't process it."
99102
);
100103
}
101104
requester = vUserWhitelist;
@@ -243,6 +246,7 @@ export const processProtectedData = async ({
243246
result,
244247
};
245248
} catch (error) {
249+
console.error('[processProtectedData] ERROR', error);
246250
handleIfProtocolError(error);
247251
throw new WorkflowError({
248252
message: processProtectedDataErrorMessage,

packages/sdk/src/lib/dataProtectorCore/protectData.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,17 @@ import { getDataProtectorCoreContract } from './smartContract/getDataProtectorCo
3636

3737
const logger = getLogger('protectData');
3838

39+
export type ProtectData = typeof protectData;
40+
3941
export const protectData = async ({
4042
iexec = throwIfMissing(),
4143
iexecDebug = throwIfMissing(),
4244
dataprotectorContractAddress,
45+
name = DEFAULT_DATA_NAME,
4346
ipfsNode,
4447
ipfsGateway,
4548
allowDebug = false,
4649
data,
47-
name = DEFAULT_DATA_NAME,
4850
onStatusUpdate = () => {},
4951
}: IExecConsumer &
5052
IExecDebugConsumer &

packages/sdk/src/lib/dataProtectorCore/revokeAllAccess.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export const revokeAllAccess = async ({
2222
onStatusUpdate = () => {},
2323
}: IExecConsumer & RevokeAllAccessParams): Promise<RevokedAccess[]> => {
2424
const vProtectedData = addressOrEnsSchema()
25+
.required()
2526
.label('protectedData')
2627
.validateSync(protectedData);
2728
const vAuthorizedApp = addressOrEnsSchema()
@@ -46,6 +47,7 @@ export const revokeAllAccess = async ({
4647
protectedData: vProtectedData,
4748
authorizedApp: vAuthorizedApp,
4849
authorizedUser: vAuthorizedUser,
50+
pageSize: 1000,
4951
}).catch((e) => {
5052
throw new WorkflowError({
5153
message: 'Failed to retrieve granted access',

packages/sdk/src/lib/dataProtectorCore/transferOwnership.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ import { IExecConsumer } from '../types/internalTypes.js';
55

66
export const transferOwnership = async ({
77
iexec = throwIfMissing(),
8-
protectedData = throwIfMissing(),
9-
newOwner = throwIfMissing(),
8+
protectedData,
9+
newOwner,
1010
}: IExecConsumer & TransferParams): Promise<TransferResponse> => {
1111
const vProtectedData = addressOrEnsSchema()
1212
.required()

0 commit comments

Comments
 (0)