Skip to content

Commit ca9e6eb

Browse files
authored
feat(library-solidity): support uninitialized handles in FHE.fromExte… (#1969)
* feat(library-solidity): support uninitialized handles in FHE.fromExternal * chore(library-solidity): fix typo
1 parent 6588055 commit ca9e6eb

File tree

5 files changed

+158
-15
lines changed

5 files changed

+158
-15
lines changed

host-contracts/lib/FHE.sol

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8453,6 +8453,10 @@ library FHE {
84538453
return ebool.wrap(Impl.verify(externalEbool.unwrap(inputHandle), inputProof, FheType.Bool));
84548454
} else {
84558455
bytes32 inputBytes32 = externalEbool.unwrap(inputHandle);
8456+
if (inputBytes32 == 0) {
8457+
inputBytes32 = Impl.trivialEncrypt(0, FheType.Bool);
8458+
return ebool.wrap(inputBytes32);
8459+
}
84568460
if (!Impl.isAllowed(inputBytes32, msg.sender)) revert SenderNotAllowedToUseHandle(inputBytes32, msg.sender);
84578461
return ebool.wrap(inputBytes32);
84588462
}
@@ -8476,13 +8480,17 @@ library FHE {
84768480
return euint8.wrap(Impl.verify(externalEuint8.unwrap(inputHandle), inputProof, FheType.Uint8));
84778481
} else {
84788482
bytes32 inputBytes32 = externalEuint8.unwrap(inputHandle);
8483+
if (inputBytes32 == 0) {
8484+
inputBytes32 = Impl.trivialEncrypt(0, FheType.Uint8);
8485+
return euint8.wrap(inputBytes32);
8486+
}
84798487
if (!Impl.isAllowed(inputBytes32, msg.sender)) revert SenderNotAllowedToUseHandle(inputBytes32, msg.sender);
84808488
return euint8.wrap(inputBytes32);
84818489
}
84828490
}
84838491

84848492
/**
8485-
* @dev Convert a plaintext value to an encrypted euint8 integer.
8493+
* @dev Convert a plaintext value to an encrypted euint8 value.
84868494
*/
84878495
function asEuint8(uint8 value) internal returns (euint8) {
84888496
return euint8.wrap(Impl.trivialEncrypt(uint256(value), FheType.Uint8));
@@ -8499,13 +8507,17 @@ library FHE {
84998507
return euint16.wrap(Impl.verify(externalEuint16.unwrap(inputHandle), inputProof, FheType.Uint16));
85008508
} else {
85018509
bytes32 inputBytes32 = externalEuint16.unwrap(inputHandle);
8510+
if (inputBytes32 == 0) {
8511+
inputBytes32 = Impl.trivialEncrypt(0, FheType.Uint16);
8512+
return euint16.wrap(inputBytes32);
8513+
}
85028514
if (!Impl.isAllowed(inputBytes32, msg.sender)) revert SenderNotAllowedToUseHandle(inputBytes32, msg.sender);
85038515
return euint16.wrap(inputBytes32);
85048516
}
85058517
}
85068518

85078519
/**
8508-
* @dev Convert a plaintext value to an encrypted euint16 integer.
8520+
* @dev Convert a plaintext value to an encrypted euint16 value.
85098521
*/
85108522
function asEuint16(uint16 value) internal returns (euint16) {
85118523
return euint16.wrap(Impl.trivialEncrypt(uint256(value), FheType.Uint16));
@@ -8522,13 +8534,17 @@ library FHE {
85228534
return euint32.wrap(Impl.verify(externalEuint32.unwrap(inputHandle), inputProof, FheType.Uint32));
85238535
} else {
85248536
bytes32 inputBytes32 = externalEuint32.unwrap(inputHandle);
8537+
if (inputBytes32 == 0) {
8538+
inputBytes32 = Impl.trivialEncrypt(0, FheType.Uint32);
8539+
return euint32.wrap(inputBytes32);
8540+
}
85258541
if (!Impl.isAllowed(inputBytes32, msg.sender)) revert SenderNotAllowedToUseHandle(inputBytes32, msg.sender);
85268542
return euint32.wrap(inputBytes32);
85278543
}
85288544
}
85298545

