From 8ee66297e40e69643eafb6f50f7a4a41226af995 Mon Sep 17 00:00:00 2001 From: jatZama Date: Mon, 2 Dec 2024 19:35:14 +0100 Subject: [PATCH 1/3] chore: adds Gateway library and addresses for test --- contracts/addresses/.env.gateway | 1 + .../addresses/GatewayContractAddress.sol | 5 + contracts/gatewayLib/GatewayCaller.sol | 107 +++++++++++ contracts/gatewayLib/lib/Gateway.sol | 166 ++++++++++++++++++ 4 files changed, 279 insertions(+) create mode 100644 contracts/addresses/.env.gateway create mode 100644 contracts/addresses/GatewayContractAddress.sol create mode 100644 contracts/gatewayLib/GatewayCaller.sol create mode 100644 contracts/gatewayLib/lib/Gateway.sol diff --git a/contracts/addresses/.env.gateway b/contracts/addresses/.env.gateway new file mode 100644 index 0000000..e0a0bdc --- /dev/null +++ b/contracts/addresses/.env.gateway @@ -0,0 +1 @@ +GATEWAY_CONTRACT_PREDEPLOY_ADDRESS=0x33347831500F1e73f0ccCBb95c9f86B94d7b1123 \ No newline at end of file diff --git a/contracts/addresses/GatewayContractAddress.sol b/contracts/addresses/GatewayContractAddress.sol new file mode 100644 index 0000000..0fdadcf --- /dev/null +++ b/contracts/addresses/GatewayContractAddress.sol @@ -0,0 +1,5 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear + +pragma solidity ^0.8.24; + +address constant GATEWAY_CONTRACT_PREDEPLOY_ADDRESS = 0x33347831500F1e73f0ccCBb95c9f86B94d7b1123; diff --git a/contracts/gatewayLib/GatewayCaller.sol b/contracts/gatewayLib/GatewayCaller.sol new file mode 100644 index 0000000..d77060a --- /dev/null +++ b/contracts/gatewayLib/GatewayCaller.sol @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear + +pragma solidity ^0.8.24; + +import "../lib/TFHE.sol"; +import "./lib/Gateway.sol"; + +abstract contract GatewayCaller { + modifier onlyGateway() { + require(msg.sender == Gateway.gatewayContractAddress()); + _; + } + mapping(uint256 => ebool[]) private paramsEBool; + mapping(uint256 => euint4[]) private paramsEUint4; + mapping(uint256 => euint8[]) private paramsEUint8; + mapping(uint256 => euint16[]) private paramsEUint16; + mapping(uint256 => euint32[]) private paramsEUint32; + mapping(uint256 => euint64[]) private paramsEUint64; + mapping(uint256 => eaddress[]) private paramsEAddress; + mapping(uint256 => address[]) private paramsAddress; + mapping(uint256 => uint256[]) private paramsUint256; + mapping(uint256 => uint256[]) private requestedHandles; + + constructor() {} + + function addParamsEBool(uint256 requestID, ebool _ebool) internal { + paramsEBool[requestID].push(_ebool); + } + + function addParamsEUint4(uint256 requestID, euint4 _euint4) internal { + paramsEUint4[requestID].push(_euint4); + } + + function addParamsEUint8(uint256 requestID, euint8 _euint8) internal { + paramsEUint8[requestID].push(_euint8); + } + + function addParamsEUint16(uint256 requestID, euint16 _euint16) internal { + paramsEUint16[requestID].push(_euint16); + } + + function addParamsEUint32(uint256 requestID, euint32 _euint32) internal { + paramsEUint32[requestID].push(_euint32); + } + + function addParamsEUint64(uint256 requestID, euint64 _euint64) internal { + paramsEUint64[requestID].push(_euint64); + } + + function addParamsEAddress(uint256 requestID, eaddress _eaddress) internal { + paramsEAddress[requestID].push(_eaddress); + } + + function addParamsAddress(uint256 requestID, address _address) internal { + paramsAddress[requestID].push(_address); + } + + function addParamsUint256(uint256 requestID, uint256 _uint) internal { + paramsUint256[requestID].push(_uint); + } + + function saveRequestedHandles(uint256 requestID, uint256[] memory handlesList) internal { + require(requestedHandles[requestID].length == 0, "requested handles already saved"); + requestedHandles[requestID] = handlesList; + } + + function loadRequestedHandles(uint256 requestID) internal view returns (uint256[] memory) { + require(requestedHandles[requestID].length != 0, "requested handles were not saved for this requestID"); + return requestedHandles[requestID]; + } + + function getParamsEBool(uint256 requestID) internal view returns (ebool[] memory) { + return paramsEBool[requestID]; + } + + function getParamsEUint4(uint256 requestID) internal view returns (euint4[] memory) { + return paramsEUint4[requestID]; + } + + function getParamsEUint8(uint256 requestID) internal view returns (euint8[] memory) { + return paramsEUint8[requestID]; + } + + function getParamsEUint16(uint256 requestID) internal view returns (euint16[] memory) { + return paramsEUint16[requestID]; + } + + function getParamsEUint32(uint256 requestID) internal view returns (euint32[] memory) { + return paramsEUint32[requestID]; + } + + function getParamsEUint64(uint256 requestID) internal view returns (euint64[] memory) { + return paramsEUint64[requestID]; + } + + function getParamsEAddress(uint256 requestID) internal view returns (eaddress[] memory) { + return paramsEAddress[requestID]; + } + + function getParamsAddress(uint256 requestID) internal view returns (address[] memory) { + return paramsAddress[requestID]; + } + + function getParamsUint256(uint256 requestID) internal view returns (uint256[] memory) { + return paramsUint256[requestID]; + } +} \ No newline at end of file diff --git a/contracts/gatewayLib/lib/Gateway.sol b/contracts/gatewayLib/lib/Gateway.sol new file mode 100644 index 0000000..c328b2c --- /dev/null +++ b/contracts/gatewayLib/lib/Gateway.sol @@ -0,0 +1,166 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear + +pragma solidity ^0.8.24; + +import "../../addresses/GatewayContractAddress.sol"; +import "../../lib/Impl.sol"; +import "../../addresses/ACLAddress.sol"; + +interface IKMSVerifier { + function verifyDecryptionEIP712KMSSignatures( + address aclAddress, + uint256[] memory handlesList, + bytes memory decryptedResult, + bytes[] memory signatures + ) external returns (bool); +} + +interface IGatewayContract { + function requestDecryption( + uint256[] calldata ctsHandles, + bytes4 callbackSelector, + uint256 msgValue, + uint256 maxTimestamp, + bool passSignaturesToCaller + ) external returns (uint256); +} + +library Gateway { + struct GatewayConfigStruct { + address GatewayContractAddress; + } + + // keccak256(abi.encode(uint256(keccak256("fhevm.storage.GatewayConfig")) - 1)) & ~bytes32(uint256(0xff)) + bytes32 private constant GatewayLocation = 0x93ab6e17f2c461cce6ea5d4ec117e51dda77a64affc2b2c05f8cd440def0e700; + + function defaultGatewayAddress() internal pure returns (address) { + return GATEWAY_CONTRACT_PREDEPLOY_ADDRESS; + } + + function getGetwayConfig() internal pure returns (GatewayConfigStruct storage $) { + assembly { + $.slot := GatewayLocation + } + } + + function setGateway(address gatewayAddress) internal { + GatewayConfigStruct storage $ = getGetwayConfig(); + $.GatewayContractAddress = gatewayAddress; + } + + function gatewayContractAddress() internal view returns (address) { + GatewayConfigStruct storage $ = getGetwayConfig(); + return $.GatewayContractAddress; + } + + function toUint256(ebool newCT) internal pure returns (uint256 ct) { + ct = ebool.unwrap(newCT); + } + + function toUint256(euint4 newCT) internal pure returns (uint256 ct) { + ct = euint4.unwrap(newCT); + } + + function toUint256(euint8 newCT) internal pure returns (uint256 ct) { + ct = euint8.unwrap(newCT); + } + + function toUint256(euint16 newCT) internal pure returns (uint256 ct) { + ct = euint16.unwrap(newCT); + } + + function toUint256(euint32 newCT) internal pure returns (uint256 ct) { + ct = euint32.unwrap(newCT); + } + + function toUint256(euint64 newCT) internal pure returns (uint256 ct) { + ct = euint64.unwrap(newCT); + } + + function toUint256(euint128 newCT) internal pure returns (uint256 ct) { + ct = euint128.unwrap(newCT); + } + + function toUint256(eaddress newCT) internal pure returns (uint256 ct) { + ct = eaddress.unwrap(newCT); + } + + function toUint256(euint256 newCT) internal pure returns (uint256 ct) { + ct = euint256.unwrap(newCT); + } + + function toUint256(ebytes64 newCT) internal pure returns (uint256 ct) { + ct = ebytes64.unwrap(newCT); + } + + function toUint256(ebytes128 newCT) internal pure returns (uint256 ct) { + ct = ebytes128.unwrap(newCT); + } + + function toUint256(ebytes256 newCT) internal pure returns (uint256 ct) { + ct = ebytes256.unwrap(newCT); + } + + function requestDecryption( + uint256[] memory ctsHandles, + bytes4 callbackSelector, + uint256 msgValue, + uint256 maxTimestamp, + bool passSignaturesToCaller + ) internal returns (uint256 requestID) { + FHEVMConfig.FHEVMConfigStruct storage $ = Impl.getFHEVMConfig(); + IACL($.ACLAddress).allowForDecryption(ctsHandles); + GatewayConfigStruct storage $$ = getGetwayConfig(); + requestID = IGatewayContract($$.GatewayContractAddress).requestDecryption( + ctsHandles, + callbackSelector, + msgValue, + maxTimestamp, + passSignaturesToCaller + ); + } + + /// @dev this function is supposed to be called inside the callback function if the dev wants the dApp contract to verify the signatures + /// @dev this is useful to give dev the choice not to rely on trusting the GatewayContract. + /// @notice this could be used only when signatures are made available to the callback, i.e when `passSignaturesToCaller` is set to true during request + function verifySignatures(uint256[] memory handlesList, bytes[] memory signatures) internal returns (bool) { + uint256 start = 4 + 32; // start position after skipping the selector (4 bytes) and the first argument (index, 32 bytes) + uint256 length = getSignedDataLength(handlesList); + bytes memory decryptedResult = new bytes(length); + assembly { + calldatacopy(add(decryptedResult, 0x20), start, length) // Copy the relevant part of calldata to decryptedResult memory + } + FHEVMConfig.FHEVMConfigStruct storage $ = Impl.getFHEVMConfig(); + return + IKMSVerifier($.KMSVerifierAddress).verifyDecryptionEIP712KMSSignatures( + aclAdd, + handlesList, + decryptedResult, + signatures + ); + } + + function getSignedDataLength(uint256[] memory handlesList) private pure returns (uint256) { + uint256 handlesListlen = handlesList.length; + uint256 signedDataLength; + for (uint256 i = 0; i < handlesListlen; i++) { + uint8 typeCt = uint8(handlesList[i] >> 8); + if (typeCt < 9) { + signedDataLength += 32; + } else if (typeCt == 9) { + //ebytes64 + signedDataLength += 128; + } else if (typeCt == 10) { + //ebytes128 + signedDataLength += 192; + } else if (typeCt == 11) { + //ebytes256 + signedDataLength += 320; + } else { + revert("Unsupported handle type"); + } + } + signedDataLength += 32; // for the signatures offset + return signedDataLength; + } +} \ No newline at end of file From 8941ec33e09a8c47561c314bf3c6dc905db0ba38 Mon Sep 17 00:00:00 2001 From: jatZama Date: Mon, 2 Dec 2024 19:48:12 +0100 Subject: [PATCH 2/3] test: make mocked tests work --- contracts/examples/BlindAuction.sol | 2 +- contracts/examples/TestAsyncDecrypt.sol | 2 +- contracts/tasks/etherscanVerify.ts | 2 +- contracts/tasks/taskDeploy.ts | 2 +- contracts/tasks/taskGatewayRelayer.ts | 17 ++++++++++------- contracts/tasks/upgradeProxy.ts | 2 +- contracts/test/asyncDecrypt.ts | 2 +- 7 files changed, 16 insertions(+), 13 deletions(-) diff --git a/contracts/examples/BlindAuction.sol b/contracts/examples/BlindAuction.sol index 7f01c15..454c6a2 100644 --- a/contracts/examples/BlindAuction.sol +++ b/contracts/examples/BlindAuction.sol @@ -5,7 +5,7 @@ pragma solidity ^0.8.24; import "../lib/TFHE.sol"; import "./EncryptedERC20.sol"; import "@openzeppelin/contracts/access/Ownable2Step.sol"; -import "../gateway/GatewayCaller.sol"; +import "../gatewayLib/GatewayCaller.sol"; /// @notice Main contract for the blind auction contract BlindAuction is Ownable2Step, GatewayCaller { diff --git a/contracts/examples/TestAsyncDecrypt.sol b/contracts/examples/TestAsyncDecrypt.sol index ce1fab6..ed6c51c 100644 --- a/contracts/examples/TestAsyncDecrypt.sol +++ b/contracts/examples/TestAsyncDecrypt.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.24; import "../lib/TFHE.sol"; -import "../gateway/GatewayCaller.sol"; +import "../gatewayLib/GatewayCaller.sol"; /// @notice Contract for testing asynchronous decryption using the Gateway contract TestAsyncDecrypt is GatewayCaller { diff --git a/contracts/tasks/etherscanVerify.ts b/contracts/tasks/etherscanVerify.ts index 1567f66..6fcd959 100644 --- a/contracts/tasks/etherscanVerify.ts +++ b/contracts/tasks/etherscanVerify.ts @@ -73,7 +73,7 @@ task('task:verifyFHEPayment').setAction(async function (taskArguments, { upgrade }); task('task:verifyGatewayContract').setAction(async function (taskArguments, { upgrades, run }) { - const parsedEnvGateway = dotenv.parse(fs.readFileSync('gateway/.env.gateway')); + const parsedEnvGateway = dotenv.parse(fs.readFileSync('addresses/.env.gateway')); const proxyGateway = parsedEnvGateway.GATEWAY_CONTRACT_PREDEPLOY_ADDRESS; const implementationGatewayAddress = await upgrades.erc1967.getImplementationAddress(proxyGateway); await run('verify:verify', { diff --git a/contracts/tasks/taskDeploy.ts b/contracts/tasks/taskDeploy.ts index bf58612..590e89b 100644 --- a/contracts/tasks/taskDeploy.ts +++ b/contracts/tasks/taskDeploy.ts @@ -17,7 +17,7 @@ task('task:deployGateway') }); await Gateway.waitForDeployment(); const GatewayContractAddress = await Gateway.getAddress(); - const envConfig = dotenv.parse(fs.readFileSync('gateway/.env.gateway')); + const envConfig = dotenv.parse(fs.readFileSync('addresses/.env.gateway')); if (GatewayContractAddress !== envConfig.GATEWAY_CONTRACT_PREDEPLOY_ADDRESS) { throw new Error( `The nonce of the deployer account is not null. Please use another deployer private key or relaunch a clean instance of the fhEVM`, diff --git a/contracts/tasks/taskGatewayRelayer.ts b/contracts/tasks/taskGatewayRelayer.ts index a2da8d2..d3c6c3a 100644 --- a/contracts/tasks/taskGatewayRelayer.ts +++ b/contracts/tasks/taskGatewayRelayer.ts @@ -23,13 +23,13 @@ task('task:computeGatewayAddress') from: deployerAddress, nonce: 1, // deployer is supposed to have nonce 0 when deploying GatewayContract (0 nonce for implementation, +1 for UUPS) }); - const envFilePath = path.join(__dirname, '../gateway/.env.gateway'); + const envFilePath = path.join(__dirname, '../addresses/.env.gateway'); const content = `GATEWAY_CONTRACT_PREDEPLOY_ADDRESS=${gatewayContractAddressPrecomputed}`; try { fs.writeFileSync(envFilePath, content, { flag: 'w' }); - console.log('gatewayContractAddress written to gateway/.env.gateway successfully!'); + console.log('gatewayContractAddress written to addresses/.env.gateway successfully!'); } catch (err) { - console.error('Failed to write to gateway/.env.gateway:', err); + console.error('Failed to write to addresses/.env.gateway:', err); } const solidityTemplate = `// SPDX-License-Identifier: BSD-3-Clause-Clear @@ -40,10 +40,13 @@ address constant GATEWAY_CONTRACT_PREDEPLOY_ADDRESS = ${gatewayContractAddressPr `; try { - fs.writeFileSync('./gateway/lib/GatewayContractAddress.sol', solidityTemplate, { encoding: 'utf8', flag: 'w' }); - console.log('gateway/lib/GatewayContractAddress.sol file has been generated successfully.'); + fs.writeFileSync('./addresses/GatewayContractAddress.sol', solidityTemplate, { + encoding: 'utf8', + flag: 'w', + }); + console.log('addresses/GatewayContractAddress.sol file has been generated successfully.'); } catch (error) { - console.error('Failed to write gateway/lib/GatewayContractAddress.sol', error); + console.error('Failed to write addresses/GatewayContractAddress.sol', error); } }); @@ -115,7 +118,7 @@ task('task:launchFhevm') } await hre.run('task:deployGateway', { privateKey: privKeyDeployer, ownerAddress: deployerAddress }); - const parsedEnv = dotenv.parse(fs.readFileSync('gateway/.env.gateway')); + const parsedEnv = dotenv.parse(fs.readFileSync('addresses/.env.gateway')); const gatewayContractAddress = parsedEnv.GATEWAY_CONTRACT_PREDEPLOY_ADDRESS; await hre.run('task:addRelayer', { diff --git a/contracts/tasks/upgradeProxy.ts b/contracts/tasks/upgradeProxy.ts index 9c99ba0..7f9063a 100644 --- a/contracts/tasks/upgradeProxy.ts +++ b/contracts/tasks/upgradeProxy.ts @@ -213,7 +213,7 @@ task('task:upgradeGatewayContract') types.boolean, ) .setAction(async function (taskArguments: TaskArguments, { ethers, upgrades, run }) { - const parsedEnv = dotenv.parse(fs.readFileSync('gateway/.env.gateway')); + const parsedEnv = dotenv.parse(fs.readFileSync('addresses/.env.gateway')); const proxyAddress = parsedEnv.GATEWAY_CONTRACT_PREDEPLOY_ADDRESS; await upgradeCurrentToNew( taskArguments.privateKey, diff --git a/contracts/test/asyncDecrypt.ts b/contracts/test/asyncDecrypt.ts index 31767ae..98524b3 100644 --- a/contracts/test/asyncDecrypt.ts +++ b/contracts/test/asyncDecrypt.ts @@ -32,7 +32,7 @@ const currentTime = (): string => { return now.toLocaleTimeString('en-US', { hour12: true, hour: 'numeric', minute: 'numeric', second: 'numeric' }); }; -const parsedEnv = dotenv.parse(fs.readFileSync('gateway/.env.gateway')); +const parsedEnv = dotenv.parse(fs.readFileSync('addresses/.env.gateway')); let relayer: Wallet; if (networkName === 'hardhat') { const privKeyRelayer = process.env.PRIVATE_KEY_GATEWAY_RELAYER; From 72cc11d52fa43cbf972293cdde2ca68b9adbc87c Mon Sep 17 00:00:00 2001 From: jatZama Date: Mon, 2 Dec 2024 19:50:00 +0100 Subject: [PATCH 3/3] v0.6.0-5 chore: prettier --- contracts/gatewayLib/GatewayCaller.sol | 2 +- contracts/gatewayLib/lib/Gateway.sol | 2 +- contracts/package-lock.json | 4 ++-- contracts/package.json | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/contracts/gatewayLib/GatewayCaller.sol b/contracts/gatewayLib/GatewayCaller.sol index d77060a..79b4699 100644 --- a/contracts/gatewayLib/GatewayCaller.sol +++ b/contracts/gatewayLib/GatewayCaller.sol @@ -104,4 +104,4 @@ abstract contract GatewayCaller { function getParamsUint256(uint256 requestID) internal view returns (uint256[] memory) { return paramsUint256[requestID]; } -} \ No newline at end of file +} diff --git a/contracts/gatewayLib/lib/Gateway.sol b/contracts/gatewayLib/lib/Gateway.sol index c328b2c..932135c 100644 --- a/contracts/gatewayLib/lib/Gateway.sol +++ b/contracts/gatewayLib/lib/Gateway.sol @@ -163,4 +163,4 @@ library Gateway { signedDataLength += 32; // for the signatures offset return signedDataLength; } -} \ No newline at end of file +} diff --git a/contracts/package-lock.json b/contracts/package-lock.json index 82d6d02..fa56008 100644 --- a/contracts/package-lock.json +++ b/contracts/package-lock.json @@ -1,12 +1,12 @@ { "name": "fhevm-core-contracts", - "version": "0.6.0-4", + "version": "0.6.0-5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "fhevm-core-contracts", - "version": "0.6.0-4", + "version": "0.6.0-5", "license": "BSD-3-Clause", "devDependencies": { "@nomicfoundation/hardhat-toolbox": "^5.0.0", diff --git a/contracts/package.json b/contracts/package.json index 440ea4d..afba231 100644 --- a/contracts/package.json +++ b/contracts/package.json @@ -1,6 +1,6 @@ { "name": "fhevm-core-contracts", - "version": "0.6.0-4", + "version": "0.6.0-5", "description": "fhEVM contracts", "repository": { "type": "git",