Skip to content

Commit f0f9ab7

Browse files
authored
feat(host-contracts): new upgrade mechanism (#325)
* feat(host-contracts): new upgrade mechanism * chore(host-contracts): fix forge tests
1 parent 4087d9e commit f0f9ab7

20 files changed

Lines changed: 171 additions & 61 deletions

host-contracts/contracts/ACL.sol

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
pragma solidity ^0.8.24;
33

44
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
5-
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
5+
import {UUPSUpgradeableEmptyProxy} from "./shared/UUPSUpgradeableEmptyProxy.sol";
66
import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol";
77
import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol";
88
import {fhevmExecutorAdd} from "../addresses/FHEVMExecutorAddress.sol";
@@ -15,7 +15,7 @@ import {ACLEvents} from "./ACLEvents.sol";
1515
* or decrypt encrypted values in fhEVM. By defining and enforcing these permissions, the ACL ensures that encrypted data remains
1616
* secure while still being usable within authorized contexts.
1717
*/
18-
contract ACL is UUPSUpgradeable, Ownable2StepUpgradeable, PausableUpgradeable, ACLEvents {
18+
contract ACL is UUPSUpgradeableEmptyProxy, Ownable2StepUpgradeable, PausableUpgradeable, ACLEvents {
1919
/// @notice Returned if the delegatee contract is already delegatee for sender & delegator addresses.
2020
/// @param delegatee delegatee address.
2121
/// @param contractAddress contract address.
@@ -63,7 +63,7 @@ contract ACL is UUPSUpgradeable, Ownable2StepUpgradeable, PausableUpgradeable, A
6363
uint256 private constant MAJOR_VERSION = 0;
6464

6565
/// @notice Minor version of the contract.
66-
uint256 private constant MINOR_VERSION = 1;
66+
uint256 private constant MINOR_VERSION = 2;
6767

6868
/// @notice Patch version of the contract.
6969
uint256 private constant PATCH_VERSION = 0;
@@ -83,10 +83,11 @@ contract ACL is UUPSUpgradeable, Ownable2StepUpgradeable, PausableUpgradeable, A
8383
}
8484

8585
/**
86-
* @notice Re-initializes the contract.
86+
* @notice Initializes the contract.
87+
* @param initialPauser Pauser address
8788
*/
8889
/// @custom:oz-upgrades-validate-as-initializer
89-
function reinitialize(address initialPauser) public virtual reinitializer(2) {
90+
function initializeFromEmptyProxy(address initialPauser) public virtual onlyFromEmptyProxy reinitializer(3) {
9091
__Ownable_init(owner());
9192
__Pausable_init();
9293

@@ -98,6 +99,19 @@ contract ACL is UUPSUpgradeable, Ownable2StepUpgradeable, PausableUpgradeable, A
9899
$.pauser = initialPauser;
99100
}
100101

102+
/**
103+
* @notice Re-initializes the contract from V1, adding new storage variable pauser.
104+
* @param initialPauser Pauser address
105+
*/
106+
function reinitializeV2(address initialPauser) public virtual reinitializer(3) {
107+
if (initialPauser == address(0)) {
108+
revert InvalidNullPauser();
109+
}
110+
111+
ACLStorage storage $ = _getACLStorage();
112+
$.pauser = initialPauser;
113+
}
114+
101115
/**
102116
* @notice Allows the use of `handle` for the address `account`.
103117
* @dev The caller must be allowed to use `handle` for allow() to succeed. If not, allow() reverts.

host-contracts/contracts/FHEVMExecutor.sol

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ pragma solidity ^0.8.24;
33

44
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
55
import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol";
6-
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
6+
import {UUPSUpgradeableEmptyProxy} from "./shared/UUPSUpgradeableEmptyProxy.sol";
77

88
import {ACL} from "./ACL.sol";
99
import {HCULimit} from "./HCULimit.sol";
@@ -31,7 +31,7 @@ interface IInputVerifier {
3131
* main responsibilities is to deterministically generate ciphertext handles.
3232
* @dev This contract is deployed using an UUPS proxy.
3333
*/
34-
contract FHEVMExecutor is UUPSUpgradeable, Ownable2StepUpgradeable, FHEEvents {
34+
contract FHEVMExecutor is UUPSUpgradeableEmptyProxy, Ownable2StepUpgradeable, FHEEvents {
3535
/// @notice Returned when the handle is not allowed in the ACL for the account.
3636
/// @param handle Handle.
3737
/// @param account Address of the account.
@@ -122,7 +122,7 @@ contract FHEVMExecutor is UUPSUpgradeable, Ownable2StepUpgradeable, FHEEvents {
122122
uint256 private constant MAJOR_VERSION = 0;
123123

124124
/// @notice Minor version of the contract.
125-
uint256 private constant MINOR_VERSION = 1;
125+
uint256 private constant MINOR_VERSION = 2;
126126

127127
/// @notice Patch version of the contract.
128128
uint256 private constant PATCH_VERSION = 0;
@@ -146,13 +146,18 @@ contract FHEVMExecutor is UUPSUpgradeable, Ownable2StepUpgradeable, FHEEvents {
146146
}
147147

148148
/**
149-
* @notice Re-initializes the contract.
149+
* @notice Initializes the contract.
150150
*/
151151
/// @custom:oz-upgrades-validate-as-initializer
152-
function reinitialize() public virtual reinitializer(2) {
152+
function initializeFromEmptyProxy() public virtual onlyFromEmptyProxy reinitializer(3) {
153153
__Ownable_init(owner());
154154
}
155155

156+
/**
157+
* @notice Re-initializes the contract from V1.
158+
*/
159+
function reinitializeV2() public virtual reinitializer(3) {}
160+
156161
/**
157162
* @notice Computes FHEAdd operation.
158163
* @param lhs LHS.

host-contracts/contracts/HCULimit.sol

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
pragma solidity ^0.8.24;
33

44
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
5-
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
5+
import {UUPSUpgradeableEmptyProxy} from "./shared/UUPSUpgradeableEmptyProxy.sol";
66
import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol";
77
import {fhevmExecutorAdd} from "../addresses/FHEVMExecutorAddress.sol";
88

@@ -14,7 +14,7 @@ import {FheType} from "./shared/FheType.sol";
1414
* transaction level, including the maximum number of homomorphic complexity units (HCU) per transaction.
1515
* @dev The contract is designed to be used with the FHEVMExecutor contract.
1616
*/
17-
contract HCULimit is UUPSUpgradeable, Ownable2StepUpgradeable {
17+
contract HCULimit is UUPSUpgradeableEmptyProxy, Ownable2StepUpgradeable {
1818
/// @notice Returned if the sender is not the FHEVMExecutor.
1919
error CallerMustBeFHEVMExecutorContract();
2020

@@ -37,7 +37,7 @@ contract HCULimit is UUPSUpgradeable, Ownable2StepUpgradeable {
3737
uint256 private constant MAJOR_VERSION = 0;
3838

3939
/// @notice Minor version of the contract.
40-
uint256 private constant MINOR_VERSION = 1;
40+
uint256 private constant MINOR_VERSION = 2;
4141

4242
/// @notice Patch version of the contract.
4343
uint256 private constant PATCH_VERSION = 0;
@@ -63,13 +63,18 @@ contract HCULimit is UUPSUpgradeable, Ownable2StepUpgradeable {
6363
}
6464

6565
/**
66-
* @notice Re-initializes the contract.
66+
* @notice Initializes the contract.
6767
*/
6868
/// @custom:oz-upgrades-validate-as-initializer
69-
function reinitialize() public virtual reinitializer(2) {
69+
function initializeFromEmptyProxy() public virtual onlyFromEmptyProxy reinitializer(3) {
7070
__Ownable_init(owner());
7171
}
7272

73+
/**
74+
* @notice Re-initializes the contract from V1.
75+
*/
76+
function reinitializeV2() public virtual reinitializer(3) {}
77+
7378
/**
7479
* @notice Check the homomorphic complexity units limit for FheAdd.
7580
* @param resultType Result type.

host-contracts/contracts/InputVerifier.sol

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ import {FHEVMExecutor} from "./FHEVMExecutor.sol";
55

66
// Importing OpenZeppelin contracts for cryptographic signature verification and access control.
77
import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
8-
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
98
import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol";
9+
import {UUPSUpgradeableEmptyProxy} from "./shared/UUPSUpgradeableEmptyProxy.sol";
1010
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
1111
import {EIP712UpgradeableCrossChain} from "./shared/EIP712UpgradeableCrossChain.sol";
1212

@@ -16,7 +16,7 @@ import {EIP712UpgradeableCrossChain} from "./shared/EIP712UpgradeableCrossChain.
1616
* This contract is called by the FHEVMExecutor inside verifyCiphertext function
1717
* @dev The contract uses EIP712UpgradeableCrossChain for cryptographic operations.
1818
*/
19-
contract InputVerifier is UUPSUpgradeable, Ownable2StepUpgradeable, EIP712UpgradeableCrossChain {
19+
contract InputVerifier is UUPSUpgradeableEmptyProxy, Ownable2StepUpgradeable, EIP712UpgradeableCrossChain {
2020
/// @notice Emitted when a signer is added.
2121
/// @param signer The address of the signer that was added.
2222
event SignerAdded(address indexed signer);
@@ -127,14 +127,17 @@ contract InputVerifier is UUPSUpgradeable, Ownable2StepUpgradeable, EIP712Upgrad
127127
}
128128

129129
/**
130-
* @notice Re-initializes the contract.
130+
* @notice Initializes the contract.
131+
* @param verifyingContractSource InputVerification contract address from Gateway chain.
132+
* @param chainIDSource chainID of Gateway chain.
133+
* @param initialSigners initial set of coprocessors.
131134
*/
132135
/// @custom:oz-upgrades-validate-as-initializer
133-
function reinitialize(
136+
function initializeFromEmptyProxy(
134137
address verifyingContractSource,
135138
uint64 chainIDSource,
136139
address[] calldata initialSigners
137-
) public virtual reinitializer(2) {
140+
) public virtual onlyFromEmptyProxy reinitializer(2) {
138141
__Ownable_init(owner());
139142
__EIP712_init(CONTRACT_NAME_SOURCE, "1", verifyingContractSource, chainIDSource);
140143
uint256 initialSignersLen = initialSigners.length;

host-contracts/contracts/KMSVerifier.sol

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
pragma solidity ^0.8.24;
33

44
import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
5-
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
5+
import {UUPSUpgradeableEmptyProxy} from "./shared/UUPSUpgradeableEmptyProxy.sol";
66
import {EIP712UpgradeableCrossChain} from "./shared/EIP712UpgradeableCrossChain.sol";
77
import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol";
88
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
@@ -13,7 +13,7 @@ import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
1313
* signature verification functions.
1414
* @dev The contract uses EIP712UpgradeableCrossChain for cryptographic operations and is deployed using an UUPS proxy.
1515
*/
16-
contract KMSVerifier is UUPSUpgradeable, Ownable2StepUpgradeable, EIP712UpgradeableCrossChain {
16+
contract KMSVerifier is UUPSUpgradeableEmptyProxy, Ownable2StepUpgradeable, EIP712UpgradeableCrossChain {
1717
/// @notice Returned if the KMS signer to add is already a signer.
1818
error KMSAlreadySigner();
1919

@@ -101,12 +101,12 @@ contract KMSVerifier is UUPSUpgradeable, Ownable2StepUpgradeable, EIP712Upgradea
101101
* @param initialThreshold Initial threshold, should be non-null and less or equal to the initialSigners length.
102102
*/
103103
/// @custom:oz-upgrades-validate-as-initializer
104-
function reinitialize(
104+
function initializeFromEmptyProxy(
105105
address verifyingContractSource,
106106
uint64 chainIDSource,
107107
address[] calldata initialSigners,
108108
uint256 initialThreshold
109-
) public virtual reinitializer(2) {
109+
) public virtual onlyFromEmptyProxy reinitializer(2) {
110110
__Ownable_init(owner());
111111
__EIP712_init(CONTRACT_NAME_SOURCE, "1", verifyingContractSource, chainIDSource);
112112
defineNewContext(initialSigners, initialThreshold);
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// SPDX-License-Identifier: BSD-3-Clause-Clear
2+
pragma solidity ^0.8.24;
3+
4+
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
5+
6+
/**
7+
* @title UUPSUpgradeableEmptyProxy
8+
* @dev Abstract base contract for upgradeable contracts that are intended to be deployed behind
9+
* empty proxies. This contract provides a modifier that ensures functions can only be called
10+
* during the first initialization phase (i.e., when initialized version is 1), enforcing
11+
* correct deployment from an empty proxy using the UUPSUpgradeable pattern.
12+
*
13+
* Inheriting contracts should use the `onlyFromEmptyProxy` modifier to protect initialization logic
14+
* that must not run on upgrades or reinitializations.
15+
*/
16+
abstract contract UUPSUpgradeableEmptyProxy is UUPSUpgradeable {
17+
error NotInitializingFromEmptyProxy();
18+
19+
modifier onlyFromEmptyProxy() {
20+
if (_getInitializedVersion() != 1) {
21+
revert NotInitializingFromEmptyProxy();
22+
}
23+
_;
24+
}
25+
}

host-contracts/examples/ACLUpgradedExample.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ contract ACLUpgradedExample is ACL {
1010

1111
/// @notice Version of the contract
1212
uint256 private constant MAJOR_VERSION = 0;
13-
uint256 private constant MINOR_VERSION = 2;
13+
uint256 private constant MINOR_VERSION = 3;
1414
uint256 private constant PATCH_VERSION = 0;
1515

1616
/// @notice Getter for the name and version of the contract

host-contracts/examples/ACLUpgradedExample2.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ contract ACLUpgradedExample2 is ACL {
1010

1111
/// @notice Version of the contract
1212
uint256 private constant MAJOR_VERSION = 0;
13-
uint256 private constant MINOR_VERSION = 3;
13+
uint256 private constant MINOR_VERSION = 4;
1414
uint256 private constant PATCH_VERSION = 0;
1515

1616
/// @notice Getter for the name and version of the contract

host-contracts/examples/FHEVMExecutorUpgradedExample.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ contract FHEVMExecutorUpgradedExample is FHEVMExecutor {
1212

1313
/// @dev Version numbers
1414
uint256 private constant MAJOR_VERSION = 0;
15-
uint256 private constant MINOR_VERSION = 2;
15+
uint256 private constant MINOR_VERSION = 3;
1616
uint256 private constant PATCH_VERSION = 0;
1717

1818
/// @notice Returns the full version string of the contract

host-contracts/examples/HCULimitUpgradedExample.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ contract HCULimitUpgradedExample is HCULimit {
1010

1111
/// @notice Version of the contract
1212
uint256 private constant MAJOR_VERSION = 0;
13-
uint256 private constant MINOR_VERSION = 2;
13+
uint256 private constant MINOR_VERSION = 3;
1414
uint256 private constant PATCH_VERSION = 0;
1515

1616
/// @notice Getter for the name and version of the contract

0 commit comments

Comments
 (0)