Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: improve e2e base #14

Merged
merged 25 commits into from
Nov 27, 2023
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1,9 +1,22 @@
## For e2e tests and deployment scripts
# For anvil to fork from
MAINNET_RPC=
GOERLI_RPC=
OPTIMISM_RPC=
OPTIMISM_GOERLI_RPC=

# Deployer address for the E2E Tests
MAINNET_DEPlOYER_ADDR=
# Safe owner address for the E2E Tests
MAINNET_SAFE_OWNER_ADDR=
# Deployer private key for the E2E Tests
MAINNET_DEPLOYER_PK=
# Safe owner private key for the E2E Tests
MAINNET_SAFE_OWNER_PK=
# Mainnet rpc for the E2E Tests, should be the anvil url
MAINNET_E2E_RPC=
# Optimism rpc for the E2E Tests, should be the anvil url
OPTIMISM_E2E_RPC=

## For deployment scripts
DEPLOYER_MAINNNET_PRIVATE_KEY=
DEPLOYER_GOERLI_PRIVATE_KEY=
Expand Down
13 changes: 13 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,21 @@ jobs:
touch .env
echo MAINNET_RPC="${{ secrets.MAINNET_RPC }}" >> .env
echo GOERLI_RPC="${{ secrets.GOERLI_RPC }}" >> .env
echo OPTIMISM_RPC="${{ secrets.OPTIMISM_RPC }}" >> .env
echo MAINNET_E2E_RPC="${{ secrets.MAINNET_E2E_RPC }}" >> .env
echo OPTIMISM_E2E_RPC="${{ secrets.OPTIMISM_E2E_RPC }}" >> .env
echo MAINNET_SAFE_OWNER_ADDR="${{ secrets.MAINNET_SAFE_OWNER_ADDR }}" >> .env
echo MAINNET_SAFE_OWNER_PK="${{ secrets.MAINNET_SAFE_OWNER_PK }}" >> .env
echo MAINNET_DEPlOYER_ADDR="${{ secrets.MAINNET_DEPlOYER_ADDR }}" >> .env
echo MAINNET_DEPlOYER_PK="${{ secrets.MAINNET_DEPlOYER_PK }}" >> .env
cat .env

- name: Run Anvil Mainnet
run: yarn anvil:mainnet

- name: Run Anvil Optimism
run: yarn anvil:optimism

- name: Run tests
shell: bash
run: yarn test
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ yarn build

### Available Commands

Make sure to set `MAINNET_RPC` environment variable before running end-to-end tests.
Make sure to set `MAINNET_RPC` and `OPTIMISM_RPC` environment variable before running end-to-end tests.

