From 1777fc4c05459a6f494d43f23e832ba2c40510ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Danjou?= Date: Sat, 14 Dec 2024 19:50:59 +0100 Subject: [PATCH 1/5] fix: fix public key id --- docs/getting_started/ethereum.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting_started/ethereum.md b/docs/getting_started/ethereum.md index 2b7661f4..b76faf5b 100644 --- a/docs/getting_started/ethereum.md +++ b/docs/getting_started/ethereum.md @@ -128,5 +128,5 @@ Save this in your `.env` file: | PAYMENT_CONTRACT | 0x25FE5d92Ae6f89AF37D177cF818bF27EDFe37F7c | | KMS_VERIFIER_CONTRACT | 0x904Af2B61068f686838bD6257E385C2cE7a09195 | | GATEWAY_CONTRACT | 0x7455c89669cdE1f7Cb6D026DFB87263422D821ca | -| PUBLIC_KEY_ID | 55729ddea48547ea837137d122e1c90043e94c41 | +| PUBLIC_KEY_ID | 0301c5dd3e2702992b7c12930b7d4defeaaa52cf | | GATEWAY_URL | `https://gateway.sepolia.zama.ai/` | From 9cf752ef5a5ee9e6125fad5c8289994267755c57 Mon Sep 17 00:00:00 2001 From: Aurora Poppyseed <30662672+poppyseedDev@users.noreply.github.com> Date: Sun, 15 Dec 2024 10:59:21 +0900 Subject: [PATCH 2/5] docs: update first_smart_contract.md --- docs/getting_started/first_smart_contract.md | 32 -------------------- 1 file changed, 32 deletions(-) diff --git a/docs/getting_started/first_smart_contract.md b/docs/getting_started/first_smart_contract.md index f9d6ca47..d568a50c 100644 --- a/docs/getting_started/first_smart_contract.md +++ b/docs/getting_started/first_smart_contract.md @@ -164,38 +164,6 @@ The test file demonstrates key concepts for testing fhEVM smart contracts: - Handle asynchronous operations properly with async/await - Set up proper encryption instances for testing encrypted values -### No constant nor immutable encrypted state variables - -Never use encrypted types for constant or immutable state variables, even if they should actually stay constants, or else any transaction involving those will fail. This is because ciphertexts should always be stored in the privileged storage of the contract (see paragraph 4.4 of [whitepaper](../../fhevm-whitepaper.pdf)) while constant and immutable variables are just appended to the bytecode of the deployed contract at construction time. - -❌ So, even if `a` and `b` should never change after construction, the following example : - -```solidity -contract C { - euint32 internal constant a = TFHE.asEuint32(42); - euint32 internal immutable b; - - constructor(uint32 _b) { - b = TFHE.asEuint32(_b); - TFHE.allowThis(b); - } -} -``` - -✅ Should be replaced by this snippet: - -```solidity -contract C { - euint32 internal a = TFHE.asEuint32(42); - euint32 internal b; - - constructor(uint32 _b) { - b = TFHE.asEuint32(_b); - TFHE.allowThis(b); - } -} -``` - ## **Next steps** Congratulations! You’ve configured and written your first confidential smart contract. Here are some ideas to expand your knowledge: From dd34f8b0b18db0b742b0b5728b94ad24320dd729 Mon Sep 17 00:00:00 2001 From: PacificYield <173040337+PacificYield@users.noreply.github.com> Date: Mon, 16 Dec 2024 11:34:47 +0100 Subject: [PATCH 3/5] refactor: add custom errors for TFHE --- codegen/templates.ts | 44 +++++++++++++++++++++++++++++++++++--------- lib/TFHE.sol | 43 ++++++++++++++++++++++++++++++++++--------- 2 files changed, 69 insertions(+), 18 deletions(-) diff --git a/codegen/templates.ts b/codegen/templates.ts index 44245e5f..bc05d2d4 100644 --- a/codegen/templates.ts +++ b/codegen/templates.ts @@ -265,6 +265,16 @@ ${commonSolLib()} * that interact with TFHE. */ library TFHE { + +/// @notice Returned if the input's length is greater than 64 bytes. +error InputLengthAbove64Bytes(uint256 inputLength); + +/// @notice Returned if the input's length is greater than 128 bytes. +error InputLengthAbove128Bytes(uint256 inputLength); + +/// @notice Returned if the input's length is greater than 256 bytes. +error InputLengthAbove256Bytes(uint256 inputLength); + function setFHEVM(FHEVMConfigStruct memory fhevmConfig) internal { Impl.setFHEVM(fhevmConfig); } @@ -1011,13 +1021,19 @@ function tfheCustomMethods(): string { // Left-pad a bytes array with zeros such that it becomes of length 64. function padToBytes64(bytes memory input) internal pure returns (bytes memory) { - require(input.length <= 64, "Input exceeds 64 bytes"); + uint256 inputLength = input.length; + + if (inputLength > 64) { + revert InputLengthAbove64Bytes(inputLength); + } + bytes memory result = new bytes(64); - uint256 paddingLength = 64 - input.length; + uint256 paddingLength = 64 - inputLength; + for (uint256 i = 0; i < paddingLength; i++) { result[i] = 0; } - for (uint256 i = 0; i < input.length; i++) { + for (uint256 i = 0; i < inputLength; i++) { result[paddingLength + i] = input[i]; } return result; @@ -1035,13 +1051,18 @@ function tfheCustomMethods(): string { // Left-pad a bytes array with zeros such that it becomes of length 128. function padToBytes128(bytes memory input) internal pure returns (bytes memory) { - require(input.length <= 128, "Input exceeds 128 bytes"); + uint256 inputLength = input.length; + + if (inputLength > 128) { + revert InputLengthAbove128Bytes(inputLength); + } + bytes memory result = new bytes(128); - uint256 paddingLength = 128 - input.length; + uint256 paddingLength = 128 - inputLength; for (uint256 i = 0; i < paddingLength; i++) { result[i] = 0; } - for (uint256 i = 0; i < input.length; i++) { + for (uint256 i = 0; i < inputLength; i++) { result[paddingLength + i] = input[i]; } return result; @@ -1059,13 +1080,18 @@ function tfheCustomMethods(): string { // Left-pad a bytes array with zeros such that it becomes of length 256. function padToBytes256(bytes memory input) internal pure returns (bytes memory) { - require(input.length <= 256, "Input exceeds 256 bytes"); + uint256 inputLength = input.length; + + if (inputLength > 256) { + revert InputLengthAbove256Bytes(inputLength); + } + bytes memory result = new bytes(256); - uint256 paddingLength = 256 - input.length; + uint256 paddingLength = 256 - inputLength; for (uint256 i = 0; i < paddingLength; i++) { result[i] = 0; } - for (uint256 i = 0; i < input.length; i++) { + for (uint256 i = 0; i < inputLength; i++) { result[paddingLength + i] = input[i]; } return result; diff --git a/lib/TFHE.sol b/lib/TFHE.sol index ac1b9f6f..de2f4e3d 100644 --- a/lib/TFHE.sol +++ b/lib/TFHE.sol @@ -42,6 +42,15 @@ library Common { * that interact with TFHE. */ library TFHE { + /// @notice Returned if the input's length is greater than 64 bytes. + error InputLengthAbove64Bytes(uint256 inputLength); + + /// @notice Returned if the input's length is greater than 128 bytes. + error InputLengthAbove128Bytes(uint256 inputLength); + + /// @notice Returned if the input's length is greater than 256 bytes. + error InputLengthAbove256Bytes(uint256 inputLength); + function setFHEVM(FHEVMConfigStruct memory fhevmConfig) internal { Impl.setFHEVM(fhevmConfig); } @@ -10430,13 +10439,19 @@ library TFHE { // Left-pad a bytes array with zeros such that it becomes of length 64. function padToBytes64(bytes memory input) internal pure returns (bytes memory) { - require(input.length <= 64, "Input exceeds 64 bytes"); + uint256 inputLength = input.length; + + if (inputLength > 64) { + revert InputLengthAbove64Bytes(inputLength); + } + bytes memory result = new bytes(64); - uint256 paddingLength = 64 - input.length; + uint256 paddingLength = 64 - inputLength; + for (uint256 i = 0; i < paddingLength; i++) { result[i] = 0; } - for (uint256 i = 0; i < input.length; i++) { + for (uint256 i = 0; i < inputLength; i++) { result[paddingLength + i] = input[i]; } return result; @@ -10454,13 +10469,18 @@ library TFHE { // Left-pad a bytes array with zeros such that it becomes of length 128. function padToBytes128(bytes memory input) internal pure returns (bytes memory) { - require(input.length <= 128, "Input exceeds 128 bytes"); + uint256 inputLength = input.length; + + if (inputLength > 128) { + revert InputLengthAbove128Bytes(inputLength); + } + bytes memory result = new bytes(128); - uint256 paddingLength = 128 - input.length; + uint256 paddingLength = 128 - inputLength; for (uint256 i = 0; i < paddingLength; i++) { result[i] = 0; } - for (uint256 i = 0; i < input.length; i++) { + for (uint256 i = 0; i < inputLength; i++) { result[paddingLength + i] = input[i]; } return result; @@ -10478,13 +10498,18 @@ library TFHE { // Left-pad a bytes array with zeros such that it becomes of length 256. function padToBytes256(bytes memory input) internal pure returns (bytes memory) { - require(input.length <= 256, "Input exceeds 256 bytes"); + uint256 inputLength = input.length; + + if (inputLength > 256) { + revert InputLengthAbove256Bytes(inputLength); + } + bytes memory result = new bytes(256); - uint256 paddingLength = 256 - input.length; + uint256 paddingLength = 256 - inputLength; for (uint256 i = 0; i < paddingLength; i++) { result[i] = 0; } - for (uint256 i = 0; i < input.length; i++) { + for (uint256 i = 0; i < inputLength; i++) { result[paddingLength + i] = input[i]; } return result; From 655dc24f8c81c0d9b52c975a7d62d6ab71a034f6 Mon Sep 17 00:00:00 2001 From: PacificYield <173040337+PacificYield@users.noreply.github.com> Date: Mon, 16 Dec 2024 17:17:42 +0100 Subject: [PATCH 4/5] test: revert paths --- examples/tests/TFHERevertTest.sol | 23 +++++++++++++ test/tfheOperations/tfheRevertPaths.ts | 45 ++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 examples/tests/TFHERevertTest.sol create mode 100644 test/tfheOperations/tfheRevertPaths.ts diff --git a/examples/tests/TFHERevertTest.sol b/examples/tests/TFHERevertTest.sol new file mode 100644 index 00000000..a910aba5 --- /dev/null +++ b/examples/tests/TFHERevertTest.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +pragma solidity ^0.8.24; + +import "../../lib/TFHE.sol"; +import "../FHEVMConfig.sol"; + +contract TFHERevertTest { + constructor() { + TFHE.setFHEVM(FHEVMConfig.defaultConfig()); + } + + function padToBytes64(bytes memory input) public pure { + TFHE.padToBytes64(input); + } + + function padToBytes128(bytes memory input) public pure { + TFHE.padToBytes128(input); + } + + function padToBytes256(bytes memory input) public pure { + TFHE.padToBytes256(input); + } +} diff --git a/test/tfheOperations/tfheRevertPaths.ts b/test/tfheOperations/tfheRevertPaths.ts new file mode 100644 index 00000000..bc0c5383 --- /dev/null +++ b/test/tfheOperations/tfheRevertPaths.ts @@ -0,0 +1,45 @@ +import { expect } from 'chai'; +import { randomBytes } from 'crypto'; +import { ethers } from 'hardhat'; + +import { getSigners, initSigners } from '../signers'; + +describe('TFHE revert paths', function () { + before(async function () { + await initSigners(1); + this.signers = await getSigners(); + + const contractFactory = await ethers.getContractFactory('TFHERevertTest'); + const contract = await contractFactory.connect(this.signers.alice).deploy(); + await contract.waitForDeployment(); + + this.contract = contract; + }); + + it('padToBytes64 reverts if input > 64 bytes', async function () { + const numberBytes = 65; + const input = randomBytes(numberBytes); + + await expect(this.contract.padToBytes64(input)) + .to.be.revertedWithCustomError(this.contract, 'InputLengthAbove64Bytes') + .withArgs(numberBytes); + }); + + it('padToBytes128 reverts if input > 128 bytes', async function () { + const numberBytes = 129; + const input = randomBytes(numberBytes); + + await expect(this.contract.padToBytes128(input)) + .to.be.revertedWithCustomError(this.contract, 'InputLengthAbove128Bytes') + .withArgs(numberBytes); + }); + + it('padToBytes256 reverts if input > 256 bytes', async function () { + const numberBytes = 257; + const input = randomBytes(numberBytes); + + await expect(this.contract.padToBytes256(input)) + .to.be.revertedWithCustomError(this.contract, 'InputLengthAbove256Bytes') + .withArgs(numberBytes); + }); +}); From 607ea2eeae06a86c9b3bf41034dbee534c44183d Mon Sep 17 00:00:00 2001 From: jatZama Date: Fri, 20 Dec 2024 10:31:57 +0100 Subject: [PATCH 5/5] docs: mocked frontend chore: typo --- docs/guides/frontend/webapp.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/guides/frontend/webapp.md b/docs/guides/frontend/webapp.md index b8db7536..93728117 100644 --- a/docs/guides/frontend/webapp.md +++ b/docs/guides/frontend/webapp.md @@ -18,6 +18,10 @@ You can also use [this template](https://github.com/zama-ai/fhevmjs-vue-template You can also use [this template](https://github.com/zama-ai/fhevmjs-next-template) to start an application with fhevmjs, using Next + TypeScript. +## Using the mocked coprocessor for front end + +As an alternative to use the real coprocessor deployed on Sepolia to help you develop your dApp faster and without needing testnet tokens, you can use a mocked fhevm. Currently, we recommend you to use the `ConfidentialERC20` dApp example available on the `mockedFrontend` branch of the [React template](https://github.com/zama-ai/fhevm-react-template/tree/mockedFrontend). Follow the README on this branch, and you will be able to deploy exactly the same dApp both on Sepolia as well as on the mocked coprocessor seamlessly. + ## Using directly the library ### Step 1: Setup the library