Skip to content

Commit

Permalink
evm: add fork upgrade slot test
Browse files Browse the repository at this point in the history
  • Loading branch information
a5-pickle committed Jan 3, 2024
1 parent ea14e8d commit 5ca4dbd
Show file tree
Hide file tree
Showing 4 changed files with 465 additions and 1 deletion.
358 changes: 358 additions & 0 deletions evm/forge/tests/ForkSlots.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,358 @@
// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.22;

import "forge-std/Test.sol";
import "forge-std/console.sol";

import {IWormhole} from "src/interfaces/IWormhole.sol";
import {ICircleIntegration} from "src/interfaces/ICircleIntegration.sol";

import {Utils} from "src/libraries/Utils.sol";

import {Implementation} from "src/contracts/CircleIntegration/Implementation.sol";

import {
CircleIntegrationOverride,
CraftedCctpMessageParams,
CraftedVaaParams
} from "test-helpers/libraries/CircleIntegrationOverride.sol";
import {SlotCheck} from "test-helpers/libraries/SlotCheck.sol";
import {WormholeOverride} from "test-helpers/libraries/WormholeOverride.sol";

contract ForkSlots is Test {
using CircleIntegrationOverride for *;
using WormholeOverride for *;
using Utils for address;
using SlotCheck for *;

bytes32 constant GOVERNANCE_MODULE =
0x000000000000000000000000000000436972636c65496e746567726174696f6e;

address constant FORKED_CIRCLE_INTEGRATION_ADDRESS = 0x2703483B1a5a7c577e8680de9Df8Be03c6f30e3c;

ICircleIntegration forked;
address forkedAddress;

IWormhole wormhole;

// Expected at slot 0x0.

uint16 expectedChainId;
uint8 expectedWormholeFinality;
uint32 expectedLocalDomain;
address expectedWormholeAddress;
uint16 expectedGovernanceChainId;

// Expected at slot 0x1.
bytes32 expectedGovernanceContract;

// Expected at slot 0x2.
address expectedCircleBridgeAddress;

// Expected at slot 0x3.
address expectedCircleTransmitterAddress;

// Expected at slot 0x4.
address expectedCircleTokenMinterAddress;

// Expected at slot 0xa.
uint256 expectedEvmChain;

function setUp() public {
forked = ICircleIntegration(FORKED_CIRCLE_INTEGRATION_ADDRESS);
forked.setUpOverride(uint256(vm.envBytes32("TESTING_DEVNET_GUARDIAN")));
forkedAddress = address(forked);

wormhole = forked.wormhole();

// Set expected values.
expectedChainId = forked.chainId();
expectedWormholeFinality = forked.wormholeFinality();
expectedLocalDomain = forked.localDomain();
expectedWormholeAddress = address(forked.wormhole());
expectedGovernanceChainId = forked.governanceChainId();
expectedGovernanceContract = forked.governanceContract();
expectedCircleBridgeAddress = address(forked.circleBridge());
expectedCircleTransmitterAddress = address(forked.circleTransmitter());
expectedCircleTokenMinterAddress = address(forked.circleTokenMinter());
expectedEvmChain = forked.evmChain();
}

function test_UpgradeForkAndCheckSlots() public {
// Deploy new implementation.
Implementation implementation = new Implementation(
address(wormhole),
vm.envAddress("TESTING_CIRCLE_BRIDGE_ADDRESS") // tokenMessenger
);

// Should not be initialized yet.
bool isInitialized = forked.isInitialized(address(implementation));
assertFalse(isInitialized, "already initialized");

(IWormhole.VM memory vaa, bytes memory encodedVaa) = wormhole.craftGovernanceVaa(
GOVERNANCE_MODULE,
3, // action
forked.chainId(),
69, // sequence
abi.encodePacked(address(implementation).toUniversalAddress())
);

// This VAA should not have been consumed yet.
bool isVaaConsumed = forked.isMessageConsumed(vaa.hash);
assertFalse(isVaaConsumed, "VAA already consumed");

// Before upgrading, fetch some expected values.
uint16 expectedRegisteredChainId = 2;
bytes32 expectedEmitter = forked.getRegisteredEmitter(expectedRegisteredChainId);
uint32 expectedCctpDomain = forked.getDomainFromChainId(expectedRegisteredChainId);

// Check slots before upgrade.
{
bytes32 slotZeroData = vm.load(address(forked), bytes32(0));

// If the data is already zeroed, check the remaining zeroed slots. Otherwise check that
// the slots are the expected values from the existing getters.
if (slotZeroData != 0) {
// Now check slots that will be zeroed.
uint256 bitOffset;

// First 2 bytes is chain ID.
assertEq(
uint16(uint256(slotZeroData >> bitOffset)),
expectedChainId,
"slot 0x0 not equal to expected before upgrade"
);
bitOffset += 16;

// Next byte is wormhole finality.
assertEq(
uint8(uint256(slotZeroData >> bitOffset)),
expectedWormholeFinality,
"slot 0x0 not equal to expected before upgrade"
);
bitOffset += 8;

// Next 4 bytes is local domain.
assertEq(
uint32(uint256(slotZeroData >> bitOffset)),
expectedLocalDomain,
"slot 0x0 not equal to expected before upgrade"
);
bitOffset += 32;

// Next 20 bytes is wormhole address.
assertEq(
address(uint160(uint256(slotZeroData >> bitOffset))),
expectedWormholeAddress,
"slot 0x0 not equal to expected before upgrade"
);
bitOffset += 160;

// Next 2 bytes is governance chain ID.
assertEq(
uint16(uint256(slotZeroData >> bitOffset)),
expectedGovernanceChainId,
"slot 0x0 not equal to expected before upgrade"
);
bitOffset += 16;

// Remaining bytes are zero.
assertEq(
uint256(slotZeroData >> bitOffset),
0,
"slot 0x0 not equal to expected before upgrade"
);
}
if (slotZeroData == 0) {
assertTrue(
forkedAddress.slotValueZero(0x1),
"slot 0x1 not equal to expected before upgrade"
);
assertTrue(
forkedAddress.slotValueZero(0x2),
"slot 0x2 not equal to expected before upgrade"
);
assertTrue(
forkedAddress.slotValueZero(0x3),
"slot 0x3 not equal to expected before upgrade"
);
assertTrue(
forkedAddress.slotValueZero(0x4),
"slot 0x4 not equal to expected before upgrade"
);
} else {
assertTrue(
forkedAddress.slotValueEquals(0x1, expectedGovernanceContract),
"slot 0x1 not equal to expected before upgrade"
);
assertTrue(
forkedAddress.slotValueEquals(0x2, expectedCircleBridgeAddress),
"slot 0x2 not equal to expected before upgrade"
);
assertTrue(
forkedAddress.slotValueEquals(0x3, expectedCircleTransmitterAddress),
"slot 0x3 not equal to expected before upgrade"
);
assertTrue(
forkedAddress.slotValueEquals(0x4, expectedCircleTokenMinterAddress),
"slot 0x4 not equal to expected before upgrade"
);
}
assertTrue(
forkedAddress.slotValueEquals(
keccak256(abi.encode(address(implementation), uint256(0x5))), isInitialized
),
"mapped slot 0x5 not equal to expected before upgrade"
);
assertTrue(
forkedAddress.slotValueEquals(
keccak256(abi.encode(expectedRegisteredChainId, uint256(0x6))), expectedEmitter
),
"mapped slot 0x6 not equal to expected before upgrade"
);
assertTrue(
forkedAddress.slotValueEquals(
keccak256(abi.encode(expectedRegisteredChainId, uint256(0x7))),
expectedCctpDomain
),
"mapped slot 0x7 not equal to expected before upgrade"
);
assertTrue(
forkedAddress.slotValueEquals(
keccak256(abi.encode(expectedCctpDomain, uint256(0x8))),
expectedRegisteredChainId
),
"mapped slot 0x8 not equal to expected before upgrade"
);
assertTrue(
forkedAddress.slotValueEquals(
keccak256(abi.encode(vaa.hash, uint256(0x9))), isVaaConsumed
),
"mapped slot 0x9 not equal to expected before upgrade"
);
if (slotZeroData == 0) {
assertTrue(
forkedAddress.slotValueZero(0xa),
"slot 0xa not equal to expected before upgrade"
);
} else {
assertTrue(
forkedAddress.slotValueEquals(0xa, expectedEvmChain),
"slot 0xa not equal to expected before upgrade"
);
}
}

// Upgrade contract.
forked.upgradeContract(encodedVaa);

// Now initialized.
isInitialized = forked.isInitialized(address(implementation));
assertTrue(isInitialized, "implementation not initialized");

// VAA now consumed.
isVaaConsumed = forked.isMessageConsumed(vaa.hash);
assertTrue(isVaaConsumed, "VAA not consumed");

// Now check all slots that were checked before.
{
assertTrue(
forkedAddress.slotValueZero(bytes32(0)),
"slot 0x0 not equal to expected after upgrade"
);
assertTrue(
forkedAddress.slotValueZero(0x1), "slot 0x1 not equal to expected after upgrade"
);
assertTrue(
forkedAddress.slotValueZero(0x2), "slot 0x2 not equal to expected after upgrade"
);
assertTrue(
forkedAddress.slotValueZero(0x3), "slot 0x3 not equal to expected after upgrade"
);
assertTrue(
forkedAddress.slotValueZero(0x4), "slot 0x4 not equal to expected after upgrade"
);
assertTrue(
forkedAddress.slotValueEquals(
keccak256(abi.encode(address(implementation), uint256(0x5))), isInitialized
),
"mapped slot 0x5 not equal to expected after upgrade"
);
assertTrue(
forkedAddress.slotValueEquals(
keccak256(abi.encode(expectedRegisteredChainId, uint256(0x6))), expectedEmitter
),
"mapped slot 0x6 not equal to expected after upgrade"
);
assertTrue(
forkedAddress.slotValueEquals(
keccak256(abi.encode(expectedRegisteredChainId, uint256(0x7))),
expectedCctpDomain
),
"mapped slot 0x7 not equal to expected after upgrade"
);
assertTrue(
forkedAddress.slotValueEquals(
keccak256(abi.encode(expectedCctpDomain, uint256(0x8))),
expectedRegisteredChainId
),
"mapped slot 0x8 not equal to expected after upgrade"
);
assertTrue(
forkedAddress.slotValueEquals(
keccak256(abi.encode(vaa.hash, uint256(0x9))), isVaaConsumed
),
"mapped slot 0x9 not equal to expected after upgrade"
);
assertTrue(
forkedAddress.slotValueZero(0xa), "slot 0xa not equal to expected after upgrade"
);
}

// Make sure getters still retrieve expected values.
assertEq(forked.chainId(), expectedChainId, "chainId not equal to expected after upgrade");
assertEq(
forked.wormholeFinality(),
expectedWormholeFinality,
"wormholeFinality not equal to expected after upgrade"
);
assertEq(
forked.localDomain(),
expectedLocalDomain,
"localDomain not equal to expected after upgrade"
);
assertEq(
address(forked.wormhole()),
expectedWormholeAddress,
"wormholeAddress not equal to expected after upgrade"
);
assertEq(
forked.governanceChainId(),
expectedGovernanceChainId,
"governanceChainId not equal to expected after upgrade"
);
assertEq(
forked.governanceContract(),
expectedGovernanceContract,
"governanceContract not equal to expected after upgrade"
);
assertEq(
address(forked.circleBridge()),
expectedCircleBridgeAddress,
"circleBridgeAddress not equal to expected after upgrade"
);
assertEq(
address(forked.circleTransmitter()),
expectedCircleTransmitterAddress,
"circleTransmitterAddress not equal to expected after upgrade"
);
assertEq(
address(forked.circleTokenMinter()),
expectedCircleTokenMinterAddress,
"circleTokenMinterAddress not equal to expected after upgrade"
);
assertEq(
forked.evmChain(), expectedEvmChain, "evmChain not equal to expected after upgrade"
);
}
}
2 changes: 1 addition & 1 deletion evm/forge/tests/gas/CircleIntegrationComparison.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ contract CircleIntegrationComparison is Test {
bytes32 immutable FOREIGN_USDC_ADDRESS =
bytes32(uint256(uint160(vm.envAddress("TESTING_FOREIGN_USDC_TOKEN_ADDRESS"))));

address immutable FORKED_CIRCLE_INTEGRATION_ADDRESS = 0x2703483B1a5a7c577e8680de9Df8Be03c6f30e3c;
address constant FORKED_CIRCLE_INTEGRATION_ADDRESS = 0x2703483B1a5a7c577e8680de9Df8Be03c6f30e3c;

// dependencies
IWormhole wormhole;
Expand Down
Loading

0 comments on commit 5ca4dbd

Please sign in to comment.