Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions host-contracts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ npm install
```

To run forge tests:

```
npm run forge:soldeer
npm run test:forge
Expand Down
6 changes: 3 additions & 3 deletions host-contracts/lib/FHE.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
pragma solidity ^0.8.24;

import "./Impl.sol";
import "./cryptography/ECDSA.sol";
import {FhevmECDSA} from "./cryptography/FhevmECDSA.sol";
import {FheType} from "../contracts/shared/FheType.sol";

import "encrypted-types/EncryptedTypes.sol";
Expand Down Expand Up @@ -9646,7 +9646,7 @@ library FHE {
address[] memory recoveredSigners = new address[](numSignatures);
uint256 uniqueValidCount;
for (uint256 i = 0; i < numSignatures; i++) {
address signerRecovered = ECDSA.recover(digest, signatures[i]);
address signerRecovered = FhevmECDSA.recover(digest, signatures[i]);
if (!_isSigner(signerRecovered, KMSSigners)) {
revert KMSInvalidSigner(signerRecovered);
}
Expand Down Expand Up @@ -9685,7 +9685,7 @@ library FHE {
* @return signer The address that supposedly signed the message.
*/
function _recoverSigner(bytes32 message, bytes memory signature) private pure returns (address) {
address signerRecovered = ECDSA.recover(message, signature);
address signerRecovered = FhevmECDSA.recover(message, signature);
return signerRecovered;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@ pragma solidity ^0.8.20;
*
* These functions can be used to verify that a message was signed by the holder
* of the private keys of a given address.
*
* @dev This library is forked from OpenZeppelin's ECDSA and renamed to FhevmECDSA
* to avoid naming conflicts with the original when both are used in the same project.
*/
library ECDSA {
library FhevmECDSA {
enum RecoverError {
NoError,
InvalidSignature,
Expand Down
4 changes: 3 additions & 1 deletion host-contracts/test/kmsVerifier/kmsVerifier.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -565,7 +565,9 @@ contract KMSVerifierTest is Test {
/**
* @dev Tests that the verifyDecryptionEIP712KMSSignatures function fails if the length of the decryption proof is invalid.
*/
function test_VerifyDecryptionEIP712KMSSignaturesFailsIfDeserializingDecryptionProofFail(uint256 randomValue) public {
function test_VerifyDecryptionEIP712KMSSignaturesFailsIfDeserializingDecryptionProofFail(
uint256 randomValue
) public {
_upgradeProxyWithSigners(3);
bytes32[] memory handlesList = _generateMockHandlesList(3);

Expand Down
2 changes: 2 additions & 0 deletions library-solidity/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@ _See full details in the [Key concepts](https://docs.zama.ai/fhevm/smart-contrac
To start writing confidential smart contracts using FHEVM Solidity, follow the Hardhat setup guide here: [Getting Started with Hardhat](https://docs.zama.ai/fhevm/getting-started/overview-1/hardhat).

run

```
npm install
```

To run forge tests:

```
npm run forge:soldeer
npm run test:forge
Expand Down
16 changes: 14 additions & 2 deletions library-solidity/codegen/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { generateSolidityHCULimit } from './hcuLimitGenerator';
import { ALL_OPERATORS } from './operators';
import { ALL_OPERATORS_PRICES } from './operatorsPrices';
import { fromDirToFile, fromFileToFile, isDirectory } from './paths';
import { generateSolidityFHELib } from './templateFHEDotSol';
import { generateFhevmECDSALib, generateSolidityFHELib } from './templateFHEDotSol';
import { generateSolidityFheType } from './templateFheTypeDotSol';
import { generateSolidityImplLib } from './templateImpDotSol';
import {
Expand Down Expand Up @@ -147,6 +147,7 @@ export async function commandGenerateAllFiles(options: any) {

const fheTypesDotSol = `${path.join(absConfig.lib.fheTypeDir, 'FheType.sol')}`;
const implDotSol = `${path.join(absConfig.lib.outDir, 'Impl.sol')}`;
const ecdsaDotSol = `${path.join(absConfig.lib.outDir, 'cryptography', 'FhevmECDSA.sol')}`;
const fheDotSol = `${path.join(absConfig.lib.outDir, 'FHE.sol')}`;
const hcuLimitDotSol = `${path.join(absConfig.hostContracts.outDir, 'HCULimit.sol')}`;

Expand All @@ -162,6 +163,7 @@ export async function commandGenerateAllFiles(options: any) {
const implRelFheTypesDotSol = fromFileToFile(implDotSol, fheTypesDotSol);
const fheRelFheTypesDotSol = fromFileToFile(fheDotSol, fheTypesDotSol);
const fheRelImplDotSol = fromFileToFile(fheDotSol, implDotSol);
const fheRelEcdsaDotSol = fromFileToFile(fheDotSol, ecdsaDotSol);

debugLog(`============ Config ============`);
debugLog(`basePath: ${absConfig.baseDir}`);
Expand All @@ -170,6 +172,7 @@ export async function commandGenerateAllFiles(options: any) {
debugLog(`noTest: ${absConfig.noTest}`);
debugLog(`============= Lib =============`);
debugLog(`libDir: ${absConfig.lib.outDir}`);
debugLog(`FhevmECDSA.sol: ${ecdsaDotSol}`);
debugLog(`Impl.sol: ${implDotSol}`);
debugLog(`FHE.sol: ${fheDotSol}`);
debugLog(`fheTypeDir: ${absConfig.lib.fheTypeDir}`);
Expand Down Expand Up @@ -197,15 +200,24 @@ export async function commandGenerateAllFiles(options: any) {
if (config.noLib !== true) {
const fheTypesCode = generateSolidityFheType(ALL_FHE_TYPE_INFOS);
const implCode = generateSolidityImplLib(ALL_OPERATORS, implRelFheTypesDotSol);
const fheCode = generateSolidityFHELib(ALL_OPERATORS, ALL_FHE_TYPE_INFOS, fheRelFheTypesDotSol, fheRelImplDotSol);
const fheCode = generateSolidityFHELib({
operators: ALL_OPERATORS,
fheTypes: ALL_FHE_TYPE_INFOS,
fheTypeDotSol: fheRelFheTypesDotSol,
implDotSol: fheRelImplDotSol,
ecdsaDotSol: fheRelEcdsaDotSol,
});
const ecdsaCode = generateFhevmECDSALib();

mkDir(path.dirname(fheTypesDotSol));
mkDir(path.dirname(implDotSol));
mkDir(path.dirname(ecdsaDotSol));
mkDir(path.dirname(fheDotSol));

// Generate core Solidity contract files.
await formatAndWriteFile(`${fheTypesDotSol}`, fheTypesCode);
await formatAndWriteFile(`${implDotSol}`, implCode);
await formatAndWriteFile(`${ecdsaDotSol}`, ecdsaCode);
await formatAndWriteFile(`${fheDotSol}`, fheCode);
} else {
debugLog(`Skipping lib generation.`);
Expand Down
96 changes: 73 additions & 23 deletions library-solidity/codegen/src/templateFHEDotSol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,30 @@ import { OperatorArguments, ReturnType } from './common';
import { resolveTemplatePath } from './paths';
import { getUint, removeTemplateComments } from './utils';

export function generateSolidityFHELib(
operators: Operator[],
fheTypes: FheTypeInfo[],
fheTypeDotSol: string,
implDotSol: string,
): string {
export function generateFhevmECDSALib() {
const file = resolveTemplatePath('FhevmECDSA.sol-template');
const template = readFileSync(file, 'utf8');
let code = removeTemplateComments(template);
return code;
}

export function generateSolidityFHELib({
operators,
fheTypes,
fheTypeDotSol,
implDotSol,
ecdsaDotSol,
}: {
operators: Operator[];
fheTypes: FheTypeInfo[];
fheTypeDotSol: string;
implDotSol: string;
ecdsaDotSol: string;
}): string {
// Placeholders:
// =============
// $${ImplDotSol}$$
// $${EcdsaDotSol}$$
// $${FheTypeDotSol}$$
// $${FHEOperators}$$
// $${ACLFunctions}$$
Expand All @@ -25,6 +40,7 @@ export function generateSolidityFHELib(
let code = removeTemplateComments(template);

code = code.replace('$${ImplDotSol}$$', implDotSol);
code = code.replace('$${EcdsaDotSol}$$', ecdsaDotSol);
code = code.replace('$${FheTypeDotSol}$$', fheTypeDotSol);

// Exclude types that do not support any operators.
Expand Down Expand Up @@ -191,9 +207,13 @@ function handleSolidityTFHEEncryptedOperatorForTwoEncryptedTypes(

res.push(`
/**
* @dev Evaluates ${operator.name}(e${lhsFheType.type.toLowerCase()} a, e${rhsFheType.type.toLowerCase()} b) and returns the result.
* @dev Evaluates ${
operator.name
}(e${lhsFheType.type.toLowerCase()} a, e${rhsFheType.type.toLowerCase()} b) and returns the result.
*/
function ${operator.name}(e${lhsFheType.type.toLowerCase()} a, e${rhsFheType.type.toLowerCase()} b) internal returns (${returnType}) {
function ${
operator.name
}(e${lhsFheType.type.toLowerCase()} a, e${rhsFheType.type.toLowerCase()} b) internal returns (${returnType}) {
if (!isInitialized(a)) {
a = asE${lhsFheType.type.toLowerCase()}(0);
}
Expand Down Expand Up @@ -272,7 +292,9 @@ function generateSolidityTFHEScalarOperator(fheType: AdjustedFheType, operator:
let implExpressionA;

if (fheType.type == 'Bool') {
implExpressionA = `Impl.${operator.name}(e${fheType.type.toLowerCase()}.unwrap(a), bytes32(uint256(b?1:0))${scalarFlag})`;
implExpressionA = `Impl.${
operator.name
}(e${fheType.type.toLowerCase()}.unwrap(a), bytes32(uint256(b?1:0))${scalarFlag})`;
} else if (fheType.type.startsWith('Int')) {
throw new Error('Int types are not supported!');
} else {
Expand Down Expand Up @@ -318,9 +340,13 @@ function generateSolidityTFHEScalarOperator(fheType: AdjustedFheType, operator:
res.push(`

/**
* @dev Evaluates ${operator.name}(e${fheType.type.toLowerCase()} a, ${clearMatchingType.toLowerCase()} b) and returns the result.
* @dev Evaluates ${
operator.name
}(e${fheType.type.toLowerCase()} a, ${clearMatchingType.toLowerCase()} b) and returns the result.
*/
function ${operator.name}(e${fheType.type.toLowerCase()} a, ${clearMatchingType.toLowerCase()} b) internal returns (${returnType}) {
function ${
operator.name
}(e${fheType.type.toLowerCase()} a, ${clearMatchingType.toLowerCase()} b) internal returns (${returnType}) {
if (!isInitialized(a)) {
a = asE${fheType.type.toLowerCase()}(${
fheType.type == 'Bool' ? 'false' : fheType.type == 'Address' ? `${clearMatchingType.toLowerCase()}(0)` : 0
Expand All @@ -335,9 +361,13 @@ function generateSolidityTFHEScalarOperator(fheType: AdjustedFheType, operator:
res.push(`

/**
* @dev Evaluates ${operator.name}(${clearMatchingType.toLowerCase()} a, e${fheType.type.toLowerCase()} b) and returns the result.
* @dev Evaluates ${
operator.name
}(${clearMatchingType.toLowerCase()} a, e${fheType.type.toLowerCase()} b) and returns the result.
*/
function ${operator.name}(${clearMatchingType.toLowerCase()} a, e${fheType.type.toLowerCase()} b) internal returns (${returnType}) {
function ${
operator.name
}(${clearMatchingType.toLowerCase()} a, e${fheType.type.toLowerCase()} b) internal returns (${returnType}) {
${maybeEncryptLeft}
if (!isInitialized(b)) {
b = asE${fheType.type.toLowerCase()}(${
Expand Down Expand Up @@ -379,7 +409,9 @@ function handleSolidityTFHEShiftOperator(fheType: AdjustedFheType, operator: Ope

const leftExpr = 'a';
const rightExpr = castRightToLeft ? `asE${fheType.type.toLowerCase()}(b)` : 'b';
let implExpression: string = `Impl.${operator.name}(e${fheType.type.toLowerCase()}.unwrap(${leftExpr}), e${fheType.type.toLowerCase()}.unwrap(${rightExpr})${scalarFlag})`;
let implExpression: string = `Impl.${
operator.name
}(e${fheType.type.toLowerCase()}.unwrap(${leftExpr}), e${fheType.type.toLowerCase()}.unwrap(${rightExpr})${scalarFlag})`;

res.push(`
/**
Expand All @@ -398,13 +430,17 @@ function handleSolidityTFHEShiftOperator(fheType: AdjustedFheType, operator: Ope

// Code and test for shift(euint{inputBits},uint8}
scalarFlag = ', true';
implExpression = `Impl.${operator.name}(e${fheType.type.toLowerCase()}.unwrap(a), bytes32(uint256(b))${scalarFlag})`;
implExpression = `Impl.${
operator.name
}(e${fheType.type.toLowerCase()}.unwrap(a), bytes32(uint256(b))${scalarFlag})`;

res.push(`
/**
* @dev Evaluates ${operator.name}(e${fheType.type.toLowerCase()} a, ${getUint(rhsBits)}) and returns the result.
*/
function ${operator.name}(e${fheType.type.toLowerCase()} a, ${getUint(rhsBits)} b) internal returns (e${fheType.type.toLowerCase()}) {
function ${operator.name}(e${fheType.type.toLowerCase()} a, ${getUint(
rhsBits,
)} b) internal returns (e${fheType.type.toLowerCase()}) {
if (!isInitialized(a)) {
a = asE${fheType.type.toLowerCase()}(0);
}
Expand Down Expand Up @@ -466,7 +502,9 @@ function handleSolidityTFHECustomCastBetweenTwoEuint(
*/
function asE${outputFheType.type.toLowerCase()}(e${inputFheType.type.toLowerCase()} value) internal returns (e${outputFheType.type.toLowerCase()}) {
${checkInitialized('value', inputFheType.type)}
return e${outputFheType.type.toLowerCase()}.wrap(Impl.cast(e${inputFheType.type.toLowerCase()}.unwrap(value), FheType.${outputFheType.type}));
return e${outputFheType.type.toLowerCase()}.wrap(Impl.cast(e${inputFheType.type.toLowerCase()}.unwrap(value), FheType.${
outputFheType.type
}));
}
`;
}
Expand Down Expand Up @@ -543,7 +581,9 @@ function handleSolidityTFHEConvertPlaintextAndEinputToRespectiveType(fheType: Ad
*/
function fromExternal(externalE${fheType.type.toLowerCase()} inputHandle, bytes memory inputProof) internal returns (e${fheType.type.toLowerCase()}) {
if (inputProof.length!=0) {
return e${fheType.type.toLowerCase()}.wrap(Impl.verify(externalE${fheType.type.toLowerCase()}.unwrap(inputHandle), inputProof, FheType.${fheType.isAlias ? fheType.aliasType : fheType.type}));
return e${fheType.type.toLowerCase()}.wrap(Impl.verify(externalE${fheType.type.toLowerCase()}.unwrap(inputHandle), inputProof, FheType.${
fheType.isAlias ? fheType.aliasType : fheType.type
}));
} else {
bytes32 inputBytes32 = externalE${fheType.type.toLowerCase()}.unwrap(inputHandle);
if(inputBytes32 == 0){
Expand Down Expand Up @@ -583,8 +623,12 @@ function handleSolidityTFHEConvertPlaintextAndEinputToRespectiveType(fheType: Ad
/**
* @dev Convert a plaintext value to an encrypted e${fheType.type.toLowerCase()} value.
*/
function asE${fheType.type.toLowerCase()}(${fheType.clearMatchingType} value) internal returns (e${fheType.type.toLowerCase()}) {
return e${fheType.type.toLowerCase()}.wrap(Impl.trivialEncrypt(uint256(${value}), FheType.${fheType.isAlias ? fheType.aliasType : fheType.type}));
function asE${fheType.type.toLowerCase()}(${
fheType.clearMatchingType
} value) internal returns (e${fheType.type.toLowerCase()}) {
return e${fheType.type.toLowerCase()}.wrap(Impl.trivialEncrypt(uint256(${value}), FheType.${
fheType.isAlias ? fheType.aliasType : fheType.type
}));
}

`;
Expand Down Expand Up @@ -690,7 +734,9 @@ function handleSolidityTFHERand(fheType: AdjustedFheType): string {
* @dev Generates a random encrypted value.
*/
function randE${fheType.type.toLowerCase()}() internal returns (e${fheType.type.toLowerCase()}) {
return e${fheType.type.toLowerCase()}.wrap(Impl.rand(FheType.${fheType.isAlias ? fheType.aliasType : fheType.type}));
return e${fheType.type.toLowerCase()}.wrap(Impl.rand(FheType.${
fheType.isAlias ? fheType.aliasType : fheType.type
}));
}

`;
Expand All @@ -702,8 +748,12 @@ function handleSolidityTFHERand(fheType: AdjustedFheType): string {
* @dev Generates a random encrypted ${fheType.bitLength}-bit unsigned integer in the [0, upperBound) range.
* The upperBound must be a power of 2.
*/
function randE${fheType.type.toLowerCase()}(uint${fheType.bitLength} upperBound) internal returns (e${fheType.type.toLowerCase()}) {
return e${fheType.type.toLowerCase()}.wrap(Impl.randBounded(upperBound, FheType.${fheType.isAlias ? fheType.aliasType : fheType.type}));
function randE${fheType.type.toLowerCase()}(uint${
fheType.bitLength
} upperBound) internal returns (e${fheType.type.toLowerCase()}) {
return e${fheType.type.toLowerCase()}.wrap(Impl.randBounded(upperBound, FheType.${
fheType.isAlias ? fheType.aliasType : fheType.type
}));
}

`;
Expand Down
6 changes: 3 additions & 3 deletions library-solidity/codegen/src/templates/FHE.sol-template
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ pragma solidity ^0.8.24;
//$$ -
//$$ -----------------------------------------------------------------------
import "$${ImplDotSol}$$";
import "./cryptography/ECDSA.sol";
import {FhevmECDSA} from "$${EcdsaDotSol}$$";
import {FheType} from "$${FheTypeDotSol}$$";
//$$ -----------------------------------------------------------------------

Expand Down Expand Up @@ -439,7 +439,7 @@ library FHE {
address[] memory recoveredSigners = new address[](numSignatures);
uint256 uniqueValidCount;
for (uint256 i = 0; i < numSignatures; i++) {
address signerRecovered = ECDSA.recover(digest, signatures[i]);
address signerRecovered = FhevmECDSA.recover(digest, signatures[i]);
if (!_isSigner(signerRecovered, KMSSigners)) {
revert KMSInvalidSigner(signerRecovered);
}
Expand Down Expand Up @@ -478,7 +478,7 @@ library FHE {
* @return signer The address that supposedly signed the message.
*/
function _recoverSigner(bytes32 message, bytes memory signature) private pure returns (address) {
address signerRecovered = ECDSA.recover(message, signature);
address signerRecovered = FhevmECDSA.recover(message, signature);
return signerRecovered;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@ pragma solidity ^0.8.20;
*
* These functions can be used to verify that a message was signed by the holder
* of the private keys of a given address.
*
* @dev This library is forked from OpenZeppelin's ECDSA and renamed to FhevmECDSA
* to avoid naming conflicts with the original when both are used in the same project.
*/
library ECDSA {
library FhevmECDSA {
enum RecoverError {
NoError,
InvalidSignature,
Expand Down
6 changes: 3 additions & 3 deletions library-solidity/lib/FHE.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
pragma solidity ^0.8.24;

import "./Impl.sol";
import "./cryptography/ECDSA.sol";
import {FhevmECDSA} from "./cryptography/FhevmECDSA.sol";
import {FheType} from "./FheType.sol";

import "encrypted-types/EncryptedTypes.sol";
Expand Down Expand Up @@ -9646,7 +9646,7 @@ library FHE {
address[] memory recoveredSigners = new address[](numSignatures);
uint256 uniqueValidCount;
for (uint256 i = 0; i < numSignatures; i++) {
address signerRecovered = ECDSA.recover(digest, signatures[i]);
address signerRecovered = FhevmECDSA.recover(digest, signatures[i]);
if (!_isSigner(signerRecovered, KMSSigners)) {
revert KMSInvalidSigner(signerRecovered);
}
Expand Down Expand Up @@ -9685,7 +9685,7 @@ library FHE {
* @return signer The address that supposedly signed the message.
*/
function _recoverSigner(bytes32 message, bytes memory signature) private pure returns (address) {
address signerRecovered = ECDSA.recover(message, signature);
address signerRecovered = FhevmECDSA.recover(message, signature);
return signerRecovered;
}

Expand Down
Loading
Loading