| Yarn Command | Description |
| ----------------------- | ---------------------------------------------------------- |
Expand Down
3 changes: 3 additions & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,6 @@ src = 'solidity/test'
[rpc_endpoints]
mainnet = "${MAINNET_RPC}"
goerli = "${GOERLI_RPC}"
optimism = "${OPTIMISM_RPC}"
mainnet_e2e = "${MAINNET_E2E_RPC}"
optimism_e2e = "${OPTIMISM_E2E_RPC}"
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
},
"author": "Wonderland",
"scripts": {
"anvil:mainnet": "anvil --port 8545 -f $MAINNET_RPC --fork-block-number 18621047 --chain-id 1",
"anvil:optimism": "anvil --port 9545 -f $OPTIMISM_RPC --fork-block-number 112491451 --chain-id 10",
"build": "forge build",
"build:optimized": "FOUNDRY_PROFILE=optimized forge build",
"coverage": "forge coverage --match-contract Unit",
Expand Down
1 change: 0 additions & 1 deletion solidity/contracts/VerifierModule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,6 @@ contract VerifierModule is IVerifierModule {
* @param _blockHeader The block header of the latest block
* @return _storageRoot The verified storage root
* @return _blockNumber The block number from the _blockHeader

*/
function extractStorageMirrorStorageRoot(
bytes memory _storageMirrorAccountProof,
Expand Down
123 changes: 94 additions & 29 deletions solidity/test/e2e/Common.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,30 @@ import {UpdateStorageMirrorGuard} from 'contracts/UpdateStorageMirrorGuard.sol';
import {GuardCallbackModule} from 'contracts/GuardCallbackModule.sol';
import {BlockHeaderOracle} from 'contracts/BlockHeaderOracle.sol';
import {NeedsUpdateGuard} from 'contracts/NeedsUpdateGuard.sol';
import {VerifierModule} from 'contracts/VerifierModule.sol';
import {StorageMirrorRootRegistry} from 'contracts/StorageMirrorRootRegistry.sol';

import {IGuardCallbackModule} from 'interfaces/IGuardCallbackModule.sol';
import {ISafe} from 'interfaces/ISafe.sol';
import {IVerifierModule} from 'interfaces/IVerifierModule.sol';
import {IStorageMirrorRootRegistry} from 'interfaces/IStorageMirrorRootRegistry.sol';
import {IBlockHeaderOracle} from 'interfaces/IBlockHeaderOracle.sol';

import {IGnosisSafeProxyFactory} from 'test/e2e/IGnosisSafeProxyFactory.sol';
import {TestConstants} from 'test/utils/TestConstants.sol';
import {ContractDeploymentAddress} from 'test/utils/ContractDeploymentAddress.sol';

// solhint-disable-next-line max-states-count
contract CommonE2EBase is DSTestPlus, TestConstants {
uint256 internal constant _FORK_BLOCK = 15_452_788;
uint256 internal constant _MAINNET_FORK_BLOCK = 18_621_047;
uint256 internal constant _OPTIMISM_FORK_BLOCK = 112_491_451;

address public deployer = makeAddr('deployer');
uint256 internal _mainnetForkId;
uint256 internal _optimismForkId;

address public deployer;
uint256 public deployerKey;
address public deployerOptimism = makeAddr('deployerOptimism');
address public proposer = makeAddr('proposer');
address public safeOwner;
uint256 public safeOwnerKey;
Expand All @@ -36,35 +47,45 @@ contract CommonE2EBase is DSTestPlus, TestConstants {
GuardCallbackModule public guardCallbackModule;
BlockHeaderOracle public oracle;
NeedsUpdateGuard public needsUpdateGuard;
VerifierModule public verifierModule;
StorageMirrorRootRegistry public storageMirrorRootRegistry;
ISafe public safe;
ISafe public nonHomeChainSafe;
IVerifierModule public verifierModule = IVerifierModule(makeAddr('verifierModule'));
IGnosisSafeProxyFactory public gnosisSafeProxyFactory = IGnosisSafeProxyFactory(GNOSIS_SAFE_PROXY_FACTORY);

function setUp() public virtual {
vm.createSelectFork(vm.rpcUrl('mainnet'), _FORK_BLOCK);
// Set up both forks
_mainnetForkId = vm.createFork(vm.rpcUrl('mainnet_e2e'), _MAINNET_FORK_BLOCK);
_optimismForkId = vm.createFork(vm.rpcUrl('optimism_e2e'), _OPTIMISM_FORK_BLOCK);
// Select mainnet fork
vm.selectFork(_mainnetForkId);

// Make address and key of safe owner
(safeOwner, safeOwnerKey) = makeAddrAndKey('safeOwner');
// Make address and key of non home chain safe owner
(nonHomeChainSafeOwner, nonHomeChainSafeOwnerKey) = makeAddrAndKey('nonHomeChainSafeOwner');
safeOwner = vm.envAddress('MAINNET_SAFE_OWNER_ADDR');
safeOwnerKey = vm.envUint('MAINNET_SAFE_OWNER_PK');

// Make address and key of deployer
deployer = vm.envAddress('MAINNET_DEPlOYER_ADDR');
deployerKey = vm.envUint('MAINNET_DEPLOYER_PK');

/// =============== HOME CHAIN ===============
vm.prank(safeOwner);
vm.broadcast(safeOwnerKey);
safe = ISafe(address(gnosisSafeProxyFactory.createProxy(GNOSIS_SAFE_SINGLETON, ''))); // safeOwner nonce 0
label(address(safe), 'SafeProxy');

address _updateStorageMirrorGuardTheoriticalAddress = ContractDeploymentAddress.addressFrom(deployer, 2);
uint256 _nonce = vm.getNonce(deployer);

address _updateStorageMirrorGuardTheoriticalAddress = ContractDeploymentAddress.addressFrom(deployer, _nonce + 2);

vm.prank(deployer);
vm.broadcast(deployer);
storageMirror = new StorageMirror(); // deployer nonce 0
label(address(storageMirror), 'StorageMirror');

vm.prank(deployer);
vm.broadcast(deployer);
guardCallbackModule = new GuardCallbackModule(address(storageMirror), _updateStorageMirrorGuardTheoriticalAddress); // deployer nonce 1
label(address(guardCallbackModule), 'GuardCallbackModule');

vm.prank(deployer);
vm.broadcast(deployer);
updateStorageMirrorGuard = new UpdateStorageMirrorGuard(guardCallbackModule); // deployer nonce 2
label(address(updateStorageMirrorGuard), 'UpdateStorageMirrorGuard');

Expand All @@ -74,11 +95,11 @@ contract CommonE2EBase is DSTestPlus, TestConstants {
// Set up owner home chain safe
address[] memory _owners = new address[](1);
_owners[0] = safeOwner;
vm.prank(safeOwner); // safeOwner nonce 1
vm.broadcast(safeOwnerKey); // safeOwner nonce 1
safe.setup(_owners, 1, address(safe), bytes(''), address(0), address(0), 0, payable(0));

// Enable guard callback module
enableModule(safe, safeOwner, safeOwnerKey, address(guardCallbackModule));
enableModule(safe, safeOwnerKey, address(guardCallbackModule));

// data to sign and send to set the guard
bytes memory _setGuardData = abi.encodeWithSelector(IGuardCallbackModule.setGuard.selector);
Expand All @@ -91,7 +112,7 @@ contract CommonE2EBase is DSTestPlus, TestConstants {
bytes memory _setGuardSignature = abi.encodePacked(_r, _s, _v);

// execute setup of guard
vm.prank(safeOwner);
vm.broadcast(safeOwnerKey);
safe.execTransaction(
address(guardCallbackModule),
0,
Expand All @@ -106,45 +127,89 @@ contract CommonE2EBase is DSTestPlus, TestConstants {
);

/// =============== NON HOME CHAIN ===============
vm.selectFork(_optimismForkId);
// Make address and key of non home chain safe owner
(nonHomeChainSafeOwner, nonHomeChainSafeOwnerKey) = makeAddrAndKey('nonHomeChainSafeOwner');

address _storageMirrorRootRegistryTheoriticalAddress = ContractDeploymentAddress.addressFrom(deployerOptimism, 2);

// Set up non home chain safe
vm.prank(nonHomeChainSafeOwner);
nonHomeChainSafe = ISafe(address(gnosisSafeProxyFactory.createProxy(GNOSIS_SAFE_SINGLETON, ''))); // nonHomeChainSafeOwner nonce 0
vm.broadcast(nonHomeChainSafeOwnerKey);
nonHomeChainSafe = ISafe(address(gnosisSafeProxyFactory.createProxy(GNOSIS_SAFE_SINGLETON_L2, ''))); // nonHomeChainSafeOwner nonce 0
label(address(nonHomeChainSafe), 'NonHomeChainSafeProxy');

// Deploy non home chain contracts
vm.prank(deployer);
oracle = new BlockHeaderOracle(); // deployer nonce 3
label(address(oracle), 'MockOracle');
oracle = new BlockHeaderOracle(); // deployerOptimism nonce 0
label(address(oracle), 'BlockHeaderOracle');

// vm.prank(deployer);
// verifierModule = new VerifierModule(..); // deployer nonce 4
// label(address(verifierModule), 'VerifierModule');
vm.broadcast(deployerOptimism);
verifierModule =
new VerifierModule(IStorageMirrorRootRegistry(_storageMirrorRootRegistryTheoriticalAddress), address(storageMirror)); // deployerOptimism nonce 1
label(address(verifierModule), 'VerifierModule');

vm.prank(deployer);
needsUpdateGuard = new NeedsUpdateGuard(verifierModule); // deployer nonce 5
vm.broadcast(deployerOptimism);
storageMirrorRootRegistry =
new StorageMirrorRootRegistry(address(storageMirror), IVerifierModule(verifierModule), IBlockHeaderOracle(oracle)); // deployerOptimism nonce 2
label(address(storageMirrorRootRegistry), 'StorageMirrorRootRegistry');

vm.broadcast(deployerOptimism);
needsUpdateGuard = new NeedsUpdateGuard(verifierModule); // deployer nonce 3
label(address(needsUpdateGuard), 'NeedsUpdateGuard');

// set up non home chain safe
address[] memory _nonHomeChainSafeOwners = new address[](1);
_nonHomeChainSafeOwners[0] = nonHomeChainSafeOwner;
vm.prank(nonHomeChainSafeOwner); // nonHomeChainSafeOwner nonce 1

vm.broadcast(nonHomeChainSafeOwnerKey); // nonHomeChainSafeOwner nonce 1
nonHomeChainSafe.setup(
_nonHomeChainSafeOwners, 1, address(nonHomeChainSafe), bytes(''), address(0), address(0), 0, payable(0)
);

// enable verifier module
enableModule(nonHomeChainSafe, nonHomeChainSafeOwnerKey, address(verifierModule));

// data to sign and send to set the guard
_setGuardData = abi.encodeWithSelector(ISafe.setGuard.selector, address(needsUpdateGuard));
_setGuardEncodedTxData = nonHomeChainSafe.encodeTransactionData(
address(nonHomeChainSafe),
0,
_setGuardData,
Enum.Operation.Call,
0,
0,
0,
address(0),
payable(0),
nonHomeChainSafe.nonce()
);

// signature
(_v, _r, _s) = vm.sign(nonHomeChainSafeOwnerKey, keccak256(_setGuardEncodedTxData));
_setGuardSignature = abi.encodePacked(_r, _s, _v);

// set needs update guard
vm.broadcast(nonHomeChainSafeOwnerKey);
nonHomeChainSafe.execTransaction(
address(nonHomeChainSafe),
0,
_setGuardData,
Enum.Operation.Call,
0,
0,
0,
address(0),
payable(0),
_setGuardSignature
);
}

/**
* @notice Enables a module for the given safe
* @param _safe The safe that will enable the module
* @param _safeOwner The address of the owner of the safe
* @param _safeOwnerKey The private key to sign the tx
* @param _module The module address to enable
*/
function enableModule(ISafe _safe, address _safeOwner, uint256 _safeOwnerKey, address _module) public {
function enableModule(ISafe _safe, uint256 _safeOwnerKey, address _module) public {
uint256 _safeNonce = _safe.nonce();
// data to sign to enable module
bytes memory _enableModuleData = abi.encodeWithSelector(ISafe.enableModule.selector, address(_module));
Expand All @@ -157,7 +222,7 @@ contract CommonE2EBase is DSTestPlus, TestConstants {
bytes memory _enableModuleSignature = abi.encodePacked(_r, _s, _v);

// execute enable module
vm.prank(_safeOwner);
vm.broadcast(safeOwnerKey);
_safe.execTransaction(
address(_safe), 0, _enableModuleData, Enum.Operation.Call, 0, 0, 0, address(0), payable(0), _enableModuleSignature
);
Expand Down
Loading