Skip to content

Commit 2e9cc44

Browse files
committed
evm: add fork upgrade test (WIP)
1 parent ea14e8d commit 2e9cc44

File tree

3 files changed

+254
-1
lines changed

3 files changed

+254
-1
lines changed

evm/forge/tests/ForkSlots.t.sol

+236
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
// SPDX-License-Identifier: Apache 2
2+
pragma solidity ^0.8.22;
3+
4+
import "forge-std/Test.sol";
5+
import "forge-std/console.sol";
6+
7+
import {IWormhole} from "src/interfaces/IWormhole.sol";
8+
import {ICircleIntegration} from "src/interfaces/ICircleIntegration.sol";
9+
10+
import {Utils} from "src/libraries/Utils.sol";
11+
12+
import {Implementation} from "src/contracts/CircleIntegration/Implementation.sol";
13+
14+
import {
15+
CircleIntegrationOverride,
16+
CraftedCctpMessageParams,
17+
CraftedVaaParams
18+
} from "test-helpers/libraries/CircleIntegrationOverride.sol";
19+
import {UsdcDeal} from "test-helpers/libraries/UsdcDeal.sol";
20+
import {WormholeOverride} from "test-helpers/libraries/WormholeOverride.sol";
21+
22+
contract ForkSlots is Test {
23+
using CircleIntegrationOverride for *;
24+
using WormholeOverride for *;
25+
using Utils for address;
26+
27+
bytes32 constant GOVERNANCE_MODULE =
28+
0x000000000000000000000000000000436972636c65496e746567726174696f6e;
29+
30+
address constant FORKED_CIRCLE_INTEGRATION_ADDRESS = 0x2703483B1a5a7c577e8680de9Df8Be03c6f30e3c;
31+
32+
ICircleIntegration forked;
33+
IWormhole wormhole;
34+
35+
function setUp() public {
36+
forked = ICircleIntegration(FORKED_CIRCLE_INTEGRATION_ADDRESS);
37+
forked.setUpOverride(uint256(vm.envBytes32("TESTING_DEVNET_GUARDIAN")));
38+
39+
wormhole = forked.wormhole();
40+
}
41+
42+
function test_UpgradeForkAndCheckSlots() public {
43+
// Deploy new implementation.
44+
Implementation implementation = new Implementation(
45+
address(wormhole),
46+
vm.envAddress("TESTING_CIRCLE_BRIDGE_ADDRESS") // tokenMessenger
47+
);
48+
49+
// Should not be initialized yet.
50+
assertFalse(forked.isInitialized(address(implementation)), "already initialized");
51+
52+
// Also verify slot reflects same information.
53+
{
54+
bytes32 data = vm.load(
55+
address(forked), keccak256(abi.encode(address(implementation), uint256(0x5)))
56+
);
57+
assertEq(
58+
uint256(data),
59+
0, // false
60+
"isInitialized does not match slot value (false)"
61+
);
62+
}
63+
64+
// Check slots 0x0, 0x1, 0x2, 0x3, 0x4 and 0xa before upgrade.
65+
66+
// 0x0:
67+
//
68+
// uint16 chainId;
69+
// uint8 wormholeFinality;
70+
// uint32 localDomain;
71+
// address wormhole;
72+
// uint16 governanceChainId;
73+
{
74+
bytes32 data = vm.load(address(forked), bytes32(0));
75+
76+
uint256 bitOffset;
77+
78+
// First 2 bytes is chain ID.
79+
assertEq(uint16(uint256(data >> bitOffset)), forked.chainId());
80+
bitOffset += 16;
81+
82+
// Next byte is wormhole finality.
83+
assertEq(uint8(uint256(data >> bitOffset)), forked.wormholeFinality());
84+
bitOffset += 8;
85+
86+
// Next 4 bytes is local domain.
87+
assertEq(uint32(uint256(data >> bitOffset)), forked.localDomain());
88+
bitOffset += 32;
89+
90+
// Next 20 bytes is wormhole address.
91+
assertEq(address(uint160(uint256(data >> bitOffset))), address(wormhole));
92+
bitOffset += 160;
93+
94+
// Next 2 bytes is governance chain ID.
95+
assertEq(uint16(uint256(data >> bitOffset)), forked.governanceChainId());
96+
bitOffset += 16;
97+
98+
// Remaining bytes are zero.
99+
assertEq(uint256(data >> bitOffset), 0);
100+
}
101+
102+
// 0x1:
103+
//
104+
// governanceContract
105+
{
106+
bytes32 data = vm.load(address(forked), bytes32(uint256(0x1)));
107+
assertEq(data, forked.governanceContract());
108+
}
109+
110+
// 0x2:
111+
//
112+
// circleBridgeAddress
113+
{
114+
bytes32 data = vm.load(address(forked), bytes32(uint256(0x2)));
115+
assertEq(address(uint160(uint256(data))), address(forked.circleBridge()));
116+
}
117+
118+
// 0x3:
119+
//
120+
// circleTransmitterAddress
121+
{
122+
bytes32 data = vm.load(address(forked), bytes32(uint256(0x3)));
123+
assertEq(address(uint160(uint256(data))), address(forked.circleTransmitter()));
124+
}
125+
126+
// 0x4:
127+
//
128+
// circleTokenMinterAddress
129+
{
130+
bytes32 data = vm.load(address(forked), bytes32(uint256(0x4)));
131+
assertEq(address(uint160(uint256(data))), address(forked.circleTokenMinter()));
132+
}
133+
134+
// 0xa:
135+
//
136+
// evmChain
137+
{
138+
bytes32 data = vm.load(address(forked), bytes32(uint256(0xa)));
139+
assertEq(uint256(data), forked.evmChain());
140+
}
141+
142+
// Before upgrading, fetch some expected values.
143+
uint16 registeredChainId = 2;
144+
bytes32 expectedEmitter = forked.getRegisteredEmitter(registeredChainId);
145+
146+
// Also verify slot reflects same information.
147+
{
148+
bytes32 data =
149+
vm.load(address(forked), keccak256(abi.encode(registeredChainId, uint256(0x6))));
150+
assertEq(data, expectedEmitter);
151+
}
152+
153+
uint32 expectedCctpDomain = forked.getDomainFromChainId(registeredChainId);
154+
155+
// Also verify slot reflects same information.
156+
{
157+
bytes32 data =
158+
vm.load(address(forked), keccak256(abi.encode(registeredChainId, uint256(0x7))));
159+
assertEq(uint32(uint256(data)), expectedCctpDomain);
160+
}
161+
162+
assertEq(forked.getChainIdFromDomain(expectedCctpDomain), registeredChainId);
163+
164+
// Also verify slot reflects same information.
165+
{
166+
bytes32 data =
167+
vm.load(address(forked), keccak256(abi.encode(expectedCctpDomain, uint256(0x8))));
168+
assertEq(uint16(uint256(data)), registeredChainId);
169+
}
170+
171+
(IWormhole.VM memory vaa, bytes memory encodedVaa) = wormhole.craftGovernanceVaa(
172+
GOVERNANCE_MODULE,
173+
3, // action
174+
forked.chainId(),
175+
69, // sequence
176+
abi.encodePacked(address(implementation).toUniversalAddress())
177+
);
178+
179+
// Show that VAA has not been consumed yet.
180+
assertFalse(forked.isMessageConsumed(vaa.hash), "VAA already consumed");
181+
182+
// Also verify slot reflects same information.
183+
{
184+
bytes32 data = vm.load(address(forked), keccak256(abi.encode(vaa.hash, uint256(0x9))));
185+
assertEq(
186+
uint256(data),
187+
0, // false
188+
"isMessageConsumed does not match slot value (true)"
189+
);
190+
}
191+
192+
// Upgrade contract.
193+
forked.upgradeContract(encodedVaa);
194+
195+
// Now initialized.
196+
assertTrue(forked.isInitialized(address(implementation)), "implementation not initialized");
197+
198+
// Also verify slot reflects same information.
199+
{
200+
bytes32 data = vm.load(
201+
address(forked), keccak256(abi.encode(address(implementation), uint256(0x5)))
202+
);
203+
assertEq(
204+
uint256(data),
205+
1, // true
206+
"isInitialized does not match slot value (true)"
207+
);
208+
}
209+
210+
// Show that VAA is now consumed.
211+
assertTrue(forked.isMessageConsumed(vaa.hash), "VAA not consumed");
212+
213+
// Also verify slot reflects same information.
214+
{
215+
bytes32 data = vm.load(address(forked), keccak256(abi.encode(vaa.hash, uint256(0x9))));
216+
assertEq(
217+
uint256(data),
218+
1, // true
219+
"isMessageConsumed does not match slot value (true)"
220+
);
221+
}
222+
223+
// Slots checked above should now be zero.
224+
{
225+
uint256[6] memory slots =
226+
[0, uint256(0x1), uint256(0x2), uint256(0x3), uint256(0x4), uint256(0xa)];
227+
for (uint256 i; i < slots.length;) {
228+
bytes32 data = vm.load(address(forked), bytes32(slots[i]));
229+
assertEq(uint256(data), 0);
230+
unchecked {
231+
++i;
232+
}
233+
}
234+
}
235+
}
236+
}

