mirror of
https://github.com/zama-ai/fhevm-solidity.git
synced 2026-01-10 04:57:59 -05:00
feat: made all fhevm contracts UUPS
chore: fixed prettier fix: typo in address starting with 0 fix: get correct value of counter when doing snapshot
This commit is contained in:
@@ -28,13 +28,9 @@ class CustomProvider extends ProviderWrapper {
|
||||
this.lastBlockSnapshotForDecrypt = parseInt(blockNumberHex);
|
||||
const parsedEnvCoprocessor = dotenv.parse(fs.readFileSync('lib/.env.exec'));
|
||||
const coprocAdd = parsedEnvCoprocessor.TFHE_EXECUTOR_CONTRACT_ADDRESS;
|
||||
const callData = {
|
||||
to: coprocAdd,
|
||||
data: '0x1f20d85c',
|
||||
};
|
||||
this.lastCounterRand = await this._wrappedProvider.request({
|
||||
method: 'eth_call',
|
||||
params: [callData, 'latest'],
|
||||
method: 'eth_getStorageAt',
|
||||
params: [coprocAdd, '0xa436a06f0efce5ea38c956a21e24202a59b3b746d48a23fb52b4a5bc33fe3e00', 'latest'],
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -14,8 +14,9 @@ export function generateFHEPayment(priceData: PriceData): string {
|
||||
|
||||
import "./TFHEExecutorAddress.sol";
|
||||
import "@openzeppelin/contracts/utils/Strings.sol";
|
||||
import "@openzeppelin/contracts/access/Ownable2Step.sol";
|
||||
|
||||
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
|
||||
import "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol";
|
||||
|
||||
error FHEGasBlockLimitExceeded();
|
||||
error CallerMustBeTFHEExecutorContract();
|
||||
error OnlyScalarOperationsAreSupported();
|
||||
@@ -28,7 +29,7 @@ export function generateFHEPayment(priceData: PriceData): string {
|
||||
error AllContractsNotAuthorized();
|
||||
error ContractNotWhitelisted();
|
||||
|
||||
contract FHEPayment is Ownable2Step {
|
||||
contract FHEPayment is UUPSUpgradeable, Ownable2StepUpgradeable {
|
||||
/// @notice Name of the contract
|
||||
string private constant CONTRACT_NAME = "FHEPayment";
|
||||
|
||||
@@ -36,135 +37,183 @@ export function generateFHEPayment(priceData: PriceData): string {
|
||||
uint256 private constant MAJOR_VERSION = 0;
|
||||
uint256 private constant MINOR_VERSION = 1;
|
||||
uint256 private constant PATCH_VERSION = 0;
|
||||
address public immutable tfheExecutorAddress = tfheExecutorAdd;
|
||||
address public constant tfheExecutorAddress = tfheExecutorAdd;
|
||||
|
||||
uint256 private constant FHE_GAS_BLOCKLIMIT = 10_000_000;
|
||||
uint256 private constant MIN_FHE_GASPRICE = 10_000_000; // minimum of 0.01 Gwei
|
||||
uint256 private constant FHE_GASPRICE_NATIVE_RATIO = 1000; // fhe gas price is set to 0.1% of native gas price (if above minimum)
|
||||
|
||||
uint256 private lastBlock;
|
||||
uint256 private currentBlockConsumption;
|
||||
uint256 public claimableUsedFHEGas;
|
||||
|
||||
mapping(address payer => uint256 depositedAmount) private depositsETH;
|
||||
mapping(address user => bool allowedAllContracts) private allowedAll;
|
||||
mapping(address user => mapping(address dappContract => bool isWhitelisted)) private whitelistedDapps;
|
||||
|
||||
constructor() Ownable(msg.sender) {}
|
||||
|
||||
function recoverBurntFunds(address receiver) external onlyOwner {
|
||||
uint256 claimableUsedFHEGas_ = claimableUsedFHEGas;
|
||||
claimableUsedFHEGas = 0;
|
||||
(bool success, ) = receiver.call{value: claimableUsedFHEGas_}("");
|
||||
if(!success) revert RecoveryFailed();
|
||||
/// @custom:storage-location erc7201:fhevm.storage.FHEPayment
|
||||
struct FHEPaymentStorage {
|
||||
uint256 lastBlock;
|
||||
uint256 currentBlockConsumption;
|
||||
uint256 claimableUsedFHEGas;
|
||||
mapping(address payer => uint256 depositedAmount) depositsETH;
|
||||
mapping(address user => bool allowedAllContracts) allowedAll;
|
||||
mapping(address user => mapping(address dappContract => bool isWhitelisted)) whitelistedDapps;
|
||||
}
|
||||
|
||||
function depositETH(address account) external payable {
|
||||
depositsETH[account] += msg.value;
|
||||
// keccak256(abi.encode(uint256(keccak256("fhevm.storage.FHEPayment")) - 1)) & ~bytes32(uint256(0xff))
|
||||
bytes32 private constant FHEPaymentStorageLocation =
|
||||
0x4c5af501c90907b9fb888b6dd79405547def38a1dc3110f42d77f5dbc3222e00;
|
||||
|
||||
function _getFHEPaymentStorage() internal pure returns (FHEPaymentStorage storage $) {
|
||||
assembly {
|
||||
$.slot := FHEPaymentStorageLocation
|
||||
}
|
||||
}
|
||||
|
||||
function withdrawETH(uint256 amount, address receiver) external {
|
||||
depositsETH[msg.sender] -= amount;
|
||||
function getTFHEExecutorAddress() public view virtual returns (address) {
|
||||
return tfheExecutorAddress;
|
||||
}
|
||||
|
||||
function getClaimableUsedFHEGas() public view virtual returns (uint256) {
|
||||
FHEPaymentStorage storage $ = _getFHEPaymentStorage();
|
||||
return $.claimableUsedFHEGas;
|
||||
}
|
||||
|
||||
function _authorizeUpgrade(address _newImplementation) internal virtual override onlyOwner {}
|
||||
|
||||
/// @custom:oz-upgrades-unsafe-allow constructor
|
||||
constructor() {
|
||||
_disableInitializers();
|
||||
}
|
||||
|
||||
/// @notice Initializes the contract setting \`initialOwner\` as the initial owner
|
||||
function initialize(address initialOwner) external initializer {
|
||||
__Ownable_init(initialOwner);
|
||||
}
|
||||
|
||||
function recoverBurntFunds(address receiver) external virtual onlyOwner {
|
||||
FHEPaymentStorage storage $ = _getFHEPaymentStorage();
|
||||
uint256 claimableUsedFHEGas_ = $.claimableUsedFHEGas;
|
||||
$.claimableUsedFHEGas = 0;
|
||||
(bool success, ) = receiver.call{value: claimableUsedFHEGas_}("");
|
||||
if (!success) revert RecoveryFailed();
|
||||
}
|
||||
|
||||
function depositETH(address account) external payable virtual {
|
||||
FHEPaymentStorage storage $ = _getFHEPaymentStorage();
|
||||
$.depositsETH[account] += msg.value;
|
||||
}
|
||||
|
||||
function withdrawETH(uint256 amount, address receiver) external virtual {
|
||||
FHEPaymentStorage storage $ = _getFHEPaymentStorage();
|
||||
$.depositsETH[msg.sender] -= amount;
|
||||
(bool success, ) = receiver.call{value: amount}("");
|
||||
if(!success) revert WithdrawalFailed();
|
||||
if (!success) revert WithdrawalFailed();
|
||||
}
|
||||
|
||||
function getAvailableDepositsETH(address account) external view returns (uint256) {
|
||||
return depositsETH[account];
|
||||
function getAvailableDepositsETH(address account) external view virtual returns (uint256) {
|
||||
FHEPaymentStorage storage $ = _getFHEPaymentStorage();
|
||||
return $.depositsETH[account];
|
||||
}
|
||||
|
||||
function didAuthorizeAllContracts(address account) external view returns (bool) {
|
||||
return allowedAll[account];
|
||||
|
||||
function didAuthorizeAllContracts(address account) external view virtual returns (bool) {
|
||||
FHEPaymentStorage storage $ = _getFHEPaymentStorage();
|
||||
return $.allowedAll[account];
|
||||
}
|
||||
|
||||
function didWhitelistContract(address user, address dappContract) external view returns (bool) {
|
||||
return whitelistedDapps[user][dappContract];
|
||||
|
||||
function didWhitelistContract(address user, address dappContract) external view virtual returns (bool) {
|
||||
FHEPaymentStorage storage $ = _getFHEPaymentStorage();
|
||||
return $.whitelistedDapps[user][dappContract];
|
||||
}
|
||||
|
||||
function authorizeAllContracts() external {
|
||||
if (allowedAll[msg.sender]) revert AlreadyAuthorizedAllContracts();
|
||||
allowedAll[msg.sender] = true;
|
||||
|
||||
function authorizeAllContracts() external virtual {
|
||||
FHEPaymentStorage storage $ = _getFHEPaymentStorage();
|
||||
if ($.allowedAll[msg.sender]) revert AlreadyAuthorizedAllContracts();
|
||||
$.allowedAll[msg.sender] = true;
|
||||
}
|
||||
|
||||
function whitelistContract(address dappContract) external {
|
||||
if (whitelistedDapps[msg.sender][dappContract]) revert AlreadyWhitelistedContract();
|
||||
whitelistedDapps[msg.sender][dappContract] = true;
|
||||
|
||||
function whitelistContract(address dappContract) external virtual {
|
||||
FHEPaymentStorage storage $ = _getFHEPaymentStorage();
|
||||
if ($.whitelistedDapps[msg.sender][dappContract]) revert AlreadyWhitelistedContract();
|
||||
$.whitelistedDapps[msg.sender][dappContract] = true;
|
||||
}
|
||||
|
||||
function removeAuthorizationAllContracts() external {
|
||||
if (!allowedAll[msg.sender]) revert AllContractsNotAuthorized();
|
||||
allowedAll[msg.sender] = false;
|
||||
|
||||
function removeAuthorizationAllContracts() external virtual {
|
||||
FHEPaymentStorage storage $ = _getFHEPaymentStorage();
|
||||
if (!$.allowedAll[msg.sender]) revert AllContractsNotAuthorized();
|
||||
$.allowedAll[msg.sender] = false;
|
||||
}
|
||||
|
||||
function removeWhitelistedContract(address dappContract) external {
|
||||
if (!whitelistedDapps[msg.sender][dappContract]) revert ContractNotWhitelisted();
|
||||
whitelistedDapps[msg.sender][dappContract] = false;
|
||||
|
||||
function removeWhitelistedContract(address dappContract) external virtual {
|
||||
FHEPaymentStorage storage $ = _getFHEPaymentStorage();
|
||||
if (!$.whitelistedDapps[msg.sender][dappContract]) revert ContractNotWhitelisted();
|
||||
$.whitelistedDapps[msg.sender][dappContract] = false;
|
||||
}
|
||||
|
||||
|
||||
// @notice: to be used in the context of account abstraction, before an FHE tx, to make the contract address replace tx.origin as a spender
|
||||
function becomeTransientSpender() external {
|
||||
assembly {
|
||||
tstore(0, caller())
|
||||
}
|
||||
function becomeTransientSpender() external virtual {
|
||||
assembly {
|
||||
tstore(0, caller())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// @notice: to be used in the context of account abstraction, after an FHE tx, to avoid issues if batched with other userOps
|
||||
function stopBeingTransientSpender() external {
|
||||
assembly {
|
||||
tstore(0, 0)
|
||||
}
|
||||
function stopBeingTransientSpender() external virtual {
|
||||
assembly {
|
||||
tstore(0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
function updateFunding(address payer, uint256 paidAmountGas) private {
|
||||
|
||||
function updateFunding(address payer, uint256 paidAmountGas) internal virtual {
|
||||
FHEPaymentStorage storage $ = _getFHEPaymentStorage();
|
||||
uint256 ratio_gas = (tx.gasprice * FHE_GASPRICE_NATIVE_RATIO) / 1_000_000;
|
||||
uint256 effective_fhe_gasPrice = ratio_gas > MIN_FHE_GASPRICE ? ratio_gas : MIN_FHE_GASPRICE;
|
||||
uint256 paidAmountWei = effective_fhe_gasPrice * paidAmountGas;
|
||||
uint256 depositedAmount = depositsETH[payer];
|
||||
uint256 depositedAmount = $.depositsETH[payer];
|
||||
if (paidAmountWei > depositedAmount) {
|
||||
// if dApp is not enough funded, fallbacks to user (tx.origin by default, in case of an EOA,
|
||||
// if dApp is not enough funded, fallbacks to user (tx.origin by default, in case of an EOA,
|
||||
// otherwise a smart contract account should call \`becomeTransientSpender\` before, in the same tx
|
||||
address spender;
|
||||
assembly {
|
||||
spender := tload(0)
|
||||
spender := tload(0)
|
||||
}
|
||||
spender = spender == address(0) ? tx.origin : spender;
|
||||
if (allowedAll[spender] || whitelistedDapps[spender][payer]) {
|
||||
uint256 depositedAmountUser = depositsETH[spender];
|
||||
if ($.allowedAll[spender] || $.whitelistedDapps[spender][payer]) {
|
||||
uint256 depositedAmountUser = $.depositsETH[spender];
|
||||
if (paidAmountWei > depositedAmountUser) revert AccountNotEnoughFunded();
|
||||
unchecked {
|
||||
depositsETH[spender] = depositedAmountUser - paidAmountWei;
|
||||
$.depositsETH[spender] = depositedAmountUser - paidAmountWei;
|
||||
}
|
||||
currentBlockConsumption += paidAmountGas;
|
||||
claimableUsedFHEGas += paidAmountWei;
|
||||
$.currentBlockConsumption += paidAmountGas;
|
||||
$.claimableUsedFHEGas += paidAmountWei;
|
||||
} else {
|
||||
revert AccountNotEnoughFunded();
|
||||
}
|
||||
} else {
|
||||
unchecked {
|
||||
depositsETH[payer] = depositedAmount - paidAmountWei;
|
||||
$.depositsETH[payer] = depositedAmount - paidAmountWei;
|
||||
}
|
||||
currentBlockConsumption += paidAmountGas;
|
||||
claimableUsedFHEGas += paidAmountWei;
|
||||
$.currentBlockConsumption += paidAmountGas;
|
||||
$.claimableUsedFHEGas += paidAmountWei;
|
||||
}
|
||||
}
|
||||
|
||||
function checkIfNewBlock() private {
|
||||
|
||||
function checkIfNewBlock() internal virtual {
|
||||
FHEPaymentStorage storage $ = _getFHEPaymentStorage();
|
||||
uint256 lastBlock_ = block.number;
|
||||
if (block.number > lastBlock) {
|
||||
lastBlock = lastBlock_;
|
||||
currentBlockConsumption = 0;
|
||||
if (block.number > $.lastBlock) {
|
||||
$.lastBlock = lastBlock_;
|
||||
$.currentBlockConsumption = 0;
|
||||
}
|
||||
}
|
||||
|
||||
function checkFHEGasBlockLimit() internal view virtual {
|
||||
FHEPaymentStorage storage $ = _getFHEPaymentStorage();
|
||||
if ($.currentBlockConsumption >= FHE_GAS_BLOCKLIMIT) revert FHEGasBlockLimitExceeded();
|
||||
}\n\n`;
|
||||
|
||||
for (const [operation, data] of Object.entries(priceData)) {
|
||||
const functionName = `payFor${operation.charAt(0).toUpperCase() + operation.slice(1)}`;
|
||||
if (data.binary) {
|
||||
output += ` function ${functionName}(address payer, uint8 resultType, bytes1 scalarByte) external {
|
||||
output += ` function ${functionName}(address payer, uint8 resultType, bytes1 scalarByte) external virtual {
|
||||
if(msg.sender != tfheExecutorAddress) revert CallerMustBeTFHEExecutorContract();
|
||||
checkIfNewBlock();
|
||||
`;
|
||||
} else {
|
||||
output += ` function ${functionName}(address payer, uint8 resultType) external {
|
||||
output += ` function ${functionName}(address payer, uint8 resultType) external virtual {
|
||||
if(msg.sender != tfheExecutorAddress) revert CallerMustBeTFHEExecutorContract();
|
||||
`;
|
||||
}
|
||||
@@ -185,7 +234,7 @@ ${generatePriceChecks(data.nonScalar)}
|
||||
if (data.types) output += `${generatePriceChecks(data.types)}`;
|
||||
}
|
||||
|
||||
output += `if (currentBlockConsumption >= FHE_GAS_BLOCKLIMIT) revert FHEGasBlockLimitExceeded();
|
||||
output += `checkFHEGasBlockLimit();
|
||||
}\n\n`;
|
||||
}
|
||||
|
||||
@@ -193,7 +242,7 @@ ${generatePriceChecks(data.nonScalar)}
|
||||
output +
|
||||
` /// @notice Getter for the name and version of the contract
|
||||
/// @return string representing the name and the version of the contract
|
||||
function getVersion() external pure returns (string memory) {
|
||||
function getVersion() external pure virtual returns (string memory) {
|
||||
return
|
||||
string(
|
||||
abi.encodePacked(
|
||||
|
||||
32
examples/ACLUpgradedExample.sol
Normal file
32
examples/ACLUpgradedExample.sol
Normal file
@@ -0,0 +1,32 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
|
||||
pragma solidity ^0.8.24;
|
||||
|
||||
import "../lib/ACL.sol";
|
||||
|
||||
contract ACLUpgradedExample is ACL {
|
||||
/// @notice Name of the contract
|
||||
string private constant CONTRACT_NAME = "ACL";
|
||||
|
||||
/// @notice Version of the contract
|
||||
uint256 private constant MAJOR_VERSION = 0;
|
||||
uint256 private constant MINOR_VERSION = 2;
|
||||
uint256 private constant PATCH_VERSION = 0;
|
||||
|
||||
/// @notice Getter for the name and version of the contract
|
||||
/// @return string representing the name and the version of the contract
|
||||
function getVersion() external pure virtual override returns (string memory) {
|
||||
return
|
||||
string(
|
||||
abi.encodePacked(
|
||||
CONTRACT_NAME,
|
||||
" v",
|
||||
Strings.toString(MAJOR_VERSION),
|
||||
".",
|
||||
Strings.toString(MINOR_VERSION),
|
||||
".",
|
||||
Strings.toString(PATCH_VERSION)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
32
examples/ACLUpgradedExample2.sol
Normal file
32
examples/ACLUpgradedExample2.sol
Normal file
@@ -0,0 +1,32 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
|
||||
pragma solidity ^0.8.24;
|
||||
|
||||
import "../lib/ACL.sol";
|
||||
|
||||
contract ACLUpgradedExample2 is ACL {
|
||||
/// @notice Name of the contract
|
||||
string private constant CONTRACT_NAME = "ACL";
|
||||
|
||||
/// @notice Version of the contract
|
||||
uint256 private constant MAJOR_VERSION = 0;
|
||||
uint256 private constant MINOR_VERSION = 3;
|
||||
uint256 private constant PATCH_VERSION = 0;
|
||||
|
||||
/// @notice Getter for the name and version of the contract
|
||||
/// @return string representing the name and the version of the contract
|
||||
function getVersion() external pure virtual override returns (string memory) {
|
||||
return
|
||||
string(
|
||||
abi.encodePacked(
|
||||
CONTRACT_NAME,
|
||||
" v",
|
||||
Strings.toString(MAJOR_VERSION),
|
||||
".",
|
||||
Strings.toString(MINOR_VERSION),
|
||||
".",
|
||||
Strings.toString(PATCH_VERSION)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
32
examples/FHEPaymentUpgradedExample.sol
Normal file
32
examples/FHEPaymentUpgradedExample.sol
Normal file
@@ -0,0 +1,32 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
|
||||
pragma solidity ^0.8.24;
|
||||
|
||||
import "../lib/FHEPayment.sol";
|
||||
|
||||
contract FHEPaymentUpgradedExample is FHEPayment {
|
||||
/// @notice Name of the contract
|
||||
string private constant CONTRACT_NAME = "FHEPayment";
|
||||
|
||||
/// @notice Version of the contract
|
||||
uint256 private constant MAJOR_VERSION = 0;
|
||||
uint256 private constant MINOR_VERSION = 2;
|
||||
uint256 private constant PATCH_VERSION = 0;
|
||||
|
||||
/// @notice Getter for the name and version of the contract
|
||||
/// @return string representing the name and the version of the contract
|
||||
function getVersion() external pure virtual override returns (string memory) {
|
||||
return
|
||||
string(
|
||||
abi.encodePacked(
|
||||
CONTRACT_NAME,
|
||||
" v",
|
||||
Strings.toString(MAJOR_VERSION),
|
||||
".",
|
||||
Strings.toString(MINOR_VERSION),
|
||||
".",
|
||||
Strings.toString(PATCH_VERSION)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
32
examples/GatewayContractUpgradedExample.sol
Normal file
32
examples/GatewayContractUpgradedExample.sol
Normal file
@@ -0,0 +1,32 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
|
||||
pragma solidity ^0.8.24;
|
||||
|
||||
import "../gateway/GatewayContract.sol";
|
||||
|
||||
contract GatewayContractUpgradedExample is GatewayContract {
|
||||
/// @notice Name of the contract
|
||||
string private constant CONTRACT_NAME = "GatewayContract";
|
||||
|
||||
/// @notice Version of the contract
|
||||
uint256 private constant MAJOR_VERSION = 0;
|
||||
uint256 private constant MINOR_VERSION = 2;
|
||||
uint256 private constant PATCH_VERSION = 0;
|
||||
|
||||
/// @notice Getter for the name and version of the contract
|
||||
/// @return string representing the name and the version of the contract
|
||||
function getVersion() external pure virtual override returns (string memory) {
|
||||
return
|
||||
string(
|
||||
abi.encodePacked(
|
||||
CONTRACT_NAME,
|
||||
" v",
|
||||
Strings.toString(MAJOR_VERSION),
|
||||
".",
|
||||
Strings.toString(MINOR_VERSION),
|
||||
".",
|
||||
Strings.toString(PATCH_VERSION)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
32
examples/KMSUpgradedExample.sol
Normal file
32
examples/KMSUpgradedExample.sol
Normal file
@@ -0,0 +1,32 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
|
||||
pragma solidity ^0.8.24;
|
||||
|
||||
import "../lib/KMSVerifier.sol";
|
||||
|
||||
contract KMSVerifierUpgradedExample is KMSVerifier {
|
||||
/// @notice Name of the contract
|
||||
string private constant CONTRACT_NAME = "KMSVerifier";
|
||||
|
||||
/// @notice Version of the contract
|
||||
uint256 private constant MAJOR_VERSION = 0;
|
||||
uint256 private constant MINOR_VERSION = 2;
|
||||
uint256 private constant PATCH_VERSION = 0;
|
||||
|
||||
/// @notice Getter for the name and version of the contract
|
||||
/// @return string representing the name and the version of the contract
|
||||
function getVersion() external pure virtual override returns (string memory) {
|
||||
return
|
||||
string(
|
||||
abi.encodePacked(
|
||||
CONTRACT_NAME,
|
||||
" v",
|
||||
Strings.toString(MAJOR_VERSION),
|
||||
".",
|
||||
Strings.toString(MINOR_VERSION),
|
||||
".",
|
||||
Strings.toString(PATCH_VERSION)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
32
examples/TFHEExecutorUpgradedExample.sol
Normal file
32
examples/TFHEExecutorUpgradedExample.sol
Normal file
@@ -0,0 +1,32 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
|
||||
pragma solidity ^0.8.24;
|
||||
|
||||
import "../lib/TFHEExecutor.sol";
|
||||
|
||||
contract TFHEExecutorUpgradedExample is TFHEExecutor {
|
||||
/// @notice Name of the contract
|
||||
string private constant CONTRACT_NAME = "TFHEExecutor";
|
||||
|
||||
/// @notice Version of the contract
|
||||
uint256 private constant MAJOR_VERSION = 0;
|
||||
uint256 private constant MINOR_VERSION = 2;
|
||||
uint256 private constant PATCH_VERSION = 0;
|
||||
|
||||
/// @notice Getter for the name and version of the contract
|
||||
/// @return string representing the name and the version of the contract
|
||||
function getVersion() external pure virtual override returns (string memory) {
|
||||
return
|
||||
string(
|
||||
abi.encodePacked(
|
||||
CONTRACT_NAME,
|
||||
" v",
|
||||
Strings.toString(MAJOR_VERSION),
|
||||
".",
|
||||
Strings.toString(MINOR_VERSION),
|
||||
".",
|
||||
Strings.toString(PATCH_VERSION)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -3,11 +3,13 @@
|
||||
pragma solidity ^0.8.24;
|
||||
|
||||
import "../lib/TFHE.sol";
|
||||
import "@openzeppelin/contracts/access/Ownable2Step.sol";
|
||||
import "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol";
|
||||
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
|
||||
import "@openzeppelin/contracts/utils/Strings.sol";
|
||||
import "../lib/KMSVerifier.sol";
|
||||
import "../lib/KMSVerifierAddress.sol";
|
||||
import "./IKMSVerifier.sol";
|
||||
|
||||
contract GatewayContract is Ownable2Step {
|
||||
contract GatewayContract is UUPSUpgradeable, Ownable2StepUpgradeable {
|
||||
/// @notice Name of the contract
|
||||
string private constant CONTRACT_NAME = "GatewayContract";
|
||||
|
||||
@@ -16,9 +18,9 @@ contract GatewayContract is Ownable2Step {
|
||||
uint256 private constant MINOR_VERSION = 1;
|
||||
uint256 private constant PATCH_VERSION = 0;
|
||||
|
||||
KMSVerifier immutable kmsVerifier;
|
||||
IKMSVerifier private constant kmsVerifier = IKMSVerifier(kmsVerifierAdd);
|
||||
|
||||
uint256 public constant MAX_DELAY = 1 days;
|
||||
uint256 private constant MAX_DELAY = 1 days;
|
||||
|
||||
struct DecryptionRequest {
|
||||
uint256[] cts;
|
||||
@@ -29,12 +31,6 @@ contract GatewayContract is Ownable2Step {
|
||||
bool passSignaturesToCaller;
|
||||
}
|
||||
|
||||
uint256 public counter; // tracks the number of decryption requests
|
||||
|
||||
mapping(address => bool) public isRelayer;
|
||||
mapping(uint256 => DecryptionRequest) internal decryptionRequests;
|
||||
mapping(uint256 => bool) internal isFulfilled;
|
||||
|
||||
event EventDecryption(
|
||||
uint256 indexed requestID,
|
||||
uint256[] cts,
|
||||
@@ -51,32 +47,80 @@ contract GatewayContract is Ownable2Step {
|
||||
|
||||
event ResultCallback(uint256 indexed requestID, bool success, bytes result);
|
||||
|
||||
constructor(address _gatewayOwner, address _kmsVerifier) Ownable(_gatewayOwner) {
|
||||
kmsVerifier = KMSVerifier(_kmsVerifier);
|
||||
function getMAX_DELAY() external virtual returns (uint256) {
|
||||
return MAX_DELAY;
|
||||
}
|
||||
|
||||
function getKmsVerifierAddress() external virtual returns (address) {
|
||||
return kmsVerifierAdd;
|
||||
}
|
||||
|
||||
function getCounter() external virtual returns (uint256) {
|
||||
GatewayContractStorage storage $ = _getGatewayContractStorage();
|
||||
return $.counter;
|
||||
}
|
||||
|
||||
function isRelayer(address account) external virtual returns (bool) {
|
||||
GatewayContractStorage storage $ = _getGatewayContractStorage();
|
||||
return $.isRelayer[account];
|
||||
}
|
||||
|
||||
function _authorizeUpgrade(address _newImplementation) internal virtual override onlyOwner {}
|
||||
|
||||
/// @custom:storage-location erc7201:fhevm.storage.GatewayContract
|
||||
struct GatewayContractStorage {
|
||||
uint256 counter; // tracks the number of decryption requests
|
||||
mapping(address => bool) isRelayer;
|
||||
mapping(uint256 => DecryptionRequest) decryptionRequests;
|
||||
mapping(uint256 => bool) isFulfilled;
|
||||
}
|
||||
|
||||
// keccak256(abi.encode(uint256(keccak256("fhevm.storage.GatewayContract")) - 1)) & ~bytes32(uint256(0xff))
|
||||
bytes32 private constant GatewayContractStorageLocation =
|
||||
0x2f81b8bba57448689ab73c47570e3de1ee7f779a62f121c9631b35b3eda2aa00;
|
||||
|
||||
function _getGatewayContractStorage() internal pure returns (GatewayContractStorage storage $) {
|
||||
assembly {
|
||||
$.slot := GatewayContractStorageLocation
|
||||
}
|
||||
}
|
||||
|
||||
/// @custom:oz-upgrades-unsafe-allow constructor
|
||||
constructor() {
|
||||
_disableInitializers();
|
||||
}
|
||||
|
||||
function initialize(address _gatewayOwner) external initializer {
|
||||
__Ownable_init(_gatewayOwner);
|
||||
}
|
||||
|
||||
modifier onlyRelayer() {
|
||||
require(isRelayer[msg.sender], "Not relayer");
|
||||
GatewayContractStorage storage $ = _getGatewayContractStorage();
|
||||
require($.isRelayer[msg.sender], "Not relayer");
|
||||
_;
|
||||
}
|
||||
|
||||
function addRelayer(address relayerAddress) external onlyOwner {
|
||||
require(!isRelayer[relayerAddress], "Address is already relayer");
|
||||
isRelayer[relayerAddress] = true;
|
||||
function addRelayer(address relayerAddress) external virtual onlyOwner {
|
||||
GatewayContractStorage storage $ = _getGatewayContractStorage();
|
||||
require(!$.isRelayer[relayerAddress], "Address is already relayer");
|
||||
$.isRelayer[relayerAddress] = true;
|
||||
emit AddedRelayer(relayerAddress);
|
||||
}
|
||||
|
||||
function removeRelayer(address relayerAddress) external onlyOwner {
|
||||
require(isRelayer[relayerAddress], "Address is not a relayer");
|
||||
isRelayer[relayerAddress] = false;
|
||||
function removeRelayer(address relayerAddress) external virtual onlyOwner {
|
||||
GatewayContractStorage storage $ = _getGatewayContractStorage();
|
||||
require($.isRelayer[relayerAddress], "Address is not a relayer");
|
||||
$.isRelayer[relayerAddress] = false;
|
||||
emit RemovedRelayer(relayerAddress);
|
||||
}
|
||||
|
||||
function isExpiredOrFulfilled(uint256 requestID) external view returns (bool) {
|
||||
function isExpiredOrFulfilled(uint256 requestID) external view virtual returns (bool) {
|
||||
GatewayContractStorage storage $ = _getGatewayContractStorage();
|
||||
uint256 timeNow = block.timestamp;
|
||||
return
|
||||
isFulfilled[requestID] ||
|
||||
(timeNow > decryptionRequests[requestID].maxTimestamp && decryptionRequests[requestID].maxTimestamp != 0);
|
||||
$.isFulfilled[requestID] ||
|
||||
(timeNow > $.decryptionRequests[requestID].maxTimestamp &&
|
||||
$.decryptionRequests[requestID].maxTimestamp != 0);
|
||||
}
|
||||
|
||||
/// @notice Requests the decryption of n ciphertexts `ctsHandles` with the result returned in a callback.
|
||||
@@ -94,11 +138,12 @@ contract GatewayContract is Ownable2Step {
|
||||
uint256 msgValue,
|
||||
uint256 maxTimestamp,
|
||||
bool passSignaturesToCaller
|
||||
) external returns (uint256 initialCounter) {
|
||||
) external virtual returns (uint256 initialCounter) {
|
||||
require(maxTimestamp > block.timestamp, "maxTimestamp must be a future date");
|
||||
require(maxTimestamp <= block.timestamp + MAX_DELAY, "maxTimestamp exceeded MAX_DELAY");
|
||||
initialCounter = counter;
|
||||
DecryptionRequest storage decryptionReq = decryptionRequests[initialCounter];
|
||||
GatewayContractStorage storage $ = _getGatewayContractStorage();
|
||||
initialCounter = $.counter;
|
||||
DecryptionRequest storage decryptionReq = $.decryptionRequests[initialCounter];
|
||||
|
||||
uint256 len = ctsHandles.length;
|
||||
for (uint256 i = 0; i < len; i++) {
|
||||
@@ -119,7 +164,7 @@ contract GatewayContract is Ownable2Step {
|
||||
maxTimestamp,
|
||||
passSignaturesToCaller
|
||||
);
|
||||
counter++;
|
||||
$.counter++;
|
||||
}
|
||||
|
||||
// Called by the relayer to pass the decryption results from the KMS to the callback function
|
||||
@@ -128,14 +173,15 @@ contract GatewayContract is Ownable2Step {
|
||||
uint256 requestID,
|
||||
bytes memory decryptedCts,
|
||||
bytes[] memory signatures
|
||||
) external payable onlyRelayer {
|
||||
) external payable virtual onlyRelayer {
|
||||
// TODO: this should be un-commented once KMS will have the signatures implemented
|
||||
//require(
|
||||
// kmsVerifier.verifySignatures(decryptionRequests[requestID].cts, decryptedCts, signatures),
|
||||
// "KMS signature verification failed"
|
||||
//);
|
||||
require(!isFulfilled[requestID], "Request is already fulfilled");
|
||||
DecryptionRequest memory decryptionReq = decryptionRequests[requestID];
|
||||
GatewayContractStorage storage $ = _getGatewayContractStorage();
|
||||
require(!$.isFulfilled[requestID], "Request is already fulfilled");
|
||||
DecryptionRequest memory decryptionReq = $.decryptionRequests[requestID];
|
||||
require(block.timestamp <= decryptionReq.maxTimestamp, "Too late");
|
||||
bytes memory callbackCalldata = abi.encodeWithSelector(decryptionReq.callbackSelector, requestID);
|
||||
bool passSignatures = decryptionReq.passSignaturesToCaller;
|
||||
@@ -147,12 +193,12 @@ contract GatewayContract is Ownable2Step {
|
||||
callbackCalldata
|
||||
);
|
||||
emit ResultCallback(requestID, success, result);
|
||||
isFulfilled[requestID] = true;
|
||||
$.isFulfilled[requestID] = true;
|
||||
}
|
||||
|
||||
/// @notice Getter for the name and version of the contract
|
||||
/// @return string representing the name and the version of the contract
|
||||
function getVersion() external pure returns (string memory) {
|
||||
function getVersion() external pure virtual returns (string memory) {
|
||||
return
|
||||
string(
|
||||
abi.encodePacked(
|
||||
|
||||
11
gateway/IKMSVerifier.sol
Normal file
11
gateway/IKMSVerifier.sol
Normal file
@@ -0,0 +1,11 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
|
||||
pragma solidity ^0.8.24;
|
||||
|
||||
interface IKMSVerifier {
|
||||
function verifySignatures(
|
||||
uint256[] memory handlesList,
|
||||
bytes memory decryptedResult,
|
||||
bytes[] memory signatures
|
||||
) external returns (bool);
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
pragma solidity ^0.8.24;
|
||||
|
||||
import "./GatewayContractAddress.sol";
|
||||
import "../IKMSVerifier.sol";
|
||||
import "../../lib/Impl.sol";
|
||||
|
||||
interface IGatewayContract {
|
||||
@@ -15,14 +16,6 @@ interface IGatewayContract {
|
||||
) external returns (uint256);
|
||||
}
|
||||
|
||||
interface IKMSVerifier {
|
||||
function verifySignatures(
|
||||
uint256[] memory handlesList,
|
||||
bytes memory decryptedResult,
|
||||
bytes[] memory signatures
|
||||
) external returns (bool);
|
||||
}
|
||||
|
||||
library Gateway {
|
||||
struct GatewayConfigStruct {
|
||||
address GatewayContractAddress;
|
||||
|
||||
@@ -2,5 +2,4 @@
|
||||
|
||||
pragma solidity ^0.8.24;
|
||||
|
||||
address constant GATEWAY_CONTRACT_PREDEPLOY_ADDRESS = 0xc8c9303Cd7F337fab769686B593B87DC3403E0ce;
|
||||
|
||||
address constant GATEWAY_CONTRACT_PREDEPLOY_ADDRESS = 0x096b4679d45fB675d4e2c1E4565009Cec99A12B1;
|
||||
|
||||
@@ -1 +1 @@
|
||||
ACL_CONTRACT_ADDRESS=0x2Fb4341027eb1d2aD8B5D9708187df8633cAFA92
|
||||
ACL_CONTRACT_ADDRESS=0x05fD9B5EFE0a996095f42Ed7e77c390810CF660c
|
||||
|
||||
@@ -1 +1 @@
|
||||
TFHE_EXECUTOR_CONTRACT_ADDRESS=0x05fD9B5EFE0a996095f42Ed7e77c390810CF660c
|
||||
TFHE_EXECUTOR_CONTRACT_ADDRESS=0xcCAe95fF1d11656358E782570dF0418F59fA40e1
|
||||
|
||||
@@ -1 +1 @@
|
||||
FHE_PAYMENT_CONTRACT_ADDRESS=0xcCAe95fF1d11656358E782570dF0418F59fA40e1
|
||||
FHE_PAYMENT_CONTRACT_ADDRESS=0x52054F36036811ca418be59e41Fc6DD1b9e4F4c8
|
||||
|
||||
@@ -1 +1 @@
|
||||
KMS_VERIFIER_CONTRACT_ADDRESS=0x12B064FB845C1cc05e9493856a1D637a73e944bE
|
||||
KMS_VERIFIER_CONTRACT_ADDRESS=0x857Ca72A957920Fa0FB138602995839866Bd4005
|
||||
|
||||
94
lib/ACL.sol
94
lib/ACL.sol
@@ -3,9 +3,11 @@
|
||||
pragma solidity ^0.8.24;
|
||||
|
||||
import "@openzeppelin/contracts/utils/Strings.sol";
|
||||
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
|
||||
import "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol";
|
||||
import "./TFHEExecutorAddress.sol";
|
||||
|
||||
contract ACL {
|
||||
contract ACL is UUPSUpgradeable, Ownable2StepUpgradeable {
|
||||
/// @notice Name of the contract
|
||||
string private constant CONTRACT_NAME = "ACL";
|
||||
|
||||
@@ -14,25 +16,48 @@ contract ACL {
|
||||
uint256 private constant MINOR_VERSION = 1;
|
||||
uint256 private constant PATCH_VERSION = 0;
|
||||
|
||||
address public immutable tfheExecutorAddress = tfheExecutorAdd;
|
||||
address private constant tfheExecutorAddress = tfheExecutorAdd;
|
||||
|
||||
mapping(uint256 => bool) public allowedForDecryption;
|
||||
/// @custom:storage-location erc7201:fhevm.storage.ACL
|
||||
struct ACLStorage {
|
||||
mapping(uint256 handle => mapping(address account => bool isAllowed)) persistedAllowedPairs;
|
||||
mapping(uint256 => bool) allowedForDecryption;
|
||||
mapping(address account => mapping(address delegatee => mapping(address contractAddress => bool isDelegate))) delegates;
|
||||
}
|
||||
|
||||
// A set of (handle, address) pairs.
|
||||
// If address A is in the set for handle H, full access is granted to H for A.
|
||||
mapping(uint256 handle => mapping(address account => bool isAllowed)) public persistedAllowedPairs;
|
||||
// keccak256(abi.encode(uint256(keccak256("fhevm.storage.ACL")) - 1)) & ~bytes32(uint256(0xff))
|
||||
bytes32 private constant ACLStorageLocation = 0xa688f31953c2015baaf8c0a488ee1ee22eb0e05273cc1fd31ea4cbee42febc00;
|
||||
|
||||
mapping(address account => mapping(address delegatee => mapping(address contractAddress => bool isDelegate)))
|
||||
public delegates;
|
||||
function _getACLStorage() internal pure returns (ACLStorage storage $) {
|
||||
assembly {
|
||||
$.slot := ACLStorageLocation
|
||||
}
|
||||
}
|
||||
|
||||
function getTFHEExecutorAddress() public view virtual returns (address) {
|
||||
return tfheExecutorAddress;
|
||||
}
|
||||
|
||||
event NewDelegation(address indexed sender, address indexed delegatee, address indexed contractAddress);
|
||||
event RevokedDelegation(address indexed sender, address indexed delegatee, address indexed contractAddress);
|
||||
event AllowedForDecryption(uint256[] handlesList);
|
||||
|
||||
function _authorizeUpgrade(address _newImplementation) internal virtual override onlyOwner {}
|
||||
|
||||
/// @custom:oz-upgrades-unsafe-allow constructor
|
||||
constructor() {
|
||||
_disableInitializers();
|
||||
}
|
||||
|
||||
/// @notice Initializes the contract setting `initialOwner` as the initial owner
|
||||
function initialize(address initialOwner) external initializer {
|
||||
__Ownable_init(initialOwner);
|
||||
}
|
||||
|
||||
// allowTransient use of `handle` for address `account`.
|
||||
// The caller must be allowed to use `handle` for allowTransient() to succeed. If not, allowTransient() reverts.
|
||||
// @note: The Coprocessor contract can always `allowTransient`, contrarily to `allow`
|
||||
function allowTransient(uint256 handle, address account) public {
|
||||
function allowTransient(uint256 handle, address account) public virtual {
|
||||
if (msg.sender != tfheExecutorAddress) {
|
||||
require(isAllowed(handle, msg.sender), "sender isn't allowed");
|
||||
}
|
||||
@@ -46,7 +71,7 @@ contract ACL {
|
||||
}
|
||||
}
|
||||
|
||||
function allowedTransient(uint256 handle, address account) public view returns (bool) {
|
||||
function allowedTransient(uint256 handle, address account) public view virtual returns (bool) {
|
||||
bool isAllowedTransient;
|
||||
bytes32 key = keccak256(abi.encodePacked(handle, account));
|
||||
assembly {
|
||||
@@ -55,7 +80,7 @@ contract ACL {
|
||||
return isAllowedTransient;
|
||||
}
|
||||
|
||||
function cleanTransientStorage() external {
|
||||
function cleanTransientStorage() external virtual {
|
||||
// this function removes the transient allowances, could be useful for integration with Account Abstraction when bundling several UserOps calling the TFHEExecutorCoprocessor
|
||||
assembly {
|
||||
let length := tload(0)
|
||||
@@ -75,31 +100,35 @@ contract ACL {
|
||||
|
||||
// Allow use of `handle` for address `account`.
|
||||
// The caller must be allowed to use `handle` for allow() to succeed. If not, allow() reverts.
|
||||
function allow(uint256 handle, address account) external {
|
||||
function allow(uint256 handle, address account) external virtual {
|
||||
ACLStorage storage $ = _getACLStorage();
|
||||
require(isAllowed(handle, msg.sender), "sender isn't allowed");
|
||||
persistedAllowedPairs[handle][account] = true;
|
||||
$.persistedAllowedPairs[handle][account] = true;
|
||||
}
|
||||
|
||||
// Returns true if address `a` is allowed to use `c` and false otherwise.
|
||||
function persistAllowed(uint256 handle, address account) public view returns (bool) {
|
||||
return persistedAllowedPairs[handle][account];
|
||||
function persistAllowed(uint256 handle, address account) public view virtual returns (bool) {
|
||||
ACLStorage storage $ = _getACLStorage();
|
||||
return $.persistedAllowedPairs[handle][account];
|
||||
}
|
||||
|
||||
// Useful in the context of account abstraction for issuing reencryption requests from a smart contract account
|
||||
function isAllowed(uint256 handle, address account) public view returns (bool) {
|
||||
function isAllowed(uint256 handle, address account) public view virtual returns (bool) {
|
||||
return allowedTransient(handle, account) || persistAllowed(handle, account);
|
||||
}
|
||||
|
||||
function delegateAccountForContract(address delegatee, address contractAddress) external {
|
||||
function delegateAccountForContract(address delegatee, address contractAddress) external virtual {
|
||||
require(contractAddress != msg.sender, "contractAddress should be different from msg.sender");
|
||||
require(!delegates[msg.sender][delegatee][contractAddress], "already delegated");
|
||||
delegates[msg.sender][delegatee][contractAddress] = true;
|
||||
ACLStorage storage $ = _getACLStorage();
|
||||
require(!$.delegates[msg.sender][delegatee][contractAddress], "already delegated");
|
||||
$.delegates[msg.sender][delegatee][contractAddress] = true;
|
||||
emit NewDelegation(msg.sender, delegatee, contractAddress);
|
||||
}
|
||||
|
||||
function removeDelegationForContract(address delegatee, address contractAddress) external {
|
||||
require(delegates[msg.sender][delegatee][contractAddress], "not delegated yet");
|
||||
delegates[msg.sender][delegatee][contractAddress] = false;
|
||||
function removeDelegationForContract(address delegatee, address contractAddress) external virtual {
|
||||
ACLStorage storage $ = _getACLStorage();
|
||||
require($.delegates[msg.sender][delegatee][contractAddress], "not delegated yet");
|
||||
$.delegates[msg.sender][delegatee][contractAddress] = false;
|
||||
emit RevokedDelegation(msg.sender, delegatee, contractAddress);
|
||||
}
|
||||
|
||||
@@ -108,26 +137,33 @@ contract ACL {
|
||||
uint256 handle,
|
||||
address contractAddress,
|
||||
address account
|
||||
) external view returns (bool) {
|
||||
) external view virtual returns (bool) {
|
||||
ACLStorage storage $ = _getACLStorage();
|
||||
return
|
||||
persistedAllowedPairs[handle][account] &&
|
||||
persistedAllowedPairs[handle][contractAddress] &&
|
||||
delegates[account][delegatee][contractAddress];
|
||||
$.persistedAllowedPairs[handle][account] &&
|
||||
$.persistedAllowedPairs[handle][contractAddress] &&
|
||||
$.delegates[account][delegatee][contractAddress];
|
||||
}
|
||||
|
||||
function allowForDecryption(uint256[] memory handlesList) external {
|
||||
function allowForDecryption(uint256[] memory handlesList) external virtual {
|
||||
uint256 len = handlesList.length;
|
||||
ACLStorage storage $ = _getACLStorage();
|
||||
for (uint256 k = 0; k < len; k++) {
|
||||
uint256 handle = handlesList[k];
|
||||
require(isAllowed(handle, msg.sender), "sender isn't allowed");
|
||||
allowedForDecryption[handle] = true;
|
||||
$.allowedForDecryption[handle] = true;
|
||||
}
|
||||
emit AllowedForDecryption(handlesList);
|
||||
}
|
||||
|
||||
function isAllowedForDecryption(uint256 handle) public virtual returns (bool) {
|
||||
ACLStorage storage $ = _getACLStorage();
|
||||
return $.allowedForDecryption[handle];
|
||||
}
|
||||
|
||||
/// @notice Getter for the name and version of the contract
|
||||
/// @return string representing the name and the version of the contract
|
||||
function getVersion() external pure returns (string memory) {
|
||||
function getVersion() external pure virtual returns (string memory) {
|
||||
return
|
||||
string(
|
||||
abi.encodePacked(
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
|
||||
pragma solidity ^0.8.24;
|
||||
|
||||
address constant aclAdd = 0x2Fb4341027eb1d2aD8B5D9708187df8633cAFA92;
|
||||
address constant aclAdd = 0x05fD9B5EFE0a996095f42Ed7e77c390810CF660c;
|
||||
|
||||
@@ -4,7 +4,8 @@ pragma solidity ^0.8.24;
|
||||
|
||||
import "./TFHEExecutorAddress.sol";
|
||||
import "@openzeppelin/contracts/utils/Strings.sol";
|
||||
import "@openzeppelin/contracts/access/Ownable2Step.sol";
|
||||
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
|
||||
import "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol";
|
||||
|
||||
error FHEGasBlockLimitExceeded();
|
||||
error CallerMustBeTFHEExecutorContract();
|
||||
@@ -18,7 +19,7 @@ error AlreadyWhitelistedContract();
|
||||
error AllContractsNotAuthorized();
|
||||
error ContractNotWhitelisted();
|
||||
|
||||
contract FHEPayment is Ownable2Step {
|
||||
contract FHEPayment is UUPSUpgradeable, Ownable2StepUpgradeable {
|
||||
/// @notice Name of the contract
|
||||
string private constant CONTRACT_NAME = "FHEPayment";
|
||||
|
||||
@@ -26,90 +27,132 @@ contract FHEPayment is Ownable2Step {
|
||||
uint256 private constant MAJOR_VERSION = 0;
|
||||
uint256 private constant MINOR_VERSION = 1;
|
||||
uint256 private constant PATCH_VERSION = 0;
|
||||
address public immutable tfheExecutorAddress = tfheExecutorAdd;
|
||||
address public constant tfheExecutorAddress = tfheExecutorAdd;
|
||||
|
||||
uint256 private constant FHE_GAS_BLOCKLIMIT = 10_000_000;
|
||||
uint256 private constant MIN_FHE_GASPRICE = 10_000_000; // minimum of 0.01 Gwei
|
||||
uint256 private constant FHE_GASPRICE_NATIVE_RATIO = 1000; // fhe gas price is set to 0.1% of native gas price (if above minimum)
|
||||
|
||||
uint256 private lastBlock;
|
||||
uint256 private currentBlockConsumption;
|
||||
uint256 public claimableUsedFHEGas;
|
||||
/// @custom:storage-location erc7201:fhevm.storage.FHEPayment
|
||||
struct FHEPaymentStorage {
|
||||
uint256 lastBlock;
|
||||
uint256 currentBlockConsumption;
|
||||
uint256 claimableUsedFHEGas;
|
||||
mapping(address payer => uint256 depositedAmount) depositsETH;
|
||||
mapping(address user => bool allowedAllContracts) allowedAll;
|
||||
mapping(address user => mapping(address dappContract => bool isWhitelisted)) whitelistedDapps;
|
||||
}
|
||||
|
||||
mapping(address payer => uint256 depositedAmount) private depositsETH;
|
||||
mapping(address user => bool allowedAllContracts) private allowedAll;
|
||||
mapping(address user => mapping(address dappContract => bool isWhitelisted)) private whitelistedDapps;
|
||||
// keccak256(abi.encode(uint256(keccak256("fhevm.storage.FHEPayment")) - 1)) & ~bytes32(uint256(0xff))
|
||||
bytes32 private constant FHEPaymentStorageLocation =
|
||||
0x4c5af501c90907b9fb888b6dd79405547def38a1dc3110f42d77f5dbc3222e00;
|
||||
|
||||
constructor() Ownable(msg.sender) {}
|
||||
function _getFHEPaymentStorage() internal pure returns (FHEPaymentStorage storage $) {
|
||||
assembly {
|
||||
$.slot := FHEPaymentStorageLocation
|
||||
}
|
||||
}
|
||||
|
||||
function recoverBurntFunds(address receiver) external onlyOwner {
|
||||
uint256 claimableUsedFHEGas_ = claimableUsedFHEGas;
|
||||
claimableUsedFHEGas = 0;
|
||||
function getTFHEExecutorAddress() public view virtual returns (address) {
|
||||
return tfheExecutorAddress;
|
||||
}
|
||||
|
||||
function getClaimableUsedFHEGas() public view virtual returns (uint256) {
|
||||
FHEPaymentStorage storage $ = _getFHEPaymentStorage();
|
||||
return $.claimableUsedFHEGas;
|
||||
}
|
||||
|
||||
function _authorizeUpgrade(address _newImplementation) internal virtual override onlyOwner {}
|
||||
|
||||
/// @custom:oz-upgrades-unsafe-allow constructor
|
||||
constructor() {
|
||||
_disableInitializers();
|
||||
}
|
||||
|
||||
/// @notice Initializes the contract setting `initialOwner` as the initial owner
|
||||
function initialize(address initialOwner) external initializer {
|
||||
__Ownable_init(initialOwner);
|
||||
}
|
||||
|
||||
function recoverBurntFunds(address receiver) external virtual onlyOwner {
|
||||
FHEPaymentStorage storage $ = _getFHEPaymentStorage();
|
||||
uint256 claimableUsedFHEGas_ = $.claimableUsedFHEGas;
|
||||
$.claimableUsedFHEGas = 0;
|
||||
(bool success, ) = receiver.call{value: claimableUsedFHEGas_}("");
|
||||
if (!success) revert RecoveryFailed();
|
||||
}
|
||||
|
||||
function depositETH(address account) external payable {
|
||||
depositsETH[account] += msg.value;
|
||||
function depositETH(address account) external payable virtual {
|
||||
FHEPaymentStorage storage $ = _getFHEPaymentStorage();
|
||||
$.depositsETH[account] += msg.value;
|
||||
}
|
||||
|
||||
function withdrawETH(uint256 amount, address receiver) external {
|
||||
depositsETH[msg.sender] -= amount;
|
||||
function withdrawETH(uint256 amount, address receiver) external virtual {
|
||||
FHEPaymentStorage storage $ = _getFHEPaymentStorage();
|
||||
$.depositsETH[msg.sender] -= amount;
|
||||
(bool success, ) = receiver.call{value: amount}("");
|
||||
if (!success) revert WithdrawalFailed();
|
||||
}
|
||||
|
||||
function getAvailableDepositsETH(address account) external view returns (uint256) {
|
||||
return depositsETH[account];
|
||||
function getAvailableDepositsETH(address account) external view virtual returns (uint256) {
|
||||
FHEPaymentStorage storage $ = _getFHEPaymentStorage();
|
||||
return $.depositsETH[account];
|
||||
}
|
||||
|
||||
function didAuthorizeAllContracts(address account) external view returns (bool) {
|
||||
return allowedAll[account];
|
||||
function didAuthorizeAllContracts(address account) external view virtual returns (bool) {
|
||||
FHEPaymentStorage storage $ = _getFHEPaymentStorage();
|
||||
return $.allowedAll[account];
|
||||
}
|
||||
|
||||
function didWhitelistContract(address user, address dappContract) external view returns (bool) {
|
||||
return whitelistedDapps[user][dappContract];
|
||||
function didWhitelistContract(address user, address dappContract) external view virtual returns (bool) {
|
||||
FHEPaymentStorage storage $ = _getFHEPaymentStorage();
|
||||
return $.whitelistedDapps[user][dappContract];
|
||||
}
|
||||
|
||||
function authorizeAllContracts() external {
|
||||
if (allowedAll[msg.sender]) revert AlreadyAuthorizedAllContracts();
|
||||
allowedAll[msg.sender] = true;
|
||||
function authorizeAllContracts() external virtual {
|
||||
FHEPaymentStorage storage $ = _getFHEPaymentStorage();
|
||||
if ($.allowedAll[msg.sender]) revert AlreadyAuthorizedAllContracts();
|
||||
$.allowedAll[msg.sender] = true;
|
||||
}
|
||||
|
||||
function whitelistContract(address dappContract) external {
|
||||
if (whitelistedDapps[msg.sender][dappContract]) revert AlreadyWhitelistedContract();
|
||||
whitelistedDapps[msg.sender][dappContract] = true;
|
||||
function whitelistContract(address dappContract) external virtual {
|
||||
FHEPaymentStorage storage $ = _getFHEPaymentStorage();
|
||||
if ($.whitelistedDapps[msg.sender][dappContract]) revert AlreadyWhitelistedContract();
|
||||
$.whitelistedDapps[msg.sender][dappContract] = true;
|
||||
}
|
||||
|
||||
function removeAuthorizationAllContracts() external {
|
||||
if (!allowedAll[msg.sender]) revert AllContractsNotAuthorized();
|
||||
allowedAll[msg.sender] = false;
|
||||
function removeAuthorizationAllContracts() external virtual {
|
||||
FHEPaymentStorage storage $ = _getFHEPaymentStorage();
|
||||
if (!$.allowedAll[msg.sender]) revert AllContractsNotAuthorized();
|
||||
$.allowedAll[msg.sender] = false;
|
||||
}
|
||||
|
||||
function removeWhitelistedContract(address dappContract) external {
|
||||
if (!whitelistedDapps[msg.sender][dappContract]) revert ContractNotWhitelisted();
|
||||
whitelistedDapps[msg.sender][dappContract] = false;
|
||||
function removeWhitelistedContract(address dappContract) external virtual {
|
||||
FHEPaymentStorage storage $ = _getFHEPaymentStorage();
|
||||
if (!$.whitelistedDapps[msg.sender][dappContract]) revert ContractNotWhitelisted();
|
||||
$.whitelistedDapps[msg.sender][dappContract] = false;
|
||||
}
|
||||
|
||||
// @notice: to be used in the context of account abstraction, before an FHE tx, to make the contract address replace tx.origin as a spender
|
||||
function becomeTransientSpender() external {
|
||||
function becomeTransientSpender() external virtual {
|
||||
assembly {
|
||||
tstore(0, caller())
|
||||
}
|
||||
}
|
||||
|
||||
// @notice: to be used in the context of account abstraction, after an FHE tx, to avoid issues if batched with other userOps
|
||||
function stopBeingTransientSpender() external {
|
||||
function stopBeingTransientSpender() external virtual {
|
||||
assembly {
|
||||
tstore(0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
function updateFunding(address payer, uint256 paidAmountGas) private {
|
||||
function updateFunding(address payer, uint256 paidAmountGas) internal virtual {
|
||||
FHEPaymentStorage storage $ = _getFHEPaymentStorage();
|
||||
uint256 ratio_gas = (tx.gasprice * FHE_GASPRICE_NATIVE_RATIO) / 1_000_000;
|
||||
uint256 effective_fhe_gasPrice = ratio_gas > MIN_FHE_GASPRICE ? ratio_gas : MIN_FHE_GASPRICE;
|
||||
uint256 paidAmountWei = effective_fhe_gasPrice * paidAmountGas;
|
||||
uint256 depositedAmount = depositsETH[payer];
|
||||
uint256 depositedAmount = $.depositsETH[payer];
|
||||
if (paidAmountWei > depositedAmount) {
|
||||
// if dApp is not enough funded, fallbacks to user (tx.origin by default, in case of an EOA,
|
||||
// otherwise a smart contract account should call `becomeTransientSpender` before, in the same tx
|
||||
@@ -118,35 +161,41 @@ contract FHEPayment is Ownable2Step {
|
||||
spender := tload(0)
|
||||
}
|
||||
spender = spender == address(0) ? tx.origin : spender;
|
||||
if (allowedAll[spender] || whitelistedDapps[spender][payer]) {
|
||||
uint256 depositedAmountUser = depositsETH[spender];
|
||||
if ($.allowedAll[spender] || $.whitelistedDapps[spender][payer]) {
|
||||
uint256 depositedAmountUser = $.depositsETH[spender];
|
||||
if (paidAmountWei > depositedAmountUser) revert AccountNotEnoughFunded();
|
||||
unchecked {
|
||||
depositsETH[spender] = depositedAmountUser - paidAmountWei;
|
||||
$.depositsETH[spender] = depositedAmountUser - paidAmountWei;
|
||||
}
|
||||
currentBlockConsumption += paidAmountGas;
|
||||
claimableUsedFHEGas += paidAmountWei;
|
||||
$.currentBlockConsumption += paidAmountGas;
|
||||
$.claimableUsedFHEGas += paidAmountWei;
|
||||
} else {
|
||||
revert AccountNotEnoughFunded();
|
||||
}
|
||||
} else {
|
||||
unchecked {
|
||||
depositsETH[payer] = depositedAmount - paidAmountWei;
|
||||
$.depositsETH[payer] = depositedAmount - paidAmountWei;
|
||||
}
|
||||
currentBlockConsumption += paidAmountGas;
|
||||
claimableUsedFHEGas += paidAmountWei;
|
||||
$.currentBlockConsumption += paidAmountGas;
|
||||
$.claimableUsedFHEGas += paidAmountWei;
|
||||
}
|
||||
}
|
||||
|
||||
function checkIfNewBlock() private {
|
||||
function checkIfNewBlock() internal virtual {
|
||||
FHEPaymentStorage storage $ = _getFHEPaymentStorage();
|
||||
uint256 lastBlock_ = block.number;
|
||||
if (block.number > lastBlock) {
|
||||
lastBlock = lastBlock_;
|
||||
currentBlockConsumption = 0;
|
||||
if (block.number > $.lastBlock) {
|
||||
$.lastBlock = lastBlock_;
|
||||
$.currentBlockConsumption = 0;
|
||||
}
|
||||
}
|
||||
|
||||
function payForFheAdd(address payer, uint8 resultType, bytes1 scalarByte) external {
|
||||
function checkFHEGasBlockLimit() internal view virtual {
|
||||
FHEPaymentStorage storage $ = _getFHEPaymentStorage();
|
||||
if ($.currentBlockConsumption >= FHE_GAS_BLOCKLIMIT) revert FHEGasBlockLimitExceeded();
|
||||
}
|
||||
|
||||
function payForFheAdd(address payer, uint8 resultType, bytes1 scalarByte) external virtual {
|
||||
if (msg.sender != tfheExecutorAddress) revert CallerMustBeTFHEExecutorContract();
|
||||
checkIfNewBlock();
|
||||
if (scalarByte == 0x01) {
|
||||
@@ -174,10 +223,10 @@ contract FHEPayment is Ownable2Step {
|
||||
updateFunding(payer, 188000);
|
||||
}
|
||||
}
|
||||
if (currentBlockConsumption >= FHE_GAS_BLOCKLIMIT) revert FHEGasBlockLimitExceeded();
|
||||
checkFHEGasBlockLimit();
|
||||
}
|
||||
|
||||
function payForFheSub(address payer, uint8 resultType, bytes1 scalarByte) external {
|
||||
function payForFheSub(address payer, uint8 resultType, bytes1 scalarByte) external virtual {
|
||||
if (msg.sender != tfheExecutorAddress) revert CallerMustBeTFHEExecutorContract();
|
||||
checkIfNewBlock();
|
||||
if (scalarByte == 0x01) {
|
||||
@@ -205,10 +254,10 @@ contract FHEPayment is Ownable2Step {
|
||||
updateFunding(payer, 188000);
|
||||
}
|
||||
}
|
||||
if (currentBlockConsumption >= FHE_GAS_BLOCKLIMIT) revert FHEGasBlockLimitExceeded();
|
||||
checkFHEGasBlockLimit();
|
||||
}
|
||||
|
||||
function payForFheMul(address payer, uint8 resultType, bytes1 scalarByte) external {
|
||||
function payForFheMul(address payer, uint8 resultType, bytes1 scalarByte) external virtual {
|
||||
if (msg.sender != tfheExecutorAddress) revert CallerMustBeTFHEExecutorContract();
|
||||
checkIfNewBlock();
|
||||
if (scalarByte == 0x01) {
|
||||
@@ -236,10 +285,10 @@ contract FHEPayment is Ownable2Step {
|
||||
updateFunding(payer, 641000);
|
||||
}
|
||||
}
|
||||
if (currentBlockConsumption >= FHE_GAS_BLOCKLIMIT) revert FHEGasBlockLimitExceeded();
|
||||
checkFHEGasBlockLimit();
|
||||
}
|
||||
|
||||
function payForFheDiv(address payer, uint8 resultType, bytes1 scalarByte) external {
|
||||
function payForFheDiv(address payer, uint8 resultType, bytes1 scalarByte) external virtual {
|
||||
if (msg.sender != tfheExecutorAddress) revert CallerMustBeTFHEExecutorContract();
|
||||
checkIfNewBlock();
|
||||
if (scalarByte != 0x01) revert OnlyScalarOperationsAreSupported();
|
||||
@@ -254,10 +303,10 @@ contract FHEPayment is Ownable2Step {
|
||||
} else if (resultType == 5) {
|
||||
updateFunding(payer, 584000);
|
||||
}
|
||||
if (currentBlockConsumption >= FHE_GAS_BLOCKLIMIT) revert FHEGasBlockLimitExceeded();
|
||||
checkFHEGasBlockLimit();
|
||||
}
|
||||
|
||||
function payForFheRem(address payer, uint8 resultType, bytes1 scalarByte) external {
|
||||
function payForFheRem(address payer, uint8 resultType, bytes1 scalarByte) external virtual {
|
||||
if (msg.sender != tfheExecutorAddress) revert CallerMustBeTFHEExecutorContract();
|
||||
checkIfNewBlock();
|
||||
if (scalarByte != 0x01) revert OnlyScalarOperationsAreSupported();
|
||||
@@ -272,10 +321,10 @@ contract FHEPayment is Ownable2Step {
|
||||
} else if (resultType == 5) {
|
||||
updateFunding(payer, 1095000);
|
||||
}
|
||||
if (currentBlockConsumption >= FHE_GAS_BLOCKLIMIT) revert FHEGasBlockLimitExceeded();
|
||||
checkFHEGasBlockLimit();
|
||||
}
|
||||
|
||||
function payForFheBitAnd(address payer, uint8 resultType, bytes1 scalarByte) external {
|
||||
function payForFheBitAnd(address payer, uint8 resultType, bytes1 scalarByte) external virtual {
|
||||
if (msg.sender != tfheExecutorAddress) revert CallerMustBeTFHEExecutorContract();
|
||||
checkIfNewBlock();
|
||||
if (scalarByte != 0x00) revert OnlyNonScalarOperationsAreSupported();
|
||||
@@ -292,10 +341,10 @@ contract FHEPayment is Ownable2Step {
|
||||
} else if (resultType == 5) {
|
||||
updateFunding(payer, 38000);
|
||||
}
|
||||
if (currentBlockConsumption >= FHE_GAS_BLOCKLIMIT) revert FHEGasBlockLimitExceeded();
|
||||
checkFHEGasBlockLimit();
|
||||
}
|
||||
|
||||
function payForFheBitOr(address payer, uint8 resultType, bytes1 scalarByte) external {
|
||||
function payForFheBitOr(address payer, uint8 resultType, bytes1 scalarByte) external virtual {
|
||||
if (msg.sender != tfheExecutorAddress) revert CallerMustBeTFHEExecutorContract();
|
||||
checkIfNewBlock();
|
||||
if (scalarByte != 0x00) revert OnlyNonScalarOperationsAreSupported();
|
||||
@@ -312,10 +361,10 @@ contract FHEPayment is Ownable2Step {
|
||||
} else if (resultType == 5) {
|
||||
updateFunding(payer, 38000);
|
||||
}
|
||||
if (currentBlockConsumption >= FHE_GAS_BLOCKLIMIT) revert FHEGasBlockLimitExceeded();
|
||||
checkFHEGasBlockLimit();
|
||||
}
|
||||
|
||||
function payForFheBitXor(address payer, uint8 resultType, bytes1 scalarByte) external {
|
||||
function payForFheBitXor(address payer, uint8 resultType, bytes1 scalarByte) external virtual {
|
||||
if (msg.sender != tfheExecutorAddress) revert CallerMustBeTFHEExecutorContract();
|
||||
checkIfNewBlock();
|
||||
if (scalarByte != 0x00) revert OnlyNonScalarOperationsAreSupported();
|
||||
@@ -332,10 +381,10 @@ contract FHEPayment is Ownable2Step {
|
||||
} else if (resultType == 5) {
|
||||
updateFunding(payer, 38000);
|
||||
}
|
||||
if (currentBlockConsumption >= FHE_GAS_BLOCKLIMIT) revert FHEGasBlockLimitExceeded();
|
||||
checkFHEGasBlockLimit();
|
||||
}
|
||||
|
||||
function payForFheShl(address payer, uint8 resultType, bytes1 scalarByte) external {
|
||||
function payForFheShl(address payer, uint8 resultType, bytes1 scalarByte) external virtual {
|
||||
if (msg.sender != tfheExecutorAddress) revert CallerMustBeTFHEExecutorContract();
|
||||
checkIfNewBlock();
|
||||
if (scalarByte == 0x01) {
|
||||
@@ -363,10 +412,10 @@ contract FHEPayment is Ownable2Step {
|
||||
updateFunding(payer, 227000);
|
||||
}
|
||||
}
|
||||
if (currentBlockConsumption >= FHE_GAS_BLOCKLIMIT) revert FHEGasBlockLimitExceeded();
|
||||
checkFHEGasBlockLimit();
|
||||
}
|
||||
|
||||
function payForFheShr(address payer, uint8 resultType, bytes1 scalarByte) external {
|
||||
function payForFheShr(address payer, uint8 resultType, bytes1 scalarByte) external virtual {
|
||||
if (msg.sender != tfheExecutorAddress) revert CallerMustBeTFHEExecutorContract();
|
||||
checkIfNewBlock();
|
||||
if (scalarByte == 0x01) {
|
||||
@@ -394,10 +443,10 @@ contract FHEPayment is Ownable2Step {
|
||||
updateFunding(payer, 227000);
|
||||
}
|
||||
}
|
||||
if (currentBlockConsumption >= FHE_GAS_BLOCKLIMIT) revert FHEGasBlockLimitExceeded();
|
||||
checkFHEGasBlockLimit();
|
||||
}
|
||||
|
||||
function payForFheRotl(address payer, uint8 resultType, bytes1 scalarByte) external {
|
||||
function payForFheRotl(address payer, uint8 resultType, bytes1 scalarByte) external virtual {
|
||||
if (msg.sender != tfheExecutorAddress) revert CallerMustBeTFHEExecutorContract();
|
||||
checkIfNewBlock();
|
||||
if (scalarByte == 0x01) {
|
||||
@@ -425,10 +474,10 @@ contract FHEPayment is Ownable2Step {
|
||||
updateFunding(payer, 227000);
|
||||
}
|
||||
}
|
||||
if (currentBlockConsumption >= FHE_GAS_BLOCKLIMIT) revert FHEGasBlockLimitExceeded();
|
||||
checkFHEGasBlockLimit();
|
||||
}
|
||||
|
||||
function payForFheRotr(address payer, uint8 resultType, bytes1 scalarByte) external {
|
||||
function payForFheRotr(address payer, uint8 resultType, bytes1 scalarByte) external virtual {
|
||||
if (msg.sender != tfheExecutorAddress) revert CallerMustBeTFHEExecutorContract();
|
||||
checkIfNewBlock();
|
||||
if (scalarByte == 0x01) {
|
||||
@@ -456,10 +505,10 @@ contract FHEPayment is Ownable2Step {
|
||||
updateFunding(payer, 227000);
|
||||
}
|
||||
}
|
||||
if (currentBlockConsumption >= FHE_GAS_BLOCKLIMIT) revert FHEGasBlockLimitExceeded();
|
||||
checkFHEGasBlockLimit();
|
||||
}
|
||||
|
||||
function payForFheEq(address payer, uint8 resultType, bytes1 scalarByte) external {
|
||||
function payForFheEq(address payer, uint8 resultType, bytes1 scalarByte) external virtual {
|
||||
if (msg.sender != tfheExecutorAddress) revert CallerMustBeTFHEExecutorContract();
|
||||
checkIfNewBlock();
|
||||
if (scalarByte == 0x01) {
|
||||
@@ -495,10 +544,10 @@ contract FHEPayment is Ownable2Step {
|
||||
updateFunding(payer, 300000);
|
||||
}
|
||||
}
|
||||
if (currentBlockConsumption >= FHE_GAS_BLOCKLIMIT) revert FHEGasBlockLimitExceeded();
|
||||
checkFHEGasBlockLimit();
|
||||
}
|
||||
|
||||
function payForFheNe(address payer, uint8 resultType, bytes1 scalarByte) external {
|
||||
function payForFheNe(address payer, uint8 resultType, bytes1 scalarByte) external virtual {
|
||||
if (msg.sender != tfheExecutorAddress) revert CallerMustBeTFHEExecutorContract();
|
||||
checkIfNewBlock();
|
||||
if (scalarByte == 0x01) {
|
||||
@@ -534,10 +583,10 @@ contract FHEPayment is Ownable2Step {
|
||||
updateFunding(payer, 300000);
|
||||
}
|
||||
}
|
||||
if (currentBlockConsumption >= FHE_GAS_BLOCKLIMIT) revert FHEGasBlockLimitExceeded();
|
||||
checkFHEGasBlockLimit();
|
||||
}
|
||||
|
||||
function payForFheGe(address payer, uint8 resultType, bytes1 scalarByte) external {
|
||||
function payForFheGe(address payer, uint8 resultType, bytes1 scalarByte) external virtual {
|
||||
if (msg.sender != tfheExecutorAddress) revert CallerMustBeTFHEExecutorContract();
|
||||
checkIfNewBlock();
|
||||
if (scalarByte == 0x01) {
|
||||
@@ -565,10 +614,10 @@ contract FHEPayment is Ownable2Step {
|
||||
updateFunding(payer, 156000);
|
||||
}
|
||||
}
|
||||
if (currentBlockConsumption >= FHE_GAS_BLOCKLIMIT) revert FHEGasBlockLimitExceeded();
|
||||
checkFHEGasBlockLimit();
|
||||
}
|
||||
|
||||
function payForFheGt(address payer, uint8 resultType, bytes1 scalarByte) external {
|
||||
function payForFheGt(address payer, uint8 resultType, bytes1 scalarByte) external virtual {
|
||||
if (msg.sender != tfheExecutorAddress) revert CallerMustBeTFHEExecutorContract();
|
||||
checkIfNewBlock();
|
||||
if (scalarByte == 0x01) {
|
||||
@@ -596,10 +645,10 @@ contract FHEPayment is Ownable2Step {
|
||||
updateFunding(payer, 156000);
|
||||
}
|
||||
}
|
||||
if (currentBlockConsumption >= FHE_GAS_BLOCKLIMIT) revert FHEGasBlockLimitExceeded();
|
||||
checkFHEGasBlockLimit();
|
||||
}
|
||||
|
||||
function payForFheLe(address payer, uint8 resultType, bytes1 scalarByte) external {
|
||||
function payForFheLe(address payer, uint8 resultType, bytes1 scalarByte) external virtual {
|
||||
if (msg.sender != tfheExecutorAddress) revert CallerMustBeTFHEExecutorContract();
|
||||
checkIfNewBlock();
|
||||
if (scalarByte == 0x01) {
|
||||
@@ -627,10 +676,10 @@ contract FHEPayment is Ownable2Step {
|
||||
updateFunding(payer, 156000);
|
||||
}
|
||||
}
|
||||
if (currentBlockConsumption >= FHE_GAS_BLOCKLIMIT) revert FHEGasBlockLimitExceeded();
|
||||
checkFHEGasBlockLimit();
|
||||
}
|
||||
|
||||
function payForFheLt(address payer, uint8 resultType, bytes1 scalarByte) external {
|
||||
function payForFheLt(address payer, uint8 resultType, bytes1 scalarByte) external virtual {
|
||||
if (msg.sender != tfheExecutorAddress) revert CallerMustBeTFHEExecutorContract();
|
||||
checkIfNewBlock();
|
||||
if (scalarByte == 0x01) {
|
||||
@@ -658,10 +707,10 @@ contract FHEPayment is Ownable2Step {
|
||||
updateFunding(payer, 156000);
|
||||
}
|
||||
}
|
||||
if (currentBlockConsumption >= FHE_GAS_BLOCKLIMIT) revert FHEGasBlockLimitExceeded();
|
||||
checkFHEGasBlockLimit();
|
||||
}
|
||||
|
||||
function payForFheMin(address payer, uint8 resultType, bytes1 scalarByte) external {
|
||||
function payForFheMin(address payer, uint8 resultType, bytes1 scalarByte) external virtual {
|
||||
if (msg.sender != tfheExecutorAddress) revert CallerMustBeTFHEExecutorContract();
|
||||
checkIfNewBlock();
|
||||
if (scalarByte == 0x01) {
|
||||
@@ -689,10 +738,10 @@ contract FHEPayment is Ownable2Step {
|
||||
updateFunding(payer, 210000);
|
||||
}
|
||||
}
|
||||
if (currentBlockConsumption >= FHE_GAS_BLOCKLIMIT) revert FHEGasBlockLimitExceeded();
|
||||
checkFHEGasBlockLimit();
|
||||
}
|
||||
|
||||
function payForFheMax(address payer, uint8 resultType, bytes1 scalarByte) external {
|
||||
function payForFheMax(address payer, uint8 resultType, bytes1 scalarByte) external virtual {
|
||||
if (msg.sender != tfheExecutorAddress) revert CallerMustBeTFHEExecutorContract();
|
||||
checkIfNewBlock();
|
||||
if (scalarByte == 0x01) {
|
||||
@@ -720,10 +769,10 @@ contract FHEPayment is Ownable2Step {
|
||||
updateFunding(payer, 210000);
|
||||
}
|
||||
}
|
||||
if (currentBlockConsumption >= FHE_GAS_BLOCKLIMIT) revert FHEGasBlockLimitExceeded();
|
||||
checkFHEGasBlockLimit();
|
||||
}
|
||||
|
||||
function payForFheNeg(address payer, uint8 resultType) external {
|
||||
function payForFheNeg(address payer, uint8 resultType) external virtual {
|
||||
if (msg.sender != tfheExecutorAddress) revert CallerMustBeTFHEExecutorContract();
|
||||
if (resultType == 1) {
|
||||
updateFunding(payer, 60000);
|
||||
@@ -736,10 +785,10 @@ contract FHEPayment is Ownable2Step {
|
||||
} else if (resultType == 5) {
|
||||
updateFunding(payer, 199000);
|
||||
}
|
||||
if (currentBlockConsumption >= FHE_GAS_BLOCKLIMIT) revert FHEGasBlockLimitExceeded();
|
||||
checkFHEGasBlockLimit();
|
||||
}
|
||||
|
||||
function payForFheNot(address payer, uint8 resultType) external {
|
||||
function payForFheNot(address payer, uint8 resultType) external virtual {
|
||||
if (msg.sender != tfheExecutorAddress) revert CallerMustBeTFHEExecutorContract();
|
||||
if (resultType == 0) {
|
||||
updateFunding(payer, 30000);
|
||||
@@ -754,10 +803,10 @@ contract FHEPayment is Ownable2Step {
|
||||
} else if (resultType == 5) {
|
||||
updateFunding(payer, 37000);
|
||||
}
|
||||
if (currentBlockConsumption >= FHE_GAS_BLOCKLIMIT) revert FHEGasBlockLimitExceeded();
|
||||
checkFHEGasBlockLimit();
|
||||
}
|
||||
|
||||
function payForCast(address payer, uint8 resultType) external {
|
||||
function payForCast(address payer, uint8 resultType) external virtual {
|
||||
if (msg.sender != tfheExecutorAddress) revert CallerMustBeTFHEExecutorContract();
|
||||
if (resultType == 1) {
|
||||
updateFunding(payer, 200);
|
||||
@@ -770,10 +819,10 @@ contract FHEPayment is Ownable2Step {
|
||||
} else if (resultType == 5) {
|
||||
updateFunding(payer, 200);
|
||||
}
|
||||
if (currentBlockConsumption >= FHE_GAS_BLOCKLIMIT) revert FHEGasBlockLimitExceeded();
|
||||
checkFHEGasBlockLimit();
|
||||
}
|
||||
|
||||
function payForTrivialEncrypt(address payer, uint8 resultType) external {
|
||||
function payForTrivialEncrypt(address payer, uint8 resultType) external virtual {
|
||||
if (msg.sender != tfheExecutorAddress) revert CallerMustBeTFHEExecutorContract();
|
||||
if (resultType == 0) {
|
||||
updateFunding(payer, 100);
|
||||
@@ -790,10 +839,10 @@ contract FHEPayment is Ownable2Step {
|
||||
} else if (resultType == 7) {
|
||||
updateFunding(payer, 700);
|
||||
}
|
||||
if (currentBlockConsumption >= FHE_GAS_BLOCKLIMIT) revert FHEGasBlockLimitExceeded();
|
||||
checkFHEGasBlockLimit();
|
||||
}
|
||||
|
||||
function payForIfThenElse(address payer, uint8 resultType) external {
|
||||
function payForIfThenElse(address payer, uint8 resultType) external virtual {
|
||||
if (msg.sender != tfheExecutorAddress) revert CallerMustBeTFHEExecutorContract();
|
||||
if (resultType == 1) {
|
||||
updateFunding(payer, 45000);
|
||||
@@ -808,10 +857,10 @@ contract FHEPayment is Ownable2Step {
|
||||
} else if (resultType == 7) {
|
||||
updateFunding(payer, 80000);
|
||||
}
|
||||
if (currentBlockConsumption >= FHE_GAS_BLOCKLIMIT) revert FHEGasBlockLimitExceeded();
|
||||
checkFHEGasBlockLimit();
|
||||
}
|
||||
|
||||
function payForFheRand(address payer, uint8 resultType) external {
|
||||
function payForFheRand(address payer, uint8 resultType) external virtual {
|
||||
if (msg.sender != tfheExecutorAddress) revert CallerMustBeTFHEExecutorContract();
|
||||
if (resultType == 2) {
|
||||
updateFunding(payer, 100000);
|
||||
@@ -822,10 +871,10 @@ contract FHEPayment is Ownable2Step {
|
||||
} else if (resultType == 5) {
|
||||
updateFunding(payer, 100000);
|
||||
}
|
||||
if (currentBlockConsumption >= FHE_GAS_BLOCKLIMIT) revert FHEGasBlockLimitExceeded();
|
||||
checkFHEGasBlockLimit();
|
||||
}
|
||||
|
||||
function payForFheRandBounded(address payer, uint8 resultType) external {
|
||||
function payForFheRandBounded(address payer, uint8 resultType) external virtual {
|
||||
if (msg.sender != tfheExecutorAddress) revert CallerMustBeTFHEExecutorContract();
|
||||
if (resultType == 2) {
|
||||
updateFunding(payer, 100000);
|
||||
@@ -836,12 +885,12 @@ contract FHEPayment is Ownable2Step {
|
||||
} else if (resultType == 5) {
|
||||
updateFunding(payer, 100000);
|
||||
}
|
||||
if (currentBlockConsumption >= FHE_GAS_BLOCKLIMIT) revert FHEGasBlockLimitExceeded();
|
||||
checkFHEGasBlockLimit();
|
||||
}
|
||||
|
||||
/// @notice Getter for the name and version of the contract
|
||||
/// @return string representing the name and the version of the contract
|
||||
function getVersion() external pure returns (string memory) {
|
||||
function getVersion() external pure virtual returns (string memory) {
|
||||
return
|
||||
string(
|
||||
abi.encodePacked(
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
|
||||
pragma solidity ^0.8.24;
|
||||
|
||||
address constant fhePaymentAdd = 0xcCAe95fF1d11656358E782570dF0418F59fA40e1;
|
||||
address constant fhePaymentAdd = 0x52054F36036811ca418be59e41Fc6DD1b9e4F4c8;
|
||||
|
||||
@@ -3,21 +3,22 @@ pragma solidity ^0.8.24;
|
||||
|
||||
// Importing OpenZeppelin contracts for cryptographic signature verification and access control.
|
||||
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
|
||||
import "@openzeppelin/contracts/utils/cryptography/EIP712.sol";
|
||||
import "@openzeppelin/contracts/access/Ownable2Step.sol";
|
||||
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
|
||||
import "@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.sol";
|
||||
import "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol";
|
||||
import "@openzeppelin/contracts/utils/Strings.sol";
|
||||
|
||||
/// @title KMS Verifier for signature verification and verifier management
|
||||
/// @author The developer
|
||||
/// @notice This contract allows for the management of verifiers and provides methods to verify signatures
|
||||
/// @dev The contract uses OpenZeppelin's SignatureChecker for cryptographic operations
|
||||
contract KMSVerifier is Ownable2Step, EIP712 {
|
||||
contract KMSVerifier is UUPSUpgradeable, Ownable2StepUpgradeable, EIP712Upgradeable {
|
||||
struct DecryptionResult {
|
||||
uint256[] handlesList;
|
||||
bytes decryptedResult;
|
||||
}
|
||||
|
||||
string public constant DECRYPTIONRESULT_TYPE = "DecryptionResult(uint256[] handlesList,bytes decryptedResult)";
|
||||
string private constant DECRYPTIONRESULT_TYPE = "DecryptionResult(uint256[] handlesList,bytes decryptedResult)";
|
||||
bytes32 private constant DECRYPTIONRESULT_TYPE_HASH = keccak256(bytes(DECRYPTIONRESULT_TYPE));
|
||||
|
||||
/// @notice Name of the contract
|
||||
@@ -28,6 +29,44 @@ contract KMSVerifier is Ownable2Step, EIP712 {
|
||||
uint256 private constant MINOR_VERSION = 1;
|
||||
uint256 private constant PATCH_VERSION = 0;
|
||||
|
||||
/// @custom:storage-location erc7201:fhevm.storage.KMSVerifier
|
||||
struct KMSVerifierStorage {
|
||||
mapping(address => bool) isVerifier; /// @notice Mapping to keep track of addresses that are verifiers
|
||||
address[] verifiers; /// @notice Array to keep track of all verifiers
|
||||
uint256 threshold; /// @notice The threshold for the number of verifiers required for a signature to be valid
|
||||
}
|
||||
|
||||
// keccak256(abi.encode(uint256(keccak256("fhevm.storage.KMSVerifier")) - 1)) & ~bytes32(uint256(0xff))
|
||||
bytes32 private constant KMSVerifierStorageLocation =
|
||||
0x7e81a744be86773af8644dd7304fa1dc9350ccabf16cfcaa614ddb78b4ce8900;
|
||||
|
||||
function _getKMSVerifierStorage() internal pure returns (KMSVerifierStorage storage $) {
|
||||
assembly {
|
||||
$.slot := KMSVerifierStorageLocation
|
||||
}
|
||||
}
|
||||
|
||||
function _authorizeUpgrade(address _newImplementation) internal virtual override onlyOwner {}
|
||||
|
||||
function isVerifier(address account) public virtual returns (bool) {
|
||||
KMSVerifierStorage storage $ = _getKMSVerifierStorage();
|
||||
return $.isVerifier[account];
|
||||
}
|
||||
|
||||
function getVerifiers() public view virtual returns (address[] memory) {
|
||||
KMSVerifierStorage storage $ = _getKMSVerifierStorage();
|
||||
return $.verifiers;
|
||||
}
|
||||
|
||||
function getThreshold() public view virtual returns (uint256) {
|
||||
KMSVerifierStorage storage $ = _getKMSVerifierStorage();
|
||||
return $.threshold;
|
||||
}
|
||||
|
||||
function get_DECRYPTIONRESULT_TYPE() public view virtual returns (string memory) {
|
||||
return DECRYPTIONRESULT_TYPE;
|
||||
}
|
||||
|
||||
/// @notice Emitted when a verifier is added
|
||||
/// @param verifier The address of the verifier that was added
|
||||
event VerifierAdded(address indexed verifier);
|
||||
@@ -36,36 +75,37 @@ contract KMSVerifier is Ownable2Step, EIP712 {
|
||||
/// @param verifier The address of the verifier that was removed
|
||||
event VerifierRemoved(address indexed verifier);
|
||||
|
||||
/// @notice Mapping to keep track of addresses that are verifiers
|
||||
mapping(address => bool) public isVerifier;
|
||||
/// @custom:oz-upgrades-unsafe-allow constructor
|
||||
constructor() {
|
||||
_disableInitializers();
|
||||
}
|
||||
|
||||
/// @notice Array to keep track of all verifiers
|
||||
address[] public verifiers;
|
||||
|
||||
/// @notice The threshold for the number of verifiers required for a signature to be valid
|
||||
uint256 public threshold;
|
||||
|
||||
/// @notice Initializes the contract setting the deployer as the initial owner
|
||||
constructor() Ownable(msg.sender) EIP712(CONTRACT_NAME, "1") {}
|
||||
/// @notice Initializes the contract setting `initialOwner` as the initial owner
|
||||
function initialize(address initialOwner) external initializer {
|
||||
__Ownable_init(initialOwner);
|
||||
__EIP712_init(CONTRACT_NAME, "1");
|
||||
}
|
||||
|
||||
/// @notice Sets the threshold for the number of verifiers required for a signature to be valid
|
||||
function applyThreshold() internal {
|
||||
threshold = (verifiers.length - 1) / 3 + 1;
|
||||
function applyThreshold() internal virtual {
|
||||
KMSVerifierStorage storage $ = _getKMSVerifierStorage();
|
||||
$.threshold = ($.verifiers.length - 1) / 3 + 1;
|
||||
}
|
||||
|
||||
/// @notice Adds a new verifier
|
||||
/// @dev Only the owner can add a verifier
|
||||
/// @param verifier The address to be added as a verifier
|
||||
function addVerifier(address verifier) public onlyOwner {
|
||||
function addVerifier(address verifier) public virtual onlyOwner {
|
||||
require(verifier != address(0), "KMSVerifier: Address is null");
|
||||
require(!isVerifier[verifier], "KMSVerifier: Address is already a verifier");
|
||||
isVerifier[verifier] = true;
|
||||
verifiers.push(verifier);
|
||||
KMSVerifierStorage storage $ = _getKMSVerifierStorage();
|
||||
require($.isVerifier[verifier], "KMSVerifier: Address is already a verifier");
|
||||
$.isVerifier[verifier] = true;
|
||||
$.verifiers.push(verifier);
|
||||
applyThreshold();
|
||||
emit VerifierAdded(verifier);
|
||||
}
|
||||
|
||||
function hashDecryptionResult(DecryptionResult memory decRes) internal view returns (bytes32) {
|
||||
function hashDecryptionResult(DecryptionResult memory decRes) internal view virtual returns (bytes32) {
|
||||
return
|
||||
_hashTypedDataV4(
|
||||
keccak256(
|
||||
@@ -81,17 +121,18 @@ contract KMSVerifier is Ownable2Step, EIP712 {
|
||||
/// @notice Removes an existing verifier
|
||||
/// @dev Only the owner can remove a verifier
|
||||
/// @param verifier The address to be removed from verifiers
|
||||
function removeVerifier(address verifier) public onlyOwner {
|
||||
require(isVerifier[verifier], "KMSVerifier: Address is not a verifier");
|
||||
function removeVerifier(address verifier) public virtual onlyOwner {
|
||||
KMSVerifierStorage storage $ = _getKMSVerifierStorage();
|
||||
require($.isVerifier[verifier], "KMSVerifier: Address is not a verifier");
|
||||
|
||||
// Remove verifier from the mapping
|
||||
isVerifier[verifier] = false;
|
||||
$.isVerifier[verifier] = false;
|
||||
|
||||
// Find the index of the verifier and remove it from the array
|
||||
for (uint i = 0; i < verifiers.length; i++) {
|
||||
if (verifiers[i] == verifier) {
|
||||
verifiers[i] = verifiers[verifiers.length - 1]; // Move the last element into the place to delete
|
||||
verifiers.pop(); // Remove the last element
|
||||
for (uint i = 0; i < $.verifiers.length; i++) {
|
||||
if ($.verifiers[i] == verifier) {
|
||||
$.verifiers[i] = $.verifiers[$.verifiers.length - 1]; // Move the last element into the place to delete
|
||||
$.verifiers.pop(); // Remove the last element
|
||||
applyThreshold();
|
||||
emit VerifierRemoved(verifier);
|
||||
return;
|
||||
@@ -104,7 +145,7 @@ contract KMSVerifier is Ownable2Step, EIP712 {
|
||||
/// @param message The hash of the message that was signed
|
||||
/// @param signature The signature to verify
|
||||
/// @return signer The address that supposedly signed the message
|
||||
function recoverSigner(bytes32 message, bytes memory signature) internal pure returns (address) {
|
||||
function recoverSigner(bytes32 message, bytes memory signature) internal pure virtual returns (address) {
|
||||
address signerRecovered = ECDSA.recover(message, signature);
|
||||
return signerRecovered;
|
||||
}
|
||||
@@ -119,7 +160,7 @@ contract KMSVerifier is Ownable2Step, EIP712 {
|
||||
uint256[] memory handlesList,
|
||||
bytes memory decryptedResult,
|
||||
bytes[] memory signatures
|
||||
) public returns (bool) {
|
||||
) public virtual returns (bool) {
|
||||
DecryptionResult memory decRes;
|
||||
decRes.handlesList = handlesList;
|
||||
decRes.decryptedResult = decryptedResult;
|
||||
@@ -132,22 +173,23 @@ contract KMSVerifier is Ownable2Step, EIP712 {
|
||||
/// @param message The hash of the message that was signed by all signers
|
||||
/// @param signatures An array of signatures to verify
|
||||
/// @return true if enough provided signatures are valid, false otherwise
|
||||
function verifySignaturesDigest(bytes32 message, bytes[] memory signatures) internal returns (bool) {
|
||||
function verifySignaturesDigest(bytes32 message, bytes[] memory signatures) internal virtual returns (bool) {
|
||||
uint256 numSignatures = signatures.length;
|
||||
require(numSignatures > 0, "KmsVerifier: no signatures provided");
|
||||
require(numSignatures >= threshold, "KmsVerifier: at least threshold number of signatures required");
|
||||
KMSVerifierStorage storage $ = _getKMSVerifierStorage();
|
||||
require(numSignatures >= $.threshold, "KmsVerifier: at least threshold number of signatures required");
|
||||
address[] memory recoveredVerifiers = new address[](numSignatures);
|
||||
uint256 uniqueValidCount;
|
||||
for (uint256 i = 0; i < numSignatures; i++) {
|
||||
address signerRecovered = recoverSigner(message, signatures[i]);
|
||||
if (isVerifier[signerRecovered]) {
|
||||
if ($.isVerifier[signerRecovered]) {
|
||||
if (!tload(signerRecovered)) {
|
||||
recoveredVerifiers[uniqueValidCount] = signerRecovered;
|
||||
uniqueValidCount++;
|
||||
tstore(signerRecovered, 1);
|
||||
}
|
||||
}
|
||||
if (uniqueValidCount >= threshold) {
|
||||
if (uniqueValidCount >= $.threshold) {
|
||||
for (uint256 j = 0; i < uniqueValidCount; i++) {
|
||||
/// @note : clearing transient storage for composability
|
||||
tstore(recoveredVerifiers[j], 0);
|
||||
@@ -162,7 +204,7 @@ contract KMSVerifier is Ownable2Step, EIP712 {
|
||||
/// @dev Uses inline assembly to access the Transient Storage's tstore operation.
|
||||
/// @param location The address used as key where transient storage of the contract is written at
|
||||
/// @param value An uint256 stored at location key in transient storage of the contract
|
||||
function tstore(address location, uint256 value) private {
|
||||
function tstore(address location, uint256 value) internal virtual {
|
||||
assembly {
|
||||
tstore(location, value)
|
||||
}
|
||||
@@ -172,7 +214,7 @@ contract KMSVerifier is Ownable2Step, EIP712 {
|
||||
/// @dev Uses inline assembly to access the Transient Storage's tload operation.
|
||||
/// @param location The address used as key where transient storage of the contract is read at
|
||||
/// @return value true if value stored at the given location is non-null, false otherwise.
|
||||
function tload(address location) private view returns (bool value) {
|
||||
function tload(address location) internal view virtual returns (bool value) {
|
||||
assembly {
|
||||
value := tload(location)
|
||||
}
|
||||
@@ -180,7 +222,7 @@ contract KMSVerifier is Ownable2Step, EIP712 {
|
||||
|
||||
/// @notice Getter for the name and version of the contract
|
||||
/// @return string representing the name and the version of the contract
|
||||
function getVersion() external pure returns (string memory) {
|
||||
function getVersion() external pure virtual returns (string memory) {
|
||||
return
|
||||
string(
|
||||
abi.encodePacked(
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
|
||||
pragma solidity ^0.8.24;
|
||||
|
||||
address constant kmsVerifierAdd = 0x12B064FB845C1cc05e9493856a1D637a73e944bE;
|
||||
address constant kmsVerifierAdd = 0x857Ca72A957920Fa0FB138602995839866Bd4005;
|
||||
|
||||
@@ -7,12 +7,13 @@ import "./FHEPayment.sol";
|
||||
import "./FhevmLib.sol";
|
||||
import "./ACLAddress.sol";
|
||||
import "./FHEPaymentAddress.sol";
|
||||
import "./FhevmLib.sol";
|
||||
import "@openzeppelin/contracts/utils/Strings.sol";
|
||||
import "@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.sol";
|
||||
import "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol";
|
||||
|
||||
address constant EXT_TFHE_LIBRARY = address(0x000000000000000000000000000000000000005d);
|
||||
|
||||
contract TFHEExecutor {
|
||||
contract TFHEExecutor is UUPSUpgradeable, Ownable2StepUpgradeable {
|
||||
/// @notice Handle version
|
||||
uint8 public constant HANDLE_VERSION = 0;
|
||||
|
||||
@@ -27,7 +28,32 @@ contract TFHEExecutor {
|
||||
ACL private constant acl = ACL(aclAdd);
|
||||
FHEPayment private constant fhePayment = FHEPayment(fhePaymentAdd);
|
||||
|
||||
uint256 public counterRand = 0; // counter used for computing handles of randomness operators
|
||||
/// @custom:storage-location erc7201:fhevm.storage.TFHEExecutor
|
||||
struct TFHEExecutorStorage {
|
||||
uint256 counterRand; /// @notice counter used for computing handles of randomness operators
|
||||
}
|
||||
|
||||
// keccak256(abi.encode(uint256(keccak256("fhevm.storage.TFHEExecutor")) - 1)) & ~bytes32(uint256(0xff))
|
||||
bytes32 private constant TFHEExecutorStorageLocation =
|
||||
0xa436a06f0efce5ea38c956a21e24202a59b3b746d48a23fb52b4a5bc33fe3e00;
|
||||
|
||||
function _getTFHEExecutorStorage() internal pure returns (TFHEExecutorStorage storage $) {
|
||||
assembly {
|
||||
$.slot := TFHEExecutorStorageLocation
|
||||
}
|
||||
}
|
||||
|
||||
function _authorizeUpgrade(address _newImplementation) internal virtual override onlyOwner {}
|
||||
|
||||
/// @custom:oz-upgrades-unsafe-allow constructor
|
||||
constructor() {
|
||||
_disableInitializers();
|
||||
}
|
||||
|
||||
/// @notice Initializes the contract setting `initialOwner` as the initial owner
|
||||
function initialize(address initialOwner) external initializer {
|
||||
__Ownable_init(initialOwner);
|
||||
}
|
||||
|
||||
enum Operators {
|
||||
fheAdd,
|
||||
@@ -60,30 +86,30 @@ contract TFHEExecutor {
|
||||
fheRandBounded
|
||||
}
|
||||
|
||||
function isPowerOfTwo(uint256 x) internal pure returns (bool) {
|
||||
function isPowerOfTwo(uint256 x) internal pure virtual returns (bool) {
|
||||
return (x > 0) && ((x & (x - 1)) == 0);
|
||||
}
|
||||
|
||||
/// @dev handle format for user inputs is: keccak256(keccak256(CiphertextFHEList)||index_handle)[0:29] || index_handle || handle_type || handle_version
|
||||
/// @dev other handles format (fhe ops results) is: keccak256(keccak256(rawCiphertextFHEList)||index_handle)[0:30] || handle_type || handle_version
|
||||
/// @dev the CiphertextFHEList actually contains: 1 byte (= N) for size of handles_list, N bytes for the handles_types : 1 per handle, then the original fhe160list raw ciphertext
|
||||
function typeOf(uint256 handle) internal pure returns (uint8) {
|
||||
function typeOf(uint256 handle) internal pure virtual returns (uint8) {
|
||||
uint8 typeCt = uint8(handle >> 8);
|
||||
return typeCt;
|
||||
}
|
||||
|
||||
function appendType(uint256 prehandle, uint8 handleType) internal pure returns (uint256 result) {
|
||||
function appendType(uint256 prehandle, uint8 handleType) internal pure virtual returns (uint256 result) {
|
||||
result = prehandle & 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000;
|
||||
result = result | (uint256(handleType) << 8); // append type
|
||||
result = result | HANDLE_VERSION;
|
||||
}
|
||||
|
||||
function requireType(uint256 handle, uint256 supportedTypes) internal pure {
|
||||
function requireType(uint256 handle, uint256 supportedTypes) internal pure virtual {
|
||||
uint8 typeCt = typeOf(handle);
|
||||
require((1 << typeCt) & supportedTypes > 0, "Unsupported type");
|
||||
}
|
||||
|
||||
function unaryOp(Operators op, uint256 ct) internal returns (uint256 result) {
|
||||
function unaryOp(Operators op, uint256 ct) internal virtual returns (uint256 result) {
|
||||
require(acl.isAllowed(ct, msg.sender), "Sender doesn't own ct on op");
|
||||
result = uint256(keccak256(abi.encodePacked(op, ct)));
|
||||
uint8 typeCt = typeOf(ct);
|
||||
@@ -97,7 +123,7 @@ contract TFHEExecutor {
|
||||
uint256 rhs,
|
||||
bytes1 scalar,
|
||||
uint8 resultType
|
||||
) internal returns (uint256 result) {
|
||||
) internal virtual returns (uint256 result) {
|
||||
require(acl.isAllowed(lhs, msg.sender), "Sender doesn't own lhs on op");
|
||||
if (scalar == 0x00) {
|
||||
require(acl.isAllowed(rhs, msg.sender), "Sender doesn't own rhs on op");
|
||||
@@ -110,7 +136,12 @@ contract TFHEExecutor {
|
||||
acl.allowTransient(result, msg.sender);
|
||||
}
|
||||
|
||||
function ternaryOp(Operators op, uint256 lhs, uint256 middle, uint256 rhs) internal returns (uint256 result) {
|
||||
function ternaryOp(
|
||||
Operators op,
|
||||
uint256 lhs,
|
||||
uint256 middle,
|
||||
uint256 rhs
|
||||
) internal virtual returns (uint256 result) {
|
||||
require(acl.isAllowed(lhs, msg.sender), "Sender doesn't own lhs on op");
|
||||
require(acl.isAllowed(middle, msg.sender), "Sender doesn't own middle on op");
|
||||
require(acl.isAllowed(rhs, msg.sender), "Sender doesn't own rhs on op");
|
||||
@@ -124,7 +155,7 @@ contract TFHEExecutor {
|
||||
acl.allowTransient(result, msg.sender);
|
||||
}
|
||||
|
||||
function fheAdd(uint256 lhs, uint256 rhs, bytes1 scalarByte) external returns (uint256 result) {
|
||||
function fheAdd(uint256 lhs, uint256 rhs, bytes1 scalarByte) external virtual returns (uint256 result) {
|
||||
uint256 supportedTypes = (1 << 1) + (1 << 2) + (1 << 3) + (1 << 4) + (1 << 5);
|
||||
requireType(lhs, supportedTypes);
|
||||
uint8 lhsType = typeOf(lhs);
|
||||
@@ -133,7 +164,7 @@ contract TFHEExecutor {
|
||||
result = binaryOp(Operators.fheAdd, lhs, rhs, scalar, lhsType);
|
||||
}
|
||||
|
||||
function fheSub(uint256 lhs, uint256 rhs, bytes1 scalarByte) external returns (uint256 result) {
|
||||
function fheSub(uint256 lhs, uint256 rhs, bytes1 scalarByte) external virtual returns (uint256 result) {
|
||||
uint256 supportedTypes = (1 << 1) + (1 << 2) + (1 << 3) + (1 << 4) + (1 << 5);
|
||||
requireType(lhs, supportedTypes);
|
||||
uint8 lhsType = typeOf(lhs);
|
||||
@@ -142,7 +173,7 @@ contract TFHEExecutor {
|
||||
result = binaryOp(Operators.fheSub, lhs, rhs, scalar, lhsType);
|
||||
}
|
||||
|
||||
function fheMul(uint256 lhs, uint256 rhs, bytes1 scalarByte) external returns (uint256 result) {
|
||||
function fheMul(uint256 lhs, uint256 rhs, bytes1 scalarByte) external virtual returns (uint256 result) {
|
||||
uint256 supportedTypes = (1 << 1) + (1 << 2) + (1 << 3) + (1 << 4) + (1 << 5);
|
||||
requireType(lhs, supportedTypes);
|
||||
uint8 lhsType = typeOf(lhs);
|
||||
@@ -151,7 +182,7 @@ contract TFHEExecutor {
|
||||
result = binaryOp(Operators.fheMul, lhs, rhs, scalar, lhsType);
|
||||
}
|
||||
|
||||
function fheDiv(uint256 lhs, uint256 rhs, bytes1 scalarByte) external returns (uint256 result) {
|
||||
function fheDiv(uint256 lhs, uint256 rhs, bytes1 scalarByte) external virtual returns (uint256 result) {
|
||||
require(scalarByte & 0x01 == 0x01, "Only fheDiv by a scalar is supported");
|
||||
uint256 supportedTypes = (1 << 1) + (1 << 2) + (1 << 3) + (1 << 4) + (1 << 5);
|
||||
requireType(lhs, supportedTypes);
|
||||
@@ -161,7 +192,7 @@ contract TFHEExecutor {
|
||||
result = binaryOp(Operators.fheDiv, lhs, rhs, scalar, lhsType);
|
||||
}
|
||||
|
||||
function fheRem(uint256 lhs, uint256 rhs, bytes1 scalarByte) external returns (uint256 result) {
|
||||
function fheRem(uint256 lhs, uint256 rhs, bytes1 scalarByte) external virtual returns (uint256 result) {
|
||||
require(scalarByte & 0x01 == 0x01, "Only fheRem by a scalar is supported");
|
||||
uint256 supportedTypes = (1 << 1) + (1 << 2) + (1 << 3) + (1 << 4) + (1 << 5);
|
||||
requireType(lhs, supportedTypes);
|
||||
@@ -171,7 +202,7 @@ contract TFHEExecutor {
|
||||
result = binaryOp(Operators.fheRem, lhs, rhs, scalar, lhsType);
|
||||
}
|
||||
|
||||
function fheBitAnd(uint256 lhs, uint256 rhs, bytes1 scalarByte) external returns (uint256 result) {
|
||||
function fheBitAnd(uint256 lhs, uint256 rhs, bytes1 scalarByte) external virtual returns (uint256 result) {
|
||||
require(scalarByte & 0x01 == 0x00, "Only fheBitAnd by a ciphertext is supported");
|
||||
uint256 supportedTypes = (1 << 0) + (1 << 1) + (1 << 2) + (1 << 3) + (1 << 4) + (1 << 5);
|
||||
requireType(lhs, supportedTypes);
|
||||
@@ -181,7 +212,7 @@ contract TFHEExecutor {
|
||||
result = binaryOp(Operators.fheBitAnd, lhs, rhs, scalar, lhsType);
|
||||
}
|
||||
|
||||
function fheBitOr(uint256 lhs, uint256 rhs, bytes1 scalarByte) external returns (uint256 result) {
|
||||
function fheBitOr(uint256 lhs, uint256 rhs, bytes1 scalarByte) external virtual returns (uint256 result) {
|
||||
require(scalarByte & 0x01 == 0x00, "Only fheBitOr by a ciphertext is supported");
|
||||
uint256 supportedTypes = (1 << 0) + (1 << 1) + (1 << 2) + (1 << 3) + (1 << 4) + (1 << 5);
|
||||
requireType(lhs, supportedTypes);
|
||||
@@ -191,7 +222,7 @@ contract TFHEExecutor {
|
||||
result = binaryOp(Operators.fheBitOr, lhs, rhs, scalar, lhsType);
|
||||
}
|
||||
|
||||
function fheBitXor(uint256 lhs, uint256 rhs, bytes1 scalarByte) external returns (uint256 result) {
|
||||
function fheBitXor(uint256 lhs, uint256 rhs, bytes1 scalarByte) external virtual returns (uint256 result) {
|
||||
require(scalarByte & 0x01 == 0x00, "Only fheBitXor by a ciphertext is supported");
|
||||
uint256 supportedTypes = (1 << 0) + (1 << 1) + (1 << 2) + (1 << 3) + (1 << 4) + (1 << 5);
|
||||
requireType(lhs, supportedTypes);
|
||||
@@ -201,7 +232,7 @@ contract TFHEExecutor {
|
||||
result = binaryOp(Operators.fheBitXor, lhs, rhs, scalar, lhsType);
|
||||
}
|
||||
|
||||
function fheShl(uint256 lhs, uint256 rhs, bytes1 scalarByte) external returns (uint256 result) {
|
||||
function fheShl(uint256 lhs, uint256 rhs, bytes1 scalarByte) external virtual returns (uint256 result) {
|
||||
uint256 supportedTypes = (1 << 1) + (1 << 2) + (1 << 3) + (1 << 4) + (1 << 5);
|
||||
requireType(lhs, supportedTypes);
|
||||
uint8 lhsType = typeOf(lhs);
|
||||
@@ -210,7 +241,7 @@ contract TFHEExecutor {
|
||||
result = binaryOp(Operators.fheShl, lhs, rhs, scalar, lhsType);
|
||||
}
|
||||
|
||||
function fheShr(uint256 lhs, uint256 rhs, bytes1 scalarByte) external returns (uint256 result) {
|
||||
function fheShr(uint256 lhs, uint256 rhs, bytes1 scalarByte) external virtual returns (uint256 result) {
|
||||
uint256 supportedTypes = (1 << 1) + (1 << 2) + (1 << 3) + (1 << 4) + (1 << 5);
|
||||
requireType(lhs, supportedTypes);
|
||||
uint8 lhsType = typeOf(lhs);
|
||||
@@ -219,7 +250,7 @@ contract TFHEExecutor {
|
||||
result = binaryOp(Operators.fheShr, lhs, rhs, scalar, lhsType);
|
||||
}
|
||||
|
||||
function fheRotl(uint256 lhs, uint256 rhs, bytes1 scalarByte) external returns (uint256 result) {
|
||||
function fheRotl(uint256 lhs, uint256 rhs, bytes1 scalarByte) external virtual returns (uint256 result) {
|
||||
uint256 supportedTypes = (1 << 1) + (1 << 2) + (1 << 3) + (1 << 4) + (1 << 5);
|
||||
requireType(lhs, supportedTypes);
|
||||
uint8 lhsType = typeOf(lhs);
|
||||
@@ -237,7 +268,7 @@ contract TFHEExecutor {
|
||||
result = binaryOp(Operators.fheRotr, lhs, rhs, scalar, lhsType);
|
||||
}
|
||||
|
||||
function fheEq(uint256 lhs, uint256 rhs, bytes1 scalarByte) external returns (uint256 result) {
|
||||
function fheEq(uint256 lhs, uint256 rhs, bytes1 scalarByte) external virtual returns (uint256 result) {
|
||||
uint256 supportedTypes = (1 << 1) + (1 << 2) + (1 << 3) + (1 << 4) + (1 << 5) + (1 << 7) + (1 << 11);
|
||||
requireType(lhs, supportedTypes);
|
||||
uint8 lhsType = typeOf(lhs);
|
||||
@@ -246,7 +277,7 @@ contract TFHEExecutor {
|
||||
result = binaryOp(Operators.fheEq, lhs, rhs, scalar, 0);
|
||||
}
|
||||
|
||||
function fheNe(uint256 lhs, uint256 rhs, bytes1 scalarByte) external returns (uint256 result) {
|
||||
function fheNe(uint256 lhs, uint256 rhs, bytes1 scalarByte) external virtual returns (uint256 result) {
|
||||
uint256 supportedTypes = (1 << 1) + (1 << 2) + (1 << 3) + (1 << 4) + (1 << 5) + (1 << 7) + (1 << 11);
|
||||
requireType(lhs, supportedTypes);
|
||||
uint8 lhsType = typeOf(lhs);
|
||||
@@ -255,7 +286,7 @@ contract TFHEExecutor {
|
||||
result = binaryOp(Operators.fheNe, lhs, rhs, scalar, 0);
|
||||
}
|
||||
|
||||
function fheGe(uint256 lhs, uint256 rhs, bytes1 scalarByte) external returns (uint256 result) {
|
||||
function fheGe(uint256 lhs, uint256 rhs, bytes1 scalarByte) external virtual returns (uint256 result) {
|
||||
uint256 supportedTypes = (1 << 1) + (1 << 2) + (1 << 3) + (1 << 4) + (1 << 5);
|
||||
requireType(lhs, supportedTypes);
|
||||
uint8 lhsType = typeOf(lhs);
|
||||
@@ -264,7 +295,7 @@ contract TFHEExecutor {
|
||||
result = binaryOp(Operators.fheGe, lhs, rhs, scalar, 0);
|
||||
}
|
||||
|
||||
function fheGt(uint256 lhs, uint256 rhs, bytes1 scalarByte) external returns (uint256 result) {
|
||||
function fheGt(uint256 lhs, uint256 rhs, bytes1 scalarByte) external virtual returns (uint256 result) {
|
||||
uint256 supportedTypes = (1 << 1) + (1 << 2) + (1 << 3) + (1 << 4) + (1 << 5);
|
||||
requireType(lhs, supportedTypes);
|
||||
uint8 lhsType = typeOf(lhs);
|
||||
@@ -273,7 +304,7 @@ contract TFHEExecutor {
|
||||
result = binaryOp(Operators.fheGt, lhs, rhs, scalar, 0);
|
||||
}
|
||||
|
||||
function fheLe(uint256 lhs, uint256 rhs, bytes1 scalarByte) external returns (uint256 result) {
|
||||
function fheLe(uint256 lhs, uint256 rhs, bytes1 scalarByte) external virtual returns (uint256 result) {
|
||||
uint256 supportedTypes = (1 << 1) + (1 << 2) + (1 << 3) + (1 << 4) + (1 << 5);
|
||||
requireType(lhs, supportedTypes);
|
||||
uint8 lhsType = typeOf(lhs);
|
||||
@@ -282,7 +313,7 @@ contract TFHEExecutor {
|
||||
result = binaryOp(Operators.fheLe, lhs, rhs, scalar, 0);
|
||||
}
|
||||
|
||||
function fheLt(uint256 lhs, uint256 rhs, bytes1 scalarByte) external returns (uint256 result) {
|
||||
function fheLt(uint256 lhs, uint256 rhs, bytes1 scalarByte) external virtual returns (uint256 result) {
|
||||
uint256 supportedTypes = (1 << 1) + (1 << 2) + (1 << 3) + (1 << 4) + (1 << 5);
|
||||
requireType(lhs, supportedTypes);
|
||||
uint8 lhsType = typeOf(lhs);
|
||||
@@ -291,7 +322,7 @@ contract TFHEExecutor {
|
||||
result = binaryOp(Operators.fheLt, lhs, rhs, scalar, 0);
|
||||
}
|
||||
|
||||
function fheMin(uint256 lhs, uint256 rhs, bytes1 scalarByte) external returns (uint256 result) {
|
||||
function fheMin(uint256 lhs, uint256 rhs, bytes1 scalarByte) external virtual returns (uint256 result) {
|
||||
uint256 supportedTypes = (1 << 1) + (1 << 2) + (1 << 3) + (1 << 4) + (1 << 5);
|
||||
requireType(lhs, supportedTypes);
|
||||
uint8 lhsType = typeOf(lhs);
|
||||
@@ -300,7 +331,7 @@ contract TFHEExecutor {
|
||||
result = binaryOp(Operators.fheMin, lhs, rhs, scalar, lhsType);
|
||||
}
|
||||
|
||||
function fheMax(uint256 lhs, uint256 rhs, bytes1 scalarByte) external returns (uint256 result) {
|
||||
function fheMax(uint256 lhs, uint256 rhs, bytes1 scalarByte) external virtual returns (uint256 result) {
|
||||
uint256 supportedTypes = (1 << 1) + (1 << 2) + (1 << 3) + (1 << 4) + (1 << 5);
|
||||
requireType(lhs, supportedTypes);
|
||||
uint8 lhsType = typeOf(lhs);
|
||||
@@ -309,7 +340,7 @@ contract TFHEExecutor {
|
||||
result = binaryOp(Operators.fheMax, lhs, rhs, scalar, lhsType);
|
||||
}
|
||||
|
||||
function fheNeg(uint256 ct) external returns (uint256 result) {
|
||||
function fheNeg(uint256 ct) external virtual returns (uint256 result) {
|
||||
uint256 supportedTypes = (1 << 1) + (1 << 2) + (1 << 3) + (1 << 4) + (1 << 5);
|
||||
requireType(ct, supportedTypes);
|
||||
uint8 typeCt = typeOf(ct);
|
||||
@@ -317,7 +348,7 @@ contract TFHEExecutor {
|
||||
result = unaryOp(Operators.fheNeg, ct);
|
||||
}
|
||||
|
||||
function fheNot(uint256 ct) external returns (uint256 result) {
|
||||
function fheNot(uint256 ct) external virtual returns (uint256 result) {
|
||||
uint256 supportedTypes = (1 << 0) + (1 << 1) + (1 << 2) + (1 << 3) + (1 << 4) + (1 << 5);
|
||||
requireType(ct, supportedTypes);
|
||||
uint8 typeCt = typeOf(ct);
|
||||
@@ -330,7 +361,7 @@ contract TFHEExecutor {
|
||||
address callerAddress,
|
||||
bytes memory inputProof,
|
||||
bytes1 inputType
|
||||
) external returns (uint256 result) {
|
||||
) external virtual returns (uint256 result) {
|
||||
result = FhevmLib(address(EXT_TFHE_LIBRARY)).verifyCiphertext(
|
||||
inputHandle,
|
||||
callerAddress,
|
||||
@@ -341,7 +372,7 @@ contract TFHEExecutor {
|
||||
acl.allowTransient(result, msg.sender);
|
||||
}
|
||||
|
||||
function cast(uint256 ct, bytes1 toType) external returns (uint256 result) {
|
||||
function cast(uint256 ct, bytes1 toType) external virtual returns (uint256 result) {
|
||||
require(acl.isAllowed(ct, msg.sender), "Sender doesn't own ct on cast");
|
||||
uint256 supportedTypesInput = (1 << 0) + (1 << 1) + (1 << 2) + (1 << 3) + (1 << 4) + (1 << 5);
|
||||
requireType(ct, supportedTypesInput);
|
||||
@@ -355,7 +386,7 @@ contract TFHEExecutor {
|
||||
acl.allowTransient(result, msg.sender);
|
||||
}
|
||||
|
||||
function trivialEncrypt(uint256 pt, bytes1 toType) external returns (uint256 result) {
|
||||
function trivialEncrypt(uint256 pt, bytes1 toType) external virtual returns (uint256 result) {
|
||||
uint256 supportedTypes = (1 << 0) + (1 << 1) + (1 << 2) + (1 << 3) + (1 << 4) + (1 << 5) + (1 << 7);
|
||||
uint8 toT = uint8(toType);
|
||||
require((1 << toT) & supportedTypes > 0, "Unsupported type");
|
||||
@@ -365,7 +396,7 @@ contract TFHEExecutor {
|
||||
acl.allowTransient(result, msg.sender);
|
||||
}
|
||||
|
||||
function fheIfThenElse(uint256 control, uint256 ifTrue, uint256 ifFalse) external returns (uint256 result) {
|
||||
function fheIfThenElse(uint256 control, uint256 ifTrue, uint256 ifFalse) external virtual returns (uint256 result) {
|
||||
uint256 supportedTypes = (1 << 1) + (1 << 2) + (1 << 3) + (1 << 4) + (1 << 5) + (1 << 7);
|
||||
requireType(ifTrue, supportedTypes);
|
||||
uint8 typeCt = typeOf(ifTrue);
|
||||
@@ -373,32 +404,34 @@ contract TFHEExecutor {
|
||||
result = ternaryOp(Operators.fheIfThenElse, control, ifTrue, ifFalse);
|
||||
}
|
||||
|
||||
function fheRand(bytes1 randType) external returns (uint256 result) {
|
||||
function fheRand(bytes1 randType) external virtual returns (uint256 result) {
|
||||
TFHEExecutorStorage storage $ = _getTFHEExecutorStorage();
|
||||
uint256 supportedTypes = (1 << 2) + (1 << 3) + (1 << 4) + (1 << 5);
|
||||
uint8 randT = uint8(randType);
|
||||
require((1 << randT) & supportedTypes > 0, "Unsupported erandom type");
|
||||
fhePayment.payForFheRand(msg.sender, randT);
|
||||
result = uint256(keccak256(abi.encodePacked(Operators.fheRand, randType, counterRand)));
|
||||
result = uint256(keccak256(abi.encodePacked(Operators.fheRand, randType, $.counterRand)));
|
||||
result = appendType(result, randT);
|
||||
acl.allowTransient(result, msg.sender);
|
||||
counterRand++;
|
||||
$.counterRand++;
|
||||
}
|
||||
|
||||
function fheRandBounded(uint256 upperBound, bytes1 randType) external returns (uint256 result) {
|
||||
function fheRandBounded(uint256 upperBound, bytes1 randType) external virtual returns (uint256 result) {
|
||||
TFHEExecutorStorage storage $ = _getTFHEExecutorStorage();
|
||||
uint256 supportedTypes = (1 << 2) + (1 << 3) + (1 << 4) + (1 << 5);
|
||||
uint8 randT = uint8(randType);
|
||||
require((1 << randT) & supportedTypes > 0, "Unsupported erandom type");
|
||||
require(isPowerOfTwo(upperBound), "UpperBound must be a power of 2");
|
||||
fhePayment.payForFheRandBounded(msg.sender, randT);
|
||||
result = uint256(keccak256(abi.encodePacked(Operators.fheRandBounded, upperBound, randType, counterRand)));
|
||||
result = uint256(keccak256(abi.encodePacked(Operators.fheRandBounded, upperBound, randType, $.counterRand)));
|
||||
result = appendType(result, randT);
|
||||
acl.allowTransient(result, msg.sender);
|
||||
counterRand++;
|
||||
$.counterRand++;
|
||||
}
|
||||
|
||||
/// @notice Getter for the name and version of the contract
|
||||
/// @return string representing the name and the version of the contract
|
||||
function getVersion() external pure returns (string memory) {
|
||||
function getVersion() external pure virtual returns (string memory) {
|
||||
return
|
||||
string(
|
||||
abi.encodePacked(
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
|
||||
pragma solidity ^0.8.24;
|
||||
|
||||
address constant tfheExecutorAdd = 0x05fD9B5EFE0a996095f42Ed7e77c390810CF660c;
|
||||
address constant tfheExecutorAdd = 0xcCAe95fF1d11656358E782570dF0418F59fA40e1;
|
||||
|
||||
@@ -15,13 +15,13 @@ task('task:deployERC20').setAction(async function (taskArguments: TaskArguments,
|
||||
task('task:deployGateway')
|
||||
.addParam('privateKey', 'The deployer private key')
|
||||
.addParam('ownerAddress', 'The owner address')
|
||||
.setAction(async function (taskArguments: TaskArguments, { ethers }) {
|
||||
.setAction(async function (taskArguments: TaskArguments, { ethers, upgrades }) {
|
||||
const deployer = new ethers.Wallet(taskArguments.privateKey).connect(ethers.provider);
|
||||
const envConfig2 = dotenv.parse(fs.readFileSync('lib/.env.kmsverifier'));
|
||||
const gatewayFactory = await ethers.getContractFactory('GatewayContract');
|
||||
const Gateway = await gatewayFactory
|
||||
.connect(deployer)
|
||||
.deploy(taskArguments.ownerAddress, envConfig2.KMS_VERIFIER_CONTRACT_ADDRESS);
|
||||
const factory = await ethers.getContractFactory('GatewayContract', deployer);
|
||||
const Gateway = await upgrades.deployProxy(factory, [taskArguments.ownerAddress], {
|
||||
initializer: 'initialize',
|
||||
kind: 'uups',
|
||||
});
|
||||
await Gateway.waitForDeployment();
|
||||
const GatewayContractAddress = await Gateway.getAddress();
|
||||
const envConfig = dotenv.parse(fs.readFileSync('gateway/.env.gateway'));
|
||||
@@ -60,54 +60,54 @@ task('task:deployIdentity').setAction(async function (taskArguments: TaskArgumen
|
||||
console.log(`npx hardhat task:identity:balanceOf --erc20 ${erc20Address} --user alice`);
|
||||
});
|
||||
|
||||
task('task:deployACL').setAction(async function (taskArguments: TaskArguments, { ethers }) {
|
||||
task('task:deployACL').setAction(async function (taskArguments: TaskArguments, { ethers, upgrades }) {
|
||||
const deployer = (await ethers.getSigners())[9];
|
||||
const factory = await ethers.getContractFactory('ACL');
|
||||
const acl = await factory.connect(deployer).deploy();
|
||||
const factory = await ethers.getContractFactory('ACL', deployer);
|
||||
const acl = await upgrades.deployProxy(factory, [deployer.address], { initializer: 'initialize', kind: 'uups' });
|
||||
await acl.waitForDeployment();
|
||||
const address = await acl.getAddress();
|
||||
const envConfigAcl = dotenv.parse(fs.readFileSync('lib/.env.acl'));
|
||||
if (address !== envConfigAcl.ACL_CONTRACT_ADDRESS) {
|
||||
throw new Error(`The nonce of the deployer account is not corret. Please relaunch a clean instance of the fhEVM`);
|
||||
throw new Error(`The nonce of the deployer account is not correct. Please relaunch a clean instance of the fhEVM`);
|
||||
}
|
||||
console.log('ACL was deployed at address:', address);
|
||||
});
|
||||
|
||||
task('task:deployTFHEExecutor').setAction(async function (taskArguments: TaskArguments, { ethers }) {
|
||||
task('task:deployTFHEExecutor').setAction(async function (taskArguments: TaskArguments, { ethers, upgrades }) {
|
||||
const deployer = (await ethers.getSigners())[9];
|
||||
const factory = await ethers.getContractFactory('TFHEExecutor');
|
||||
const exec = await factory.connect(deployer).deploy();
|
||||
const factory = await ethers.getContractFactory('TFHEExecutor', deployer);
|
||||
const exec = await upgrades.deployProxy(factory, [deployer.address], { initializer: 'initialize', kind: 'uups' });
|
||||
await exec.waitForDeployment();
|
||||
const address = await exec.getAddress();
|
||||
const envConfig = dotenv.parse(fs.readFileSync('lib/.env.exec'));
|
||||
if (address !== envConfig.TFHE_EXECUTOR_CONTRACT_ADDRESS) {
|
||||
throw new Error(`The nonce of the deployer account is not corret. Please relaunch a clean instance of the fhEVM`);
|
||||
throw new Error(`The nonce of the deployer account is not correct. Please relaunch a clean instance of the fhEVM`);
|
||||
}
|
||||
console.log('TFHEExecutor was deployed at address:', address);
|
||||
});
|
||||
|
||||
task('task:deployKMSVerifier').setAction(async function (taskArguments: TaskArguments, { ethers }) {
|
||||
task('task:deployKMSVerifier').setAction(async function (taskArguments: TaskArguments, { ethers, upgrades }) {
|
||||
const deployer = (await ethers.getSigners())[9];
|
||||
const factory = await ethers.getContractFactory('KMSVerifier');
|
||||
const exec = await factory.connect(deployer).deploy();
|
||||
await exec.waitForDeployment();
|
||||
const address = await exec.getAddress();
|
||||
const factory = await ethers.getContractFactory('KMSVerifier', deployer);
|
||||
const kms = await upgrades.deployProxy(factory, [deployer.address], { initializer: 'initialize', kind: 'uups' });
|
||||
await kms.waitForDeployment();
|
||||
const address = await kms.getAddress();
|
||||
const envConfig = dotenv.parse(fs.readFileSync('lib/.env.kmsverifier'));
|
||||
if (address !== envConfig.KMS_VERIFIER_CONTRACT_ADDRESS) {
|
||||
throw new Error(`The nonce of the deployer account is not corret. Please relaunch a clean instance of the fhEVM`);
|
||||
throw new Error(`The nonce of the deployer account is not correct. Please relaunch a clean instance of the fhEVM`);
|
||||
}
|
||||
console.log('KMSVerifier was deployed at address:', address);
|
||||
});
|
||||
|
||||
task('task:deployFHEPayment').setAction(async function (taskArguments: TaskArguments, { ethers }) {
|
||||
task('task:deployFHEPayment').setAction(async function (taskArguments: TaskArguments, { ethers, upgrades }) {
|
||||
const deployer = (await ethers.getSigners())[9];
|
||||
const factory = await ethers.getContractFactory('FHEPayment');
|
||||
const exec = await factory.connect(deployer).deploy();
|
||||
await exec.waitForDeployment();
|
||||
const address = await exec.getAddress();
|
||||
const factory = await ethers.getContractFactory('FHEPayment', deployer);
|
||||
const payment = await upgrades.deployProxy(factory, [deployer.address], { initializer: 'initialize', kind: 'uups' });
|
||||
await payment.waitForDeployment();
|
||||
const address = await payment.getAddress();
|
||||
const envConfig = dotenv.parse(fs.readFileSync('lib/.env.fhepayment'));
|
||||
if (address !== envConfig.FHE_PAYMENT_CONTRACT_ADDRESS) {
|
||||
throw new Error(`The nonce of the deployer account is not corret. Please relaunch a clean instance of the fhEVM`);
|
||||
throw new Error(`The nonce of the deployer account is not correct. Please relaunch a clean instance of the fhEVM`);
|
||||
}
|
||||
console.log('FHEPayment was deployed at address:', address);
|
||||
});
|
||||
|
||||
@@ -21,10 +21,10 @@ task('task:computePredeployAddress')
|
||||
const deployerAddress = new ethers.Wallet(taskArguments.privateKey).address;
|
||||
const gatewayContractAddressPrecomputed = ethers.getCreateAddress({
|
||||
from: deployerAddress,
|
||||
nonce: 0, // deployer is supposed to have nonce 0 when deploying GatewayContract
|
||||
nonce: 1, // deployer is supposed to have nonce 0 when deploying GatewayContract (0 nonce for implementation, +1 for UUPS)
|
||||
});
|
||||
const envFilePath = path.join(__dirname, '../gateway/.env.gateway');
|
||||
const content = `GATEWAY_CONTRACT_PREDEPLOY_ADDRESS=${gatewayContractAddressPrecomputed}\n`;
|
||||
const content = `GATEWAY_CONTRACT_PREDEPLOY_ADDRESS=${gatewayContractAddressPrecomputed}`;
|
||||
try {
|
||||
fs.writeFileSync(envFilePath, content, { flag: 'w' });
|
||||
console.log('gatewayContractAddress written to gateway/.env.gateway successfully!');
|
||||
@@ -37,7 +37,7 @@ task('task:computePredeployAddress')
|
||||
pragma solidity ^0.8.24;
|
||||
|
||||
address constant GATEWAY_CONTRACT_PREDEPLOY_ADDRESS = ${gatewayContractAddressPrecomputed};
|
||||
`;
|
||||
`;
|
||||
|
||||
try {
|
||||
fs.writeFileSync('./gateway/lib/GatewayContractAddress.sol', solidityTemplate, { encoding: 'utf8', flag: 'w' });
|
||||
|
||||
@@ -7,7 +7,7 @@ task('task:computeACLAddress').setAction(async function (taskArguments: TaskArgu
|
||||
const deployer = (await ethers.getSigners())[9].address;
|
||||
const aclAddress = ethers.getCreateAddress({
|
||||
from: deployer,
|
||||
nonce: 0, // using nonce of 0 for the ACL contract
|
||||
nonce: 1, // using nonce of 1 for the ACL contract (0 for original implementation, +1 for proxy)
|
||||
});
|
||||
const envFilePath = path.join(__dirname, '../lib/.env.acl');
|
||||
const content = `ACL_CONTRACT_ADDRESS=${aclAddress}\n`;
|
||||
@@ -36,7 +36,7 @@ task('task:computeTFHEExecutorAddress').setAction(async function (taskArguments:
|
||||
const deployer = (await ethers.getSigners())[9].address;
|
||||
const execAddress = ethers.getCreateAddress({
|
||||
from: deployer,
|
||||
nonce: 1, // using nonce of 1 for the TFHEExecutor contract
|
||||
nonce: 3, // using nonce of 3 for the TFHEExecutor contract (2 for original implementation, +1 for proxy)
|
||||
});
|
||||
const envFilePath = path.join(__dirname, '../lib/.env.exec');
|
||||
const content = `TFHE_EXECUTOR_CONTRACT_ADDRESS=${execAddress}\n`;
|
||||
@@ -65,7 +65,7 @@ task('task:computeKMSVerifierAddress').setAction(async function (taskArguments:
|
||||
const deployer = (await ethers.getSigners())[9].address;
|
||||
const kmsVerfierAddress = ethers.getCreateAddress({
|
||||
from: deployer,
|
||||
nonce: 2, // using nonce of 2 for the Kms Verifier contract
|
||||
nonce: 5, // using nonce of 5 for the KMSVerifier contract (4 for original implementation, +1 for proxy)
|
||||
});
|
||||
const envFilePath = path.join(__dirname, '../lib/.env.kmsverifier');
|
||||
const content = `KMS_VERIFIER_CONTRACT_ADDRESS=${kmsVerfierAddress}\n`;
|
||||
@@ -94,7 +94,7 @@ task('task:computeFHEPaymentAddress').setAction(async function (taskArguments: T
|
||||
const deployer = (await ethers.getSigners())[9].address;
|
||||
const fhePaymentAddress = ethers.getCreateAddress({
|
||||
from: deployer,
|
||||
nonce: 3, // using nonce of 3 for the FHEPayment contract
|
||||
nonce: 7, // using nonce of 7 for the FHEPayment contract (6 for original implementation, +1 for proxy)
|
||||
});
|
||||
const envFilePath = path.join(__dirname, '../lib/.env.fhepayment');
|
||||
const content = `FHE_PAYMENT_CONTRACT_ADDRESS=${fhePaymentAddress}\n`;
|
||||
|
||||
@@ -9,7 +9,7 @@ import { waitNBlocks } from './utils';
|
||||
const networkName = network.name;
|
||||
|
||||
const parsedEnvACL = dotenv.parse(fs.readFileSync('lib/.env.acl'));
|
||||
const aclAdd = parsedEnvACL.ACL_CONTRACT_ADDRESS.replace(/^0x/, '').replace(/^0+/, '').toLowerCase();
|
||||
const aclAdd = parsedEnvACL.ACL_CONTRACT_ADDRESS;
|
||||
|
||||
const CiphertextType = {
|
||||
0: 'bool',
|
||||
@@ -123,8 +123,8 @@ const fulfillAllPastRequestsIds = async (mocked: boolean) => {
|
||||
|
||||
// first check tat all handles are allowed for decryption
|
||||
const aclFactory = await ethers.getContractFactory('ACL');
|
||||
const acl = aclFactory.attach(`0x${aclAdd}`);
|
||||
const isAllowedForDec = await Promise.all(handles.map(async (handle) => acl.allowedForDecryption(handle)));
|
||||
const acl = aclFactory.attach(aclAdd);
|
||||
const isAllowedForDec = await Promise.all(handles.map(async (handle) => acl.isAllowedForDecryption(handle)));
|
||||
if (!allTrue(isAllowedForDec)) {
|
||||
throw new Error('Some handle is not authorized for decryption');
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import { awaitCoprocessor, getClearText } from './coprocessorUtils';
|
||||
const hre = require('hardhat');
|
||||
|
||||
const parsedEnvACL = dotenv.parse(fs.readFileSync('lib/.env.acl'));
|
||||
const aclAdd = parsedEnvACL.ACL_CONTRACT_ADDRESS.replace(/^0x/, '').replace(/^0+/, '').toLowerCase();
|
||||
const aclAdd = parsedEnvACL.ACL_CONTRACT_ADDRESS;
|
||||
|
||||
enum Types {
|
||||
ebool = 0,
|
||||
@@ -122,7 +122,7 @@ export const reencryptRequestMocked = async (
|
||||
|
||||
// ACL checking
|
||||
const aclFactory = await hre.ethers.getContractFactory('ACL');
|
||||
const acl = aclFactory.attach(`0x${aclAdd}`);
|
||||
const acl = aclFactory.attach(aclAdd);
|
||||
const userAllowed = await acl.persistAllowed(handle, userAddress);
|
||||
const contractAllowed = await acl.persistAllowed(handle, contractAddress);
|
||||
const isAllowed = userAllowed && contractAllowed;
|
||||
|
||||
113
test/upgrades/upgrades.ts
Normal file
113
test/upgrades/upgrades.ts
Normal file
@@ -0,0 +1,113 @@
|
||||
import { expect } from 'chai';
|
||||
import dotenv from 'dotenv';
|
||||
import fs from 'fs';
|
||||
import { ethers, upgrades } from 'hardhat';
|
||||
|
||||
import { getSigners, initSigners } from '../signers';
|
||||
|
||||
describe('Upgrades', function () {
|
||||
before(async function () {
|
||||
await initSigners(2);
|
||||
this.signers = await getSigners();
|
||||
this.aclFactory = await ethers.getContractFactory('ACL');
|
||||
this.aclFactoryUpgraded = await ethers.getContractFactory('ACLUpgradedExample');
|
||||
this.kmsFactory = await ethers.getContractFactory('KMSVerifier');
|
||||
this.kmsFactoryUpgraded = await ethers.getContractFactory('KMSVerifierUpgradedExample');
|
||||
this.executorFactory = await ethers.getContractFactory('TFHEExecutor');
|
||||
this.executorFactoryUpgraded = await ethers.getContractFactory('TFHEExecutorUpgradedExample');
|
||||
this.paymentFactory = await ethers.getContractFactory('FHEPayment');
|
||||
this.paymentFactoryUpgraded = await ethers.getContractFactory('FHEPaymentUpgradedExample');
|
||||
this.gatewayFactory = await ethers.getContractFactory('GatewayContract');
|
||||
this.gatewayFactoryUpgraded = await ethers.getContractFactory('GatewayContractUpgradedExample');
|
||||
});
|
||||
|
||||
it('deploy upgradable ACL', async function () {
|
||||
const nonceBef = await ethers.provider.getTransactionCount(this.signers.alice);
|
||||
const acl = await upgrades.deployProxy(this.aclFactory, [this.signers.alice.address], {
|
||||
initializer: 'initialize',
|
||||
kind: 'uups',
|
||||
});
|
||||
await acl.waitForDeployment();
|
||||
const ownerBef = await acl.owner();
|
||||
expect(await acl.getVersion()).to.equal('ACL v0.1.0');
|
||||
const acl2 = await upgrades.upgradeProxy(acl, this.aclFactoryUpgraded);
|
||||
await acl2.waitForDeployment();
|
||||
const ownerAft = await acl2.owner();
|
||||
expect(ownerBef).to.equal(ownerAft);
|
||||
expect(await acl2.getVersion()).to.equal('ACL v0.2.0');
|
||||
const aclAddress = ethers.getCreateAddress({
|
||||
from: this.signers.alice.address,
|
||||
nonce: nonceBef, // using nonce of nonceBef instead of nonceBef+1 here, since the original implementation has already been deployer during the setup phase, and hardhat-upgrades plugin is able to detect this and not redeploy twice same contract
|
||||
});
|
||||
expect(aclAddress).to.equal(await acl2.getAddress());
|
||||
});
|
||||
|
||||
it('deploy upgradable KMSVerifier', async function () {
|
||||
const kms = await upgrades.deployProxy(this.kmsFactory, [this.signers.alice.address], {
|
||||
initializer: 'initialize',
|
||||
kind: 'uups',
|
||||
});
|
||||
await kms.waitForDeployment();
|
||||
expect(await kms.getVersion()).to.equal('KMSVerifier v0.1.0');
|
||||
const kms2 = await upgrades.upgradeProxy(kms, this.kmsFactoryUpgraded);
|
||||
await kms2.waitForDeployment();
|
||||
expect(await kms2.getVersion()).to.equal('KMSVerifier v0.2.0');
|
||||
});
|
||||
|
||||
it('deploy upgradable TFHEExecutor', async function () {
|
||||
const executor = await upgrades.deployProxy(this.executorFactory, [this.signers.alice.address], {
|
||||
initializer: 'initialize',
|
||||
kind: 'uups',
|
||||
});
|
||||
await executor.waitForDeployment();
|
||||
expect(await executor.getVersion()).to.equal('TFHEExecutor v0.1.0');
|
||||
const executor2 = await upgrades.upgradeProxy(executor, this.executorFactoryUpgraded);
|
||||
await executor2.waitForDeployment();
|
||||
expect(await executor2.getVersion()).to.equal('TFHEExecutor v0.2.0');
|
||||
});
|
||||
|
||||
it('deploy upgradable FHEPayment', async function () {
|
||||
const payment = await upgrades.deployProxy(this.paymentFactory, [this.signers.alice.address], {
|
||||
initializer: 'initialize',
|
||||
kind: 'uups',
|
||||
});
|
||||
await payment.waitForDeployment();
|
||||
expect(await payment.getVersion()).to.equal('FHEPayment v0.1.0');
|
||||
const payment2 = await upgrades.upgradeProxy(payment, this.paymentFactoryUpgraded);
|
||||
await payment2.waitForDeployment();
|
||||
expect(await payment2.getVersion()).to.equal('FHEPayment v0.2.0');
|
||||
});
|
||||
|
||||
it('deploy upgradable GatewayContract', async function () {
|
||||
const gateway = await upgrades.deployProxy(this.gatewayFactory, [this.signers.alice.address], {
|
||||
initializer: 'initialize',
|
||||
kind: 'uups',
|
||||
});
|
||||
await gateway.waitForDeployment();
|
||||
expect(await gateway.getVersion()).to.equal('GatewayContract v0.1.0');
|
||||
const gateway2 = await upgrades.upgradeProxy(gateway, this.gatewayFactoryUpgraded);
|
||||
await gateway2.waitForDeployment();
|
||||
expect(await gateway2.getVersion()).to.equal('GatewayContract v0.2.0');
|
||||
});
|
||||
|
||||
it('original owner upgrades the original ACL and transfer ownership', async function () {
|
||||
const origACLAdd = dotenv.parse(fs.readFileSync('lib/.env.acl')).ACL_CONTRACT_ADDRESS;
|
||||
const deployer = (await ethers.getSigners())[9];
|
||||
const acl = await this.aclFactory.attach(origACLAdd, deployer);
|
||||
expect(await acl.getVersion()).to.equal('ACL v0.1.0');
|
||||
const newaclFactoryUpgraded = await ethers.getContractFactory('ACLUpgradedExample', deployer);
|
||||
const acl2 = await upgrades.upgradeProxy(acl, newaclFactoryUpgraded);
|
||||
await acl2.waitForDeployment();
|
||||
expect(await acl2.getVersion()).to.equal('ACL v0.2.0');
|
||||
expect(await acl2.getAddress()).to.equal(origACLAdd);
|
||||
const newSigner = (await ethers.getSigners())[1];
|
||||
await acl2.transferOwnership(newSigner);
|
||||
await acl2.connect(newSigner).acceptOwnership();
|
||||
const newaclFactoryUpgraded2 = await ethers.getContractFactory('ACLUpgradedExample2', deployer);
|
||||
await expect(upgrades.upgradeProxy(acl2, newaclFactoryUpgraded2)).to.be.reverted; // old owner can no longer upgrade ACL
|
||||
const newaclFactoryUpgraded3 = await ethers.getContractFactory('ACLUpgradedExample2', newSigner);
|
||||
const acl3 = await upgrades.upgradeProxy(acl2, newaclFactoryUpgraded3); // new owner can upgrade ACL
|
||||
await acl3.waitForDeployment();
|
||||
expect(await acl3.getVersion()).to.equal('ACL v0.3.0');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user