Files
fhevm-solidity/lib/Impl.sol
Petar Ivanov fe1e97065e feat: add upper bound for encrypted random integers
Add overloads of TFHE.randEuint() methods that accept an upper bound,
e.g. TFHE.randEuint32(uint32 upperBound). The returned integer will be
in the [0, upperBound) range. Moreover, upperBound must be a power of
2 - that is a design choice for performance reasons.

Note that, as of now, the random numbers are generated in the plain and
are completely public and predictable. An FHE version is coming soon.

Add tests to cover above functionality.
2024-01-09 17:17:40 +02:00

295 lines
11 KiB
Solidity

// SPDX-License-Identifier: BSD-3-Clause-Clear
pragma solidity ^0.8.20;
import "./TFHE.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 fheRem(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 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 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);
function fheIfThenElse(uint256 control, uint256 ifTrue, uint256 ifFalse) 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);
}
address constant EXT_TFHE_LIBRARY = address(0x000000000000000000000000000000000000005d);
library Impl {
// 32 bytes for the 'byte' type header + 48 bytes for the NaCl anonymous
// box overhead + 4 bytes for the plaintext value.
uint256 constant reencryptedSize = 32 + 48 + 4;
// 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 pure returns (uint256 result) {
bytes1 scalarByte;
if (scalar) {
scalarByte = 0x01;
} else {
scalarByte = 0x00;
}
result = FhevmLib(address(EXT_TFHE_LIBRARY)).fheAdd(lhs, rhs, scalarByte);
}
function sub(uint256 lhs, uint256 rhs, bool scalar) internal pure returns (uint256 result) {
bytes1 scalarByte;
if (scalar) {
scalarByte = 0x01;
} else {
scalarByte = 0x00;
}
result = FhevmLib(address(EXT_TFHE_LIBRARY)).fheSub(lhs, rhs, scalarByte);
}
function mul(uint256 lhs, uint256 rhs, bool scalar) internal pure returns (uint256 result) {
bytes1 scalarByte;
if (scalar) {
scalarByte = 0x01;
} else {
scalarByte = 0x00;
}
result = FhevmLib(address(EXT_TFHE_LIBRARY)).fheMul(lhs, rhs, scalarByte);
}
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 rem(uint256 lhs, uint256 rhs) internal pure returns (uint256 result) {
bytes1 scalarByte = 0x01;
result = FhevmLib(address(EXT_TFHE_LIBRARY)).fheRem(lhs, rhs, scalarByte);
}
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 pure returns (uint256 result) {
bytes1 scalarByte = 0x00;
result = FhevmLib(address(EXT_TFHE_LIBRARY)).fheBitOr(lhs, rhs, scalarByte);
}
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 pure returns (uint256 result) {
bytes1 scalarByte;
if (scalar) {
scalarByte = 0x01;
} else {
scalarByte = 0x00;
}
result = FhevmLib(address(EXT_TFHE_LIBRARY)).fheShl(lhs, rhs, scalarByte);
}
function shr(uint256 lhs, uint256 rhs, bool scalar) internal pure returns (uint256 result) {
bytes1 scalarByte;
if (scalar) {
scalarByte = 0x01;
} else {
scalarByte = 0x00;
}
result = FhevmLib(address(EXT_TFHE_LIBRARY)).fheShr(lhs, rhs, scalarByte);
}
function eq(uint256 lhs, uint256 rhs, bool scalar) internal pure returns (uint256 result) {
bytes1 scalarByte;
if (scalar) {
scalarByte = 0x01;
} else {
scalarByte = 0x00;
}
result = FhevmLib(address(EXT_TFHE_LIBRARY)).fheEq(lhs, rhs, scalarByte);
}
function ne(uint256 lhs, uint256 rhs, bool scalar) internal pure returns (uint256 result) {
bytes1 scalarByte;
if (scalar) {
scalarByte = 0x01;
} else {
scalarByte = 0x00;
}
result = FhevmLib(address(EXT_TFHE_LIBRARY)).fheNe(lhs, rhs, scalarByte);
}
function ge(uint256 lhs, uint256 rhs, bool scalar) internal pure returns (uint256 result) {
bytes1 scalarByte;
if (scalar) {
scalarByte = 0x01;
} else {
scalarByte = 0x00;
}
result = FhevmLib(address(EXT_TFHE_LIBRARY)).fheGe(lhs, rhs, scalarByte);
}
function gt(uint256 lhs, uint256 rhs, bool scalar) internal pure returns (uint256 result) {
bytes1 scalarByte;
if (scalar) {
scalarByte = 0x01;
} else {
scalarByte = 0x00;
}
result = FhevmLib(address(EXT_TFHE_LIBRARY)).fheGt(lhs, rhs, scalarByte);
}
function le(uint256 lhs, uint256 rhs, bool scalar) internal pure returns (uint256 result) {
bytes1 scalarByte;
if (scalar) {
scalarByte = 0x01;
} else {
scalarByte = 0x00;
}
result = FhevmLib(address(EXT_TFHE_LIBRARY)).fheLe(lhs, rhs, scalarByte);
}
function lt(uint256 lhs, uint256 rhs, bool scalar) internal pure returns (uint256 result) {
bytes1 scalarByte;
if (scalar) {
scalarByte = 0x01;
} else {
scalarByte = 0x00;
}
result = FhevmLib(address(EXT_TFHE_LIBRARY)).fheLt(lhs, rhs, scalarByte);
}
function min(uint256 lhs, uint256 rhs, bool scalar) internal pure returns (uint256 result) {
bytes1 scalarByte;
if (scalar) {
scalarByte = 0x01;
} else {
scalarByte = 0x00;
}
result = FhevmLib(address(EXT_TFHE_LIBRARY)).fheMin(lhs, rhs, scalarByte);
}
function max(uint256 lhs, uint256 rhs, bool scalar) internal pure returns (uint256 result) {
bytes1 scalarByte;
if (scalar) {
scalarByte = 0x01;
} else {
scalarByte = 0x00;
}
result = FhevmLib(address(EXT_TFHE_LIBRARY)).fheMax(lhs, rhs, scalarByte);
}
function neg(uint256 ct) internal pure returns (uint256 result) {
result = FhevmLib(address(EXT_TFHE_LIBRARY)).fheNeg(ct);
}
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 pure returns (uint256 result) {
result = FhevmLib(address(EXT_TFHE_LIBRARY)).fheIfThenElse(control, ifTrue, ifFalse);
}
// We do assembly here because ordinary call will emit extcodesize check which is zero for our precompiles
// and revert the transaction because we don't return any data for this precompile method
function optReq(uint256 ciphertext) internal view {
bytes memory input = abi.encodeWithSignature("optimisticRequire(uint256)", ciphertext);
uint256 inputLen = input.length;
// Call the optimistic require method in precompile.
address precompile = EXT_TFHE_LIBRARY;
assembly {
if iszero(staticcall(gas(), precompile, add(input, 32), inputLen, 0, 0)) {
revert(0, 0)
}
}
}
function reencrypt(uint256 ciphertext, bytes32 publicKey) internal view returns (bytes memory reencrypted) {
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.
key = FhevmLib(address(EXT_TFHE_LIBRARY)).fhePubKey(bytes1(0x01));
}
function verify(bytes memory _ciphertextBytes, uint8 _toType) internal pure returns (uint256 result) {
bytes memory input = bytes.concat(_ciphertextBytes, bytes1(_toType));
result = FhevmLib(address(EXT_TFHE_LIBRARY)).verifyCiphertext(input);
}
function cast(uint256 ciphertext, uint8 toType) internal pure returns (uint256 result) {
result = FhevmLib(address(EXT_TFHE_LIBRARY)).cast(ciphertext, bytes1(toType));
}
function trivialEncrypt(uint256 value, uint8 toType) internal pure returns (uint256 result) {
result = FhevmLib(address(EXT_TFHE_LIBRARY)).trivialEncrypt(value, bytes1(toType));
}
function decrypt(uint256 ciphertext) internal view returns (uint256 result) {
result = FhevmLib(address(EXT_TFHE_LIBRARY)).decrypt(ciphertext);
}
function rand(uint8 randType) internal view returns (uint256 result) {
result = FhevmLib(address(EXT_TFHE_LIBRARY)).fheRand(bytes1(randType));
}
function randBounded(uint256 upperBound, uint8 randType) internal view returns (uint256 result) {
result = FhevmLib(address(EXT_TFHE_LIBRARY)).fheRandBounded(upperBound, bytes1(randType));
}
}