evm/forge/tests/gas/CircleIntegrationComparison.t.sol

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ contract CircleIntegrationComparison is Test {
3838
bytes32 immutable FOREIGN_USDC_ADDRESS =
3939
bytes32(uint256(uint160(vm.envAddress("TESTING_FOREIGN_USDC_TOKEN_ADDRESS"))));
4040

41-
address immutable FORKED_CIRCLE_INTEGRATION_ADDRESS = 0x2703483B1a5a7c577e8680de9Df8Be03c6f30e3c;
41+
address constant FORKED_CIRCLE_INTEGRATION_ADDRESS = 0x2703483B1a5a7c577e8680de9Df8Be03c6f30e3c;
4242

4343
// dependencies
4444
IWormhole wormhole;

evm/forge/tests/helpers/libraries/WormholeOverride.sol

+17
Original file line numberDiff line numberDiff line change
@@ -168,4 +168,21 @@ library WormholeOverride {
168168
encodedBody
169169
);
170170
}
171+
172+
function craftGovernanceVaa(
173+
IWormhole wormhole,
174+
bytes32 module,
175+
uint8 action,
176+
uint16 targetChain,
177+
uint64 sequence,
178+
bytes memory decree
179+
) internal view returns (IWormhole.VM memory vaa, bytes memory encoded) {
180+
(vaa, encoded) = craftVaa(
181+
wormhole,
182+
GOVERNANCE_CHAIN_ID,
183+
GOVERNANCE_CONTRACT,
184+
sequence,
185+
abi.encodePacked(module, action, targetChain, decree)
186+
);
187+
}
171188
}

0 commit comments

Comments
 (0)