Skip to content

Commit 2349970

Browse files
gfournierProzguesmiLe-CaignecCopilot
authored
feat: Ignore TEE framework bits in dataset tags for cross-framework compatibility (#325)
Co-authored-by: Zied <[email protected]> Co-authored-by: Robin Le Caignec <[email protected]> Co-authored-by: Copilot <[email protected]> Co-authored-by: Le-Caignec <[email protected]>
1 parent 55729ad commit 2349970

File tree

3 files changed

+192
-27
lines changed

3 files changed

+192
-27
lines changed

contracts/facets/IexecPoco1Facet.sol

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -126,10 +126,15 @@ contract IexecPoco1Facet is
126126
revert IncompatibleDatasetOrder("Requester restriction not satisfied");
127127
}
128128
// The deal's tag should include all tag bits of the dataset order.
129-
// Deal: 0b0101, Dataset: 0b0101 => ok
130-
// Deal: 0b0101, Dataset: 0b0001 => ok
131-
// Deal: 0b0101, Dataset: 0b0010 => !ok
132-
if ((deal.tag & datasetOrder.tag) != datasetOrder.tag) {
129+
// For dataset orders: ignore Scone, Gramine, and TDX framework bits to allow
130+
// dataset orders from SGX workerpools to be consumed on TDX workerpools and vice versa.
131+
// Examples after masking:
132+
// Deal: 0b0101, Dataset: 0b0101 => Masked Dataset: 0b0001 => ok
133+
// Deal: 0b0101, Dataset: 0b0001 => Masked Dataset: 0b0001 => ok
134+
// Deal: 0b1001 (TDX), Dataset: 0b0011 (Scone) => Masked Dataset: 0b0001 => ok (cross-framework compatibility)
135+
bytes32 maskedDatasetTag = datasetOrder.tag &
136+
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1;
137+
if ((deal.tag & maskedDatasetTag) != maskedDatasetTag) {
133138
revert IncompatibleDatasetOrder("Tag compatibility not satisfied");
134139
}
135140
}
@@ -239,7 +244,13 @@ contract IexecPoco1Facet is
239244
"iExecV5-matchOrders-0x05"
240245
);
241246
// The workerpool tag should include all tag bits of dataset, app, and requester orders.
242-
bytes32 tag = _apporder.tag | _datasetorder.tag | _requestorder.tag;
247+
// For dataset orders: ignore Scone, Gramine, and TDX framework bits to allow
248+
// dataset orders from SGX workerpools to be consumed on TDX workerpools and vice versa.
249+
// Bit positions: bit 0 = TEE, bit 1 = Scone, bit 2 = Gramine, bit 3 = TDX
250+
// Mask: ~(BIT_SCONE | BIT_GRAMINE | BIT_TDX) = ~0xE = 0xFFF...FF1
251+
bytes32 maskedDatasetTag = _datasetorder.tag &
252+
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1;
253+
bytes32 tag = _apporder.tag | maskedDatasetTag | _requestorder.tag;
243254
require(tag & ~_workerpoolorder.tag == 0x0, "iExecV5-matchOrders-0x06");
244255
require((tag ^ _apporder.tag)[31] & 0x01 == 0x0, "iExecV5-matchOrders-0x07");
245256

test/byContract/IexecPoco/IexecPoco1.test.ts

Lines changed: 159 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,17 @@ import {
1616
IexecPocoAccessors__factory,
1717
OwnableMock__factory,
1818
} from '../../../typechain';
19-
import { TAG_STANDARD, TAG_TEE } from '../../../utils/constants';
19+
import {
20+
TAG_ALL_TEE_FRAMEWORKS,
21+
TAG_BIT_2,
22+
TAG_BIT_4,
23+
TAG_BIT_4_AND_TEE,
24+
TAG_STANDARD,
25+
TAG_TEE,
26+
TAG_TEE_GRAMINE,
27+
TAG_TEE_SCONE,
28+
TAG_TEE_TDX,
29+
} from '../../../utils/constants';
2030
import {
2131
IexecOrders,
2232
OrdersActors,
@@ -627,6 +637,44 @@ describe('IexecPoco1', () => {
627637
);
628638
});
629639

