Skip to content

Commit f69090b

Browse files
eudelins-zamamanoranjith
authored andcommitted
chore(test-suite): use acl relayer (#2064)
* chore(test-suite): update copro params * chore(test-suite): update contract addresses * chore(gateway-contracts): pauser task minor fix * chore(test-suite): update relayer * chore(test-suite): update relayer-sdk v0.4.1 * chore(test-suite): draft add negative acl tests * chore(test-suite): fix expired delegation, acl not allow tests (#2060) * chore(test-suite): update acl failure test for delegated user decr - Previously: negative delegated user decryption tests asserted on raw Solidity selector 0x0190c506 and was using relayer-sdk v0.4.1 that was not handling the label 'now_allowed_on_host_acl' from relayer. - Now: bump relayer-sdk to v0.4.2 that handles the label and asserts on relayer-sdk error label not_allowed_on_host_acl, matching the structured error returned by the relayer on HTTP 400 * chore(test-suite): fix expired delegation test - Issue: setting pastExpiration=1 reverted at the contract level (ExpirationDateBeforeOneHour) so the test never reached the decryption step - Fix: delegate with a valid expiration (now + 1h1m), then use evm_increaseTime to fast-forward past it before attempting decryption * chore(common): update package-lock.json --------- Co-authored-by: Simon Eudeline <simon.eudeline@zama.ai> * fix(test-suite): relayer and copro config update * chore(test-suite): update test-suite versions --------- Co-authored-by: Manoranjith <manoranjith.ponnuraj@zama.ai>
1 parent 72bcfc2 commit f69090b

File tree

18 files changed

+219
-109
lines changed

18 files changed

+219
-109
lines changed

gateway-contracts/tasks/addPausers.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { getRequiredEnvVar, loadGatewayAddresses } from "./utils/loadVariables";
77
// for local testing. By default, we use the PAUSER_SET_ADDRESS env var, as done in deployment
88
task("task:addGatewayPausers")
99
.addParam("useInternalPauserSetAddress", "If internal PauserSet address should be used", false, types.boolean)
10-
.setAction(async function ({ useInternalGatewayConfigAddress }, hre) {
10+
.setAction(async function ({ useInternalPauserSetAddress }, hre) {
1111
await hre.run("compile:specific", { contract: "contracts/immutable" });
1212
console.log("Adding pausers to PauserSet contract");
1313

@@ -21,7 +21,7 @@ task("task:addGatewayPausers")
2121
pausers.push(getRequiredEnvVar(`PAUSER_ADDRESS_${idx}`));
2222
}
2323

24-
if (useInternalGatewayConfigAddress) {
24+
if (useInternalPauserSetAddress) {
2525
loadGatewayAddresses();
2626
}
2727
const pauserSetAddress = getRequiredEnvVar("PAUSER_SET_ADDRESS");

package-lock.json

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test-suite/e2e/Dockerfile

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ RUN apk add --no-cache \
1010
curl \
1111
wget \
1212
vim \
13-
jq && \
13+
jq \
14+
python3 \
15+
build-base && \
1416
rm -rf /var/cache/apk/*
1517

1618
RUN addgroup -g 10001 fhevm && \

test-suite/e2e/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@
1616
"dependencies": {
1717
"@fhevm/solidity": "*",
1818
"@openzeppelin/contracts": "^5.3.0",
19-
"@zama-fhe/relayer-sdk": "^0.4.0",
19+
"@zama-fhe/relayer-sdk": "^0.4.2",
2020
"bigint-buffer": "^1.1.5",
2121
"dotenv": "^16.0.3",
2222
"encrypted-types": "^0.0.4"
2323
}
24-
}
24+
}

test-suite/e2e/test/delegatedUserDecryption/delegatedUserDecryption.ts

Lines changed: 103 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ import { createInstances } from '../instance';
55
import { getSigners, initSigners } from '../signers';
66
import { delegatedUserDecryptSingleHandle, waitForBlock } from '../utils';
77

8-
const USER_DECRYPTION_NOT_DELEGATED_SELECTOR = '0x0190c506';
8+
const NOT_ALLOWED_ON_HOST_ACL = 'not_allowed_on_host_acl';
99

1010
describe('Delegated user decryption', function () {
1111
before(async function () {
12-
await initSigners(3);
12+
await initSigners(5);
1313
this.signers = await getSigners();
1414
this.instances = await createInstances(this.signers);
1515

@@ -233,8 +233,8 @@ describe('Delegated user decryption', function () {
233233
const balanceHandle = await this.token.balanceOf(this.smartWalletAddress);
234234
const { publicKey, privateKey } = this.instances.bob.generateKeypair();
235235

236-
await expect(
237-
delegatedUserDecryptSingleHandle(
236+
try {
237+
await delegatedUserDecryptSingleHandle(
238238
this.instances.bob,
239239
balanceHandle,
240240
this.tokenAddress,
@@ -243,9 +243,104 @@ describe('Delegated user decryption', function () {
243243
this.signers.bob,
244244
privateKey,
245245
publicKey,
246-
)
247-
).to.be.rejectedWith(
248-
new RegExp(USER_DECRYPTION_NOT_DELEGATED_SELECTOR),
249-
);
246+
);
247+
expect.fail('Expected delegated user decrypt to be rejected after revocation');
248+
} catch (err: any) {
249+
expect(err.relayerApiError?.label).to.equal(NOT_ALLOWED_ON_HOST_ACL);
250+
}
251+
});
252+
253+
it('test delegated user decryption should fail when no delegation exists', async function () {
254+
const balanceHandle = await this.token.balanceOf(this.smartWalletAddress);
255+
const { publicKey, privateKey } = this.instances.dave.generateKeypair();
256+
257+
try {
258+
await delegatedUserDecryptSingleHandle(
259+
this.instances.dave,
260+
balanceHandle,
261+
this.tokenAddress,
262+
this.smartWalletAddress,
263+
this.signers.dave.address,
264+
this.signers.dave,
265+
privateKey,
266+
publicKey,
267+
);
268+
expect.fail('Expected delegated user decrypt to be rejected without delegation');
269+
} catch (err: any) {
270+
expect(err.relayerApiError?.label).to.equal(NOT_ALLOWED_ON_HOST_ACL);
271+
}
272+
});
273+
274+
it('test delegated user decryption should fail when delegation is for wrong contract', async function () {
275+
const dummyFactory = await ethers.getContractFactory('UserDecrypt');
276+
const dummy = await dummyFactory.connect(this.signers.alice).deploy();
277+
await dummy.waitForDeployment();
278+
const wrongAddress = await dummy.getAddress();
279+
280+
const expirationTimestamp = Math.floor(Date.now() / 1000) + 86400;
281+
const tx = await this.smartWallet
282+
.connect(this.signers.bob)
283+
.delegateUserDecryption(this.signers.eve.address, wrongAddress, expirationTimestamp);
284+
await tx.wait();
285+
const currentBlock = await ethers.provider.getBlockNumber();
286+
await waitForBlock(currentBlock + 15);
287+
288+
const balanceHandle = await this.token.balanceOf(this.smartWalletAddress);
289+
const { publicKey, privateKey } = this.instances.eve.generateKeypair();
290+
291+
try {
292+
await delegatedUserDecryptSingleHandle(
293+
this.instances.eve,
294+
balanceHandle,
295+
this.tokenAddress,
296+
this.smartWalletAddress,
297+
this.signers.eve.address,
298+
this.signers.eve,
299+
privateKey,
300+
publicKey,
301+
);
302+
expect.fail('Expected delegated user decrypt to be rejected for wrong contract');
303+
} catch (err: any) {
304+
expect(err.relayerApiError?.label).to.equal(NOT_ALLOWED_ON_HOST_ACL);
305+
}
306+
});
307+
308+
it('test delegated user decryption should fail when delegation has expired', async function () {
309+
// Expiration must be >1h from chain time (FHE library constraint).
310+
// Use block timestamp, not Date.now(), since evm_increaseTime shifts chain clock.
311+
const oneHour = 3600;
312+
const buffer = 60;
313+
const latestBlock = await ethers.provider.getBlock('latest');
314+
const expirationTimestamp = latestBlock!.timestamp + oneHour + buffer;
315+
const tx = await this.smartWallet
316+
.connect(this.signers.bob)
317+
.delegateUserDecryption(this.signers.eve.address, this.tokenAddress, expirationTimestamp);
318+
await tx.wait();
319+
320+
// Fast-forward time past the expiration.
321+
await ethers.provider.send('evm_increaseTime', [oneHour + buffer + 1]);
322+
await ethers.provider.send('evm_mine', []);
323+
324+
const currentBlock = await ethers.provider.getBlockNumber();
325+
await waitForBlock(currentBlock + 15);
326+
327+
const balanceHandle = await this.token.balanceOf(this.smartWalletAddress);
328+
const { publicKey, privateKey } = this.instances.eve.generateKeypair();
329+
330+
try {
331+
await delegatedUserDecryptSingleHandle(
332+
this.instances.eve,
333+
balanceHandle,
334+
this.tokenAddress,
335+
this.smartWalletAddress,
336+
this.signers.eve.address,
337+
this.signers.eve,
338+
privateKey,
339+
publicKey,
340+
);
341+
expect.fail('Expected delegated user decrypt to be rejected for expired delegation');
342+
} catch (err: any) {
343+
expect(err.relayerApiError?.label).to.equal(NOT_ALLOWED_ON_HOST_ACL);
344+
}
250345
});
251346
});

test-suite/e2e/test/httpPublicDecrypt/httpPublicDecrypt.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { assert } from 'chai';
1+
import { assert, expect } from 'chai';
22
import { ethers } from 'hardhat';
33

44
import { createInstances } from '../instance';
@@ -38,4 +38,18 @@ describe('HTTPPublicDecrypt', function () {
3838
};
3939
assert.deepEqual(res.clearValues, expectedRes);
4040
});
41+
42+
it('test public decrypt should fail for non-publicly-decryptable handle', async function () {
43+
const factory = await ethers.getContractFactory('UserDecrypt');
44+
const contract = await factory.connect(this.signers.alice).deploy();
45+
await contract.waitForDeployment();
46+
const handle = await contract.xBool();
47+
48+
try {
49+
await this.instances.alice.publicDecrypt([handle]);
50+
expect.fail('Expected an error - handle is not publicly decryptable');
51+
} catch (error) {
52+
expect(error.message).to.include('not allowed for public decryption');
53+
}
54+
});
4155
});

test-suite/e2e/test/userDecryption/userDecryption.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,43 @@ describe('User decryption', function () {
190190
expect(decryptedValue).to.equal(74285495974541385002137713624115238327312291047062397922780925695323480915729n);
191191
});
192192

193+
it('test user decrypt should fail when dapp contract is not allowed for handle', async function () {
194+
const handle = await this.contract.xBool();
195+
// Deploy a second contract to get an address NOT allowed on the handle
196+
const factory2 = await ethers.getContractFactory('HTTPPublicDecrypt');
197+
const contract2 = await factory2.connect(this.signers.alice).deploy();
198+
await contract2.waitForDeployment();
199+
const wrongContractAddress = await contract2.getAddress();
200+
201+
const { publicKey, privateKey } = this.instances.alice.generateKeypair();
202+
const handleContractPairs = [{ handle, contractAddress: wrongContractAddress }];
203+
const startTimeStamp = Math.floor(Date.now() / 1000);
204+
const durationDays = 10;
205+
const contractAddresses = [wrongContractAddress];
206+
const eip712 = this.instances.alice.createEIP712(publicKey, contractAddresses, startTimeStamp, durationDays);
207+
const signature = await this.signers.alice.signTypedData(
208+
eip712.domain,
209+
{ UserDecryptRequestVerification: eip712.types.UserDecryptRequestVerification },
210+
eip712.message,
211+
);
212+
213+
try {
214+
await this.instances.alice.userDecrypt(
215+
handleContractPairs,
216+
privateKey,
217+
publicKey,
218+
signature.replace('0x', ''),
219+
contractAddresses,
220+
this.signers.alice.address,
221+
startTimeStamp,
222+
durationDays,
223+
);
224+
expect.fail('Expected an error - contract should not be allowed');
225+
} catch (error) {
226+
expect(error.message).to.include('is not authorized to user decrypt handle');
227+
}
228+
});
229+
193230
it('test user decrypt request expired', async function () {
194231
const handle = await this.contract.xBool();
195232
const HandleContractPairs = [

test-suite/fhevm/config/connector/config.toml

Lines changed: 0 additions & 42 deletions
This file was deleted.

test-suite/fhevm/config/relayer/local.yaml

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
host_chains:
2+
- chain_id: 12345
3+
url: "http://host-node:8545"
4+
acl_address: "0x05fD9B5EFE0a996095f42Ed7e77c390810CF660c"
15

26
gateway:
37
blockchain_rpc:
@@ -70,9 +74,14 @@ gateway:
7074
tx_throttler_capacity: 11000 # To allow actual capacity of 10k, after accounting for safety margin.
7175
tx_throttler_safety_margin: 1000
7276
readiness_checker:
73-
retry:
74-
max_attempts: 30
75-
retry_interval_ms: 2000
77+
host_acl_check:
78+
retry:
79+
max_attempts: 3
80+
retry_interval_ms: 1000
81+
gw_ciphertext_check:
82+
retry:
83+
max_attempts: 15
84+
retry_interval_ms: 2000
7685
public_decrypt:
7786
max_concurrency: 250
7887
capacity: 11000
@@ -86,24 +95,23 @@ gateway:
8695
capacity: 11000
8796
safety_margin: 1000
8897
contracts:
89-
decryption_address: "0x35760912360E875DA50D40a74305575c23D55783"
90-
input_verification_address: "0x1ceFA8E3F3271358218B52c33929Cf76078004c1"
98+
decryption_address: "0xF0bFB159C7381F7CB332586004d8247252C5b816"
99+
input_verification_address: "0x3b12Fc766Eb598b285998877e8E90F3e43a1F8d2"
91100
user_decrypt_shares_threshold: 1 # KMS centralized mode
92101
log:
93-
94-
format: "compact" # compact, pretty, or json
102+
format: "compact" # compact, pretty, or json
95103
show_file_line: true
96104
show_thread_ids: true
97105
show_timestamp: true
98106
show_target: true
99107

100108
keyurl:
101-
fhe_public_key:
102-
data_id: "fhe-public-key-data-id"
103-
url: "http://0.0.0.0:3001/publicKey.bin"
104-
crs:
105-
data_id: "crs-data-id"
106-
url: "http://0.0.0.0:3001/crs2048.bin"
109+
fhe_public_key:
110+
data_id: "fhe-public-key-data-id"
111+
url: "http://0.0.0.0:3001/publicKey.bin"
112+
crs:
113+
data_id: "crs-data-id"
114+
url: "http://0.0.0.0:3001/crs2048.bin"
107115

108116
http:
109117
endpoint: "0.0.0.0:3000"
@@ -194,4 +202,4 @@ storage:
194202
public_decrypt_expiry: "365d" # 1 year
195203
user_decrypt_expiry: "7d"
196204
input_proof_expiry: "7d"
197-
cron_startup_delay_after_recovery: "30s"
205+
cron_startup_delay_after_recovery: "30s"

test-suite/fhevm/docker-compose/coprocessor-docker-compose.yml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ services:
8181
- --acl-contract-address=${ACL_CONTRACT_ADDRESS}
8282
- --tfhe-contract-address=${FHEVM_EXECUTOR_CONTRACT_ADDRESS}
8383
- --url=${RPC_HTTP_URL}
84+
- --finality-lag=2
8485
depends_on:
8586
coprocessor-db-migration:
8687
condition: service_completed_successfully
@@ -236,9 +237,6 @@ services:
236237
- --verify-proof-resp-max-retries=15
237238
- --verify-proof-remove-after-max-retries
238239
- --signer-type=private-key
239-
- --delegation-fallback-polling=30
240-
- --delegation-max-retry=100000
241-
- --retry-immediately-on-nonce-error=2
242240
depends_on:
243241
coprocessor-db-migration:
244242
condition: service_completed_successfully

0 commit comments

Comments
 (0)