85308546
/**
8531-
* @dev Convert a plaintext value to an encrypted euint32 integer.
8547+
* @dev Convert a plaintext value to an encrypted euint32 value.
85328548
*/
85338549
function asEuint32(uint32 value) internal returns (euint32) {
85348550
return euint32.wrap(Impl.trivialEncrypt(uint256(value), FheType.Uint32));
@@ -8545,13 +8561,17 @@ library FHE {
85458561
return euint64.wrap(Impl.verify(externalEuint64.unwrap(inputHandle), inputProof, FheType.Uint64));
85468562
} else {
85478563
bytes32 inputBytes32 = externalEuint64.unwrap(inputHandle);
8564+
if (inputBytes32 == 0) {
8565+
inputBytes32 = Impl.trivialEncrypt(0, FheType.Uint64);
8566+
return euint64.wrap(inputBytes32);
8567+
}
85488568
if (!Impl.isAllowed(inputBytes32, msg.sender)) revert SenderNotAllowedToUseHandle(inputBytes32, msg.sender);
85498569
return euint64.wrap(inputBytes32);
85508570
}
85518571
}
85528572

85538573
/**
8554-
* @dev Convert a plaintext value to an encrypted euint64 integer.
8574+
* @dev Convert a plaintext value to an encrypted euint64 value.
85558575
*/
85568576
function asEuint64(uint64 value) internal returns (euint64) {
85578577
return euint64.wrap(Impl.trivialEncrypt(uint256(value), FheType.Uint64));
@@ -8568,13 +8588,17 @@ library FHE {
85688588
return euint128.wrap(Impl.verify(externalEuint128.unwrap(inputHandle), inputProof, FheType.Uint128));
85698589
} else {
85708590
bytes32 inputBytes32 = externalEuint128.unwrap(inputHandle);
8591+
if (inputBytes32 == 0) {
8592+
inputBytes32 = Impl.trivialEncrypt(0, FheType.Uint128);
8593+
return euint128.wrap(inputBytes32);
8594+
}
85718595
if (!Impl.isAllowed(inputBytes32, msg.sender)) revert SenderNotAllowedToUseHandle(inputBytes32, msg.sender);
85728596
return euint128.wrap(inputBytes32);
85738597
}
85748598
}
85758599

85768600
/**
8577-
* @dev Convert a plaintext value to an encrypted euint128 integer.
8601+
* @dev Convert a plaintext value to an encrypted euint128 value.
85788602
*/
85798603
function asEuint128(uint128 value) internal returns (euint128) {
85808604
return euint128.wrap(Impl.trivialEncrypt(uint256(value), FheType.Uint128));
@@ -8591,13 +8615,17 @@ library FHE {
85918615
return eaddress.wrap(Impl.verify(externalEaddress.unwrap(inputHandle), inputProof, FheType.Uint160));
85928616
} else {
85938617
bytes32 inputBytes32 = externalEaddress.unwrap(inputHandle);
8618+
if (inputBytes32 == 0) {
8619+
inputBytes32 = Impl.trivialEncrypt(0, FheType.Uint160);
8620+
return eaddress.wrap(inputBytes32);
8621+
}
85948622
if (!Impl.isAllowed(inputBytes32, msg.sender)) revert SenderNotAllowedToUseHandle(inputBytes32, msg.sender);
85958623
return eaddress.wrap(inputBytes32);
85968624
}
85978625
}
85988626

85998627
/**
8600-
* @dev Convert a plaintext value to an encrypted eaddress integer.
8628+
* @dev Convert a plaintext value to an encrypted eaddress value.
86018629
*/
86028630
function asEaddress(address value) internal returns (eaddress) {
86038631
return eaddress.wrap(Impl.trivialEncrypt(uint256(uint160(value)), FheType.Uint160));
@@ -8614,13 +8642,17 @@ library FHE {
86148642
return euint256.wrap(Impl.verify(externalEuint256.unwrap(inputHandle), inputProof, FheType.Uint256));
86158643
} else {
86168644
bytes32 inputBytes32 = externalEuint256.unwrap(inputHandle);
8645+
if (inputBytes32 == 0) {
8646+
inputBytes32 = Impl.trivialEncrypt(0, FheType.Uint256);
8647+
return euint256.wrap(inputBytes32);
8648+
}
86178649
if (!Impl.isAllowed(inputBytes32, msg.sender)) revert SenderNotAllowedToUseHandle(inputBytes32, msg.sender);
86188650
return euint256.wrap(inputBytes32);
86198651
}
86208652
}
86218653

