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

feat: invariants tests #107

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
61 changes: 61 additions & 0 deletions test/invariant-tests/AccessControl.Invariant.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.8.9;

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

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

/// handler import
import {AccessControlSenderHandler} from "test/invariant-tests/handlers/AccessControlSender.Handler.sol";

contract AccessControlHandlerInvariant is Setup {
AccessControlSenderHandler public handler;

/// @notice nonce snapshot for assertions
uint256 public localNonceState;
uint256 public localAddState;

function setUp() public override {
/// @dev calls setup to spin up test contracts
super.setUp();

/// @dev selects fork and deploy the handlers
vm.selectFork(fork[SRC_CHAIN_ID]);
handler = new AccessControlSenderHandler(
contractAddress[SRC_CHAIN_ID]["GAC"],
contractAddress[SRC_CHAIN_ID]["MMA_SENDER"]
);

/// @dev bind the handler as target for invariant
targetContract(address(handler));
}

/// @notice invariant-1: only authorized callers can execute calls
/// @notice invariant-2: only the global owner should be able to add an adapter to the allowed lis
function invariant_test_acess_control_src() public {
if (
handler.lastCaller() == MessageSenderGAC(contractAddress[SRC_CHAIN_ID]["GAC"]).authorisedCaller()
&& handler.lastCalledFunction() == 1
) {
++localNonceState;
}

if (
handler.lastCaller() == MessageSenderGAC(contractAddress[SRC_CHAIN_ID]["GAC"]).authorisedCaller()
&& handler.lastCalledFunction() == 2
) {
++localAddState;
}

assertEq(MultiBridgeMessageSender(contractAddress[SRC_CHAIN_ID]["MMA_SENDER"]).nonce(), localNonceState);

if (localAddState > 0) {
assertTrue(
MultiBridgeMessageSender(contractAddress[SRC_CHAIN_ID]["MMA_SENDER"]).senderAdapters(localAddState - 1)
!= address(0)
);
}
}
}
63 changes: 63 additions & 0 deletions test/invariant-tests/AdapterList.Invariant.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.8.9;

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

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

/// handler import
import {AdapterListHandler} from "test/invariant-tests/handlers/AdapterList.Handler.sol";

/// @notice invariants for maintaining adapter list on `MultiBridgeMessageSender`
contract AdapterListInvariant is Setup {
AdapterListHandler public handler;

/// @notice initializes the setup
function setUp() public override {
/// @dev calls setup to spin up test contracts
super.setUp();

/// @dev selects fork and deploy the handlers
vm.selectFork(fork[SRC_CHAIN_ID]);
handler = new AdapterListHandler(
contractAddress[SRC_CHAIN_ID]["GAC"],
contractAddress[SRC_CHAIN_ID]["MMA_SENDER"]
);

/// @dev bind the handler as target for invariant
targetContract(address(handler));
}

/// @notice invariant-1: adding an adapter should always increase the length of the adapter list
/// @notice invariant-2: once a trusted executor is added, its entry should exist in the adapter list
/// @notice invariant-3: the adapter list should never contain duplicates
/// @notice invariant-4: the adapter list should always be in order

/// @notice invariant-5: removing an adapter should always decrease the length of the adapter list
/// @notice invariant-6: once a trusted executor is removed, it should not persist in the adapter list
function invariant_test_adapter_mutations() public {
MultiBridgeMessageSender targetContract = MultiBridgeMessageSender(contractAddress[SRC_CHAIN_ID]["MMA_SENDER"]);

if (handler.success()) {
uint256 currAdds = handler.currAdds();

address newAddition = targetContract.senderAdapters(currAdds - 1);
/// @dev if this asset passes then invariant-2 holds
assertTrue(newAddition != address(0));

/// @dev if this revert invariant-1 and invariant-5 holds
try targetContract.senderAdapters(currAdds) {
assertFalse(1 == 2);
} catch {}

/// @dev assertions for invariant-3, invariant-4, invariant-5, invariant-6
if (currAdds > 1) {
for (uint256 i = currAdds; i > 0; i--) {
assertTrue(targetContract.senderAdapters(i) > targetContract.senderAdapters(i - 1));
}
}
}
}
}
60 changes: 60 additions & 0 deletions test/invariant-tests/BridgeAdapter.Invariant.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
pragma solidity >=0.8.9;

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

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

/// handler import
import {BridgeAdapterHandler} from "test/invariant-tests/handlers/BridgeAdapter.Handler.sol";