640+
[
641+
{
642+
datasetTag: TAG_TEE_SCONE,
643+
workerpoolTag: TAG_TEE_TDX,
644+
description: 'Scone tag (0x3) and workerpool has TDX tag (0x9)',
645+
},
646+
{
647+
datasetTag: TAG_TEE_GRAMINE,
648+
workerpoolTag: TAG_TEE_TDX,
649+
description: 'Gramine tag (0x5) and workerpool has TDX tag (0x9)',
650+
},
651+
{
652+
datasetTag: TAG_TEE_TDX,
653+
workerpoolTag: TAG_TEE_SCONE,
654+
description: 'TDX tag (0x9) and workerpool has Scone tag (0x3)',
655+
},
656+
{
657+
datasetTag: TAG_ALL_TEE_FRAMEWORKS,
658+
workerpoolTag: TAG_TEE,
659+
description: 'all TEE framework bits (0xF) and workerpool has TEE only (0x1)',
660+
},
661+
].forEach(({ datasetTag, workerpoolTag, description }) => {
662+
it(`Should match orders when dataset has ${description}`, async () => {
663+
orders.dataset.tag = datasetTag;
664+
orders.workerpool.tag = workerpoolTag;
665+
orders.app.tag = TAG_TEE;
666+
orders.requester.tag = TAG_TEE;
667+
668+
await depositForRequesterAndSchedulerWithDefaultPrices(volume);
669+
await signOrders(iexecWrapper.getDomain(), orders, ordersActors);
670+
671+
await expect(iexecPocoAsRequester.matchOrders(...orders.toArray())).to.emit(
672+
iexecPoco,
673+
'OrdersMatched',
674+
);
675+
});
676+
});
677+
630678
// TODO add success tests for:
631679
// - identity groups
632680
// - pre-signatures
@@ -680,32 +728,32 @@ describe('IexecPoco1', () => {
680728
});
681729

682730
it('Should fail when workerpool tag does not satisfy app, dataset and request requirements', async () => {
683-
orders.app.tag = '0x0000000000000000000000000000000000000000000000000000000000000001'; // 0b0001
684-
orders.dataset.tag =
685-
'0x0000000000000000000000000000000000000000000000000000000000000002'; // 0b0010
686-
orders.requester.tag =
687-
'0x0000000000000000000000000000000000000000000000000000000000000003'; // 0b0011
688-
// Workerpool order is supposed to satisfy conditions of all actors.
689-
// Bad tag, correct tag should be 0b0011.
690-
orders.workerpool.tag =
691-
'0x0000000000000000000000000000000000000000000000000000000000000004'; // 0b0100
692-
// Match orders.
731+
orders.app.tag = TAG_TEE; //0b0001
732+
orders.dataset.tag = TAG_TEE_SCONE; // 0b0011
733+
orders.requester.tag = TAG_TEE_SCONE; // 0b0011
734+
orders.workerpool.tag = TAG_BIT_2; // 0b0100 - does not satisfy last bits of app, dataset, request
735+
await expect(iexecPocoAsRequester.matchOrders(...orders.toArray())).to.be.revertedWith(
736+
'iExecV5-matchOrders-0x06',
737+
);
738+
});
739+
740+
it('Should fail when dataset has other bits set that are not ignored', async () => {
741+
orders.dataset.tag = TAG_BIT_4_AND_TEE; // 0b10001 bit 4 set (not ignored)
742+
orders.workerpool.tag = TAG_TEE;
743+
orders.app.tag = TAG_TEE;
744+
orders.requester.tag = TAG_TEE;
745+
693746
await expect(iexecPocoAsRequester.matchOrders(...orders.toArray())).to.be.revertedWith(
694747
'iExecV5-matchOrders-0x06',
695748
);
696749
});
697750