86228654
/**
8623-
* @dev Convert a plaintext value to an encrypted euint256 integer.
8655+
* @dev Convert a plaintext value to an encrypted euint256 value.
86248656
*/
86258657
function asEuint256(uint256 value) internal returns (euint256) {
86268658
return euint256.wrap(Impl.trivialEncrypt(uint256(value), FheType.Uint256));

library-solidity/codegen/src/templateFHEDotSol.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -546,6 +546,10 @@ function handleSolidityTFHEConvertPlaintextAndEinputToRespectiveType(fheType: Ad
546546
return e${fheType.type.toLowerCase()}.wrap(Impl.verify(externalE${fheType.type.toLowerCase()}.unwrap(inputHandle), inputProof, FheType.${fheType.isAlias ? fheType.aliasType : fheType.type}));
547547
} else {
548548
bytes32 inputBytes32 = externalE${fheType.type.toLowerCase()}.unwrap(inputHandle);
549+
if(inputBytes32 == 0){
550+
inputBytes32 = Impl.trivialEncrypt(0, FheType.${fheType.isAlias ? fheType.aliasType : fheType.type});
551+
return e${fheType.type.toLowerCase()}.wrap(inputBytes32);
552+
}
549553
if (!Impl.isAllowed(inputBytes32, msg.sender)) revert SenderNotAllowedToUseHandle(inputBytes32, msg.sender);
550554
return e${fheType.type.toLowerCase()}.wrap(inputBytes32);
551555
}
@@ -572,7 +576,7 @@ function handleSolidityTFHEConvertPlaintextAndEinputToRespectiveType(fheType: Ad
572576

573577
result += `
574578
/**
575-
* @dev Convert a plaintext value to an encrypted e${fheType.type.toLowerCase()} integer.
579+
* @dev Convert a plaintext value to an encrypted e${fheType.type.toLowerCase()} value.
576580
*/
577581
function asE${fheType.type.toLowerCase()}(${fheType.clearMatchingType} value) internal returns (e${fheType.type.toLowerCase()}) {
578582
return e${fheType.type.toLowerCase()}.wrap(Impl.trivialEncrypt(uint256(${value}), FheType.${fheType.isAlias ? fheType.aliasType : fheType.type}));

library-solidity/examples/multisig/SimpleMultiSig.sol

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
// SPDX-License-Identifier: BSD-3-Clause-Clear
22
pragma solidity ^0.8.24;
3+
import "../../lib/FHE.sol";
4+
import {CoprocessorSetup} from "../CoprocessorSetup.sol";
5+
6+
interface IEncryptedSetter {
7+
function setEncryptedValue(externalEuint64 inputHandle, bytes memory inputProof) external;
8+
}
39

410
/// @notice Simple MultiSig contract, where all owners must approve a tx before executing it
511
contract SimpleMultiSig {
@@ -16,8 +22,10 @@ contract SimpleMultiSig {
1622
mapping(uint256 => Transaction) public transactions;
1723
mapping(uint256 => mapping(address => bool)) public isApprovedByOwner;
1824
mapping(uint256 => bool) public executed;
25+
euint64 public uninitializedHandle; // To be used in the uninitialized handle in fromExternal test
1926

2027
constructor(address[] memory _owners) {
28+
FHE.setCoprocessor(CoprocessorSetup.defaultConfig());
2129
uint256 length = _owners.length;
2230
require(length > 1, "Multisig should have several owners");
2331
for (uint256 i; i < length; i++) {
@@ -56,6 +64,15 @@ contract SimpleMultiSig {
5664
executed[txId] = true;
5765
}
5866

67+
function executeSpecialTx(address encryptedSetter) external {
68+
// this function is just for testing the edge case of an uninitialized external handle without inputProof
69+
FHE.allowTransient(uninitializedHandle, encryptedSetter); // this line is not strictly needed in the edge case of uninitialized handle, but we keep it as best practice.
70+
IEncryptedSetter(encryptedSetter).setEncryptedValue(
71+
externalEuint64.wrap(euint64.unwrap(uninitializedHandle)),
72+
hex""
73+
);
74+
}
75+
5976
function getOwners() external view returns (address[] memory) {
6077
return owners;
6178
}

library-solidity/lib/FHE.sol

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8453,6 +8453,10 @@ library FHE {
84538453
return ebool.wrap(Impl.verify(externalEbool.unwrap(inputHandle), inputProof, FheType.Bool));
84548454
} else {
84558455
bytes32 inputBytes32 = externalEbool.unwrap(inputHandle);
8456+
if (inputBytes32 == 0) {
8457+
inputBytes32 = Impl.trivialEncrypt(0, FheType.Bool);
8458+
return ebool.wrap(inputBytes32);
8459+
}
84568460
if (!Impl.isAllowed(inputBytes32, msg.sender)) revert SenderNotAllowedToUseHandle(inputBytes32, msg.sender);
84578461
return ebool.wrap(inputBytes32);
84588462
}
@@ -8476,13 +8480,17 @@ library FHE {
84768480
return euint8.wrap(Impl.verify(externalEuint8.unwrap(inputHandle), inputProof, FheType.Uint8));
84778481
} else {
84788482
bytes32 inputBytes32 = externalEuint8.unwrap(inputHandle);
8483+
if (inputBytes32 == 0) {
8484+
inputBytes32 = Impl.trivialEncrypt(0, FheType.Uint8);
8485+
return euint8.wrap(inputBytes32);
8486+
}
84798487
if (!Impl.isAllowed(inputBytes32, msg.sender)) revert SenderNotAllowedToUseHandle(inputBytes32, msg.sender);
84808488
return euint8.wrap(inputBytes32);
84818489
}
84828490
}
84838491

84848492
/**
8485-
* @dev Convert a plaintext value to an encrypted euint8 integer.
8493+
* @dev Convert a plaintext value to an encrypted euint8 value.
84868494
*/
84878495
function asEuint8(uint8 value) internal returns (euint8) {
84888496
return euint8.wrap(Impl.trivialEncrypt(uint256(value), FheType.Uint8));
@@ -8499,13 +8507,17 @@ library FHE {
84998507
return euint16.wrap(Impl.verify(externalEuint16.unwrap(inputHandle), inputProof, FheType.Uint16));
85008508
} else {
85018509
bytes32 inputBytes32 = externalEuint16.unwrap(inputHandle);
8510+
if (inputBytes32 == 0) {
8511+
inputBytes32 = Impl.trivialEncrypt(0, FheType.Uint16);
8512+
return euint16.wrap(inputBytes32);
8513+
}
85028514
if (!Impl.isAllowed(inputBytes32, msg.sender)) revert SenderNotAllowedToUseHandle(inputBytes32, msg.sender);
85038515
return euint16.wrap(inputBytes32);
85048516
}
85058517
}
85068518

85078519
/**
8508-
* @dev Convert a plaintext value to an encrypted euint16 integer.
8520+
* @dev Convert a plaintext value to an encrypted euint16 value.
85098521
*/
85108522
function asEuint16(uint16 value) internal returns (euint16) {
85118523
return euint16.wrap(Impl.trivialEncrypt(uint256(value), FheType.Uint16));
@@ -8522,13 +8534,17 @@ library FHE {
85228534
return euint32.wrap(Impl.verify(externalEuint32.unwrap(inputHandle), inputProof, FheType.Uint32));
85238535
} else {
85248536
bytes32 inputBytes32 = externalEuint32.unwrap(inputHandle);
8537+
if (inputBytes32 == 0) {
8538+
inputBytes32 = Impl.trivialEncrypt(0, FheType.Uint32);
8539+
return euint32.wrap(inputBytes32);
8540+
}
85258541
if (!Impl.isAllowed(inputBytes32, msg.sender)) revert SenderNotAllowedToUseHandle(inputBytes32, msg.sender);
85268542
return euint32.wrap(inputBytes32);
85278543
}
85288544
}
85298545

85308546
/**
8531-
* @dev Convert a plaintext value to an encrypted euint32 integer.
8547+
* @dev Convert a plaintext value to an encrypted euint32 value.
85328548
*/
85338549
function asEuint32(uint32 value) internal returns (euint32) {
85348550
return euint32.wrap(Impl.trivialEncrypt(uint256(value), FheType.Uint32));
@@ -8545,13 +8561,17 @@ library FHE {
85458561
return euint64.wrap(Impl.verify(externalEuint64.unwrap(inputHandle), inputProof, FheType.Uint64));
85468562
} else {
85478563
bytes32 inputBytes32 = externalEuint64.unwrap(inputHandle);
8564+
if (inputBytes32 == 0) {
8565+
inputBytes32 = Impl.trivialEncrypt(0, FheType.Uint64);
8566+
return euint64.wrap(inputBytes32);
8567+
}
85488568
if (!Impl.isAllowed(inputBytes32, msg.sender)) revert SenderNotAllowedToUseHandle(inputBytes32, msg.sender);
85498569
return euint64.wrap(inputBytes32);
85508570
}
85518571
}
85528572

85538573
/**
8554-
* @dev Convert a plaintext value to an encrypted euint64 integer.
8574+
* @dev Convert a plaintext value to an encrypted euint64 value.
85558575
*/
85568576
function asEuint64(uint64 value) internal returns (euint64) {
85578577
return euint64.wrap(Impl.trivialEncrypt(uint256(value), FheType.Uint64));
@@ -8568,13 +8588,17 @@ library FHE {
85688588
return euint128.wrap(Impl.verify(externalEuint128.unwrap(inputHandle), inputProof, FheType.Uint128));
85698589
} else {
85708590
bytes32 inputBytes32 = externalEuint128.unwrap(inputHandle);
8591+
if (inputBytes32 == 0) {
8592+
inputBytes32 = Impl.trivialEncrypt(0, FheType.Uint128);
8593+
return euint128.wrap(inputBytes32);
8594+
}
85718595
if (!Impl.isAllowed(inputBytes32, msg.sender)) revert SenderNotAllowedToUseHandle(inputBytes32, msg.sender);
85728596
return euint128.wrap(inputBytes32);
85738597
}
85748598
}
85758599

85768600
/**
8577-
* @dev Convert a plaintext value to an encrypted euint128 integer.
8601+
* @dev Convert a plaintext value to an encrypted euint128 value.
85788602
*/
85798603
function asEuint128(uint128 value) internal returns (euint128) {
85808604
return euint128.wrap(Impl.trivialEncrypt(uint256(value), FheType.Uint128));
@@ -8591,13 +8615,17 @@ library FHE {
85918615
return eaddress.wrap(Impl.verify(externalEaddress.unwrap(inputHandle), inputProof, FheType.Uint160));
85928616
} else {
85938617
bytes32 inputBytes32 = externalEaddress.unwrap(inputHandle);
8618+
if (inputBytes32 == 0) {
8619+
inputBytes32 = Impl.trivialEncrypt(0, FheType.Uint160);
8620+
return eaddress.wrap(inputBytes32);
8621+
}
85948622
if (!Impl.isAllowed(inputBytes32, msg.sender)) revert SenderNotAllowedToUseHandle(inputBytes32, msg.sender);
85958623
return eaddress.wrap(inputBytes32);
85968624
}
85978625
}
85988626

85998627
/**
8600-
* @dev Convert a plaintext value to an encrypted eaddress integer.
8628+
* @dev Convert a plaintext value to an encrypted eaddress value.
86018629
*/
86028630
function asEaddress(address value) internal returns (eaddress) {
86038631
return eaddress.wrap(Impl.trivialEncrypt(uint256(uint160(value)), FheType.Uint160));
@@ -8614,13 +8642,17 @@ library FHE {
86148642
return euint256.wrap(Impl.verify(externalEuint256.unwrap(inputHandle), inputProof, FheType.Uint256));
86158643
} else {
86168644
bytes32 inputBytes32 = externalEuint256.unwrap(inputHandle);
8645+
if (inputBytes32 == 0) {
8646+
inputBytes32 = Impl.trivialEncrypt(0, FheType.Uint256);
8647+
return euint256.wrap(inputBytes32);
8648+
}
86178649
if (!Impl.isAllowed(inputBytes32, msg.sender)) revert SenderNotAllowedToUseHandle(inputBytes32, msg.sender);
86188650
return euint256.wrap(inputBytes32);
86198651
}
86208652
}
86218653

86228654
/**
8623-
* @dev Convert a plaintext value to an encrypted euint256 integer.
8655+
* @dev Convert a plaintext value to an encrypted euint256 value.
86248656
*/
86258657
function asEuint256(uint256 value) internal returns (euint256) {
86268658
return euint256.wrap(Impl.trivialEncrypt(uint256(value), FheType.Uint256));

0 commit comments

Comments
 (0)