Skip to content

Commit

Permalink
feat: add support for list-based inputs
Browse files Browse the repository at this point in the history
More info: zama-ai/fhevm-go#120

API changes will be described in the documentation.

More tests and examples are to be done in a future commit.
  • Loading branch information
dartdart26 committed Jun 18, 2024
1 parent b16a842 commit dc96b02
Show file tree
Hide file tree
Showing 29 changed files with 2,508 additions and 1,754 deletions.
75 changes: 52 additions & 23 deletions codegen/templates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ type euint16 is uint256;
type euint32 is uint256;
type euint64 is uint256;
type eaddress is uint256;
type ebytes256 is uint256;
type einput is bytes32;
library Common {
// Values used to communicate types to the runtime.
Expand All @@ -24,6 +26,7 @@ library Common {
uint8 internal constant euint64_t = 5;
uint8 internal constant euint128_t = 6;
uint8 internal constant euint160_t = 7;
uint8 internal constant ebytes256_t = 11;
}
`;
}
Expand Down Expand Up @@ -216,7 +219,7 @@ function fheLibCustomInterfaceFunctions(): string {
return `
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 pure returns (uint256 result);
function verifyCiphertext(bytes32 inputHandle, address callerAddress, bytes memory inputProof, bytes1 inputType) external pure returns (uint256 result);
function cast(uint256 ct, bytes1 toType) external pure returns (uint256 result);
function trivialEncrypt(uint256 ct, bytes1 toType) external pure returns (uint256 result);
function decrypt(uint256 ct) external view returns (uint256 result);
Expand All @@ -236,8 +239,8 @@ function tfheExecutorCustomFunctions(): string {
function fhePubKey(bytes1 fromLib) public view returns (bytes memory result) {
return FhevmLib(address(EXT_TFHE_LIBRARY)).fhePubKey(fromLib);
}
function verifyCiphertext(bytes memory input) public returns (uint256 result) {
result = FhevmLib(address(EXT_TFHE_LIBRARY)).verifyCiphertext(input);
function verifyCiphertext(bytes32 inputHandle, address callerAddress, bytes memory inputProof, bytes1 inputType) public returns (uint256 result) {
result = FhevmLib(address(EXT_TFHE_LIBRARY)).verifyCiphertext(inputHandle, callerAddress, inputProof, inputType);
acl.allowTransient(result, msg.sender);
}
function cast(uint256 ct, bytes1 toType) public returns (uint256 result) {
Expand Down Expand Up @@ -702,8 +705,8 @@ function tfheAsEboolUnaryCast(bits: number): string {
if (bits == 8) {
res.push(`
// Convert a serialized 'ciphertext' to an encrypted euint8 integer.
function asEbool(bytes memory ciphertext) internal returns (ebool) {
return ebool.wrap(Impl.verify(ciphertext, Common.ebool_t));
function asEbool(einput inputHandle, bytes memory inputProof) internal returns (ebool) {
return ebool.wrap(Impl.verify(einput.unwrap(inputHandle), inputProof, Common.ebool_t));
}
// Convert a plaintext value to an encrypted euint8 integer.
Expand Down Expand Up @@ -780,9 +783,9 @@ function tfheUnaryOperators(bits: number, operators: Operator[], signatures: Ove

function tfheCustomUnaryOperators(bits: number, signatures: OverloadSignature[], mocked: boolean): string {
let result = `
// Convert a serialized 'ciphertext' to an encrypted euint${bits} integer.
function asEuint${bits}(bytes memory ciphertext) internal returns (euint${bits}) {
return euint${bits}.wrap(Impl.verify(ciphertext, Common.euint${bits}_t));
// Convert the given inputHandle and inputProof to an encrypted euint${bits} integer.
function asEuint${bits}(einput inputHandle, bytes memory inputProof) internal returns (euint${bits}) {
return euint${bits}.wrap(Impl.verify(einput.unwrap(inputHandle), inputProof, Common.euint${bits}_t));
}
// Convert a plaintext value to an encrypted euint${bits} integer.
Expand Down Expand Up @@ -948,21 +951,30 @@ function tfheCustomMethods(ctx: CodegenContext, mocked: boolean): string {
return Impl.reencrypt(eaddress.unwrap(value), publicKey);
}
// From bytes to eaddress
function asEaddress(bytes memory ciphertext) internal returns (eaddress) {
return eaddress.wrap(Impl.verify(ciphertext, Common.euint160_t));
// Convert the given inputHandle and inputProof to an encrypted eaddress.
function asEaddress(einput inputHandle, bytes memory inputProof) internal returns (eaddress) {
return eaddress.wrap(Impl.verify(einput.unwrap(inputHandle), inputProof, Common.euint160_t));
}
// Convert a plaintext value to an encrypted asEaddress.
function asEaddress(address value) internal returns (eaddress) {
return eaddress.wrap(Impl.trivialEncrypt(uint160(value), Common.euint160_t));
}
// Convert the given inputHandle and inputProof to an encrypted ebytes256 value.
function asEbytes256(einput inputHandle, bytes memory inputProof) internal returns (ebytes256) {
return ebytes256.wrap(Impl.verify(einput.unwrap(inputHandle), inputProof, Common.ebytes256_t));
}
// Return true if the enrypted integer is initialized and false otherwise.
// Return true if the enrypted value is initialized and false otherwise.
function isInitialized(eaddress v) internal pure returns (bool) {
return eaddress.unwrap(v) != 0;
}
// Return true if the enrypted value is initialized and false otherwise.
function isInitialized(ebytes256 v) internal pure returns (bool) {
return ebytes256.unwrap(v) != 0;
}
// Evaluate eq(a, b) and return the result.
function eq(eaddress a, eaddress b) internal returns (ebool) {
Expand Down Expand Up @@ -1021,6 +1033,20 @@ function tfheCustomMethods(ctx: CodegenContext, mocked: boolean): string {
uint256 bProc = uint256(uint160(b));
return ebool.wrap(Impl.ne(eaddress.unwrap(a), bProc, true));
}
// Evaluate eq(a, b) and return the result.
function eq(ebytes256 a, ebytes256 b) internal returns (ebool) {
require(isInitialized(a), "a is uninitialized");
require(isInitialized(b), "b is uninitialized");
return ebool.wrap(Impl.eq(ebytes256.unwrap(a), ebytes256.unwrap(b), false));
}
// Evaluate ne(a, b) and return the result.
function ne(ebytes256 a, ebytes256 b) internal returns (ebool) {
require(isInitialized(a), "a is uninitialized");
require(isInitialized(b), "b is uninitialized");
return ebool.wrap(Impl.ne(ebytes256.unwrap(a), ebytes256.unwrap(b), false));
}
function select(ebool control, eaddress a, eaddress b) internal returns (eaddress) {
return eaddress.wrap(Impl.select(ebool.unwrap(control), eaddress.unwrap(a), eaddress.unwrap(b)));
Expand Down Expand Up @@ -1070,11 +1096,11 @@ function implCustomMethods(ctx: CodegenContext): string {
}
function verify(
bytes memory _ciphertextBytes,
uint8 _toType
bytes32 inputHandle,
bytes memory inputProof,
uint8 toType
) internal returns (uint256 result) {
bytes memory input = bytes.concat(_ciphertextBytes, bytes1(_toType));
result = exec.verifyCiphertext(input);
result = exec.verifyCiphertext(inputHandle, msg.sender, inputProof, bytes1(toType));
acl.allowTransient(result, msg.sender);
}
Expand Down Expand Up @@ -1298,19 +1324,22 @@ library Impl {
key = hex"0123456789ABCDEF";
}
function verify(bytes memory _ciphertextBytes, uint8 /*_toType*/) internal returns (uint256 result) {
function verify(einput inputHandle,
bytes memory inputProof,
uint8 toType) internal returns (uint256 result) {
// TODO: fix implementation
uint256 x;
assembly {
switch gt(mload(_ciphertextBytes), 31)
switch gt(mload(inputProof), 31)
case 1 {
x := mload(add(_ciphertextBytes, add(32, sub(mload(_ciphertextBytes), 32))))
x := mload(add(inputProof, add(32, sub(mload(inputProof), 32))))
}
default {
x := mload(add(_ciphertextBytes, 32))
x := mload(add(inputProof, 32))
}
}
if (_ciphertextBytes.length < 32) {
x = x >> ((32 - _ciphertextBytes.length) * 8);
if (inputProof.length < 32) {
x = x >> ((32 - inputProof.length) * 8);
}
return x;
}
Expand Down
5 changes: 3 additions & 2 deletions codegen/testgen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,7 @@ function signatureContractArguments(s: OverloadSignature): string {
res.push(`${functionTypeToCalldataType(a)} ${String.fromCharCode(argName)}`);
argName++;
});
res.push('bytes calldata inputProof');

return res.join(', ');
}
Expand All @@ -289,7 +290,7 @@ function signatureContractEncryptedSignature(s: OverloadSignature): string {
function castExpressionToType(argExpr: string, outputType: FunctionType): string {
switch (outputType.type) {
case ArgumentType.EUint:
return `TFHE.asEuint${outputType.bits}(${argExpr})`;
return `TFHE.asEuint${outputType.bits}(${argExpr}, inputProof)`;
case ArgumentType.Uint:
return argExpr;
case ArgumentType.Ebool:
Expand All @@ -300,7 +301,7 @@ function castExpressionToType(argExpr: string, outputType: FunctionType): string
function functionTypeToCalldataType(t: FunctionType): string {
switch (t.type) {
case ArgumentType.EUint:
return `bytes calldata`;
return `einput`;
case ArgumentType.Uint:
return getUint(t.bits);
case ArgumentType.Ebool:
Expand Down
4 changes: 2 additions & 2 deletions examples/BlindAuction.sol
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ contract BlindAuction is Reencrypt {
}

// Bid an `encryptedValue`.
function bid(bytes calldata encryptedValue) public onlyBeforeEnd {
euint64 value = TFHE.asEuint64(encryptedValue);
function bid(einput encryptedValue, bytes calldata inputProof) public onlyBeforeEnd {
euint64 value = TFHE.asEuint64(encryptedValue, inputProof);
euint64 existingBid = bids[msg.sender];
if (TFHE.isInitialized(existingBid)) {
euint64 balanceBefore = tokenContract.balanceOfMe();
Expand Down
8 changes: 4 additions & 4 deletions examples/CMUX.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ contract SELECT is Reencrypt {
constructor() {}

// Set result = if control { ifTrue } else { ifFalse }
function select(bytes calldata controlBytes, bytes calldata ifTrueBytes, bytes calldata ifFalseBytes) public {
ebool control = TFHE.asEbool(controlBytes);
euint8 ifTrue = TFHE.asEuint8(ifTrueBytes);
euint8 ifFalse = TFHE.asEuint8(ifFalseBytes);
function select(einput controlBytes, einput ifTrueBytes, einput ifFalseBytes, bytes calldata inputProof) public {
ebool control = TFHE.asEbool(controlBytes, inputProof);
euint8 ifTrue = TFHE.asEuint8(ifTrueBytes, inputProof);
euint8 ifFalse = TFHE.asEuint8(ifFalseBytes, inputProof);
result = TFHE.select(control, ifTrue, ifFalse);
}

Expand Down
12 changes: 6 additions & 6 deletions examples/EncryptedERC20.sol
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ contract EncryptedERC20 is Ownable2Step {
}

// Transfers an encrypted amount from the message sender address to the `to` address.
function transfer(address to, bytes calldata encryptedAmount) public virtual {
transfer(to, TFHE.asEuint64(encryptedAmount));
function transfer(address to, einput amount, bytes calldata inputProof) public virtual {
transfer(to, TFHE.asEuint64(amount, inputProof));
}

// Transfers an amount from the message sender address to the `to` address.
Expand All @@ -74,8 +74,8 @@ contract EncryptedERC20 is Ownable2Step {
}

// Sets the `encryptedAmount` as the allowance of `spender` over the caller's tokens.
function approve(address spender, bytes calldata encryptedAmount) public virtual returns (bool) {
approve(spender, TFHE.asEuint64(encryptedAmount));
function approve(address spender, einput encryptedAmount, bytes calldata inputProof) public virtual returns (bool) {
approve(spender, TFHE.asEuint64(encryptedAmount, inputProof));
return true;
}

Expand All @@ -95,8 +95,8 @@ contract EncryptedERC20 is Ownable2Step {
}

// Transfers `encryptedAmount` tokens using the caller's allowance.
function transferFrom(address from, address to, bytes calldata encryptedAmount) public virtual {
transferFrom(from, to, TFHE.asEuint64(encryptedAmount));
function transferFrom(address from, address to, einput encryptedAmount, bytes calldata inputProof) public virtual {
transferFrom(from, to, TFHE.asEuint64(encryptedAmount, inputProof));
}

// Transfers `amount` tokens using the caller's allowance.
Expand Down
17 changes: 11 additions & 6 deletions examples/Governor/Comp.sol
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,9 @@ contract Comp is Reencrypt {
* @param encryptedAmount The number of tokens that are approved
* @return bool Whether or not the approval succeeded
*/
function approve(address spender, bytes calldata encryptedAmount) external returns (bool) {
function approve(address spender, einput encryptedAmount, bytes calldata inputProof) external returns (bool) {
address owner = msg.sender;
_approve(owner, spender, TFHE.asEuint64(encryptedAmount));
_approve(owner, spender, TFHE.asEuint64(encryptedAmount, inputProof));
return true;
}

Expand Down Expand Up @@ -142,8 +142,8 @@ contract Comp is Reencrypt {
* @param to The address of the destination account
* @param encryptedAmount The number of tokens to transfer
*/
function transfer(address to, bytes calldata encryptedAmount) public {
transfer(to, TFHE.asEuint64(encryptedAmount));
function transfer(address to, einput encryptedAmount, bytes calldata inputProof) public {
transfer(to, TFHE.asEuint64(encryptedAmount, inputProof));
}

/**
Expand All @@ -162,8 +162,13 @@ contract Comp is Reencrypt {
* @param encryptedAmount The number of tokens to transfer
* @return bool Whether or not the transfer succeeded
*/
function transferFrom(address from, address to, bytes calldata encryptedAmount) public returns (bool) {
transferFrom(from, to, TFHE.asEuint64(encryptedAmount));
function transferFrom(
address from,
address to,
einput encryptedAmount,
bytes calldata inputProof
) public returns (bool) {
transferFrom(from, to, TFHE.asEuint64(encryptedAmount, inputProof));
return true;
}

Expand Down
15 changes: 11 additions & 4 deletions examples/Governor/GovernorZama.sol
Original file line number Diff line number Diff line change
Expand Up @@ -337,16 +337,23 @@ contract GovernorZama {
}
}

function castVote(uint proposalId, bytes calldata support) public {
return castVote(proposalId, TFHE.asEbool(support));
function castVote(uint proposalId, einput support, bytes calldata inputProof) public {
return castVote(proposalId, TFHE.asEbool(support, inputProof));
}

function castVote(uint proposalId, ebool support) public {
return _castVote(msg.sender, proposalId, support);
}

function castVoteBySig(uint proposalId, bytes calldata support, uint8 v, bytes32 r, bytes32 s) public {
return castVoteBySig(proposalId, TFHE.asEbool(support), v, r, s);
function castVoteBySig(
uint proposalId,
einput support,
bytes calldata inputProof,
uint8 v,
bytes32 r,
bytes32 s
) public {
return castVoteBySig(proposalId, TFHE.asEbool(support, inputProof), v, r, s);
}

function castVoteBySig(uint proposalId, ebool support, uint8 v, bytes32 r, bytes32 s) public {
Expand Down
9 changes: 7 additions & 2 deletions examples/Identity/IdentityRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,13 @@ contract IdentityRegistry is Reencrypt, Ownable2Step {
}

// Set user's identifiers
function setIdentifier(address wallet, string calldata identifier, bytes calldata encryptedValue) public {
euint64 value = TFHE.asEuint64(encryptedValue);
function setIdentifier(
address wallet,
string calldata identifier,
einput encryptedValue,
bytes calldata inputProof
) public {
euint64 value = TFHE.asEuint64(encryptedValue, inputProof);
setIdentifier(wallet, identifier, value);
}

Expand Down
Loading

0 comments on commit dc96b02

Please sign in to comment.