698751
it('Should fail when the last bit of app tag does not satisfy dataset or request requirements', async () => {
699-
// The last bit of dataset and request tag is 1, but app tag does not set it
700-
orders.app.tag = '0x0000000000000000000000000000000000000000000000000000000000000002'; // 0b0010
701-
orders.dataset.tag =
702-
'0x0000000000000000000000000000000000000000000000000000000000000003'; // 0b0011
703-
orders.requester.tag =
704-
'0x0000000000000000000000000000000000000000000000000000000000000003'; // 0b0011
752+
orders.app.tag = TAG_BIT_2; // 0b0100
753+
orders.dataset.tag = TAG_TEE_GRAMINE; // 0b0101
754+
orders.requester.tag = TAG_TEE_GRAMINE;
705755
// Set the workerpool tag in a way to pass first tag check.
706-
orders.workerpool.tag =
707-
'0x0000000000000000000000000000000000000000000000000000000000000003'; // 0b0011
708-
// Match orders.
756+
orders.workerpool.tag = TAG_TEE_GRAMINE;
709757
await expect(iexecPocoAsRequester.matchOrders(...orders.toArray())).to.be.revertedWith(
710758
'iExecV5-matchOrders-0x07',
711759
);
@@ -1253,11 +1301,11 @@ describe('IexecPoco1', () => {
12531301
.withArgs('Requester restriction not satisfied');
12541302
});
12551303

1256-
it('Should revert when tag compatibility is not satisfied', async () => {
1304+
it('Should revert when tag compatibility with deal is not satisfied', async () => {
12571305
// Create dataset order with incompatible tag
12581306
const incompatibleTagDatasetOrder = {
12591307
...compatibleDatasetOrder,
1260-
tag: '0x0000000000000000000000000000000000000000000000000000000000000010', // Different tag
1308+
tag: TAG_BIT_4, // Different tag
12611309
};
12621310
await signOrder(iexecWrapper.getDomain(), incompatibleTagDatasetOrder, datasetProvider);
12631311
await expect(
@@ -1269,6 +1317,95 @@ describe('IexecPoco1', () => {
12691317
.to.be.revertedWithCustomError(iexecPoco, 'IncompatibleDatasetOrder')
12701318
.withArgs('Tag compatibility not satisfied');
12711319
});
1320+
1321+
// TODO: Add more test cases for tag compatibility
1322+
[
1323+
{
1324+
datasetTag: TAG_TEE_SCONE,
1325+
dealTag: TAG_TEE_TDX,
1326+
description: 'Scone tag (0x3) and deal has TDX tag (0x9)',
1327+
saltPrefix: 'scone-tdx',
1328+
},
1329+
{
1330+
datasetTag: TAG_TEE_GRAMINE,
1331+
dealTag: TAG_TEE_TDX,
1332+
description: 'Gramine tag (0x5) and deal has TDX tag (0x9)',
1333+
saltPrefix: 'gramine-tdx',
1334+
},
1335+
{
1336+
datasetTag: TAG_TEE_TDX,
1337+
dealTag: TAG_TEE_SCONE,
1338+
description: 'TDX tag (0x9) and deal has Scone tag (0x3)',
1339+
saltPrefix: 'tdx-scone',
1340+
},
1341+
{
1342+
datasetTag: TAG_ALL_TEE_FRAMEWORKS,
1343+
dealTag: TAG_TEE,
1344+
description: 'all TEE framework bits (0xF) and deal has TEE only (0x1)',
1345+
saltPrefix: 'all-frameworks-tee',
1346+
},
1347+
].forEach(({ datasetTag, dealTag, description, saltPrefix }) => {
1348+
it(`Should not revert when dataset has ${description}`, async () => {
1349+
// Create a deal with the specified tag
1350+
const dealOrders = buildOrders({
1351+
assets: { ...ordersAssets, dataset: ZeroAddress },
1352+
prices: ordersPrices,
1353+
requester: requester.address,
1354+
tag: dealTag,
1355+
volume: volume,
1356+
});
1357+
dealOrders.app.salt = ethers.id(`${saltPrefix}-app-salt`);
1358+
dealOrders.workerpool.salt = ethers.id(`${saltPrefix}-workerpool-salt`);
1359+
dealOrders.requester.salt = ethers.id(`${saltPrefix}-requester-salt`);
1360+
await depositForRequesterAndSchedulerWithDefaultPrices(volume);
1361+
await signOrders(iexecWrapper.getDomain(), dealOrders, ordersActors);
1362+
const dealId = getDealId(iexecWrapper.getDomain(), dealOrders.requester);
1363+
await iexecPocoAsRequester.matchOrders(...dealOrders.toArray());
1364+
1365+
// Create dataset order with the specified tag
1366+
const datasetOrder = {
1367+
...compatibleDatasetOrder,
1368+
tag: datasetTag,
1369+
salt: ethers.id(`${saltPrefix}-dataset-salt`),
1370+
};
1371+
await signOrder(iexecWrapper.getDomain(), datasetOrder, datasetProvider);
1372+
1373+
// Should not revert because bits 1-3 of dataset tag are ignored
1374+
await expect(iexecPoco.assertDatasetDealCompatibility(datasetOrder, dealId)).to.not
1375+
.be.reverted;
1376+
});
1377+
});
1378+
1379+
it('Should revert when dataset has bit 4 set (not masked) and deal does not', async () => {
1380+
// Create a deal with TEE only (0b0001 = 0x1)
1381+
const teeOnlyOrders = buildOrders({
1382+
assets: { ...ordersAssets, dataset: ZeroAddress },
1383+
prices: ordersPrices,
1384+
requester: requester.address,
1385+
tag: TAG_TEE, // TEE only
1386+
volume: volume,
1387+
});
1388+
teeOnlyOrders.app.salt = ethers.id('tee-bit4-app-salt');
1389+
teeOnlyOrders.workerpool.salt = ethers.id('tee-bit4-workerpool-salt');
1390+
teeOnlyOrders.requester.salt = ethers.id('tee-bit4-requester-salt');
1391+
await depositForRequesterAndSchedulerWithDefaultPrices(volume);
1392+
await signOrders(iexecWrapper.getDomain(), teeOnlyOrders, ordersActors);
1393+
const teeOnlyDealId = getDealId(iexecWrapper.getDomain(), teeOnlyOrders.requester);
1394+
await iexecPocoAsRequester.matchOrders(...teeOnlyOrders.toArray());
1395+
1396+
// Create dataset order with bit 4 set (0b10001 = 0x11)
1397+
const bit4DatasetOrder = {
1398+
...compatibleDatasetOrder,
1399+
tag: TAG_BIT_4_AND_TEE,
1400+
salt: ethers.id('bit4-dataset-salt'),
1401+
};
1402+
await signOrder(iexecWrapper.getDomain(), bit4DatasetOrder, datasetProvider);
1403+
1404+
// Should revert because bit 4 is NOT masked and the deal doesn't have it
1405+
await expect(iexecPoco.assertDatasetDealCompatibility(bit4DatasetOrder, teeOnlyDealId))
1406+
.to.be.revertedWithCustomError(iexecPoco, 'IncompatibleDatasetOrder')
1407+
.withArgs('Tag compatibility not satisfied');
1408+
});
12721409
});
12731410

12741411
/**

utils/constants.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,25 @@
11
// SPDX-FileCopyrightText: 2020-2025 IEXEC BLOCKCHAIN TECH <[email protected]>
22
// SPDX-License-Identifier: Apache-2.0
33

4+
/**
5+
* Tag constants:
6+
* - Bit 0: TEE
7+
* - Bit 1: Scone
8+
* - Bit 2: Gramine
9+
* - Bit 3: TDX
10+
* - Bit 4: GPU (0x10)
11+
*/
412
export const TAG_STANDARD = '0x0000000000000000000000000000000000000000000000000000000000000000';
513
export const TAG_TEE = '0x0000000000000000000000000000000000000000000000000000000000000001';
14+
export const TAG_TEE_SCONE = '0x0000000000000000000000000000000000000000000000000000000000000003'; // 0b0011 = TEE + Scone
15+
export const TAG_TEE_GRAMINE = '0x0000000000000000000000000000000000000000000000000000000000000005'; // 0b0101 = TEE + Gramine
16+
export const TAG_TEE_TDX = '0x0000000000000000000000000000000000000000000000000000000000000009'; // 0b1001 = TEE + TDX
17+
export const TAG_ALL_TEE_FRAMEWORKS =
18+
'0x000000000000000000000000000000000000000000000000000000000000000F'; // 0b1111 = TEE + Scone + Gramine + TDX
19+
export const TAG_BIT_2 = '0x0000000000000000000000000000000000000000000000000000000000000004'; // 0b0100
20+
export const TAG_BIT_4 = '0x0000000000000000000000000000000000000000000000000000000000000010'; // 0b10000 (bit 4 in 0-indexed)
21+
export const TAG_BIT_4_AND_TEE =
22+
'0x0000000000000000000000000000000000000000000000000000000000000011'; // 0b10001
623

724
export const NULL = {
825
BYTES32: '0x0000000000000000000000000000000000000000000000000000000000000000',

0 commit comments

Comments
 (0)