/// @notice invariants for bridge adapters receiving messages
contract BridgeAdapterInvariant is Setup {
BridgeAdapterHandler public handler;

/// @notice initializes the setup
function setUp() public override {
/// @dev calls setup to spin up test contracts
super.setUp();

/// @dev selects fork and deploy the handlers
vm.selectFork(fork[BSC_CHAIN_ID]);
handler = new BridgeAdapterHandler(
contractAddress[BSC_CHAIN_ID]["WORMHOLE_RECEIVER_ADAPTER"],
contractAddress[BSC_CHAIN_ID]["AXELAR_RECEIVER_ADAPTER"],
contractAddress[BSC_CHAIN_ID]["MMA_RECEIVER"]
);

/// @dev bind the handler as target for invariant
targetContract(address(handler));
}

function invariant_test_bridge_adapter_receivers() public {
if (handler.success() && handler.lastBridge() == 1) {
assertTrue(
WormholeReceiverAdapter(contractAddress[BSC_CHAIN_ID]["WORMHOLE_RECEIVER_ADAPTER"]).isMessageExecuted(
handler.lastMessageId()
)
);
assertTrue(
WormholeReceiverAdapter(contractAddress[BSC_CHAIN_ID]["WORMHOLE_RECEIVER_ADAPTER"]).deliveryHashStatus(
handler.lastMessageHash()
)
);
}

if (handler.success() && handler.lastBridge() == 2) {
assertTrue(
AxelarReceiverAdapter(contractAddress[BSC_CHAIN_ID]["AXELAR_RECEIVER_ADAPTER"]).isMessageExecuted(
handler.lastMessageId()
)
);
assertTrue(
AxelarReceiverAdapter(contractAddress[BSC_CHAIN_ID]["AXELAR_RECEIVER_ADAPTER"]).commandIdStatus(
handler.lastMessageHash()
)
);
}
}
}
72 changes: 72 additions & 0 deletions test/invariant-tests/handlers/AccessControlSender.Handler.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.8.9;

/// library imports
import "forge-std/Test.sol";

/// local imports
import {MessageSenderGAC} from "src/controllers/MessageSenderGAC.sol";
import {MultiBridgeMessageSender} from "src/MultiBridgeMessageSender.sol";

/// @notice handler for testing access control invariants
contract AccessControlSenderHandler is Test {
/// @notice local state
MultiBridgeMessageSender public multiBridgeMessageSender;
MessageSenderGAC public gac;

/// @notice logs last caller for validations
address public lastCaller;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lastCaller doesn't seem to be updated in this contract when the different functions are called

uint8 public lastCalledFunction;

/// @notice modifier to prank caller
modifier prank(address _prankster) {
vm.startPrank(_prankster);
_;
vm.stopPrank();
}

/// @notice initial setup contracts
constructor(address _gac, address _multiBridge) {
gac = MessageSenderGAC(_gac);
multiBridgeMessageSender = MultiBridgeMessageSender(_multiBridge);
}

/// @notice helper for remote call
function remoteCall(
address simulatedCaller,
uint256 _dstChainId,
address _target,
bytes memory _callData,
uint256 _nativeValue,
uint256 _expiration,
address _refundAddress,
uint256[] memory _fees,
uint256 _successThreshold,
address[] memory _excludedAdapters
) external prank(simulatedCaller) {
lastCalledFunction = 1;
multiBridgeMessageSender.remoteCall(
_dstChainId,
_target,
_callData,
_nativeValue,
_expiration,
_refundAddress,
_fees,
_successThreshold,
_excludedAdapters
);
}

/// @notice for sender adapter addition
function addSenderAdapters(address simulatedCaller, address _newSenderAdapter) external prank(simulatedCaller) {
vm.assume(_newSenderAdapter != address(0));

address[] memory _additions = new address[](1);
_additions[0] = _newSenderAdapter;

try multiBridgeMessageSender.addSenderAdapters(_additions) {
lastCalledFunction = 2;
} catch {}
}
}
61 changes: 61 additions & 0 deletions test/invariant-tests/handlers/AdapterList.Handler.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.8.9;

/// library imports
import "forge-std/Test.sol";

/// local imports
import {MessageSenderGAC} from "src/controllers/MessageSenderGAC.sol";
import {MultiBridgeMessageSender} from "src/MultiBridgeMessageSender.sol";

/// @notice handler for invariant testing maintaining adapter list
contract AdapterListHandler is Test {
/// @notice local state
MultiBridgeMessageSender public multiBridgeMessageSender;
MessageSenderGAC public gac;

bool public success;
uint256 public currAdds;

/// @notice modifier to prank caller
modifier prank(address _prankster) {
vm.startPrank(_prankster);
_;
vm.stopPrank();
}

/// @notice initial setup contracts
constructor(address _gac, address _multiBridge) {
gac = MessageSenderGAC(_gac);
multiBridgeMessageSender = MultiBridgeMessageSender(_multiBridge);
}

/// @notice helper for adding new adapters
function addSenderAdapters(address _newSenderAdapter) external prank(gac.getGlobalOwner()) {
success = false;

vm.assume(_newSenderAdapter != address(0));

address[] memory _additions = new address[](1);
_additions[0] = _newSenderAdapter;

try multiBridgeMessageSender.addSenderAdapters(_additions) {
success = true;
currAdds++;
} catch {}
}

/// @notice helper for removing existing adapters
function removeSenderAdapters() external prank(gac.getGlobalOwner()) {
vm.assume(currAdds > 0);
success = false;

address[] memory _removals = new address[](1);
_removals[0] = multiBridgeMessageSender.senderAdapters(currAdds - 1);

try multiBridgeMessageSender.removeSenderAdapters(_removals) {
success = true;
currAdds--;
} catch {}
}
}
Loading
Loading