Skip to content

Commit

Permalink
Merge pull request #390 from zama-ai/ci/new-async-api-array-eq
Browse files Browse the repository at this point in the history
Ci/new async api array eq
  • Loading branch information
immortal-tofu authored Jun 11, 2024
2 parents ccc861b + fa1961b commit fcb40b7
Show file tree
Hide file tree
Showing 12 changed files with 371 additions and 7 deletions.
33 changes: 31 additions & 2 deletions codegen/templates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ function fheLibCustomInterfaceFunctions(): string {
function trivialEncrypt(uint256 ct, bytes1 toType) external pure returns (uint256 result);
function decrypt(uint256 ct) external view returns (uint256 result);
function fheIfThenElse(uint256 control, uint256 ifTrue, uint256 ifFalse) external pure returns (uint256 result);
function fheArrayEq(uint256[] memory lhs, uint256[] memory rhs) external pure returns (uint256 result);
function fheRand(bytes1 randType) external view returns (uint256 result);
function fheRandBounded(uint256 upperBound, bytes1 randType) external view returns (uint256 result);
`;
Expand Down Expand Up @@ -226,6 +227,7 @@ library TFHE {

// TODO: Decide whether we want to have mixed-inputs for CMUX/Select
supportedBits.forEach((bits) => res.push(tfheSelect(bits)));
supportedBits.forEach((bits) => res.push(tfheEq(bits)));
supportedBits.forEach((outputBits) => {
supportedBits.forEach((inputBits) => {
res.push(tfheAsEboolCustomCast(inputBits, outputBits));
Expand Down Expand Up @@ -534,6 +536,23 @@ function tfheSelect(inputBits: number): string {
}`;
}

function tfheEq(inputBits: number): string {
return `
function eq(euint${inputBits}[] memory a, euint${inputBits}[] memory b) internal pure returns (ebool) {
require(a.length == b.length, "Both arrays are not of the same size.");
uint256[] memory lhs = new uint256[](a.length);
uint256[] memory rhs = new uint256[](b.length);
for (uint i = 0; i < a.length; i++) {
lhs[i] = euint${inputBits}.unwrap(a[i]);
}
for (uint i = 0; i < b.length; i++) {
rhs[i] = euint${inputBits}.unwrap(b[i]);
}
return ebool.wrap(Impl.eq(lhs, rhs));
}
`;
}

