Skip to content

Commit

Permalink
chore: add more integration tests
Browse files Browse the repository at this point in the history
  • Loading branch information
sujithsomraaj committed Sep 7, 2023
1 parent f79b570 commit 62412d3
Show file tree
Hide file tree
Showing 10 changed files with 270 additions and 58 deletions.
6 changes: 3 additions & 3 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ out = "out"
libs = ["lib"]
solc = "0.8.19"

optimizer = true
runs = 100
viaIR = true
optimizer = false
runs = 200
viaIR = false

# See more config options https://github.com/foundry-rs/foundry/tree/master/config
3 changes: 2 additions & 1 deletion test/integration-tests/Setup.t.sol → test/Setup.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ abstract contract Setup is Test {
/// @dev simulated caller
address constant caller = address(10);
address constant owner = address(420);
address constant refundAddress = address(420420);

/// @dev constants for axelar
address constant ETH_GATEWAY = 0x4F4495243837681061C4743b74B3eEdf548D56A5;
Expand Down Expand Up @@ -308,7 +309,7 @@ abstract contract Setup is Test {
GAC(contractAddress[chainId][bytes("GAC")]).setMultiMessageReceiver(
ALL_CHAINS[j], contractAddress[ALL_CHAINS[j]][bytes("MMA_RECEIVER")]
);
GAC(contractAddress[chainId][bytes("GAC")]).setRefundAddress(caller);
GAC(contractAddress[chainId][bytes("GAC")]).setRefundAddress(refundAddress);
}

unchecked {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,16 @@ pragma solidity >=0.8.9;
import {Vm} from "forge-std/Test.sol";

/// local imports
import "../Setup.t.sol";
import "../../contracts-mock/MockUniswapReceiver.sol";
import "test/Setup.t.sol";
import "test/contracts-mock/MockUniswapReceiver.sol";

import {MultiMessageSender} from "src/MultiMessageSender.sol";
import {MultiMessageReceiver} from "src/MultiMessageReceiver.sol";
import {Error} from "src/libraries/Error.sol";
import {GovernanceTimelock} from "src/controllers/GovernanceTimelock.sol";

contract GracePeriodExpiry is Setup {
/// @dev scenario: tries to execute the txId after grace period ends
contract GracePeriodExpiryTest is Setup {
MockUniswapReceiver target;

/// @dev intializes the setup
Expand All @@ -24,7 +25,6 @@ contract GracePeriodExpiry is Setup {
target = new MockUniswapReceiver();
}

/// @dev just sends a message
function test_timelockCheck() public {
vm.selectFork(fork[1]);
vm.startPrank(caller);
Expand Down
177 changes: 177 additions & 0 deletions test/integration-tests/MessageDeadlineCheck.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.8.9;

/// library imports
import {Vm} from "forge-std/Test.sol";

/// local imports
import "test/Setup.t.sol";

import {MultiMessageSender} from "src/MultiMessageSender.sol";
import {MultiMessageReceiver} from "src/MultiMessageReceiver.sol";
import {Error} from "src/libraries/Error.sol";
import {GovernanceTimelock} from "src/controllers/GovernanceTimelock.sol";

/// @dev scenario: demonstrate that a admin-specified deadline is good
contract MessageDeadlineCheck is Setup {
struct TxVars {
uint256 txId;
address finalTarget;
uint256 value;
bytes data;
uint256 eta;
}

TxVars public adapterRemoveTx;
bytes32 public maliciousMsgId;

/// @dev intializes the setup
function setUp() public override {
super.setUp();
}

/// @dev just sends a message
function test_messageDeadlineCheck() public {
vm.selectFork(fork[1]);
vm.startPrank(caller);

/// from chain Ethereum uniswap passes a governance proposal to remove wormhole
/// this would fail, since quorum should be reduced before remove adapter
_removeAdapter();

vm.selectFork(fork[1]);
vm.startPrank(caller);
/// malicious governance actor queues up a malicious tx to re-update wormhole before wormhole
/// passes the quorum update proposal through just one message bridge
_queueMaliciousAdapterAdd();

vm.selectFork(fork[1]);
vm.startPrank(caller);
/// now if uniswap governace queues up the quorum update transaction
_quorumUpdateTx();

/// now the user tries to execute his old msg id which didn't pass quorum
/// NOTE: this will revert here; but with a user specified eta / expiry this might pass
vm.expectRevert(Error.MSG_EXECUTION_PASSED_DEADLINE.selector);
MultiMessageReceiver(contractAddress[137][bytes("MMA_RECEIVER")]).executeMessage(maliciousMsgId);
}

function _removeAdapter() internal {
address[] memory adaptersToRemove = new address[](1);
adaptersToRemove[0] = contractAddress[137]["WORMHOLE_RECEIVER_ADAPTER"];

/// true = add
/// false = remove
bool[] memory operation = new bool[](1);
operation[0] = false;

/// send cross-chain message using MMA infra
vm.recordLogs();
MultiMessageSender(contractAddress[1][bytes("MMA_SENDER")]).remoteCall{value: 2 ether}(
137,
address(contractAddress[137][bytes("MMA_RECEIVER")]),
abi.encodeWithSelector(MultiMessageReceiver.updateReceiverAdapter.selector, adaptersToRemove, operation),
0
);

Vm.Log[] memory logs = vm.getRecordedLogs();
vm.stopPrank();

vm.recordLogs();
/// simulate off-chain actors
_simulatePayloadDelivery(1, 137, logs);
bytes32 msgId = _getMsgId(vm.getRecordedLogs());

vm.selectFork(fork[137]);
vm.recordLogs();
/// execute the message and move it to governance timelock contract
MultiMessageReceiver(contractAddress[137][bytes("MMA_RECEIVER")]).executeMessage(msgId);
(uint256 txId, address finalTarget, uint256 value, bytes memory data, uint256 eta) =
_getExecParams(vm.getRecordedLogs());

adapterRemoveTx = TxVars(txId, finalTarget, value, data, eta);

/// increment the time by 2 day (delay time)
vm.warp(block.timestamp + 2 days);

/// CRITICAL NOTE: reverts due to quorum validation failure.
/// this could mess up the quorum.
/// If quorum is reduced before removing the colluded receiver, then the non-quorum reached messages could be replayed.
vm.expectRevert(Error.EXECUTION_FAILS_ON_DST.selector);
GovernanceTimelock(contractAddress[137][bytes("TIMELOCK")]).executeTransaction(
txId, finalTarget, value, data, eta
);
}

function _queueMaliciousAdapterAdd() internal {
address[] memory adaptersToRemove = new address[](1);
adaptersToRemove[0] = contractAddress[137]["WORMHOLE_RECEIVER_ADAPTER"];

/// true = add
/// false = remove
bool[] memory operation = new bool[](1);
operation[0] = false;

address[] memory excludeAxelar = new address[](1);
excludeAxelar[0] = contractAddress[1]["AXELAR_SENDER_ADAPTER"];

/// send cross-chain message using MMA infra
vm.recordLogs();
MultiMessageSender(contractAddress[1][bytes("MMA_SENDER")]).remoteCall{value: 2 ether}(
137,
address(contractAddress[137][bytes("MMA_RECEIVER")]),
abi.encodeWithSelector(MultiMessageReceiver.updateReceiverAdapter.selector, adaptersToRemove, operation),
0,
excludeAxelar
);

Vm.Log[] memory logs = vm.getRecordedLogs();
vm.stopPrank();

vm.recordLogs();
/// simulate off-chain actors
_simulatePayloadDelivery(1, 137, logs);
maliciousMsgId = _getMsgId(vm.getRecordedLogs());

vm.selectFork(fork[137]);

/// execute the message but will fail due to insufficient quorum

vm.expectRevert(Error.INVALID_QUORUM_FOR_EXECUTION.selector);
MultiMessageReceiver(contractAddress[137][bytes("MMA_RECEIVER")]).executeMessage(maliciousMsgId);
}

function _quorumUpdateTx() internal {
/// send cross-chain message using MMA infra
vm.recordLogs();
MultiMessageSender(contractAddress[1][bytes("MMA_SENDER")]).remoteCall{value: 2 ether}(
137,
address(contractAddress[137][bytes("MMA_RECEIVER")]),
abi.encodeWithSelector(MultiMessageReceiver.updateQuorum.selector, 1),
0
);

Vm.Log[] memory logs = vm.getRecordedLogs();
vm.stopPrank();

vm.recordLogs();
/// simulate off-chain actors
_simulatePayloadDelivery(1, 137, logs);
bytes32 msgId = _getMsgId(vm.getRecordedLogs());

vm.selectFork(fork[137]);
vm.recordLogs();
/// execute the message and move it to governance timelock contract
MultiMessageReceiver(contractAddress[137][bytes("MMA_RECEIVER")]).executeMessage(msgId);
(uint256 txId, address finalTarget, uint256 value, bytes memory data, uint256 eta) =
_getExecParams(vm.getRecordedLogs());

/// increment the time by 2 day (delay time)
vm.warp(block.timestamp + 2 days);

/// when quorum is updated here
GovernanceTimelock(contractAddress[137][bytes("TIMELOCK")]).executeTransaction(
txId, finalTarget, value, data, eta
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ pragma solidity >=0.8.9;
import {Vm} from "forge-std/Test.sol";

/// local imports
import "../Setup.t.sol";
import "../../contracts-mock/MockUniswapReceiver.sol";
import "test/Setup.t.sol";
import "test/contracts-mock/MockUniswapReceiver.sol";

import {MultiMessageSender} from "src/MultiMessageSender.sol";
import {MultiMessageReceiver} from "src/MultiMessageReceiver.sol";
import {Error} from "src/libraries/Error.sol";
import {GovernanceTimelock} from "src/controllers/GovernanceTimelock.sol";

contract MMA is Setup {
contract MultiMessageAggregationTest is Setup {
MockUniswapReceiver target;

/// @dev intializes the setup
Expand All @@ -25,7 +25,7 @@ contract MMA is Setup {
}

/// @dev just sends a message
function test_mma_send_receive() public {
function test_mmaTest() public {
vm.selectFork(fork[1]);
vm.startPrank(caller);

Expand Down
70 changes: 70 additions & 0 deletions test/integration-tests/RemoteAdapterAdd.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.8.9;

/// library imports
import {Vm} from "forge-std/Test.sol";

/// local imports
import "test/Setup.t.sol";

import {MultiMessageSender} from "src/MultiMessageSender.sol";
import {MultiMessageReceiver} from "src/MultiMessageReceiver.sol";
import {Error} from "src/libraries/Error.sol";
import {GovernanceTimelock} from "src/controllers/GovernanceTimelock.sol";

/// @dev scenario: admin updates config on dst chain using message from source chain
contract RemoteAdapterAdd is Setup {
/// @dev intializes the setup
function setUp() public override {
super.setUp();
}

/// @dev just sends a message
function test_remoteAddReceiverAdapter() public {
vm.selectFork(fork[1]);
vm.startPrank(caller);

address[] memory adaptersToAdd = new address[](1);
adaptersToAdd[0] = address(420421422);

/// true = add
/// false = remove
bool[] memory operation = new bool[](1);
operation[0] = true;

/// send cross-chain message using MMA infra
vm.recordLogs();
MultiMessageSender(contractAddress[1][bytes("MMA_SENDER")]).remoteCall{value: 2 ether}(
137,
address(contractAddress[137][bytes("MMA_RECEIVER")]),
abi.encodeWithSelector(MultiMessageReceiver.updateReceiverAdapter.selector, adaptersToAdd, operation),
0
);

Vm.Log[] memory logs = vm.getRecordedLogs();
vm.stopPrank();

vm.recordLogs();
/// simulate off-chain actors
_simulatePayloadDelivery(1, 137, logs);
bytes32 msgId = _getMsgId(vm.getRecordedLogs());

vm.selectFork(fork[137]);
vm.recordLogs();
/// execute the message and move it to governance timelock contract
MultiMessageReceiver(contractAddress[137][bytes("MMA_RECEIVER")]).executeMessage(msgId);
(uint256 txId, address finalTarget, uint256 value, bytes memory data, uint256 eta) =
_getExecParams(vm.getRecordedLogs());

/// increment the time by 2 day (delay time)
vm.warp(block.timestamp + 2 days);
GovernanceTimelock(contractAddress[137][bytes("TIMELOCK")]).executeTransaction(
txId, finalTarget, value, data, eta
);

bool isTrusted = MultiMessageReceiver(contractAddress[137][bytes("MMA_RECEIVER")]).isTrustedExecutor(
contractAddress[137]["AXELAR_RECEIVER_ADAPTER"]
);
assert(isTrusted);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,17 @@ pragma solidity >=0.8.9;
import {Vm} from "forge-std/Test.sol";

/// local imports
import "../Setup.t.sol";
import "../../contracts-mock/MockUniswapReceiver.sol";
import "test/Setup.t.sol";
import "test/contracts-mock/MockUniswapReceiver.sol";

import {MultiMessageSender} from "src/MultiMessageSender.sol";
import {MultiMessageReceiver} from "src/MultiMessageReceiver.sol";
import {Error} from "src/libraries/Error.sol";
import {GovernanceTimelock} from "src/controllers/GovernanceTimelock.sol";

contract TimelockCheck is Setup {
/// @dev scenario 1: tries to execute the txId before timelock ends
/// @dev scenario 2: tries to execute the txId post timelock ends and within expiry
contract TimelockCheckTest is Setup {
MockUniswapReceiver target;

/// @dev intializes the setup
Expand Down

This file was deleted.

Loading

0 comments on commit 62412d3

Please sign in to comment.