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:
Joseph-André Turk
2024-09-12 12:35:40 +02:00
parent fa5cb0edaf
commit a2786b4364
30 changed files with 947 additions and 388 deletions

View File

@@ -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;
}

View File

@@ -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(

View 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)
)
);
}
}

View 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)
)
);
}
}

View 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)
)
);
}
}

View 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)
)
);
}
}

View 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)
)
);
}
}

View 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)
)
);
}
}

View File

@@ -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
View 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);
}

View File

@@ -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;

View File

@@ -2,5 +2,4 @@
pragma solidity ^0.8.24;
address constant GATEWAY_CONTRACT_PREDEPLOY_ADDRESS = 0xc8c9303Cd7F337fab769686B593B87DC3403E0ce;
address constant GATEWAY_CONTRACT_PREDEPLOY_ADDRESS = 0x096b4679d45fB675d4e2c1E4565009Cec99A12B1;

View File

@@ -1 +1 @@
ACL_CONTRACT_ADDRESS=0x2Fb4341027eb1d2aD8B5D9708187df8633cAFA92
ACL_CONTRACT_ADDRESS=0x05fD9B5EFE0a996095f42Ed7e77c390810CF660c

View File

@@ -1 +1 @@
TFHE_EXECUTOR_CONTRACT_ADDRESS=0x05fD9B5EFE0a996095f42Ed7e77c390810CF660c
TFHE_EXECUTOR_CONTRACT_ADDRESS=0xcCAe95fF1d11656358E782570dF0418F59fA40e1

View File

@@ -1 +1 @@
FHE_PAYMENT_CONTRACT_ADDRESS=0xcCAe95fF1d11656358E782570dF0418F59fA40e1
FHE_PAYMENT_CONTRACT_ADDRESS=0x52054F36036811ca418be59e41Fc6DD1b9e4F4c8

View File

@@ -1 +1 @@
KMS_VERIFIER_CONTRACT_ADDRESS=0x12B064FB845C1cc05e9493856a1D637a73e944bE
KMS_VERIFIER_CONTRACT_ADDRESS=0x857Ca72A957920Fa0FB138602995839866Bd4005

View File

@@ -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(

View File

@@ -2,4 +2,4 @@
pragma solidity ^0.8.24;
address constant aclAdd = 0x2Fb4341027eb1d2aD8B5D9708187df8633cAFA92;
address constant aclAdd = 0x05fD9B5EFE0a996095f42Ed7e77c390810CF660c;

View File

@@ -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(

View File

@@ -2,4 +2,4 @@
pragma solidity ^0.8.24;
address constant fhePaymentAdd = 0xcCAe95fF1d11656358E782570dF0418F59fA40e1;
address constant fhePaymentAdd = 0x52054F36036811ca418be59e41Fc6DD1b9e4F4c8;

View File

@@ -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(

View File

@@ -2,4 +2,4 @@
pragma solidity ^0.8.24;
address constant kmsVerifierAdd = 0x12B064FB845C1cc05e9493856a1D637a73e944bE;
address constant kmsVerifierAdd = 0x857Ca72A957920Fa0FB138602995839866Bd4005;

View File

@@ -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(

View File

@@ -2,4 +2,4 @@
pragma solidity ^0.8.24;
address constant tfheExecutorAdd = 0x05fD9B5EFE0a996095f42Ed7e77c390810CF660c;
address constant tfheExecutorAdd = 0xcCAe95fF1d11656358E782570dF0418F59fA40e1;

View File

@@ -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);
});

View File

@@ -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' });

View File

@@ -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`;

View File

@@ -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');
}

View File

@@ -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
View 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');
});
});