function tfheAsEboolCustomCast(inputBits: number, outputBits: number): string {
if (inputBits == outputBits) {
return '';
Expand Down Expand Up @@ -732,8 +751,6 @@ function tfheCustomMethods(ctx: CodegenContext, mocked: boolean): string {
}
}
// Returns the network public FHE key.
function fhePubKey() internal view returns (bytes memory) {
return Impl.fhePubKey();
Expand Down Expand Up @@ -901,6 +918,10 @@ function implCustomMethods(ctx: CodegenContext): string {
result = FhevmLib(address(EXT_TFHE_LIBRARY)).fheIfThenElse(control, ifTrue, ifFalse);
}
function eq(uint256[] memory lhs, uint256[] memory rhs) internal pure returns (uint256 result) {
result = FhevmLib(address(EXT_TFHE_LIBRARY)).fheArrayEq(lhs, rhs);
}
function reencrypt(uint256 ciphertext, bytes32 publicKey) internal view returns (bytes memory reencrypted) {
return FhevmLib(address(EXT_TFHE_LIBRARY)).reencrypt(ciphertext, uint256(publicKey));
}
Expand Down Expand Up @@ -1017,6 +1038,14 @@ library Impl {
result = (lhs == rhs) ? 1 : 0;
}
function eq(uint256[] memory lhs, uint256[] memory rhs) internal pure returns (uint256 result) {
require(lhs.length == rhs.length, "Both arrays are not of the same size.");
result = 1;
for (uint i = 0; i < lhs.length; i++) {
if (lhs[i] != rhs[i]) return 0;
}
}
function ne(uint256 lhs, uint256 rhs, bool /*scalar*/) internal pure returns (uint256 result) {
result = (lhs != rhs) ? 1 : 0;
}
Expand Down
1 change: 1 addition & 0 deletions docs/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
- [Common pitfalls and best practises](guides/pitfalls.md)
- [How can I break a loop ?](guides/loop.md)
- [Encrypt an input](guides/inputs.md)
- [Reencryption](guides/reencryption.md)
- [Use the CLI](guides/cli.md)
- [Build a web application](guides/webapp.md)
- [Build with Node](guides/node.md)
Expand Down
2 changes: 1 addition & 1 deletion docs/guides/node.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ pnpm add fhevmjs
## Create an instance

```javascript
const { createInstance } = require("fhevmjs");
const { createInstance, getPublicKeyCallParams } = require("fhevmjs");
const { ethers, JsonRpcProvider } = require("ethers");

const provider = new JsonRpcProvider(`https://devnet.zama.ai/`);
Expand Down
100 changes: 100 additions & 0 deletions examples/tests/TFHEManualTestSuite.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,106 @@ import "../../abstracts/Reencrypt.sol";
import "../../lib/TFHE.sol";

contract TFHEManualTestSuite is Reencrypt {
function test_eq_array_4(
bytes calldata a,
bytes calldata b,
bytes calldata c,
bytes calldata d
) public view returns (bool) {
euint4 aProc = TFHE.asEuint4(a);
euint4 bProc = TFHE.asEuint4(b);
euint4 cProc = TFHE.asEuint4(c);
euint4 dProc = TFHE.asEuint4(d);
euint4[] memory arrA = new euint4[](2);
arrA[0] = aProc;
arrA[1] = bProc;
euint4[] memory arrB = new euint4[](2);
arrB[0] = cProc;
arrB[1] = dProc;
ebool result = TFHE.eq(arrA, arrB);
return TFHE.decrypt(result);
}

function test_eq_array_8(
bytes calldata a,
bytes calldata b,
bytes calldata c,
bytes calldata d
) public view returns (bool) {
euint8 aProc = TFHE.asEuint8(a);
euint8 bProc = TFHE.asEuint8(b);
euint8 cProc = TFHE.asEuint8(c);
euint8 dProc = TFHE.asEuint8(d);
euint8[] memory arrA = new euint8[](2);
arrA[0] = aProc;
arrA[1] = bProc;
euint8[] memory arrB = new euint8[](2);
arrB[0] = cProc;
arrB[1] = dProc;
ebool result = TFHE.eq(arrA, arrB);
return TFHE.decrypt(result);
}

function test_eq_array_16(
bytes calldata a,
bytes calldata b,
bytes calldata c,
bytes calldata d
) public view returns (bool) {
euint16 aProc = TFHE.asEuint16(a);
euint16 bProc = TFHE.asEuint16(b);
euint16 cProc = TFHE.asEuint16(c);
euint16 dProc = TFHE.asEuint16(d);
euint16[] memory arrA = new euint16[](2);
arrA[0] = aProc;
arrA[1] = bProc;
euint16[] memory arrB = new euint16[](2);
arrB[0] = cProc;
arrB[1] = dProc;
ebool result = TFHE.eq(arrA, arrB);
return TFHE.decrypt(result);
}

function test_eq_array_32(
bytes calldata a,
bytes calldata b,
bytes calldata c,
bytes calldata d
) public view returns (bool) {
euint32 aProc = TFHE.asEuint32(a);
euint32 bProc = TFHE.asEuint32(b);
euint32 cProc = TFHE.asEuint32(c);
euint32 dProc = TFHE.asEuint32(d);
euint32[] memory arrA = new euint32[](2);
arrA[0] = aProc;
arrA[1] = bProc;
euint32[] memory arrB = new euint32[](2);
arrB[0] = cProc;
arrB[1] = dProc;
ebool result = TFHE.eq(arrA, arrB);
return TFHE.decrypt(result);
}

function test_eq_array_64(
bytes calldata a,
bytes calldata b,
bytes calldata c,
bytes calldata d
) public view returns (bool) {
euint64 aProc = TFHE.asEuint64(a);
euint64 bProc = TFHE.asEuint64(b);
euint64 cProc = TFHE.asEuint64(c);
euint64 dProc = TFHE.asEuint64(d);
euint64[] memory arrA = new euint64[](2);
arrA[0] = aProc;
arrA[1] = bProc;
euint64[] memory arrB = new euint64[](2);
arrB[0] = cProc;
arrB[1] = dProc;
ebool result = TFHE.eq(arrA, arrB);
return TFHE.decrypt(result);
}

function test_select(
bytes calldata control,
bytes calldata ifTrue,
Expand Down
2 changes: 1 addition & 1 deletion launch-fhevm.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ ORACLE_CONTRACT_PREDEPLOY_ADDRESS=$(grep ORACLE_CONTRACT_PREDEPLOY_ADDRESS oracl
docker run -d -i -p 8545:8545 --rm --name fhevm \
-e PRIVATE_KEY_ORACLE_RELAYER="$PRIVATE_KEY_ORACLE_RELAYER" \
-e ORACLE_CONTRACT_PREDEPLOY_ADDRESS="$ORACLE_CONTRACT_PREDEPLOY_ADDRESS" \
ghcr.io/zama-ai/ethermint-dev-node:v0.4.3-new-api
ghcr.io/zama-ai/ethermint-dev-node:v0.5.0-1

sleep 10

Expand Down
6 changes: 6 additions & 0 deletions lib/Impl.sol
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ interface FhevmLib {

function fheIfThenElse(uint256 control, uint256 ifTrue, uint256 ifFalse) external pure returns (uint256 result);

function fheArrayEq(uint256[] memory lhs, uint256[] memory rhs) external pure returns (uint256 result);

function fheRand(bytes1 randType) external view returns (uint256 result);

function fheRandBounded(uint256 upperBound, bytes1 randType) external view returns (uint256 result);
Expand Down Expand Up @@ -267,6 +269,10 @@ library Impl {
result = FhevmLib(address(EXT_TFHE_LIBRARY)).fheIfThenElse(control, ifTrue, ifFalse);
}

function eq(uint256[] memory lhs, uint256[] memory rhs) internal pure returns (uint256 result) {
result = FhevmLib(address(EXT_TFHE_LIBRARY)).fheArrayEq(lhs, rhs);
}

function reencrypt(uint256 ciphertext, bytes32 publicKey) internal view returns (bytes memory reencrypted) {
return FhevmLib(address(EXT_TFHE_LIBRARY)).reencrypt(ciphertext, uint256(publicKey));
}
Expand Down
65 changes: 65 additions & 0 deletions lib/TFHE.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5262,6 +5262,71 @@ library TFHE {
return euint64.wrap(Impl.select(ebool.unwrap(control), euint64.unwrap(a), euint64.unwrap(b)));
}

function eq(euint4[] memory a, euint4[] memory b) internal pure returns (ebool) {
require(a.length == b.length, "Both arrays are not of the same size.");
uint256[] memory lhs = new uint256[](a.length);
uint256[] memory rhs = new uint256[](b.length);
for (uint i = 0; i < a.length; i++) {
lhs[i] = euint4.unwrap(a[i]);
}
for (uint i = 0; i < b.length; i++) {
rhs[i] = euint4.unwrap(b[i]);
}
return ebool.wrap(Impl.eq(lhs, rhs));
}

function eq(euint8[] memory a, euint8[] memory b) internal pure returns (ebool) {
require(a.length == b.length, "Both arrays are not of the same size.");
uint256[] memory lhs = new uint256[](a.length);
uint256[] memory rhs = new uint256[](b.length);
for (uint i = 0; i < a.length; i++) {
lhs[i] = euint8.unwrap(a[i]);
}
for (uint i = 0; i < b.length; i++) {
rhs[i] = euint8.unwrap(b[i]);
}
return ebool.wrap(Impl.eq(lhs, rhs));
}

function eq(euint16[] memory a, euint16[] memory b) internal pure returns (ebool) {
require(a.length == b.length, "Both arrays are not of the same size.");
uint256[] memory lhs = new uint256[](a.length);
uint256[] memory rhs = new uint256[](b.length);
for (uint i = 0; i < a.length; i++) {
lhs[i] = euint16.unwrap(a[i]);
}
for (uint i = 0; i < b.length; i++) {
rhs[i] = euint16.unwrap(b[i]);
}
return ebool.wrap(Impl.eq(lhs, rhs));
}

function eq(euint32[] memory a, euint32[] memory b) internal pure returns (ebool) {
require(a.length == b.length, "Both arrays are not of the same size.");
uint256[] memory lhs = new uint256[](a.length);
uint256[] memory rhs = new uint256[](b.length);
for (uint i = 0; i < a.length; i++) {
lhs[i] = euint32.unwrap(a[i]);
}
for (uint i = 0; i < b.length; i++) {
rhs[i] = euint32.unwrap(b[i]);
}
return ebool.wrap(Impl.eq(lhs, rhs));
}

function eq(euint64[] memory a, euint64[] memory b) internal pure returns (ebool) {
require(a.length == b.length, "Both arrays are not of the same size.");
uint256[] memory lhs = new uint256[](a.length);
uint256[] memory rhs = new uint256[](b.length);
for (uint i = 0; i < a.length; i++) {
lhs[i] = euint64.unwrap(a[i]);
}
for (uint i = 0; i < b.length; i++) {
rhs[i] = euint64.unwrap(b[i]);
}
return ebool.wrap(Impl.eq(lhs, rhs));
}

// Cast an encrypted integer from euint8 to euint4.
function asEuint4(euint8 value) internal pure returns (euint4) {
return euint4.wrap(Impl.cast(euint8.unwrap(value), Common.euint4_t));
Expand Down
8 changes: 8 additions & 0 deletions mocks/Impl.sol
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,14 @@ library Impl {
result = (lhs == rhs) ? 1 : 0;
}

function eq(uint256[] memory lhs, uint256[] memory rhs) internal pure returns (uint256 result) {
require(lhs.length == rhs.length, "Both arrays are not of the same size.");
result = 1;
for (uint i = 0; i < lhs.length; i++) {
if (lhs[i] != rhs[i]) return 0;
}
}

function ne(uint256 lhs, uint256 rhs, bool /*scalar*/) internal pure returns (uint256 result) {
result = (lhs != rhs) ? 1 : 0;
}
Expand Down
65 changes: 65 additions & 0 deletions mocks/TFHE.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5262,6 +5262,71 @@ library TFHE {
return euint64.wrap(Impl.select(ebool.unwrap(control), euint64.unwrap(a), euint64.unwrap(b)));
}

function eq(euint4[] memory a, euint4[] memory b) internal pure returns (ebool) {
require(a.length == b.length, "Both arrays are not of the same size.");
uint256[] memory lhs = new uint256[](a.length);
uint256[] memory rhs = new uint256[](b.length);
for (uint i = 0; i < a.length; i++) {
lhs[i] = euint4.unwrap(a[i]);
}
for (uint i = 0; i < b.length; i++) {
rhs[i] = euint4.unwrap(b[i]);
}
return ebool.wrap(Impl.eq(lhs, rhs));
}

function eq(euint8[] memory a, euint8[] memory b) internal pure returns (ebool) {
require(a.length == b.length, "Both arrays are not of the same size.");
uint256[] memory lhs = new uint256[](a.length);
uint256[] memory rhs = new uint256[](b.length);
for (uint i = 0; i < a.length; i++) {
lhs[i] = euint8.unwrap(a[i]);
}
for (uint i = 0; i < b.length; i++) {
rhs[i] = euint8.unwrap(b[i]);
}
return ebool.wrap(Impl.eq(lhs, rhs));
}

function eq(euint16[] memory a, euint16[] memory b) internal pure returns (ebool) {
require(a.length == b.length, "Both arrays are not of the same size.");
uint256[] memory lhs = new uint256[](a.length);
uint256[] memory rhs = new uint256[](b.length);
for (uint i = 0; i < a.length; i++) {
lhs[i] = euint16.unwrap(a[i]);
}
for (uint i = 0; i < b.length; i++) {
rhs[i] = euint16.unwrap(b[i]);
}
return ebool.wrap(Impl.eq(lhs, rhs));
}

function eq(euint32[] memory a, euint32[] memory b) internal pure returns (ebool) {
require(a.length == b.length, "Both arrays are not of the same size.");
uint256[] memory lhs = new uint256[](a.length);
uint256[] memory rhs = new uint256[](b.length);
for (uint i = 0; i < a.length; i++) {
lhs[i] = euint32.unwrap(a[i]);
}
for (uint i = 0; i < b.length; i++) {
rhs[i] = euint32.unwrap(b[i]);
}
return ebool.wrap(Impl.eq(lhs, rhs));
}

function eq(euint64[] memory a, euint64[] memory b) internal pure returns (ebool) {
require(a.length == b.length, "Both arrays are not of the same size.");
uint256[] memory lhs = new uint256[](a.length);
uint256[] memory rhs = new uint256[](b.length);
for (uint i = 0; i < a.length; i++) {
lhs[i] = euint64.unwrap(a[i]);
}
for (uint i = 0; i < b.length; i++) {
rhs[i] = euint64.unwrap(b[i]);
}
return ebool.wrap(Impl.eq(lhs, rhs));
}

// Cast an encrypted integer from euint8 to euint4.
function asEuint4(euint8 value) internal pure returns (euint4) {
return euint4.wrap(Impl.cast(euint8.unwrap(value), Common.euint4_t));
Expand Down
Loading

0 comments on commit fcb40b7

Please sign in to comment.