Skip to content

Commit 1816838

Browse files
author
Dev Kalra
authored
[executor] implement upgradability (#1198)
* get all test to work * executor upgradable * update comments * address feedback * fix tests
1 parent cd604d5 commit 1816838

File tree

6 files changed

+161
-23
lines changed

6 files changed

+161
-23
lines changed

target_chains/ethereum/contracts/contracts/executor/Executor.sol

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,13 @@ contract Executor {
4242
uint16 private ownerEmitterChainId;
4343
bytes32 private ownerEmitterAddress;
4444

45-
constructor(
45+
function _initialize(
4646
address _wormhole,
4747
uint64 _lastExecutedSequence,
4848
uint16 _chainId,
4949
uint16 _ownerEmitterChainId,
5050
bytes32 _ownerEmitterAddress
51-
) {
51+
) internal {
5252
require(_wormhole != address(0), "_wormhole is zero address");
5353

5454
wormhole = IWormhole(_wormhole);

target_chains/ethereum/contracts/contracts/executor/ExecutorErrors.sol

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,6 @@ library ExecutorErrors {
1616
error InvalidGovernanceTarget();
1717
// The target address for the contract call is not a contract
1818
error InvalidContractTarget();
19+
// The governance message is not valid
20+
error InvalidMagicValue();
1921
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
// SPDX-License-Identifier: Apache 2
2+
pragma solidity ^0.8.0;
3+
4+
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
5+
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
6+
import "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol";
7+
8+
import "./Executor.sol";
9+
import "./ExecutorErrors.sol";
10+
11+
contract ExecutorUpgradable is
12+
Initializable,
13+
Ownable2StepUpgradeable,
14+
UUPSUpgradeable,
15+
Executor
16+
{
17+
event ContractUpgraded(
18+
address oldImplementation,
19+
address newImplementation
20+
);
21+
22+
function initialize(
23+
address wormhole,
24+
uint64 lastExecutedSequence,
25+
uint16 chainId,
26+
uint16 ownerEmitterChainId,
27+
bytes32 ownerEmitterAddress
28+
) public initializer {
29+
require(wormhole != address(0), "wormhole is zero address");
30+
31+
__Ownable_init();
32+
__UUPSUpgradeable_init();
33+
34+
Executor._initialize(
35+
wormhole,
36+
lastExecutedSequence,
37+
chainId,
38+
ownerEmitterChainId,
39+
ownerEmitterAddress
40+
);
41+
42+
// Transfer ownership to the contract itself.
43+
_transferOwnership(address(this));
44+
}
45+
46+
/// Ensures the contract cannot be uninitialized and taken over.
47+
/// @custom:oz-upgrades-unsafe-allow constructor
48+
constructor() initializer {}
49+
50+
// Only allow the owner to upgrade the proxy to a new implementation.
51+
function _authorizeUpgrade(address) internal override onlyOwner {}
52+
53+
// Upgrade the contract to the given newImplementation. The `newImplementation`
54+
// should implement the method `entropyUpgradableMagic`, see below. If the method
55+
// is not implemented or if the magic is different from the current contract, this call
56+
// will revert.
57+
function upgradeTo(address newImplementation) external override onlyProxy {
58+
address oldImplementation = _getImplementation();
59+
_authorizeUpgrade(newImplementation);
60+
_upgradeToAndCallUUPS(newImplementation, new bytes(0), false);
61+
62+
magicCheck();
63+
64+
emit ContractUpgraded(oldImplementation, _getImplementation());
65+
}
66+
67+
// Upgrade the contract to the given newImplementation and call it with the given data.
68+
// The `newImplementation` should implement the method `entropyUpgradableMagic`, see
69+
// below. If the method is not implemented or if the magic is different from the current
70+
// contract, this call will revert.
71+
function upgradeToAndCall(
72+
address newImplementation,
73+
bytes memory data
74+
) external payable override onlyProxy {
75+
address oldImplementation = _getImplementation();
76+
_authorizeUpgrade(newImplementation);
77+
_upgradeToAndCallUUPS(newImplementation, data, true);
78+
79+
magicCheck();
80+
81+
emit ContractUpgraded(oldImplementation, _getImplementation());
82+
}
83+
84+
function magicCheck() internal view {
85+
// Calling a method using `this.<method>` will cause a contract call that will use
86+
// the new contract. This call will fail if the method does not exists or the magic
87+
// is different.
88+
if (this.entropyUpgradableMagic() != 0x66697288)
89+
revert ExecutorErrors.InvalidMagicValue();
90+
}
91+
92+
function entropyUpgradableMagic() public pure returns (uint32) {
93+
return 0x66697288;
94+
}
95+
}

target_chains/ethereum/contracts/forge-test/EntropyAuthorized.t.sol

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@ pragma solidity ^0.8.0;
44

55
import "./utils/EntropyTestUtils.t.sol";
66
import "../contracts/entropy/EntropyUpgradable.sol";
7-
import "./utils/EntropyTestContracts/EntropyDifferentMagic.t.sol";
7+
import "./utils/InvalidMagic.t.sol";
88
import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
99
import "@pythnetwork/entropy-sdk-solidity/EntropyErrors.sol";
1010

1111
contract EntropyAuthorized is Test, EntropyTestUtils {
1212
ERC1967Proxy public proxy;
1313
EntropyUpgradable public random;
1414
EntropyUpgradable public random2;
15-
EntropyDifferentMagic public randomDifferentMagic;
15+
InvalidMagic public randomDifferentMagic;
1616

1717
address public owner = address(1);
1818
address public admin = address(2);
@@ -36,7 +36,7 @@ contract EntropyAuthorized is Test, EntropyTestUtils {
3636
random = EntropyUpgradable(address(proxy));
3737
// to test for upgrade
3838
random2 = new EntropyUpgradable();
39-
randomDifferentMagic = new EntropyDifferentMagic();
39+
randomDifferentMagic = new InvalidMagic();
4040

4141
random.initialize(owner, admin, pythFeeInWei, provider1, false);
4242
}

target_chains/ethereum/contracts/forge-test/Executor.t.sol

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,16 @@ pragma solidity ^0.8.0;
44

55
import "forge-std/Test.sol";
66
import "@pythnetwork/entropy-sdk-solidity/EntropyStructs.sol";
7-
import "../contracts/executor/Executor.sol";
7+
import "../contracts/executor/ExecutorUpgradable.sol";
88
import "./utils/WormholeTestUtils.t.sol";
9+
import "./utils/InvalidMagic.t.sol";
910

1011
contract ExecutorTest is Test, WormholeTestUtils {
1112
Wormhole public wormhole;
12-
Executor public executor;
13+
ExecutorUpgradable public executor;
14+
ExecutorUpgradable public executor2;
1315
TestCallable public callable;
16+
InvalidMagic public executorInvalidMagic;
1417

1518
uint16 OWNER_CHAIN_ID = 7;
1619
bytes32 OWNER_EMITTER = bytes32(uint256(1));
@@ -19,7 +22,13 @@ contract ExecutorTest is Test, WormholeTestUtils {
1922

2023
function setUp() public {
2124
address _wormhole = setUpWormholeReceiver(NUM_SIGNERS);
22-
executor = new Executor(
25+
ExecutorUpgradable _executor = new ExecutorUpgradable();
26+
ERC1967Proxy _proxy = new ERC1967Proxy(address(_executor), "");
27+
executor = ExecutorUpgradable(payable(address(_proxy)));
28+
executor2 = new ExecutorUpgradable();
29+
executorInvalidMagic = new InvalidMagic();
30+
31+
executor.initialize(
2332
_wormhole,
2433
0,
2534
CHAIN_ID,
@@ -56,6 +65,51 @@ contract ExecutorTest is Test, WormholeTestUtils {
5665
executor.execute(vaa);
5766
}
5867

68+
function getTestUpgradeVaa(
69+
address newImplementation
70+
) internal returns (bytes memory vaa) {
71+
bytes memory payload = abi.encodePacked(
72+
uint32(0x5054474d),
73+
PythGovernanceInstructions.GovernanceModule.EvmExecutor,
74+
Executor.ExecutorAction.Execute,
75+
CHAIN_ID,
76+
address(executor),
77+
address(executor),
78+
abi.encodeWithSelector(
79+
ExecutorUpgradable.upgradeTo.selector,
80+
newImplementation
81+
)
82+
);
83+
84+
vaa = generateVaa(
85+
uint32(block.timestamp),
86+
OWNER_CHAIN_ID,
87+
OWNER_EMITTER,
88+
1,
89+
payload,
90+
NUM_SIGNERS
91+
);
92+
}
93+
94+
function testUpgradeCallSucceedsForContractWithCorrectMagic() public {
95+
bytes memory vaa = getTestUpgradeVaa(address(executor2));
96+
executor.execute(vaa);
97+
}
98+
99+
function testUpgradeCallFailsForNotUUPSContract() public {
100+
bytes memory vaa = getTestUpgradeVaa(address(callable));
101+
102+
vm.expectRevert("ERC1967Upgrade: new implementation is not UUPS");
103+
executor.execute(vaa);
104+
}
105+
106+
function testUpgradeCallFailsForInvalidMagic() public {
107+
bytes memory vaa = getTestUpgradeVaa(address(executorInvalidMagic));
108+
109+
vm.expectRevert(ExecutorErrors.InvalidMagicValue.selector);
110+
executor.execute(vaa);
111+
}
112+
59113
function testCallSucceeds() public {
60114
callable.reset();
61115

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,8 @@ pragma solidity ^0.8.0;
44
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
55
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
66
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
7-
import "@pythnetwork/entropy-sdk-solidity/EntropyErrors.sol";
87

9-
contract EntropyDifferentMagic is
10-
Initializable,
11-
OwnableUpgradeable,
12-
UUPSUpgradeable
13-
{
8+
contract InvalidMagic is Initializable, OwnableUpgradeable, UUPSUpgradeable {
149
function initialize() public initializer {
1510
__Ownable_init();
1611
__UUPSUpgradeable_init();
@@ -23,15 +18,7 @@ contract EntropyDifferentMagic is
2318
// // Only allow the owner to upgrade the proxy to a new implementation.
2419
function _authorizeUpgrade(address) internal override onlyOwner {}
2520

26-
function magicCheck() internal view {
27-
// Calling a method using `this.<method>` will cause a contract call that will use
28-
// the new contract. This call will fail if the method does not exists or the magic
29-
// is different.
30-
if (this.entropyUpgradableMagic() != 0x666972)
31-
revert EntropyErrors.InvalidUpgradeMagic();
32-
}
33-
3421
function entropyUpgradableMagic() public pure returns (uint32) {
35-
return 0x666972;
22+
return 0x000000;
3623
}
3724
}

0 commit comments

Comments
 (0)