diff --git a/codegen/common.ts b/codegen/common.ts index 53efc3fc..0a52f11e 100644 --- a/codegen/common.ts +++ b/codegen/common.ts @@ -13,6 +13,7 @@ export type Operator = { leftScalarEncrypt?: boolean; // disable left scalar operator leftScalarDisable?: boolean; + fheLibName?: string; }; export type Precompile = { @@ -104,6 +105,7 @@ export const ALL_OPERATORS: Operator[] = [ hasEncrypted: true, arguments: OperatorArguments.Binary, returnType: ReturnType.Uint, + fheLibName: 'fheBitAnd', }, { name: 'or', @@ -112,6 +114,7 @@ export const ALL_OPERATORS: Operator[] = [ hasEncrypted: true, arguments: OperatorArguments.Binary, returnType: ReturnType.Uint, + fheLibName: 'fheBitOr', }, { name: 'xor', @@ -120,6 +123,7 @@ export const ALL_OPERATORS: Operator[] = [ hasEncrypted: true, arguments: OperatorArguments.Binary, returnType: ReturnType.Uint, + fheLibName: 'fheBitXor', }, { name: 'shl', diff --git a/codegen/overloadTests.ts b/codegen/overloadTests.ts index 44278c29..9df1f281 100644 --- a/codegen/overloadTests.ts +++ b/codegen/overloadTests.ts @@ -6,6 +6,21 @@ type OverloadTest = { }; export const overloadTests: { [methodName: string]: OverloadTest[] } = { + neg_euint8: [ + { inputs: [0x01], output: 0xff }, + { inputs: [0x02], output: 0xfe }, + ], + not_euint8: [{ inputs: [0x03], output: 0xfc }], + neg_euint16: [ + { inputs: [0x0001], output: 0xffff }, + { inputs: [0x0002], output: 0xfffe }, + ], + not_euint16: [{ inputs: [0x00f1], output: 0xff0e }], + neg_euint32: [ + { inputs: [0x00000001], output: 0xffffffff }, + { inputs: [0x00000002], output: 0xfffffffe }, + ], + not_euint32: [{ inputs: [0x0000fffe], output: 0xffff0001 }], add_euint8_euint8: [{ inputs: [3, 4], output: 7 }], sub_euint8_euint8: [{ inputs: [4, 3], output: 1 }], mul_euint8_euint8: [{ inputs: [3, 4], output: 12 }], diff --git a/codegen/templates.ts b/codegen/templates.ts index 35529ff8..65b5433a 100644 --- a/codegen/templates.ts +++ b/codegen/templates.ts @@ -25,6 +25,7 @@ library Common { } function binaryOperatorImpl(op: Operator, isScalar: boolean, isEncrypted: boolean): string { + const fname = operatorFheLibFunction(op); const scalarArg = isScalar && isEncrypted ? ', bool scalar' : ''; const scalarByte = isScalar ? '0x01' : '0x00'; const scalarSection = @@ -34,27 +35,13 @@ function binaryOperatorImpl(op: Operator, isScalar: boolean, isEncrypted: boolea scalarByte = 0x01; } else { scalarByte = 0x00; - } - bytes memory input = bytes.concat(bytes32(lhs), bytes32(rhs), scalarByte);` - : `bytes memory input = bytes.concat(bytes32(lhs), bytes32(rhs), bytes1(${scalarByte}));`; + }` + : `bytes1 scalarByte = ${scalarByte};`; return ( ` - function ${op.name}(uint256 lhs, uint256 rhs${scalarArg}) internal view returns (uint256 result) { + function ${op.name}(uint256 lhs, uint256 rhs${scalarArg}) internal pure returns (uint256 result) { ${scalarSection} - uint256 inputLen = input.length; - - bytes32[1] memory output; - uint256 outputLen = 32; - // Call the ${op.name} precompile. - uint256 precompile = Precompiles.${op.precompileName}; - assembly { - // jump over the 32-bit 'size' field of the 'bytes' data structure of the 'input' to read actual bytes - if iszero(staticcall(gas(), precompile, add(input, 32), inputLen, output, outputLen)) { - revert(0, 0) - } - } - - result = uint256(output[0]); + result = FhevmLib(address(EXT_TFHE_LIBRARY)).${fname}(lhs, rhs, scalarByte); }` + '\n' ); } @@ -62,6 +49,8 @@ function binaryOperatorImpl(op: Operator, isScalar: boolean, isEncrypted: boolea export function implSol(operators: Operator[]): string { const res: string[] = []; + const fheLibInterface = generateImplFhevmLibInterface(operators); + res.push(` // SPDX-License-Identifier: BSD-3-Clause-Clear @@ -70,6 +59,10 @@ pragma solidity >=0.8.13 <0.8.20; import "./Common.sol"; import "./Precompiles.sol"; +${fheLibInterface} + +address constant EXT_TFHE_LIBRARY = address(93); + library Impl { // 32 bytes for the 'byte' type header + 48 bytes for the NaCl anonymous // box overhead + 4 bytes for the plaintext value. @@ -98,6 +91,55 @@ library Impl { return res.join(''); } +function operatorFheLibFunction(op: Operator): string { + if (op.fheLibName) { + return op.fheLibName; + } + const firstLetter = op.name.toUpperCase().charAt(0); + const theRest = op.name.substring(1); + return `fhe${firstLetter}${theRest}`; +} + +function generateImplFhevmLibInterface(operators: Operator[]): string { + const res: string[] = []; + + res.push('interface FhevmLib {'); + operators.forEach((op) => { + let functionName = operatorFheLibFunction(op); + const tail = 'external pure returns (uint256 result);'; + let functionArguments: string; + switch (op.arguments) { + case OperatorArguments.Binary: + functionArguments = '(uint256 lhs, uint256 rhs, bytes1 scalarByte)'; + res.push(` function ${functionName}${functionArguments} ${tail}`); + break; + case OperatorArguments.Unary: + functionArguments = '(uint256 ct)'; + res.push(` function ${functionName}${functionArguments} ${tail}`); + break; + } + }); + + res.push(fheLibCustomInterfaceFunctions()); + + res.push('}'); + + return res.join('\n'); +} + +function fheLibCustomInterfaceFunctions(): string { + return ` + function optimisticRequire(uint256 ct) external view; + function reencrypt(uint256 ct, uint256 publicKey) external view returns (bytes memory); + function fhePubKey(bytes1 fromLib) external view returns (bytes memory result); + function verifyCiphertext(bytes memory input) external view returns (uint256 result); + function cast(uint256 ct, bytes1 toType) external view returns (uint256 result); + function trivialEncrypt(uint256 ct, bytes1 toType) external view returns (uint256 result); + function decrypt(uint256 ct) external view returns (uint256 result); + function fheRand(bytes1 inp) external view returns (uint256 result); + `; +} + export function tfheSol(operators: Operator[], supportedBits: number[]): [string, OverloadSignature[]] { const signatures: OverloadSignature[] = []; const res: string[] = []; @@ -140,7 +182,8 @@ library TFHE { }); res.push(tfheAsEboolUnaryCast(outputBits)); }); - supportedBits.forEach((bits) => res.push(tfheCustomUnaryOperators(bits))); + supportedBits.forEach((bits) => res.push(tfheUnaryOperators(bits, operators, signatures))); + supportedBits.forEach((bits) => res.push(tfheCustomUnaryOperators(bits, signatures))); res.push(tfheCustomMethods()); @@ -177,7 +220,7 @@ function tfheEncryptedOperator( const leftExpr = castLeftToRight ? `asEuint${outputBits}(a)` : 'a'; const rightExpr = castRightToLeft ? `asEuint${outputBits}(b)` : 'b'; - var implExpression = `Impl.${operator.name}(euint${outputBits}.unwrap(${leftExpr}), euint${outputBits}.unwrap(${rightExpr})${scalarFlag})`; + let implExpression = `Impl.${operator.name}(euint${outputBits}.unwrap(${leftExpr}), euint${outputBits}.unwrap(${rightExpr})${scalarFlag})`; if (boolCastNeeded) { implExpression = `Impl.cast(${implExpression}, Common.ebool_t)`; } @@ -299,7 +342,7 @@ function tfheCmux(inputBits: number): string { return ` // If 'control''s value is 'true', the result has the same value as 'a'. // If 'control''s value is 'false', the result has the same value as 'b'. - function cmux(ebool control, euint${inputBits} a, euint${inputBits} b) internal view returns (euint${inputBits}) { + function cmux(ebool control, euint${inputBits} a, euint${inputBits} b) internal pure returns (euint${inputBits}) { return euint${inputBits}.wrap(Impl.cmux(ebool.unwrap(control), euint${inputBits}.unwrap(a), euint${inputBits}.unwrap(b))); }`; } @@ -355,7 +398,29 @@ function tfheAsEboolUnaryCast(bits: number): string { return res.join(''); } -function tfheCustomUnaryOperators(bits: number): string { +function tfheUnaryOperators(bits: number, operators: Operator[], signatures: OverloadSignature[]): string { + const res: string[] = []; + + operators.forEach((op) => { + if (op.arguments == OperatorArguments.Unary) { + signatures.push({ + name: op.name, + arguments: [{ type: ArgumentType.EUint, bits }], + returnType: { type: ArgumentType.EUint, bits }, + }); + + res.push(` + function ${op.name}(euint${bits} value) internal pure returns (euint${bits}) { + return euint${bits}.wrap(Impl.${op.name}(euint${bits}.unwrap(value))); + } + `); + } + }); + + return res.join('\n'); +} + +function tfheCustomUnaryOperators(bits: number, signatures: OverloadSignature[]): string { return ` // Convert a serialized 'ciphertext' to an encrypted euint${bits} integer. function asEuint${bits}(bytes memory ciphertext) internal view returns (euint${bits}) { @@ -388,16 +453,6 @@ function tfheCustomUnaryOperators(bits: number): string { function decrypt(euint${bits} value) internal view returns (uint${bits}) { return uint${bits}(Impl.decrypt(euint${bits}.unwrap(value))); } - - // Return the negation of 'value'. - function neg(euint${bits} value) internal view returns (euint${bits}) { - return euint${bits}.wrap(Impl.neg(euint${bits}.unwrap(value))); - } - - // Return '!value'. - function not(euint${bits} value) internal view returns (euint${bits}) { - return euint${bits}.wrap(Impl.not(euint${bits}.unwrap(value))); - } `; } @@ -422,26 +477,12 @@ library Precompiles { } function unaryOperatorImpl(op: Operator): string { + let fname = operatorFheLibFunction(op); return ` - function ${op.name}(uint256 ct) internal view returns (uint256 result) { - bytes32[1] memory input; - input[0] = bytes32(ct); - uint256 inputLen = 32; - - bytes32[1] memory output; - uint256 outputLen = 32; - - // Call the ${op.name} precompile. - uint256 precompile = Precompiles.${op.precompileName}; - assembly { - if iszero(staticcall(gas(), precompile, input, inputLen, output, outputLen)) { - revert(0, 0) - } - } - - result = uint256(output[0]); + function ${op.name}(uint256 ct) internal pure returns (uint256 result) { + result = FhevmLib(address(EXT_TFHE_LIBRARY)).${fname}(ct); } - `; + `; } function tfheCustomMethods(): string { @@ -526,102 +567,24 @@ function implCustomMethods(): string { return ` // If 'control's value is 'true', the result has the same value as 'ifTrue'. // If 'control's value is 'false', the result has the same value as 'ifFalse'. - function cmux(uint256 control, uint256 ifTrue, uint256 ifFalse) internal view returns (uint256 result) { + function cmux(uint256 control, uint256 ifTrue, uint256 ifFalse) internal pure returns (uint256 result) { // result = (ifTrue - ifFalse) * control + ifFalse - bytes memory input = bytes.concat(bytes32(ifTrue), bytes32(ifFalse), bytes1(0x00)); - uint256 inputLen = input.length; - - bytes32[1] memory subOutput; - uint256 outputLen = 32; - - // Call the sub precompile. - uint256 precompile = Precompiles.Subtract; - assembly { - if iszero(staticcall(gas(), precompile, add(input, 32), inputLen, subOutput, outputLen)) { - revert(0, 0) - } - } - - // Call the mul precompile. - input = bytes.concat(bytes32(control), bytes32(subOutput[0]), bytes1(0x00)); - inputLen = input.length; - precompile = Precompiles.Multiply; - bytes32[1] memory mulOutput; - assembly { - if iszero(staticcall(gas(), precompile, add(input, 32), inputLen, mulOutput, outputLen)) { - revert(0, 0) - } - } - - // Call the add precompile. - input = bytes.concat(bytes32(mulOutput[0]), bytes32(ifFalse), bytes1(0x00)); - inputLen = input.length; - precompile = Precompiles.Add; - bytes32[1] memory addOutput; - assembly { - if iszero(staticcall(gas(), precompile, add(input, 32), inputLen, addOutput, outputLen)) { - revert(0, 0) - } - } - - result = uint256(addOutput[0]); + uint256 subOutput = FhevmLib(address(EXT_TFHE_LIBRARY)).fheSub(ifTrue, ifFalse, bytes1(0x00)); + uint256 mulOutput = FhevmLib(address(EXT_TFHE_LIBRARY)).fheMul(control, subOutput, bytes1(0x00)); + result = FhevmLib(address(EXT_TFHE_LIBRARY)).fheAdd(mulOutput, ifFalse, bytes1(0x00)); } function optReq(uint256 ciphertext) internal view { - bytes32[1] memory input; - input[0] = bytes32(ciphertext); - uint256 inputLen = 32; - - // Call the optimistic require precompile. - uint256 precompile = Precompiles.OptimisticRequire; - assembly { - if iszero(staticcall(gas(), precompile, input, inputLen, 0, 0)) { - revert(0, 0) - } - } + FhevmLib(address(EXT_TFHE_LIBRARY)).optimisticRequire(ciphertext); } function reencrypt(uint256 ciphertext, bytes32 publicKey) internal view returns (bytes memory reencrypted) { - bytes32[2] memory input; - input[0] = bytes32(ciphertext); - input[1] = publicKey; - uint256 inputLen = 64; - - reencrypted = new bytes(reencryptedSize); - - // Call the reencrypt precompile. - uint256 precompile = Precompiles.Reencrypt; - assembly { - if iszero(staticcall(gas(), precompile, input, inputLen, reencrypted, reencryptedSize)) { - revert(0, 0) - } - } + return FhevmLib(address(EXT_TFHE_LIBRARY)).reencrypt(ciphertext, uint256(publicKey)); } function fhePubKey() internal view returns (bytes memory key) { // Set a byte value of 1 to signal the call comes from the library. - bytes1[1] memory input; - input[0] = 0x01; - uint256 inputLen = 1; - - key = new bytes(fhePubKeySize); - - // Call the fhePubKey precompile. - uint256 precompile = Precompiles.FhePubKey; - assembly { - if iszero( - staticcall( - gas(), - precompile, - input, - inputLen, - key, - fhePubKeySize - ) - ) { - revert(0, 0) - } - } + key = FhevmLib(address(EXT_TFHE_LIBRARY)).fhePubKey(bytes1(0x01)); } function verify( @@ -629,117 +592,29 @@ function implCustomMethods(): string { uint8 _toType ) internal view returns (uint256 result) { bytes memory input = bytes.concat(_ciphertextBytes, bytes1(_toType)); - uint256 inputLen = input.length; - - bytes32[1] memory output; - uint256 outputLen = 32; - - // Call the verify precompile. - uint256 precompile = Precompiles.Verify; - assembly { - // jump over the 32-bit 'size' field of the 'bytes' data structure of the 'input' to read actual bytes - if iszero(staticcall(gas(), precompile, add(input, 32), inputLen, output, outputLen)) { - revert(0, 0) - } - } - result = uint256(output[0]); + result = FhevmLib(address(EXT_TFHE_LIBRARY)).verifyCiphertext(input); } function cast( uint256 ciphertext, uint8 toType ) internal view returns (uint256 result) { - bytes memory input = bytes.concat(bytes32(ciphertext), bytes1(toType)); - uint256 inputLen = input.length; - - bytes32[1] memory output; - uint256 outputLen = 32; - - // Call the cast precompile. - uint256 precompile = Precompiles.Cast; - assembly { - // jump over the 32-bit 'size' field of the 'bytes' data structure of the 'input' to read actual bytes - if iszero( - staticcall( - gas(), - precompile, - add(input, 32), - inputLen, - output, - outputLen - ) - ) { - revert(0, 0) - } - } - result = uint256(output[0]); + result = FhevmLib(address(EXT_TFHE_LIBRARY)).cast(ciphertext, bytes1(toType)); } function trivialEncrypt( uint256 value, uint8 toType ) internal view returns (uint256 result) { - bytes memory input = bytes.concat(bytes32(value), bytes1(toType)); - uint256 inputLen = input.length; - - bytes32[1] memory output; - uint256 outputLen = 32; - - // Call the trivialEncrypt precompile. - uint256 precompile = Precompiles.TrivialEncrypt; - assembly { - // jump over the 32-bit 'size' field of the 'bytes' data structure of the 'input' to read actual bytes - if iszero( - staticcall( - gas(), - precompile, - add(input, 32), - inputLen, - output, - outputLen - ) - ) { - revert(0, 0) - } - } - result = uint256(output[0]); + result = FhevmLib(address(EXT_TFHE_LIBRARY)).trivialEncrypt(value, bytes1(toType)); } function decrypt(uint256 ciphertext) internal view returns(uint256 result) { - bytes32[1] memory input; - input[0] = bytes32(ciphertext); - uint256 inputLen = 32; - - bytes32[1] memory output; - uint256 outputLen = 32; - - // Call the decrypt precompile. - uint256 precompile = Precompiles.Decrypt; - assembly { - if iszero(staticcall(gas(), precompile, input, inputLen, output, outputLen)) { - revert(0, 0) - } - } - // The output is a 32-byte buffer of a 256-bit big-endian unsigned integer. - result = uint256(output[0]); + result = FhevmLib(address(EXT_TFHE_LIBRARY)).decrypt(ciphertext); } function rand(uint8 randType) internal view returns(uint256 result) { - bytes1[1] memory input; - input[0] = bytes1(randType); - uint256 inputLen = 1; - - bytes32[1] memory output; - uint256 outputLen = 32; - - // Call the rand precompile. - uint256 precompile = Precompiles.Rand; - assembly { - if iszero(staticcall(gas(), precompile, input, inputLen, output, outputLen)) { - revert(0, 0) - } - } - result = uint256(output[0]); + result = FhevmLib(address(EXT_TFHE_LIBRARY)).fheRand(bytes1(randType)); } `; } diff --git a/examples/tests/TFHEManualTestSuite.sol b/examples/tests/TFHEManualTestSuite.sol new file mode 100644 index 00000000..f1c9ccd5 --- /dev/null +++ b/examples/tests/TFHEManualTestSuite.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +pragma solidity >=0.8.13 <0.8.20; + +import "../../lib/TFHE.sol"; + +contract TFHEManualTestSuite { + function test_cmux( + bytes calldata control, + bytes calldata ifTrue, + bytes calldata ifFalse + ) public view returns (uint32) { + ebool controlProc = TFHE.asEbool(control); + euint32 ifTrueProc = TFHE.asEuint32(ifTrue); + euint32 ifFalseProc = TFHE.asEuint32(ifFalse); + return TFHE.decrypt(TFHE.cmux(controlProc, ifTrueProc, ifFalseProc)); + } +} diff --git a/examples/tests/TFHETestSuite3.sol b/examples/tests/TFHETestSuite3.sol index ee9fefca..4e11b51e 100644 --- a/examples/tests/TFHETestSuite3.sol +++ b/examples/tests/TFHETestSuite3.sol @@ -178,4 +178,40 @@ contract TFHETestSuite3 { euint32 result = TFHE.max(aProc, bProc); return TFHE.decrypt(result); } + + function neg_euint8(bytes calldata a) public view returns (uint8) { + euint8 aProc = TFHE.asEuint8(a); + euint8 result = TFHE.neg(aProc); + return TFHE.decrypt(result); + } + + function not_euint8(bytes calldata a) public view returns (uint8) { + euint8 aProc = TFHE.asEuint8(a); + euint8 result = TFHE.not(aProc); + return TFHE.decrypt(result); + } + + function neg_euint16(bytes calldata a) public view returns (uint16) { + euint16 aProc = TFHE.asEuint16(a); + euint16 result = TFHE.neg(aProc); + return TFHE.decrypt(result); + } + + function not_euint16(bytes calldata a) public view returns (uint16) { + euint16 aProc = TFHE.asEuint16(a); + euint16 result = TFHE.not(aProc); + return TFHE.decrypt(result); + } + + function neg_euint32(bytes calldata a) public view returns (uint32) { + euint32 aProc = TFHE.asEuint32(a); + euint32 result = TFHE.neg(aProc); + return TFHE.decrypt(result); + } + + function not_euint32(bytes calldata a) public view returns (uint32) { + euint32 aProc = TFHE.asEuint32(a); + euint32 result = TFHE.not(aProc); + return TFHE.decrypt(result); + } } diff --git a/lib/Impl.sol b/lib/Impl.sol index 80ddc0a4..11ce13a1 100644 --- a/lib/Impl.sol +++ b/lib/Impl.sol @@ -5,6 +5,64 @@ pragma solidity >=0.8.13 <0.8.20; import "./Common.sol"; import "./Precompiles.sol"; +interface FhevmLib { + function fheAdd(uint256 lhs, uint256 rhs, bytes1 scalarByte) external pure returns (uint256 result); + + function fheSub(uint256 lhs, uint256 rhs, bytes1 scalarByte) external pure returns (uint256 result); + + function fheMul(uint256 lhs, uint256 rhs, bytes1 scalarByte) external pure returns (uint256 result); + + function fheDiv(uint256 lhs, uint256 rhs, bytes1 scalarByte) external pure returns (uint256 result); + + function fheBitAnd(uint256 lhs, uint256 rhs, bytes1 scalarByte) external pure returns (uint256 result); + + function fheBitOr(uint256 lhs, uint256 rhs, bytes1 scalarByte) external pure returns (uint256 result); + + function fheBitXor(uint256 lhs, uint256 rhs, bytes1 scalarByte) external pure returns (uint256 result); + + function fheShl(uint256 lhs, uint256 rhs, bytes1 scalarByte) external pure returns (uint256 result); + + function fheShr(uint256 lhs, uint256 rhs, bytes1 scalarByte) external pure returns (uint256 result); + + function fheEq(uint256 lhs, uint256 rhs, bytes1 scalarByte) external pure returns (uint256 result); + + function fheNe(uint256 lhs, uint256 rhs, bytes1 scalarByte) external pure returns (uint256 result); + + function fheGe(uint256 lhs, uint256 rhs, bytes1 scalarByte) external pure returns (uint256 result); + + function fheGt(uint256 lhs, uint256 rhs, bytes1 scalarByte) external pure returns (uint256 result); + + function fheLe(uint256 lhs, uint256 rhs, bytes1 scalarByte) external pure returns (uint256 result); + + function fheLt(uint256 lhs, uint256 rhs, bytes1 scalarByte) external pure returns (uint256 result); + + function fheMin(uint256 lhs, uint256 rhs, bytes1 scalarByte) external pure returns (uint256 result); + + function fheMax(uint256 lhs, uint256 rhs, bytes1 scalarByte) external pure returns (uint256 result); + + function fheNeg(uint256 ct) external pure returns (uint256 result); + + function fheNot(uint256 ct) external pure returns (uint256 result); + + function optimisticRequire(uint256 ct) external view; + + function reencrypt(uint256 ct, uint256 publicKey) external view returns (bytes memory); + + function fhePubKey(bytes1 fromLib) external view returns (bytes memory result); + + function verifyCiphertext(bytes memory input) external view returns (uint256 result); + + function cast(uint256 ct, bytes1 toType) external view returns (uint256 result); + + function trivialEncrypt(uint256 ct, bytes1 toType) external view returns (uint256 result); + + function decrypt(uint256 ct) external view returns (uint256 result); + + function fheRand(bytes1 inp) external view returns (uint256 result); +} + +address constant EXT_TFHE_LIBRARY = address(93); + library Impl { // 32 bytes for the 'byte' type header + 48 bytes for the NaCl anonymous // box overhead + 4 bytes for the plaintext value. @@ -13,607 +71,204 @@ library Impl { // 32 bytes for the 'byte' header + 16553 bytes of key data. uint256 constant fhePubKeySize = 32 + 16553; - function add(uint256 lhs, uint256 rhs, bool scalar) internal view returns (uint256 result) { + function add(uint256 lhs, uint256 rhs, bool scalar) internal pure returns (uint256 result) { bytes1 scalarByte; if (scalar) { scalarByte = 0x01; } else { scalarByte = 0x00; } - bytes memory input = bytes.concat(bytes32(lhs), bytes32(rhs), scalarByte); - uint256 inputLen = input.length; - - bytes32[1] memory output; - uint256 outputLen = 32; - // Call the add precompile. - uint256 precompile = Precompiles.Add; - assembly { - // jump over the 32-bit 'size' field of the 'bytes' data structure of the 'input' to read actual bytes - if iszero(staticcall(gas(), precompile, add(input, 32), inputLen, output, outputLen)) { - revert(0, 0) - } - } - - result = uint256(output[0]); + result = FhevmLib(address(EXT_TFHE_LIBRARY)).fheAdd(lhs, rhs, scalarByte); } - function sub(uint256 lhs, uint256 rhs, bool scalar) internal view returns (uint256 result) { + function sub(uint256 lhs, uint256 rhs, bool scalar) internal pure returns (uint256 result) { bytes1 scalarByte; if (scalar) { scalarByte = 0x01; } else { scalarByte = 0x00; } - bytes memory input = bytes.concat(bytes32(lhs), bytes32(rhs), scalarByte); - uint256 inputLen = input.length; - - bytes32[1] memory output; - uint256 outputLen = 32; - // Call the sub precompile. - uint256 precompile = Precompiles.Subtract; - assembly { - // jump over the 32-bit 'size' field of the 'bytes' data structure of the 'input' to read actual bytes - if iszero(staticcall(gas(), precompile, add(input, 32), inputLen, output, outputLen)) { - revert(0, 0) - } - } - - result = uint256(output[0]); + result = FhevmLib(address(EXT_TFHE_LIBRARY)).fheSub(lhs, rhs, scalarByte); } - function mul(uint256 lhs, uint256 rhs, bool scalar) internal view returns (uint256 result) { + function mul(uint256 lhs, uint256 rhs, bool scalar) internal pure returns (uint256 result) { bytes1 scalarByte; if (scalar) { scalarByte = 0x01; } else { scalarByte = 0x00; } - bytes memory input = bytes.concat(bytes32(lhs), bytes32(rhs), scalarByte); - uint256 inputLen = input.length; - - bytes32[1] memory output; - uint256 outputLen = 32; - // Call the mul precompile. - uint256 precompile = Precompiles.Multiply; - assembly { - // jump over the 32-bit 'size' field of the 'bytes' data structure of the 'input' to read actual bytes - if iszero(staticcall(gas(), precompile, add(input, 32), inputLen, output, outputLen)) { - revert(0, 0) - } - } - - result = uint256(output[0]); + result = FhevmLib(address(EXT_TFHE_LIBRARY)).fheMul(lhs, rhs, scalarByte); } - function div(uint256 lhs, uint256 rhs) internal view returns (uint256 result) { - bytes memory input = bytes.concat(bytes32(lhs), bytes32(rhs), bytes1(0x01)); - uint256 inputLen = input.length; - - bytes32[1] memory output; - uint256 outputLen = 32; - // Call the div precompile. - uint256 precompile = Precompiles.Divide; - assembly { - // jump over the 32-bit 'size' field of the 'bytes' data structure of the 'input' to read actual bytes - if iszero(staticcall(gas(), precompile, add(input, 32), inputLen, output, outputLen)) { - revert(0, 0) - } - } - - result = uint256(output[0]); + function div(uint256 lhs, uint256 rhs) internal pure returns (uint256 result) { + bytes1 scalarByte = 0x01; + result = FhevmLib(address(EXT_TFHE_LIBRARY)).fheDiv(lhs, rhs, scalarByte); } - function and(uint256 lhs, uint256 rhs) internal view returns (uint256 result) { - bytes memory input = bytes.concat(bytes32(lhs), bytes32(rhs), bytes1(0x00)); - uint256 inputLen = input.length; - - bytes32[1] memory output; - uint256 outputLen = 32; - // Call the and precompile. - uint256 precompile = Precompiles.BitwiseAnd; - assembly { - // jump over the 32-bit 'size' field of the 'bytes' data structure of the 'input' to read actual bytes - if iszero(staticcall(gas(), precompile, add(input, 32), inputLen, output, outputLen)) { - revert(0, 0) - } - } - - result = uint256(output[0]); + function and(uint256 lhs, uint256 rhs) internal pure returns (uint256 result) { + bytes1 scalarByte = 0x00; + result = FhevmLib(address(EXT_TFHE_LIBRARY)).fheBitAnd(lhs, rhs, scalarByte); } - function or(uint256 lhs, uint256 rhs) internal view returns (uint256 result) { - bytes memory input = bytes.concat(bytes32(lhs), bytes32(rhs), bytes1(0x00)); - uint256 inputLen = input.length; - - bytes32[1] memory output; - uint256 outputLen = 32; - // Call the or precompile. - uint256 precompile = Precompiles.BitwiseOr; - assembly { - // jump over the 32-bit 'size' field of the 'bytes' data structure of the 'input' to read actual bytes - if iszero(staticcall(gas(), precompile, add(input, 32), inputLen, output, outputLen)) { - revert(0, 0) - } - } - - result = uint256(output[0]); + function or(uint256 lhs, uint256 rhs) internal pure returns (uint256 result) { + bytes1 scalarByte = 0x00; + result = FhevmLib(address(EXT_TFHE_LIBRARY)).fheBitOr(lhs, rhs, scalarByte); } - function xor(uint256 lhs, uint256 rhs) internal view returns (uint256 result) { - bytes memory input = bytes.concat(bytes32(lhs), bytes32(rhs), bytes1(0x00)); - uint256 inputLen = input.length; - - bytes32[1] memory output; - uint256 outputLen = 32; - // Call the xor precompile. - uint256 precompile = Precompiles.BitwiseXor; - assembly { - // jump over the 32-bit 'size' field of the 'bytes' data structure of the 'input' to read actual bytes - if iszero(staticcall(gas(), precompile, add(input, 32), inputLen, output, outputLen)) { - revert(0, 0) - } - } - - result = uint256(output[0]); + function xor(uint256 lhs, uint256 rhs) internal pure returns (uint256 result) { + bytes1 scalarByte = 0x00; + result = FhevmLib(address(EXT_TFHE_LIBRARY)).fheBitXor(lhs, rhs, scalarByte); } - function shl(uint256 lhs, uint256 rhs, bool scalar) internal view returns (uint256 result) { + function shl(uint256 lhs, uint256 rhs, bool scalar) internal pure returns (uint256 result) { bytes1 scalarByte; if (scalar) { scalarByte = 0x01; } else { scalarByte = 0x00; } - bytes memory input = bytes.concat(bytes32(lhs), bytes32(rhs), scalarByte); - uint256 inputLen = input.length; - - bytes32[1] memory output; - uint256 outputLen = 32; - // Call the shl precompile. - uint256 precompile = Precompiles.ShiftLeft; - assembly { - // jump over the 32-bit 'size' field of the 'bytes' data structure of the 'input' to read actual bytes - if iszero(staticcall(gas(), precompile, add(input, 32), inputLen, output, outputLen)) { - revert(0, 0) - } - } - - result = uint256(output[0]); + result = FhevmLib(address(EXT_TFHE_LIBRARY)).fheShl(lhs, rhs, scalarByte); } - function shr(uint256 lhs, uint256 rhs, bool scalar) internal view returns (uint256 result) { + function shr(uint256 lhs, uint256 rhs, bool scalar) internal pure returns (uint256 result) { bytes1 scalarByte; if (scalar) { scalarByte = 0x01; } else { scalarByte = 0x00; } - bytes memory input = bytes.concat(bytes32(lhs), bytes32(rhs), scalarByte); - uint256 inputLen = input.length; - - bytes32[1] memory output; - uint256 outputLen = 32; - // Call the shr precompile. - uint256 precompile = Precompiles.ShiftRight; - assembly { - // jump over the 32-bit 'size' field of the 'bytes' data structure of the 'input' to read actual bytes - if iszero(staticcall(gas(), precompile, add(input, 32), inputLen, output, outputLen)) { - revert(0, 0) - } - } - - result = uint256(output[0]); + result = FhevmLib(address(EXT_TFHE_LIBRARY)).fheShr(lhs, rhs, scalarByte); } - function eq(uint256 lhs, uint256 rhs, bool scalar) internal view returns (uint256 result) { + function eq(uint256 lhs, uint256 rhs, bool scalar) internal pure returns (uint256 result) { bytes1 scalarByte; if (scalar) { scalarByte = 0x01; } else { scalarByte = 0x00; } - bytes memory input = bytes.concat(bytes32(lhs), bytes32(rhs), scalarByte); - uint256 inputLen = input.length; - - bytes32[1] memory output; - uint256 outputLen = 32; - // Call the eq precompile. - uint256 precompile = Precompiles.Equal; - assembly { - // jump over the 32-bit 'size' field of the 'bytes' data structure of the 'input' to read actual bytes - if iszero(staticcall(gas(), precompile, add(input, 32), inputLen, output, outputLen)) { - revert(0, 0) - } - } - - result = uint256(output[0]); + result = FhevmLib(address(EXT_TFHE_LIBRARY)).fheEq(lhs, rhs, scalarByte); } - function ne(uint256 lhs, uint256 rhs, bool scalar) internal view returns (uint256 result) { + function ne(uint256 lhs, uint256 rhs, bool scalar) internal pure returns (uint256 result) { bytes1 scalarByte; if (scalar) { scalarByte = 0x01; } else { scalarByte = 0x00; } - bytes memory input = bytes.concat(bytes32(lhs), bytes32(rhs), scalarByte); - uint256 inputLen = input.length; - - bytes32[1] memory output; - uint256 outputLen = 32; - // Call the ne precompile. - uint256 precompile = Precompiles.NotEqual; - assembly { - // jump over the 32-bit 'size' field of the 'bytes' data structure of the 'input' to read actual bytes - if iszero(staticcall(gas(), precompile, add(input, 32), inputLen, output, outputLen)) { - revert(0, 0) - } - } - - result = uint256(output[0]); + result = FhevmLib(address(EXT_TFHE_LIBRARY)).fheNe(lhs, rhs, scalarByte); } - function ge(uint256 lhs, uint256 rhs, bool scalar) internal view returns (uint256 result) { + function ge(uint256 lhs, uint256 rhs, bool scalar) internal pure returns (uint256 result) { bytes1 scalarByte; if (scalar) { scalarByte = 0x01; } else { scalarByte = 0x00; } - bytes memory input = bytes.concat(bytes32(lhs), bytes32(rhs), scalarByte); - uint256 inputLen = input.length; - - bytes32[1] memory output; - uint256 outputLen = 32; - // Call the ge precompile. - uint256 precompile = Precompiles.GreaterThanOrEqual; - assembly { - // jump over the 32-bit 'size' field of the 'bytes' data structure of the 'input' to read actual bytes - if iszero(staticcall(gas(), precompile, add(input, 32), inputLen, output, outputLen)) { - revert(0, 0) - } - } - - result = uint256(output[0]); + result = FhevmLib(address(EXT_TFHE_LIBRARY)).fheGe(lhs, rhs, scalarByte); } - function gt(uint256 lhs, uint256 rhs, bool scalar) internal view returns (uint256 result) { + function gt(uint256 lhs, uint256 rhs, bool scalar) internal pure returns (uint256 result) { bytes1 scalarByte; if (scalar) { scalarByte = 0x01; } else { scalarByte = 0x00; } - bytes memory input = bytes.concat(bytes32(lhs), bytes32(rhs), scalarByte); - uint256 inputLen = input.length; - - bytes32[1] memory output; - uint256 outputLen = 32; - // Call the gt precompile. - uint256 precompile = Precompiles.GreaterThan; - assembly { - // jump over the 32-bit 'size' field of the 'bytes' data structure of the 'input' to read actual bytes - if iszero(staticcall(gas(), precompile, add(input, 32), inputLen, output, outputLen)) { - revert(0, 0) - } - } - - result = uint256(output[0]); + result = FhevmLib(address(EXT_TFHE_LIBRARY)).fheGt(lhs, rhs, scalarByte); } - function le(uint256 lhs, uint256 rhs, bool scalar) internal view returns (uint256 result) { + function le(uint256 lhs, uint256 rhs, bool scalar) internal pure returns (uint256 result) { bytes1 scalarByte; if (scalar) { scalarByte = 0x01; } else { scalarByte = 0x00; } - bytes memory input = bytes.concat(bytes32(lhs), bytes32(rhs), scalarByte); - uint256 inputLen = input.length; - - bytes32[1] memory output; - uint256 outputLen = 32; - // Call the le precompile. - uint256 precompile = Precompiles.LessThanOrEqual; - assembly { - // jump over the 32-bit 'size' field of the 'bytes' data structure of the 'input' to read actual bytes - if iszero(staticcall(gas(), precompile, add(input, 32), inputLen, output, outputLen)) { - revert(0, 0) - } - } - - result = uint256(output[0]); + result = FhevmLib(address(EXT_TFHE_LIBRARY)).fheLe(lhs, rhs, scalarByte); } - function lt(uint256 lhs, uint256 rhs, bool scalar) internal view returns (uint256 result) { + function lt(uint256 lhs, uint256 rhs, bool scalar) internal pure returns (uint256 result) { bytes1 scalarByte; if (scalar) { scalarByte = 0x01; } else { scalarByte = 0x00; } - bytes memory input = bytes.concat(bytes32(lhs), bytes32(rhs), scalarByte); - uint256 inputLen = input.length; - - bytes32[1] memory output; - uint256 outputLen = 32; - // Call the lt precompile. - uint256 precompile = Precompiles.LessThan; - assembly { - // jump over the 32-bit 'size' field of the 'bytes' data structure of the 'input' to read actual bytes - if iszero(staticcall(gas(), precompile, add(input, 32), inputLen, output, outputLen)) { - revert(0, 0) - } - } - - result = uint256(output[0]); + result = FhevmLib(address(EXT_TFHE_LIBRARY)).fheLt(lhs, rhs, scalarByte); } - function min(uint256 lhs, uint256 rhs, bool scalar) internal view returns (uint256 result) { + function min(uint256 lhs, uint256 rhs, bool scalar) internal pure returns (uint256 result) { bytes1 scalarByte; if (scalar) { scalarByte = 0x01; } else { scalarByte = 0x00; } - bytes memory input = bytes.concat(bytes32(lhs), bytes32(rhs), scalarByte); - uint256 inputLen = input.length; - - bytes32[1] memory output; - uint256 outputLen = 32; - // Call the min precompile. - uint256 precompile = Precompiles.Min; - assembly { - // jump over the 32-bit 'size' field of the 'bytes' data structure of the 'input' to read actual bytes - if iszero(staticcall(gas(), precompile, add(input, 32), inputLen, output, outputLen)) { - revert(0, 0) - } - } - - result = uint256(output[0]); + result = FhevmLib(address(EXT_TFHE_LIBRARY)).fheMin(lhs, rhs, scalarByte); } - function max(uint256 lhs, uint256 rhs, bool scalar) internal view returns (uint256 result) { + function max(uint256 lhs, uint256 rhs, bool scalar) internal pure returns (uint256 result) { bytes1 scalarByte; if (scalar) { scalarByte = 0x01; } else { scalarByte = 0x00; } - bytes memory input = bytes.concat(bytes32(lhs), bytes32(rhs), scalarByte); - uint256 inputLen = input.length; - - bytes32[1] memory output; - uint256 outputLen = 32; - // Call the max precompile. - uint256 precompile = Precompiles.Max; - assembly { - // jump over the 32-bit 'size' field of the 'bytes' data structure of the 'input' to read actual bytes - if iszero(staticcall(gas(), precompile, add(input, 32), inputLen, output, outputLen)) { - revert(0, 0) - } - } - - result = uint256(output[0]); + result = FhevmLib(address(EXT_TFHE_LIBRARY)).fheMax(lhs, rhs, scalarByte); } - function neg(uint256 ct) internal view returns (uint256 result) { - bytes32[1] memory input; - input[0] = bytes32(ct); - uint256 inputLen = 32; - - bytes32[1] memory output; - uint256 outputLen = 32; - - // Call the neg precompile. - uint256 precompile = Precompiles.Negate; - assembly { - if iszero(staticcall(gas(), precompile, input, inputLen, output, outputLen)) { - revert(0, 0) - } - } - - result = uint256(output[0]); + function neg(uint256 ct) internal pure returns (uint256 result) { + result = FhevmLib(address(EXT_TFHE_LIBRARY)).fheNeg(ct); } - function not(uint256 ct) internal view returns (uint256 result) { - bytes32[1] memory input; - input[0] = bytes32(ct); - uint256 inputLen = 32; - - bytes32[1] memory output; - uint256 outputLen = 32; - - // Call the not precompile. - uint256 precompile = Precompiles.Not; - assembly { - if iszero(staticcall(gas(), precompile, input, inputLen, output, outputLen)) { - revert(0, 0) - } - } - - result = uint256(output[0]); + function not(uint256 ct) internal pure returns (uint256 result) { + result = FhevmLib(address(EXT_TFHE_LIBRARY)).fheNot(ct); } // If 'control's value is 'true', the result has the same value as 'ifTrue'. // If 'control's value is 'false', the result has the same value as 'ifFalse'. - function cmux(uint256 control, uint256 ifTrue, uint256 ifFalse) internal view returns (uint256 result) { + function cmux(uint256 control, uint256 ifTrue, uint256 ifFalse) internal pure returns (uint256 result) { // result = (ifTrue - ifFalse) * control + ifFalse - bytes memory input = bytes.concat(bytes32(ifTrue), bytes32(ifFalse), bytes1(0x00)); - uint256 inputLen = input.length; - - bytes32[1] memory subOutput; - uint256 outputLen = 32; - - // Call the sub precompile. - uint256 precompile = Precompiles.Subtract; - assembly { - if iszero(staticcall(gas(), precompile, add(input, 32), inputLen, subOutput, outputLen)) { - revert(0, 0) - } - } - - // Call the mul precompile. - input = bytes.concat(bytes32(control), bytes32(subOutput[0]), bytes1(0x00)); - inputLen = input.length; - precompile = Precompiles.Multiply; - bytes32[1] memory mulOutput; - assembly { - if iszero(staticcall(gas(), precompile, add(input, 32), inputLen, mulOutput, outputLen)) { - revert(0, 0) - } - } - - // Call the add precompile. - input = bytes.concat(bytes32(mulOutput[0]), bytes32(ifFalse), bytes1(0x00)); - inputLen = input.length; - precompile = Precompiles.Add; - bytes32[1] memory addOutput; - assembly { - if iszero(staticcall(gas(), precompile, add(input, 32), inputLen, addOutput, outputLen)) { - revert(0, 0) - } - } - - result = uint256(addOutput[0]); + uint256 subOutput = FhevmLib(address(EXT_TFHE_LIBRARY)).fheSub(ifTrue, ifFalse, bytes1(0x00)); + uint256 mulOutput = FhevmLib(address(EXT_TFHE_LIBRARY)).fheMul(control, subOutput, bytes1(0x00)); + result = FhevmLib(address(EXT_TFHE_LIBRARY)).fheAdd(mulOutput, ifFalse, bytes1(0x00)); } function optReq(uint256 ciphertext) internal view { - bytes32[1] memory input; - input[0] = bytes32(ciphertext); - uint256 inputLen = 32; - - // Call the optimistic require precompile. - uint256 precompile = Precompiles.OptimisticRequire; - assembly { - if iszero(staticcall(gas(), precompile, input, inputLen, 0, 0)) { - revert(0, 0) - } - } + FhevmLib(address(EXT_TFHE_LIBRARY)).optimisticRequire(ciphertext); } function reencrypt(uint256 ciphertext, bytes32 publicKey) internal view returns (bytes memory reencrypted) { - bytes32[2] memory input; - input[0] = bytes32(ciphertext); - input[1] = publicKey; - uint256 inputLen = 64; - - reencrypted = new bytes(reencryptedSize); - - // Call the reencrypt precompile. - uint256 precompile = Precompiles.Reencrypt; - assembly { - if iszero(staticcall(gas(), precompile, input, inputLen, reencrypted, reencryptedSize)) { - revert(0, 0) - } - } + return FhevmLib(address(EXT_TFHE_LIBRARY)).reencrypt(ciphertext, uint256(publicKey)); } function fhePubKey() internal view returns (bytes memory key) { // Set a byte value of 1 to signal the call comes from the library. - bytes1[1] memory input; - input[0] = 0x01; - uint256 inputLen = 1; - - key = new bytes(fhePubKeySize); - - // Call the fhePubKey precompile. - uint256 precompile = Precompiles.FhePubKey; - assembly { - if iszero(staticcall(gas(), precompile, input, inputLen, key, fhePubKeySize)) { - revert(0, 0) - } - } + key = FhevmLib(address(EXT_TFHE_LIBRARY)).fhePubKey(bytes1(0x01)); } function verify(bytes memory _ciphertextBytes, uint8 _toType) internal view returns (uint256 result) { bytes memory input = bytes.concat(_ciphertextBytes, bytes1(_toType)); - uint256 inputLen = input.length; - - bytes32[1] memory output; - uint256 outputLen = 32; - - // Call the verify precompile. - uint256 precompile = Precompiles.Verify; - assembly { - // jump over the 32-bit 'size' field of the 'bytes' data structure of the 'input' to read actual bytes - if iszero(staticcall(gas(), precompile, add(input, 32), inputLen, output, outputLen)) { - revert(0, 0) - } - } - result = uint256(output[0]); + result = FhevmLib(address(EXT_TFHE_LIBRARY)).verifyCiphertext(input); } function cast(uint256 ciphertext, uint8 toType) internal view returns (uint256 result) { - bytes memory input = bytes.concat(bytes32(ciphertext), bytes1(toType)); - uint256 inputLen = input.length; - - bytes32[1] memory output; - uint256 outputLen = 32; - - // Call the cast precompile. - uint256 precompile = Precompiles.Cast; - assembly { - // jump over the 32-bit 'size' field of the 'bytes' data structure of the 'input' to read actual bytes - if iszero(staticcall(gas(), precompile, add(input, 32), inputLen, output, outputLen)) { - revert(0, 0) - } - } - result = uint256(output[0]); + result = FhevmLib(address(EXT_TFHE_LIBRARY)).cast(ciphertext, bytes1(toType)); } function trivialEncrypt(uint256 value, uint8 toType) internal view returns (uint256 result) { - bytes memory input = bytes.concat(bytes32(value), bytes1(toType)); - uint256 inputLen = input.length; - - bytes32[1] memory output; - uint256 outputLen = 32; - - // Call the trivialEncrypt precompile. - uint256 precompile = Precompiles.TrivialEncrypt; - assembly { - // jump over the 32-bit 'size' field of the 'bytes' data structure of the 'input' to read actual bytes - if iszero(staticcall(gas(), precompile, add(input, 32), inputLen, output, outputLen)) { - revert(0, 0) - } - } - result = uint256(output[0]); + result = FhevmLib(address(EXT_TFHE_LIBRARY)).trivialEncrypt(value, bytes1(toType)); } function decrypt(uint256 ciphertext) internal view returns (uint256 result) { - bytes32[1] memory input; - input[0] = bytes32(ciphertext); - uint256 inputLen = 32; - - bytes32[1] memory output; - uint256 outputLen = 32; - - // Call the decrypt precompile. - uint256 precompile = Precompiles.Decrypt; - assembly { - if iszero(staticcall(gas(), precompile, input, inputLen, output, outputLen)) { - revert(0, 0) - } - } - // The output is a 32-byte buffer of a 256-bit big-endian unsigned integer. - result = uint256(output[0]); + result = FhevmLib(address(EXT_TFHE_LIBRARY)).decrypt(ciphertext); } function rand(uint8 randType) internal view returns (uint256 result) { - bytes1[1] memory input; - input[0] = bytes1(randType); - uint256 inputLen = 1; - - bytes32[1] memory output; - uint256 outputLen = 32; - - // Call the rand precompile. - uint256 precompile = Precompiles.Rand; - assembly { - if iszero(staticcall(gas(), precompile, input, inputLen, output, outputLen)) { - revert(0, 0) - } - } - result = uint256(output[0]); + result = FhevmLib(address(EXT_TFHE_LIBRARY)).fheRand(bytes1(randType)); } } diff --git a/lib/TFHE.sol b/lib/TFHE.sol index d81b7d7c..828c72c4 100644 --- a/lib/TFHE.sol +++ b/lib/TFHE.sol @@ -2268,7 +2268,7 @@ library TFHE { // If 'control''s value is 'true', the result has the same value as 'a'. // If 'control''s value is 'false', the result has the same value as 'b'. - function cmux(ebool control, euint8 a, euint8 b) internal view returns (euint8) { + function cmux(ebool control, euint8 a, euint8 b) internal pure returns (euint8) { return euint8.wrap(Impl.cmux(ebool.unwrap(control), euint8.unwrap(a), euint8.unwrap(b))); } @@ -2345,6 +2345,30 @@ library TFHE { return ne(value, 0); } + function neg(euint8 value) internal pure returns (euint8) { + return euint8.wrap(Impl.neg(euint8.unwrap(value))); + } + + function not(euint8 value) internal pure returns (euint8) { + return euint8.wrap(Impl.not(euint8.unwrap(value))); + } + + function neg(euint16 value) internal pure returns (euint16) { + return euint16.wrap(Impl.neg(euint16.unwrap(value))); + } + + function not(euint16 value) internal pure returns (euint16) { + return euint16.wrap(Impl.not(euint16.unwrap(value))); + } + + function neg(euint32 value) internal pure returns (euint32) { + return euint32.wrap(Impl.neg(euint32.unwrap(value))); + } + + function not(euint32 value) internal pure returns (euint32) { + return euint32.wrap(Impl.not(euint32.unwrap(value))); + } + // Convert a serialized 'ciphertext' to an encrypted euint8 integer. function asEuint8(bytes memory ciphertext) internal view returns (euint8) { return euint8.wrap(Impl.verify(ciphertext, Common.euint8_t)); @@ -2381,16 +2405,6 @@ library TFHE { return uint8(Impl.decrypt(euint8.unwrap(value))); } - // Return the negation of 'value'. - function neg(euint8 value) internal view returns (euint8) { - return euint8.wrap(Impl.neg(euint8.unwrap(value))); - } - - // Return '!value'. - function not(euint8 value) internal view returns (euint8) { - return euint8.wrap(Impl.not(euint8.unwrap(value))); - } - // Convert a serialized 'ciphertext' to an encrypted euint16 integer. function asEuint16(bytes memory ciphertext) internal view returns (euint16) { return euint16.wrap(Impl.verify(ciphertext, Common.euint16_t)); @@ -2427,16 +2441,6 @@ library TFHE { return uint16(Impl.decrypt(euint16.unwrap(value))); } - // Return the negation of 'value'. - function neg(euint16 value) internal view returns (euint16) { - return euint16.wrap(Impl.neg(euint16.unwrap(value))); - } - - // Return '!value'. - function not(euint16 value) internal view returns (euint16) { - return euint16.wrap(Impl.not(euint16.unwrap(value))); - } - // Convert a serialized 'ciphertext' to an encrypted euint32 integer. function asEuint32(bytes memory ciphertext) internal view returns (euint32) { return euint32.wrap(Impl.verify(ciphertext, Common.euint32_t)); @@ -2473,16 +2477,6 @@ library TFHE { return uint32(Impl.decrypt(euint32.unwrap(value))); } - // Return the negation of 'value'. - function neg(euint32 value) internal view returns (euint32) { - return euint32.wrap(Impl.neg(euint32.unwrap(value))); - } - - // Return '!value'. - function not(euint32 value) internal view returns (euint32) { - return euint32.wrap(Impl.not(euint32.unwrap(value))); - } - // Optimistically require that 'b' is true. // // This function does not evaluate 'b' at the time of the call. diff --git a/test/tfheOperations/manual.ts b/test/tfheOperations/manual.ts new file mode 100644 index 00000000..d3f61e8d --- /dev/null +++ b/test/tfheOperations/manual.ts @@ -0,0 +1,47 @@ +import { expect } from 'chai'; +import { ethers } from 'hardhat'; + +import type { TFHEManualTestSuite } from '../../types/contracts/tests/TFHEManualTestSuite'; +import { createInstances } from '../instance'; +import { getSigners } from '../signers'; + +async function deployTfheManualTestFixture(): Promise { + const signers = await ethers.getSigners(); + const admin = signers[0]; + + const contractFactory = await ethers.getContractFactory('TFHEManualTestSuite'); + const contract = await contractFactory.connect(admin).deploy(); + await contract.waitForDeployment(); + + return contract; +} + +describe('TFHE manual operations', function () { + before(async function () { + this.signers = await getSigners(); + + const contract = await deployTfheManualTestFixture(); + this.contractAddress = await contract.getAddress(); + this.contract = contract; + const instances = await createInstances(this.contractAddress, ethers, this.signers); + this.instances = instances; + }); + + it('Cmux works returning if false', async function () { + const res = await this.contract.test_cmux( + this.instances.alice.encrypt8(0), + this.instances.alice.encrypt32(3), + this.instances.alice.encrypt32(4), + ); + expect(res).to.equal(4); + }); + + it('Cmux works returning if true', async function () { + const res = await this.contract.test_cmux( + this.instances.alice.encrypt8(1), + this.instances.alice.encrypt32(3), + this.instances.alice.encrypt32(4), + ); + expect(res).to.equal(3); + }); +}); diff --git a/test/tfheOperations/tfheOperations.ts b/test/tfheOperations/tfheOperations.ts index e2dabd3a..fb607460 100644 --- a/test/tfheOperations/tfheOperations.ts +++ b/test/tfheOperations/tfheOperations.ts @@ -3385,4 +3385,49 @@ describe('TFHE operations', function () { const res = await this.contract3.max_uint32_euint32(3416064, this.instances3.alice.encrypt32(3416063)); expect(res).to.equal(3416064); }); + + it('test operator "neg" overload (euint8) => euint8 test 1 (1)', async function () { + const res = await this.contract3.neg_euint8(this.instances3.alice.encrypt8(1)); + expect(res).to.equal(255); + }); + + it('test operator "neg" overload (euint8) => euint8 test 2 (2)', async function () { + const res = await this.contract3.neg_euint8(this.instances3.alice.encrypt8(2)); + expect(res).to.equal(254); + }); + + it('test operator "not" overload (euint8) => euint8 test 1 (3)', async function () { + const res = await this.contract3.not_euint8(this.instances3.alice.encrypt8(3)); + expect(res).to.equal(252); + }); + + it('test operator "neg" overload (euint16) => euint16 test 1 (1)', async function () { + const res = await this.contract3.neg_euint16(this.instances3.alice.encrypt16(1)); + expect(res).to.equal(65535); + }); + + it('test operator "neg" overload (euint16) => euint16 test 2 (2)', async function () { + const res = await this.contract3.neg_euint16(this.instances3.alice.encrypt16(2)); + expect(res).to.equal(65534); + }); + + it('test operator "not" overload (euint16) => euint16 test 1 (241)', async function () { + const res = await this.contract3.not_euint16(this.instances3.alice.encrypt16(241)); + expect(res).to.equal(65294); + }); + + it('test operator "neg" overload (euint32) => euint32 test 1 (1)', async function () { + const res = await this.contract3.neg_euint32(this.instances3.alice.encrypt32(1)); + expect(res).to.equal(4294967295); + }); + + it('test operator "neg" overload (euint32) => euint32 test 2 (2)', async function () { + const res = await this.contract3.neg_euint32(this.instances3.alice.encrypt32(2)); + expect(res).to.equal(4294967294); + }); + + it('test operator "not" overload (euint32) => euint32 test 1 (65534)', async function () { + const res = await this.contract3.not_euint32(this.instances3.alice.encrypt32(65534)); + expect(res).to.equal(4294901761); + }); });